# 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 */