network_content.cpp

Go to the documentation of this file.
00001 /* $Id: network_content.cpp 19001 2010-02-04 13:35:20Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #if defined(ENABLE_NETWORK)
00013 
00014 #include "../stdafx.h"
00015 #include "../rev.h"
00016 #include "../ai/ai.hpp"
00017 #include "../window_func.h"
00018 #include "../gui.h"
00019 #include "../variables.h"
00020 #include "../base_media_base.h"
00021 #include "../settings_type.h"
00022 #include "network_content.h"
00023 
00024 #include "table/strings.h"
00025 
00026 #if defined(WITH_ZLIB)
00027 #include <zlib.h>
00028 #endif
00029 
00030 extern bool TarListAddFile(const char *filename);
00031 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
00032 ClientNetworkContentSocketHandler _network_content_client;
00033 
00035 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
00036 {
00037   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? ci->md5sum : NULL) != NULL;
00038 }
00039 
00047 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
00048 
00049 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_INFO)
00050 {
00051   ContentInfo *ci = new ContentInfo();
00052   ci->type     = (ContentType)p->Recv_uint8();
00053   ci->id       = (ContentID)p->Recv_uint32();
00054   ci->filesize = p->Recv_uint32();
00055 
00056   p->Recv_string(ci->name, lengthof(ci->name));
00057   p->Recv_string(ci->version, lengthof(ci->name));
00058   p->Recv_string(ci->url, lengthof(ci->url));
00059   p->Recv_string(ci->description, lengthof(ci->description),  true);
00060 
00061   ci->unique_id = p->Recv_uint32();
00062   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00063     ci->md5sum[j] = p->Recv_uint8();
00064   }
00065 
00066   ci->dependency_count = p->Recv_uint8();
00067   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
00068   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
00069 
00070   ci->tag_count = p->Recv_uint8();
00071   ci->tags = MallocT<char[32]>(ci->tag_count);
00072   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
00073 
00074   if (!ci->IsValid()) {
00075     delete ci;
00076     this->Close();
00077     return false;
00078   }
00079 
00080   /* Find the appropriate check function */
00081   HasProc proc = NULL;
00082   switch (ci->type) {
00083     case CONTENT_TYPE_NEWGRF:
00084       proc = HasGRFConfig;
00085       break;
00086 
00087     case CONTENT_TYPE_BASE_GRAPHICS:
00088       proc = BaseGraphics::HasSet;
00089       break;
00090 
00091     case CONTENT_TYPE_BASE_MUSIC:
00092       proc = BaseMusic::HasSet;
00093       break;
00094 
00095     case CONTENT_TYPE_BASE_SOUNDS:
00096       proc = BaseSounds::HasSet;
00097       break;
00098 
00099     case CONTENT_TYPE_AI:
00100     case CONTENT_TYPE_AI_LIBRARY:
00101       proc = AI::HasAI; break;
00102       break;
00103 
00104     case CONTENT_TYPE_SCENARIO:
00105     case CONTENT_TYPE_HEIGHTMAP:
00106       proc = HasScenario;
00107       break;
00108 
00109     default:
00110       break;
00111   }
00112 
00113   if (proc != NULL) {
00114     if (proc(ci, true)) {
00115       ci->state = ContentInfo::ALREADY_HERE;
00116     } else {
00117       ci->state = ContentInfo::UNSELECTED;
00118       if (proc(ci, false)) ci->upgrade = true;
00119     }
00120   } else {
00121     ci->state = ContentInfo::UNSELECTED;
00122   }
00123 
00124   /* Something we don't have and has filesize 0 does not exist in te system */
00125   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
00126 
00127   /* Do we already have a stub for this? */
00128   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00129     ContentInfo *ici = *iter;
00130     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
00131         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
00132       /* Preserve the name if possible */
00133       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
00134       if (ici->IsSelected()) ci->state = ici->state;
00135 
00136       delete ici;
00137       *iter = ci;
00138 
00139       this->OnReceiveContentInfo(ci);
00140       return true;
00141     }
00142   }
00143 
00144   /* Missing content info? Don't list it */
00145   if (ci->filesize == 0) {
00146     delete ci;
00147     return true;
00148   }
00149 
00150   *this->infos.Append() = ci;
00151 
00152   /* Incoming data means that we might need to reconsider dependencies */
00153   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00154     this->CheckDependencyState(*iter);
00155   }
00156 
00157   this->OnReceiveContentInfo(ci);
00158 
00159   return true;
00160 }
00161 
00162 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
00163 {
00164   if (type == CONTENT_TYPE_END) {
00165     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
00166     this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
00167     this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
00168     this->RequestContentList(CONTENT_TYPE_SCENARIO);
00169     this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
00170     this->RequestContentList(CONTENT_TYPE_AI);
00171     this->RequestContentList(CONTENT_TYPE_NEWGRF);
00172     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
00173     return;
00174   }
00175 
00176   this->Connect();
00177 
00178   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
00179   p->Send_uint8 ((byte)type);
00180   p->Send_uint32(_openttd_newgrf_version);
00181 
00182   this->Send_Packet(p);
00183 }
00184 
00185 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
00186 {
00187   this->Connect();
00188 
00189   while (count > 0) {
00190     /* We can "only" send a limited number of IDs in a single packet.
00191      * A packet begins with the packet size and a byte for the type.
00192      * Then this packet adds a byte for the content type and a uint16
00193      * for the count in this packet. The rest of the packet can be
00194      * used for the IDs. */
00195     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00196 
00197     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
00198     p->Send_uint16(p_count);
00199 
00200     for (uint i = 0; i < p_count; i++) {
00201       p->Send_uint32(content_ids[i]);
00202     }
00203 
00204     this->Send_Packet(p);
00205     count -= p_count;
00206     content_ids += p_count;
00207   }
00208 }
00209 
00210 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
00211 {
00212   if (cv == NULL) return;
00213 
00214   this->Connect();
00215 
00216   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
00217   assert(cv->Length() < 255);
00218   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
00219 
00220   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
00221   p->Send_uint8(cv->Length());
00222 
00223   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00224     const ContentInfo *ci = *iter;
00225     p->Send_uint8((byte)ci->type);
00226     p->Send_uint32(ci->unique_id);
00227     if (!send_md5sum) continue;
00228 
00229     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00230       p->Send_uint8(ci->md5sum[j]);
00231     }
00232   }
00233 
00234   this->Send_Packet(p);
00235 
00236   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00237     ContentInfo *ci = *iter;
00238     bool found = false;
00239     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
00240       ContentInfo *ci2 = *iter2;
00241       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
00242           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
00243         found = true;
00244         break;
00245       }
00246     }
00247     if (!found) {
00248       *this->infos.Append() = ci;
00249     } else {
00250       delete ci;
00251     }
00252   }
00253 }
00254 
00255 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
00256 {
00257   bytes = 0;
00258 
00259   ContentIDList content;
00260   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00261     const ContentInfo *ci = *iter;
00262     if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
00263 
00264     *content.Append() = ci->id;
00265     bytes += ci->filesize;
00266   }
00267 
00268   files = content.Length();
00269 
00270   /* If there's nothing to download, do nothing. */
00271   if (files == 0) return;
00272 
00273   if (_settings_client.network.no_http_content_downloads || fallback) {
00274     this->DownloadSelectedContentFallback(content);
00275   } else {
00276     this->DownloadSelectedContentHTTP(content);
00277   }
00278 }
00279 
00280 void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const ContentIDList &content)
00281 {
00282   uint count = content.Length();
00283 
00284   /* Allocate memory for the whole request.
00285    * Requests are "id\nid\n..." (as strings), so assume the maximum ID,
00286    * which is uint32 so 10 characters long. Then the newlines and
00287    * multiply that all with the count and then add the '\0'. */
00288   uint bytes = (10 + 1) * count + 1;
00289   char *content_request = MallocT<char>(bytes);
00290   const char *lastof = content_request + bytes - 1;
00291 
00292   char *p = content_request;
00293   for (const ContentID *id = content.Begin(); id != content.End(); id++) {
00294     p += seprintf(p, lastof, "%d\n", *id);
00295   }
00296 
00297   this->http_response_index = -1;
00298 
00299   NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
00300   new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
00301   /* NetworkHTTPContentConnecter takes over freeing of content_request! */
00302 }
00303 
00304 void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const ContentIDList &content)
00305 {
00306   uint count = content.Length();
00307   const ContentID *content_ids = content.Begin();
00308   this->Connect();
00309 
00310   while (count > 0) {
00311     /* We can "only" send a limited number of IDs in a single packet.
00312      * A packet begins with the packet size and a byte for the type.
00313      * Then this packet adds a uint16 for the count in this packet.
00314      * The rest of the packet can be used for the IDs. */
00315     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00316 
00317     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
00318     p->Send_uint16(p_count);
00319 
00320     for (uint i = 0; i < p_count; i++) {
00321       p->Send_uint32(content_ids[i]);
00322     }
00323 
00324     this->Send_Packet(p);
00325     count -= p_count;
00326     content_ids += p_count;
00327   }
00328 }
00329 
00337 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
00338 {
00339   Subdirectory dir;
00340   switch (ci->type) {
00341     default: return NULL;
00342     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
00343     case CONTENT_TYPE_BASE_MUSIC:    dir = GM_DIR;         break;
00344     case CONTENT_TYPE_BASE_SOUNDS:   dir = DATA_DIR;       break;
00345     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
00346     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
00347     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
00348     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
00349     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
00350   }
00351 
00352   static char buf[MAX_PATH];
00353   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
00354   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
00355 
00356   return buf;
00357 }
00358 
00364 static bool GunzipFile(const ContentInfo *ci)
00365 {
00366 #if defined(WITH_ZLIB)
00367   bool ret = true;
00368   FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
00369   gzFile fin = gzdopen(fileno(ftmp), "rb");
00370   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
00371 
00372   if (fin == NULL || fout == NULL) {
00373     ret = false;
00374     goto exit;
00375   }
00376 
00377   byte buff[8192];
00378   while (!gzeof(fin)) {
00379     int read = gzread(fin, buff, sizeof(buff));
00380     if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
00381       ret = false;
00382       break;
00383     }
00384   }
00385 
00386 exit:
00387   if (fin != NULL) {
00388     /* Closes ftmp too! */
00389     gzclose(fin);
00390   } else if (ftmp != NULL) {
00391     /* In case the gz stream was opened correctly this will
00392      * be closed by gzclose. */
00393     fclose(ftmp);
00394   }
00395   if (fout != NULL) fclose(fout);
00396 
00397   return ret;
00398 #else
00399   NOT_REACHED();
00400 #endif /* defined(WITH_ZLIB) */
00401 }
00402 
00403 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_CONTENT)
00404 {
00405   if (this->curFile == NULL) {
00406     delete this->curInfo;
00407     /* When we haven't opened a file this must be our first packet with metadata. */
00408     this->curInfo = new ContentInfo;
00409     this->curInfo->type     = (ContentType)p->Recv_uint8();
00410     this->curInfo->id       = (ContentID)p->Recv_uint32();
00411     this->curInfo->filesize = p->Recv_uint32();
00412     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
00413 
00414     if (!this->BeforeDownload()) {
00415       this->Close();
00416       return false;
00417     }
00418   } else {
00419     /* We have a file opened, thus are downloading internal content */
00420     size_t toRead = (size_t)(p->size - p->pos);
00421     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
00422       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00423       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00424       this->Close();
00425       fclose(this->curFile);
00426       this->curFile = NULL;
00427 
00428       return false;
00429     }
00430 
00431     this->OnDownloadProgress(this->curInfo, (uint)toRead);
00432 
00433     if (toRead == 0) this->AfterDownload();
00434   }
00435 
00436   return true;
00437 }
00438 
00443 bool ClientNetworkContentSocketHandler::BeforeDownload()
00444 {
00445   if (!this->curInfo->IsValid()) {
00446     delete this->curInfo;
00447     this->curInfo = NULL;
00448     return false;
00449   }
00450 
00451   if (this->curInfo->filesize != 0) {
00452     /* The filesize is > 0, so we are going to download it */
00453     const char *filename = GetFullFilename(this->curInfo, true);
00454     if (filename == NULL) {
00455       /* Unless that fails ofcourse... */
00456       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00457       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00458       return false;
00459     }
00460 
00461     this->curFile = fopen(filename, "wb");
00462   }
00463   return true;
00464 }
00465 
00470 void ClientNetworkContentSocketHandler::AfterDownload()
00471 {
00472   /* We read nothing; that's our marker for end-of-stream.
00473    * Now gunzip the tar and make it known. */
00474   fclose(this->curFile);
00475   this->curFile = NULL;
00476 
00477   if (GunzipFile(this->curInfo)) {
00478     unlink(GetFullFilename(this->curInfo, true));
00479 
00480     TarListAddFile(GetFullFilename(this->curInfo, false));
00481 
00482     this->OnDownloadComplete(this->curInfo->id);
00483   } else {
00484     ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, 0, 0);
00485   }
00486 }
00487 
00488 /* Also called to just clean up the mess. */
00489 void ClientNetworkContentSocketHandler::OnFailure()
00490 {
00491   /* If we fail, download the rest via the 'old' system. */
00492   uint files, bytes;
00493   this->DownloadSelectedContent(files, bytes, true);
00494 
00495   this->http_response.Reset();
00496   this->http_response_index = -2;
00497 
00498   if (this->curFile != NULL) {
00499     fclose(this->curFile);
00500     this->curFile = NULL;
00501   }
00502 }
00503 
00504 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
00505 {
00506   assert(data == NULL || length != 0);
00507 
00508   /* Ignore any latent data coming from a connection we closed. */
00509   if (this->http_response_index == -2) return;
00510 
00511   if (this->http_response_index == -1) {
00512     if (data != NULL) {
00513       /* Append the rest of the response. */
00514       memcpy(this->http_response.Append((uint)length), data, length);
00515       return;
00516     } else {
00517       /* Make sure the response is properly terminated. */
00518       *this->http_response.Append() = '\0';
00519 
00520       /* And prepare for receiving the rest of the data. */
00521       this->http_response_index = 0;
00522     }
00523   }
00524 
00525   if (data != NULL) {
00526     /* We have data, so write it to the file. */
00527     if (fwrite(data, 1, length, this->curFile) != length) {
00528       /* Writing failed somehow, let try via the old method. */
00529       this->OnFailure();
00530     } else {
00531       /* Just received the data. */
00532       this->OnDownloadProgress(this->curInfo, (uint)length);
00533     }
00534     /* Nothing more to do now. */
00535     return;
00536   }
00537 
00538   if (this->curFile != NULL) {
00539     /* We've finished downloading a file. */
00540     this->AfterDownload();
00541   }
00542 
00543   if ((uint)this->http_response_index >= this->http_response.Length()) {
00544     /* It's not a real failure, but if there's
00545      * nothing more to download it helps with
00546      * cleaning up the stuff we allocated. */
00547     this->OnFailure();
00548     return;
00549   }
00550 
00551   delete this->curInfo;
00552   /* When we haven't opened a file this must be our first packet with metadata. */
00553   this->curInfo = new ContentInfo;
00554 
00556 #define check_not_null(p) { if ((p) == NULL) { this->OnFailure(); return; } }
00557 
00558 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
00559 
00560   for (;;) {
00561     char *str = this->http_response.Begin() + this->http_response_index;
00562     char *p = strchr(str, '\n');
00563     check_and_terminate(p);
00564 
00565     /* Update the index for the next one */
00566     this->http_response_index += (int)strlen(str) + 1;
00567 
00568     /* Read the ID */
00569     p = strchr(str, ',');
00570     check_and_terminate(p);
00571     this->curInfo->id = (ContentID)atoi(str);
00572 
00573     /* Read the type */
00574     str = p + 1;
00575     p = strchr(str, ',');
00576     check_and_terminate(p);
00577     this->curInfo->type = (ContentType)atoi(str);
00578 
00579     /* Read the file size */
00580     str = p + 1;
00581     p = strchr(str, ',');
00582     check_and_terminate(p);
00583     this->curInfo->filesize = atoi(str);
00584 
00585     /* Read the URL */
00586     str = p + 1;
00587     /* Is it a fallback URL? If so, just continue with the next one. */
00588     if (strncmp(str, "ottd", 4) == 0) {
00589       if ((uint)this->http_response_index >= this->http_response.Length()) {
00590         /* Have we gone through all lines? */
00591         this->OnFailure();
00592         return;
00593       }
00594       continue;
00595     }
00596 
00597     p = strrchr(str, '/');
00598     check_not_null(p);
00599 
00600     char tmp[MAX_PATH];
00601     if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
00602       this->OnFailure();
00603       return;
00604     }
00605     /* Remove the extension from the string. */
00606     for (uint i = 0; i < 2; i++) {
00607       p = strrchr(tmp, '.');
00608       check_and_terminate(p);
00609     }
00610 
00611     /* Copy the string, without extension, to the filename. */
00612     strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
00613 
00614     /* Request the next file. */
00615     if (!this->BeforeDownload()) {
00616       this->OnFailure();
00617       return;
00618     }
00619 
00620     NetworkHTTPSocketHandler::Connect(str, this);
00621     return;
00622   }
00623 
00624 #undef check
00625 #undef check_and_terminate
00626 }
00627 
00633 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
00634   NetworkContentSocketHandler(),
00635   http_response_index(-2),
00636   curFile(NULL),
00637   curInfo(NULL),
00638   isConnecting(false)
00639 {
00640 }
00641 
00643 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
00644 {
00645   delete this->curInfo;
00646   if (this->curFile != NULL) fclose(this->curFile);
00647 
00648   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00649 }
00650 
00651 class NetworkContentConnecter : TCPConnecter {
00652 public:
00653   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00654 
00655   virtual void OnFailure()
00656   {
00657     _network_content_client.isConnecting = false;
00658     _network_content_client.OnConnect(false);
00659   }
00660 
00661   virtual void OnConnect(SOCKET s)
00662   {
00663     assert(_network_content_client.sock == INVALID_SOCKET);
00664     _network_content_client.isConnecting = false;
00665     _network_content_client.sock = s;
00666     _network_content_client.Reopen();
00667     _network_content_client.OnConnect(true);
00668   }
00669 };
00670 
00674 void ClientNetworkContentSocketHandler::Connect()
00675 {
00676   this->lastActivity = _realtime_tick;
00677 
00678   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
00679   this->isConnecting = true;
00680   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
00681 }
00682 
00686 void ClientNetworkContentSocketHandler::Close()
00687 {
00688   if (this->sock == INVALID_SOCKET) return;
00689   NetworkContentSocketHandler::Close();
00690 
00691   this->OnDisconnect();
00692 }
00693 
00698 void ClientNetworkContentSocketHandler::SendReceive()
00699 {
00700   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
00701 
00702   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
00703     this->Close();
00704     return;
00705   }
00706 
00707   fd_set read_fd, write_fd;
00708   struct timeval tv;
00709 
00710   FD_ZERO(&read_fd);
00711   FD_ZERO(&write_fd);
00712 
00713   FD_SET(this->sock, &read_fd);
00714   FD_SET(this->sock, &write_fd);
00715 
00716   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
00717 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
00718   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
00719 #else
00720   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
00721 #endif
00722   if (FD_ISSET(this->sock, &read_fd)) {
00723     this->Recv_Packets();
00724     this->lastActivity = _realtime_tick;
00725   }
00726 
00727   this->writable = !!FD_ISSET(this->sock, &write_fd);
00728   this->Send_Packets();
00729 }
00730 
00735 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
00736 {
00737   /* When we tried to download it already, don't try again */
00738   if (this->requested.Contains(cid)) return;
00739 
00740   *this->requested.Append() = cid;
00741   assert(this->requested.Contains(cid));
00742   this->RequestContentList(1, &cid);
00743 }
00744 
00750 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
00751 {
00752   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00753     ContentInfo *ci = *iter;
00754     if (ci->id == cid) return ci;
00755   }
00756   return NULL;
00757 }
00758 
00759 
00764 void ClientNetworkContentSocketHandler::Select(ContentID cid)
00765 {
00766   ContentInfo *ci = this->GetContent(cid);
00767   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
00768 
00769   ci->state = ContentInfo::SELECTED;
00770   this->CheckDependencyState(ci);
00771 }
00772 
00777 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
00778 {
00779   ContentInfo *ci = this->GetContent(cid);
00780   if (ci == NULL || !ci->IsSelected()) return;
00781 
00782   ci->state = ContentInfo::UNSELECTED;
00783   this->CheckDependencyState(ci);
00784 }
00785 
00787 void ClientNetworkContentSocketHandler::SelectAll()
00788 {
00789   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00790     ContentInfo *ci = *iter;
00791     if (ci->state == ContentInfo::UNSELECTED) {
00792       ci->state = ContentInfo::SELECTED;
00793       this->CheckDependencyState(ci);
00794     }
00795   }
00796 }
00797 
00799 void ClientNetworkContentSocketHandler::SelectUpgrade()
00800 {
00801   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00802     ContentInfo *ci = *iter;
00803     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
00804       ci->state = ContentInfo::SELECTED;
00805       this->CheckDependencyState(ci);
00806     }
00807   }
00808 }
00809 
00811 void ClientNetworkContentSocketHandler::UnselectAll()
00812 {
00813   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00814     ContentInfo *ci = *iter;
00815     if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
00816   }
00817 }
00818 
00820 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
00821 {
00822   switch (ci->state) {
00823     case ContentInfo::SELECTED:
00824     case ContentInfo::AUTOSELECTED:
00825       this->Unselect(ci->id);
00826       break;
00827 
00828     case ContentInfo::UNSELECTED:
00829       this->Select(ci->id);
00830       break;
00831 
00832     default:
00833       break;
00834   }
00835 }
00836 
00842 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
00843 {
00844   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00845     const ContentInfo *ci = *iter;
00846     if (ci == child) continue;
00847 
00848     for (uint i = 0; i < ci->dependency_count; i++) {
00849       if (ci->dependencies[i] == child->id) {
00850         *parents.Append() = ci;
00851         break;
00852       }
00853     }
00854   }
00855 }
00856 
00862 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
00863 {
00864   *tree.Append() = child;
00865 
00866   /* First find all direct parents */
00867   for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
00868     ConstContentVector parents;
00869     this->ReverseLookupDependency(parents, *iter);
00870 
00871     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
00872       tree.Include(*piter);
00873     }
00874   }
00875 }
00876 
00881 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
00882 {
00883   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
00884     /* Selection is easy; just walk all children and set the
00885      * autoselected state. That way we can see what we automatically
00886      * selected and thus can unselect when a dependency is removed. */
00887     for (uint i = 0; i < ci->dependency_count; i++) {
00888       ContentInfo *c = this->GetContent(ci->dependencies[i]);
00889       if (c == NULL) {
00890         this->DownloadContentInfo(ci->dependencies[i]);
00891       } else if (c->state == ContentInfo::UNSELECTED) {
00892         c->state = ContentInfo::AUTOSELECTED;
00893         this->CheckDependencyState(c);
00894       }
00895     }
00896     return;
00897   }
00898 
00899   if (ci->state != ContentInfo::UNSELECTED) return;
00900 
00901   /* For unselection we need to find the parents of us. We need to
00902    * unselect them. After that we unselect all children that we
00903    * depend on and are not used as dependency for us, but only when
00904    * we automatically selected them. */
00905   ConstContentVector parents;
00906   this->ReverseLookupDependency(parents, ci);
00907   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00908     const ContentInfo *c = *iter;
00909     if (!c->IsSelected()) continue;
00910 
00911     this->Unselect(c->id);
00912   }
00913 
00914   for (uint i = 0; i < ci->dependency_count; i++) {
00915     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
00916     if (c == NULL) {
00917       DownloadContentInfo(ci->dependencies[i]);
00918       continue;
00919     }
00920     if (c->state != ContentInfo::AUTOSELECTED) continue;
00921 
00922     /* Only unselect when WE are the only parent. */
00923     parents.Clear();
00924     this->ReverseLookupDependency(parents, c);
00925 
00926     /* First check whether anything depends on us */
00927     int sel_count = 0;
00928     bool force_selection = false;
00929     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00930       if ((*iter)->IsSelected()) sel_count++;
00931       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
00932     }
00933     if (sel_count == 0) {
00934       /* Nothing depends on us */
00935       this->Unselect(c->id);
00936       continue;
00937     }
00938     /* Something manually selected depends directly on us */
00939     if (force_selection) continue;
00940 
00941     /* "Flood" search to find all items in the dependency graph*/
00942     parents.Clear();
00943     this->ReverseLookupTreeDependency(parents, c);
00944 
00945     /* Is there anything that is "force" selected?, if so... we're done. */
00946     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00947       if ((*iter)->state != ContentInfo::SELECTED) continue;
00948 
00949       force_selection = true;
00950       break;
00951     }
00952 
00953     /* So something depended directly on us */
00954     if (force_selection) continue;
00955 
00956     /* Nothing depends on us, mark the whole graph as unselected.
00957      * After that's done run over them once again to test their children
00958      * to unselect. Don't do it immediatelly because it'll do exactly what
00959      * we're doing now. */
00960     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00961       const ContentInfo *c = *iter;
00962       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
00963     }
00964     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00965       this->CheckDependencyState(this->GetContent((*iter)->id));
00966     }
00967   }
00968 }
00969 
00970 void ClientNetworkContentSocketHandler::Clear()
00971 {
00972   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00973 
00974   this->infos.Clear();
00975   this->requested.Clear();
00976 }
00977 
00978 /*** CALLBACK ***/
00979 
00980 void ClientNetworkContentSocketHandler::OnConnect(bool success)
00981 {
00982   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00983     ContentCallback *cb = *iter;
00984     cb->OnConnect(success);
00985     if (iter != this->callbacks.End() && *iter == cb) iter++;
00986   }
00987 }
00988 
00989 void ClientNetworkContentSocketHandler::OnDisconnect()
00990 {
00991   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00992     ContentCallback *cb = *iter;
00993     cb->OnDisconnect();
00994     if (iter != this->callbacks.End() && *iter == cb) iter++;
00995   }
00996 }
00997 
00998 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
00999 {
01000   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01001     ContentCallback *cb = *iter;
01002     cb->OnReceiveContentInfo(ci);
01003     if (iter != this->callbacks.End() && *iter == cb) iter++;
01004   }
01005 }
01006 
01007 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, uint bytes)
01008 {
01009   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01010     ContentCallback *cb = *iter;
01011     cb->OnDownloadProgress(ci, bytes);
01012     if (iter != this->callbacks.End() && *iter == cb) iter++;
01013   }
01014 }
01015 
01016 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
01017 {
01018   ContentInfo *ci = this->GetContent(cid);
01019   if (ci != NULL) {
01020     ci->state = ContentInfo::ALREADY_HERE;
01021   }
01022 
01023   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01024     ContentCallback *cb = *iter;
01025     cb->OnDownloadComplete(cid);
01026     if (iter != this->callbacks.End() && *iter == cb) iter++;
01027   }
01028 }
01029 
01030 #endif /* ENABLE_NETWORK */

Generated on Thu Feb 4 17:20:25 2010 for OpenTTD by  doxygen 1.5.6