network_content.cpp

Go to the documentation of this file.
00001 /* $Id: network_content.cpp 17928 2009-10-31 19:46:51Z alberth $ */
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 "network_content.h"
00022 
00023 #include "table/strings.h"
00024 
00025 #if defined(WITH_ZLIB)
00026 #include <zlib.h>
00027 #endif
00028 
00029 extern bool TarListAddFile(const char *filename);
00030 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
00031 ClientNetworkContentSocketHandler _network_content_client;
00032 
00034 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
00035 {
00036   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? ci->md5sum : NULL) != NULL;
00037 }
00038 
00046 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
00047 
00048 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_INFO)
00049 {
00050   ContentInfo *ci = new ContentInfo();
00051   ci->type     = (ContentType)p->Recv_uint8();
00052   ci->id       = (ContentID)p->Recv_uint32();
00053   ci->filesize = p->Recv_uint32();
00054 
00055   p->Recv_string(ci->name, lengthof(ci->name));
00056   p->Recv_string(ci->version, lengthof(ci->name));
00057   p->Recv_string(ci->url, lengthof(ci->url));
00058   p->Recv_string(ci->description, lengthof(ci->description),  true);
00059 
00060   ci->unique_id = p->Recv_uint32();
00061   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00062     ci->md5sum[j] = p->Recv_uint8();
00063   }
00064 
00065   ci->dependency_count = p->Recv_uint8();
00066   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
00067   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
00068 
00069   ci->tag_count = p->Recv_uint8();
00070   ci->tags = MallocT<char[32]>(ci->tag_count);
00071   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
00072 
00073   if (!ci->IsValid()) {
00074     delete ci;
00075     this->Close();
00076     return false;
00077   }
00078 
00079   /* Find the appropriate check function */
00080   HasProc proc = NULL;
00081   switch (ci->type) {
00082     case CONTENT_TYPE_NEWGRF:
00083       proc = HasGRFConfig;
00084       break;
00085 
00086     case CONTENT_TYPE_BASE_GRAPHICS:
00087       proc = BaseGraphics::HasSet;
00088       break;
00089 
00090     case CONTENT_TYPE_BASE_SOUNDS:
00091       proc = BaseSounds::HasSet;
00092       break;
00093 
00094     case CONTENT_TYPE_AI:
00095     case CONTENT_TYPE_AI_LIBRARY:
00096       proc = AI::HasAI; break;
00097       break;
00098 
00099     case CONTENT_TYPE_SCENARIO:
00100     case CONTENT_TYPE_HEIGHTMAP:
00101       proc = HasScenario;
00102       break;
00103 
00104     default:
00105       break;
00106   }
00107 
00108   if (proc != NULL) {
00109     if (proc(ci, true)) {
00110       ci->state = ContentInfo::ALREADY_HERE;
00111     } else {
00112       ci->state = ContentInfo::UNSELECTED;
00113       if (proc(ci, false)) ci->upgrade = true;
00114     }
00115   } else {
00116     ci->state = ContentInfo::UNSELECTED;
00117   }
00118 
00119   /* Something we don't have and has filesize 0 does not exist in te system */
00120   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
00121 
00122   /* Do we already have a stub for this? */
00123   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00124     ContentInfo *ici = *iter;
00125     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
00126         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
00127       /* Preserve the name if possible */
00128       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
00129       if (ici->IsSelected()) ci->state = ici->state;
00130 
00131       delete ici;
00132       *iter = ci;
00133 
00134       this->OnReceiveContentInfo(ci);
00135       return true;
00136     }
00137   }
00138 
00139   /* Missing content info? Don't list it */
00140   if (ci->filesize == 0) {
00141     delete ci;
00142     return true;
00143   }
00144 
00145   *this->infos.Append() = ci;
00146 
00147   /* Incoming data means that we might need to reconsider dependencies */
00148   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00149     this->CheckDependencyState(*iter);
00150   }
00151 
00152   this->OnReceiveContentInfo(ci);
00153 
00154   return true;
00155 }
00156 
00157 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
00158 {
00159   if (type == CONTENT_TYPE_END) {
00160     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
00161     this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
00162     this->RequestContentList(CONTENT_TYPE_SCENARIO);
00163     this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
00164     this->RequestContentList(CONTENT_TYPE_AI);
00165     this->RequestContentList(CONTENT_TYPE_NEWGRF);
00166     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
00167     return;
00168   }
00169 
00170   this->Connect();
00171 
00172   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
00173   p->Send_uint8 ((byte)type);
00174   p->Send_uint32(_openttd_newgrf_version);
00175 
00176   this->Send_Packet(p);
00177 }
00178 
00179 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
00180 {
00181   this->Connect();
00182 
00183   while (count > 0) {
00184     /* We can "only" send a limited number of IDs in a single packet.
00185      * A packet begins with the packet size and a byte for the type.
00186      * Then this packet adds a byte for the content type and a uint16
00187      * for the count in this packet. The rest of the packet can be
00188      * used for the IDs. */
00189     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00190 
00191     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
00192     p->Send_uint16(p_count);
00193 
00194     for (uint i = 0; i < p_count; i++) {
00195       p->Send_uint32(content_ids[i]);
00196     }
00197 
00198     this->Send_Packet(p);
00199     count -= p_count;
00200     content_ids += p_count;
00201   }
00202 }
00203 
00204 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
00205 {
00206   if (cv == NULL) return;
00207 
00208   this->Connect();
00209 
00210   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
00211   assert(cv->Length() < 255);
00212   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
00213 
00214   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
00215   p->Send_uint8(cv->Length());
00216 
00217   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00218     const ContentInfo *ci = *iter;
00219     p->Send_uint8((byte)ci->type);
00220     p->Send_uint32(ci->unique_id);
00221     if (!send_md5sum) continue;
00222 
00223     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00224       p->Send_uint8(ci->md5sum[j]);
00225     }
00226   }
00227 
00228   this->Send_Packet(p);
00229 
00230   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00231     ContentInfo *ci = *iter;
00232     bool found = false;
00233     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
00234       ContentInfo *ci2 = *iter2;
00235       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
00236           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
00237         found = true;
00238         break;
00239       }
00240     }
00241     if (!found) {
00242       *this->infos.Append() = ci;
00243     } else {
00244       delete ci;
00245     }
00246   }
00247 }
00248 
00249 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes)
00250 {
00251   files = 0;
00252   bytes = 0;
00253 
00255   ContentID *ids = MallocT<ContentID>(infos.Length());
00256   for (ContentIterator iter = infos.Begin(); iter != infos.End(); iter++) {
00257     const ContentInfo *ci = *iter;
00258     if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
00259 
00260     ids[files++] = ci->id;
00261     bytes += ci->filesize;
00262   }
00263 
00264   uint count = files;
00265   ContentID *content_ids = ids;
00266   this->Connect();
00267 
00268   while (count > 0) {
00269     /* We can "only" send a limited number of IDs in a single packet.
00270      * A packet begins with the packet size and a byte for the type.
00271      * Then this packet adds a uint16 for the count in this packet.
00272      * The rest of the packet can be used for the IDs. */
00273     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00274 
00275     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
00276     p->Send_uint16(p_count);
00277 
00278     for (uint i = 0; i < p_count; i++) {
00279       p->Send_uint32(content_ids[i]);
00280     }
00281 
00282     this->Send_Packet(p);
00283     count -= p_count;
00284     content_ids += p_count;
00285   }
00286 
00287   free(ids);
00288 }
00289 
00297 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
00298 {
00299   Subdirectory dir;
00300   switch (ci->type) {
00301     default: return NULL;
00302     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
00303     case CONTENT_TYPE_BASE_SOUNDS:   dir = DATA_DIR;       break;
00304     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
00305     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
00306     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
00307     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
00308     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
00309   }
00310 
00311   static char buf[MAX_PATH];
00312   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
00313   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
00314 
00315   return buf;
00316 }
00317 
00323 static bool GunzipFile(const ContentInfo *ci)
00324 {
00325 #if defined(WITH_ZLIB)
00326   bool ret = true;
00327   FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
00328   gzFile fin = gzdopen(fileno(ftmp), "rb");
00329   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
00330 
00331   if (fin == NULL || fout == NULL) {
00332     ret = false;
00333     goto exit;
00334   }
00335 
00336   byte buff[8192];
00337   while (!gzeof(fin)) {
00338     int read = gzread(fin, buff, sizeof(buff));
00339     if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
00340       ret = false;
00341       break;
00342     }
00343   }
00344 
00345 exit:
00346   if (fin != NULL) {
00347     /* Closes ftmp too! */
00348     gzclose(fin);
00349   } else if (ftmp != NULL) {
00350     /* In case the gz stream was opened correctly this will
00351      * be closed by gzclose. */
00352     fclose(ftmp);
00353   }
00354   if (fout != NULL) fclose(fout);
00355 
00356   return ret;
00357 #else
00358   NOT_REACHED();
00359 #endif /* defined(WITH_ZLIB) */
00360 }
00361 
00362 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_CONTENT)
00363 {
00364   if (this->curFile == NULL) {
00365     /* When we haven't opened a file this must be our first packet with metadata. */
00366     this->curInfo = new ContentInfo;
00367     this->curInfo->type     = (ContentType)p->Recv_uint8();
00368     this->curInfo->id       = (ContentID)p->Recv_uint32();
00369     this->curInfo->filesize = p->Recv_uint32();
00370     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
00371 
00372     if (!this->curInfo->IsValid()) {
00373       delete this->curInfo;
00374       this->curInfo = NULL;
00375       this->Close();
00376       return false;
00377     }
00378 
00379     if (this->curInfo->filesize != 0) {
00380       /* The filesize is > 0, so we are going to download it */
00381       const char *filename = GetFullFilename(this->curInfo, true);
00382       if (filename == NULL) {
00383         /* Unless that fails ofcourse... */
00384         DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00385         ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00386         this->Close();
00387         return false;
00388       }
00389 
00390       this->curFile = fopen(filename, "wb");
00391     }
00392   } else {
00393     /* We have a file opened, thus are downloading internal content */
00394     size_t toRead = (size_t)(p->size - p->pos);
00395     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
00396       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00397       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00398       this->Close();
00399       fclose(this->curFile);
00400       this->curFile = NULL;
00401 
00402       return false;
00403     }
00404 
00405     this->OnDownloadProgress(this->curInfo, (uint)toRead);
00406 
00407     if (toRead == 0) {
00408       /* We read nothing; that's our marker for end-of-stream.
00409        * Now gunzip the tar and make it known. */
00410       fclose(this->curFile);
00411       this->curFile = NULL;
00412 
00413       if (GunzipFile(this->curInfo)) {
00414         unlink(GetFullFilename(this->curInfo, true));
00415 
00416         TarListAddFile(GetFullFilename(this->curInfo, false));
00417 
00418         this->OnDownloadComplete(this->curInfo->id);
00419       } else {
00420         ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, 0, 0);
00421       }
00422     }
00423   }
00424 
00425   /* We ended this file, so clean up the mess */
00426   if (this->curFile == NULL) {
00427     delete this->curInfo;
00428     this->curInfo = NULL;
00429   }
00430 
00431   return true;
00432 }
00433 
00439 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
00440   NetworkContentSocketHandler(),
00441   curFile(NULL),
00442   curInfo(NULL),
00443   isConnecting(false)
00444 {
00445 }
00446 
00448 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
00449 {
00450   delete this->curInfo;
00451   if (this->curFile != NULL) fclose(this->curFile);
00452 
00453   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00454 }
00455 
00456 class NetworkContentConnecter : TCPConnecter {
00457 public:
00458   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00459 
00460   virtual void OnFailure()
00461   {
00462     _network_content_client.isConnecting = false;
00463     _network_content_client.OnConnect(false);
00464   }
00465 
00466   virtual void OnConnect(SOCKET s)
00467   {
00468     assert(_network_content_client.sock == INVALID_SOCKET);
00469     _network_content_client.isConnecting = false;
00470     _network_content_client.sock = s;
00471     _network_content_client.Reopen();
00472     _network_content_client.OnConnect(true);
00473   }
00474 };
00475 
00479 void ClientNetworkContentSocketHandler::Connect()
00480 {
00481   this->lastActivity = _realtime_tick;
00482 
00483   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
00484   this->isConnecting = true;
00485   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
00486 }
00487 
00491 void ClientNetworkContentSocketHandler::Close()
00492 {
00493   if (this->sock == INVALID_SOCKET) return;
00494   NetworkContentSocketHandler::Close();
00495 
00496   this->OnDisconnect();
00497 }
00498 
00503 void ClientNetworkContentSocketHandler::SendReceive()
00504 {
00505   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
00506 
00507   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
00508     this->Close();
00509     return;
00510   }
00511 
00512   fd_set read_fd, write_fd;
00513   struct timeval tv;
00514 
00515   FD_ZERO(&read_fd);
00516   FD_ZERO(&write_fd);
00517 
00518   FD_SET(this->sock, &read_fd);
00519   FD_SET(this->sock, &write_fd);
00520 
00521   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
00522 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
00523   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
00524 #else
00525   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
00526 #endif
00527   if (FD_ISSET(this->sock, &read_fd)) {
00528     this->Recv_Packets();
00529     this->lastActivity = _realtime_tick;
00530   }
00531 
00532   this->writable = !!FD_ISSET(this->sock, &write_fd);
00533   this->Send_Packets();
00534 }
00535 
00540 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
00541 {
00542   /* When we tried to download it already, don't try again */
00543   if (this->requested.Contains(cid)) return;
00544 
00545   *this->requested.Append() = cid;
00546   assert(this->requested.Contains(cid));
00547   this->RequestContentList(1, &cid);
00548 }
00549 
00555 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
00556 {
00557   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00558     ContentInfo *ci = *iter;
00559     if (ci->id == cid) return ci;
00560   }
00561   return NULL;
00562 }
00563 
00564 
00569 void ClientNetworkContentSocketHandler::Select(ContentID cid)
00570 {
00571   ContentInfo *ci = this->GetContent(cid);
00572   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
00573 
00574   ci->state = ContentInfo::SELECTED;
00575   this->CheckDependencyState(ci);
00576 }
00577 
00582 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
00583 {
00584   ContentInfo *ci = this->GetContent(cid);
00585   if (ci == NULL || !ci->IsSelected()) return;
00586 
00587   ci->state = ContentInfo::UNSELECTED;
00588   this->CheckDependencyState(ci);
00589 }
00590 
00592 void ClientNetworkContentSocketHandler::SelectAll()
00593 {
00594   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00595     ContentInfo *ci = *iter;
00596     if (ci->state == ContentInfo::UNSELECTED) {
00597       ci->state = ContentInfo::SELECTED;
00598       this->CheckDependencyState(ci);
00599     }
00600   }
00601 }
00602 
00604 void ClientNetworkContentSocketHandler::SelectUpgrade()
00605 {
00606   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00607     ContentInfo *ci = *iter;
00608     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
00609       ci->state = ContentInfo::SELECTED;
00610       this->CheckDependencyState(ci);
00611     }
00612   }
00613 }
00614 
00616 void ClientNetworkContentSocketHandler::UnselectAll()
00617 {
00618   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00619     ContentInfo *ci = *iter;
00620     if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
00621   }
00622 }
00623 
00625 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
00626 {
00627   switch (ci->state) {
00628     case ContentInfo::SELECTED:
00629     case ContentInfo::AUTOSELECTED:
00630       this->Unselect(ci->id);
00631       break;
00632 
00633     case ContentInfo::UNSELECTED:
00634       this->Select(ci->id);
00635       break;
00636 
00637     default:
00638       break;
00639   }
00640 }
00641 
00647 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
00648 {
00649   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00650     const ContentInfo *ci = *iter;
00651     if (ci == child) continue;
00652 
00653     for (uint i = 0; i < ci->dependency_count; i++) {
00654       if (ci->dependencies[i] == child->id) {
00655         *parents.Append() = ci;
00656         break;
00657       }
00658     }
00659   }
00660 }
00661 
00667 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
00668 {
00669   *tree.Append() = child;
00670 
00671   /* First find all direct parents */
00672   for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
00673     ConstContentVector parents;
00674     this->ReverseLookupDependency(parents, *iter);
00675 
00676     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
00677       tree.Include(*piter);
00678     }
00679   }
00680 }
00681 
00686 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
00687 {
00688   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
00689     /* Selection is easy; just walk all children and set the
00690      * autoselected state. That way we can see what we automatically
00691      * selected and thus can unselect when a dependency is removed. */
00692     for (uint i = 0; i < ci->dependency_count; i++) {
00693       ContentInfo *c = this->GetContent(ci->dependencies[i]);
00694       if (c == NULL) {
00695         this->DownloadContentInfo(ci->dependencies[i]);
00696       } else if (c->state == ContentInfo::UNSELECTED) {
00697         c->state = ContentInfo::AUTOSELECTED;
00698         this->CheckDependencyState(c);
00699       }
00700     }
00701     return;
00702   }
00703 
00704   if (ci->state != ContentInfo::UNSELECTED) return;
00705 
00706   /* For unselection we need to find the parents of us. We need to
00707    * unselect them. After that we unselect all children that we
00708    * depend on and are not used as dependency for us, but only when
00709    * we automatically selected them. */
00710   ConstContentVector parents;
00711   this->ReverseLookupDependency(parents, ci);
00712   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00713     const ContentInfo *c = *iter;
00714     if (!c->IsSelected()) continue;
00715 
00716     this->Unselect(c->id);
00717   }
00718 
00719   for (uint i = 0; i < ci->dependency_count; i++) {
00720     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
00721     if (c == NULL) {
00722       DownloadContentInfo(ci->dependencies[i]);
00723       continue;
00724     }
00725     if (c->state != ContentInfo::AUTOSELECTED) continue;
00726 
00727     /* Only unselect when WE are the only parent. */
00728     parents.Clear();
00729     this->ReverseLookupDependency(parents, c);
00730 
00731     /* First check whether anything depends on us */
00732     int sel_count = 0;
00733     bool force_selection = false;
00734     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00735       if ((*iter)->IsSelected()) sel_count++;
00736       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
00737     }
00738     if (sel_count == 0) {
00739       /* Nothing depends on us */
00740       this->Unselect(c->id);
00741       continue;
00742     }
00743     /* Something manually selected depends directly on us */
00744     if (force_selection) continue;
00745 
00746     /* "Flood" search to find all items in the dependency graph*/
00747     parents.Clear();
00748     this->ReverseLookupTreeDependency(parents, c);
00749 
00750     /* Is there anything that is "force" selected?, if so... we're done. */
00751     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00752       if ((*iter)->state != ContentInfo::SELECTED) continue;
00753 
00754       force_selection = true;
00755       break;
00756     }
00757 
00758     /* So something depended directly on us */
00759     if (force_selection) continue;
00760 
00761     /* Nothing depends on us, mark the whole graph as unselected.
00762      * After that's done run over them once again to test their children
00763      * to unselect. Don't do it immediatelly because it'll do exactly what
00764      * we're doing now. */
00765     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00766       const ContentInfo *c = *iter;
00767       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
00768     }
00769     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00770       this->CheckDependencyState(this->GetContent((*iter)->id));
00771     }
00772   }
00773 }
00774 
00775 void ClientNetworkContentSocketHandler::Clear()
00776 {
00777   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00778 
00779   this->infos.Clear();
00780   this->requested.Clear();
00781 }
00782 
00783 /*** CALLBACK ***/
00784 
00785 void ClientNetworkContentSocketHandler::OnConnect(bool success)
00786 {
00787   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00788     ContentCallback *cb = *iter;
00789     cb->OnConnect(success);
00790     if (iter != this->callbacks.End() && *iter == cb) iter++;
00791   }
00792 }
00793 
00794 void ClientNetworkContentSocketHandler::OnDisconnect()
00795 {
00796   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00797     ContentCallback *cb = *iter;
00798     cb->OnDisconnect();
00799     if (iter != this->callbacks.End() && *iter == cb) iter++;
00800   }
00801 }
00802 
00803 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
00804 {
00805   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00806     ContentCallback *cb = *iter;
00807     cb->OnReceiveContentInfo(ci);
00808     if (iter != this->callbacks.End() && *iter == cb) iter++;
00809   }
00810 }
00811 
00812 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, uint bytes)
00813 {
00814   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00815     ContentCallback *cb = *iter;
00816     cb->OnDownloadProgress(ci, bytes);
00817     if (iter != this->callbacks.End() && *iter == cb) iter++;
00818   }
00819 }
00820 
00821 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
00822 {
00823   ContentInfo *ci = this->GetContent(cid);
00824   if (ci != NULL) {
00825     ci->state = ContentInfo::ALREADY_HERE;
00826   }
00827 
00828   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
00829     ContentCallback *cb = *iter;
00830     cb->OnDownloadComplete(cid);
00831     if (iter != this->callbacks.End() && *iter == cb) iter++;
00832   }
00833 }
00834 
00835 #endif /* ENABLE_NETWORK */

Generated on Wed Dec 23 23:27:51 2009 for OpenTTD by  doxygen 1.5.6