network_content.cpp

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

Generated on Tue Jan 5 21:02:55 2010 for OpenTTD by  doxygen 1.5.6