00001
00002
00003
00004
00005
00006
00007
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 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
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
00124 if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
00125
00126
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
00132 if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
00133 if (ici->IsSelected()) ci->state = ici->state;
00134
00135
00136
00137
00138
00139
00140
00141 ici->TransferFrom(ci);
00142 delete ci;
00143
00144 this->OnReceiveContentInfo(ici);
00145 return true;
00146 }
00147 }
00148
00149
00150 if (ci->filesize == 0) {
00151 delete ci;
00152 return true;
00153 }
00154
00155 *this->infos.Append() = ci;
00156
00157
00158 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00159 this->CheckDependencyState(*iter);
00160 }
00161
00162 this->OnReceiveContentInfo(ci);
00163
00164 return true;
00165 }
00166
00167 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
00168 {
00169 if (type == CONTENT_TYPE_END) {
00170 this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
00171 this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
00172 this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
00173 this->RequestContentList(CONTENT_TYPE_SCENARIO);
00174 this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
00175 #ifdef ENABLE_AI
00176 this->RequestContentList(CONTENT_TYPE_AI);
00177 this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
00178 #endif
00179 this->RequestContentList(CONTENT_TYPE_NEWGRF);
00180 return;
00181 }
00182
00183 this->Connect();
00184
00185 Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
00186 p->Send_uint8 ((byte)type);
00187 p->Send_uint32(_openttd_newgrf_version);
00188
00189 this->Send_Packet(p);
00190 }
00191
00192 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
00193 {
00194 this->Connect();
00195
00196 while (count > 0) {
00197
00198
00199
00200
00201
00202 uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00203
00204 Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
00205 p->Send_uint16(p_count);
00206
00207 for (uint i = 0; i < p_count; i++) {
00208 p->Send_uint32(content_ids[i]);
00209 }
00210
00211 this->Send_Packet(p);
00212 count -= p_count;
00213 content_ids += p_count;
00214 }
00215 }
00216
00217 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
00218 {
00219 if (cv == NULL) return;
00220
00221 this->Connect();
00222
00223
00224 assert(cv->Length() < 255);
00225 assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
00226
00227 Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
00228 p->Send_uint8(cv->Length());
00229
00230 for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00231 const ContentInfo *ci = *iter;
00232 p->Send_uint8((byte)ci->type);
00233 p->Send_uint32(ci->unique_id);
00234 if (!send_md5sum) continue;
00235
00236 for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00237 p->Send_uint8(ci->md5sum[j]);
00238 }
00239 }
00240
00241 this->Send_Packet(p);
00242
00243 for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00244 ContentInfo *ci = *iter;
00245 bool found = false;
00246 for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
00247 ContentInfo *ci2 = *iter2;
00248 if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
00249 (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
00250 found = true;
00251 break;
00252 }
00253 }
00254 if (!found) {
00255 *this->infos.Append() = ci;
00256 } else {
00257 delete ci;
00258 }
00259 }
00260 }
00261
00262 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
00263 {
00264 bytes = 0;
00265
00266 ContentIDList content;
00267 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00268 const ContentInfo *ci = *iter;
00269 if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
00270
00271 *content.Append() = ci->id;
00272 bytes += ci->filesize;
00273 }
00274
00275 files = content.Length();
00276
00277
00278 if (files == 0) return;
00279
00280 if (_settings_client.network.no_http_content_downloads || fallback) {
00281 this->DownloadSelectedContentFallback(content);
00282 } else {
00283 this->DownloadSelectedContentHTTP(content);
00284 }
00285 }
00286
00287 void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const ContentIDList &content)
00288 {
00289 uint count = content.Length();
00290
00291
00292
00293
00294
00295 uint bytes = (10 + 1) * count + 1;
00296 char *content_request = MallocT<char>(bytes);
00297 const char *lastof = content_request + bytes - 1;
00298
00299 char *p = content_request;
00300 for (const ContentID *id = content.Begin(); id != content.End(); id++) {
00301 p += seprintf(p, lastof, "%d\n", *id);
00302 }
00303
00304 this->http_response_index = -1;
00305
00306 NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
00307 new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
00308
00309 }
00310
00311 void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const ContentIDList &content)
00312 {
00313 uint count = content.Length();
00314 const ContentID *content_ids = content.Begin();
00315 this->Connect();
00316
00317 while (count > 0) {
00318
00319
00320
00321
00322 uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00323
00324 Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
00325 p->Send_uint16(p_count);
00326
00327 for (uint i = 0; i < p_count; i++) {
00328 p->Send_uint32(content_ids[i]);
00329 }
00330
00331 this->Send_Packet(p);
00332 count -= p_count;
00333 content_ids += p_count;
00334 }
00335 }
00336
00344 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
00345 {
00346 Subdirectory dir;
00347 switch (ci->type) {
00348 default: return NULL;
00349 case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR; break;
00350 case CONTENT_TYPE_BASE_MUSIC: dir = GM_DIR; break;
00351 case CONTENT_TYPE_BASE_SOUNDS: dir = DATA_DIR; break;
00352 case CONTENT_TYPE_NEWGRF: dir = DATA_DIR; break;
00353 case CONTENT_TYPE_AI: dir = AI_DIR; break;
00354 case CONTENT_TYPE_AI_LIBRARY: dir = AI_LIBRARY_DIR; break;
00355 case CONTENT_TYPE_SCENARIO: dir = SCENARIO_DIR; break;
00356 case CONTENT_TYPE_HEIGHTMAP: dir = HEIGHTMAP_DIR; break;
00357 }
00358
00359 static char buf[MAX_PATH];
00360 FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
00361 strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
00362
00363 return buf;
00364 }
00365
00371 static bool GunzipFile(const ContentInfo *ci)
00372 {
00373 #if defined(WITH_ZLIB)
00374 bool ret = true;
00375 FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
00376 gzFile fin = gzdopen(fileno(ftmp), "rb");
00377 FILE *fout = fopen(GetFullFilename(ci, false), "wb");
00378
00379 if (fin == NULL || fout == NULL) {
00380 ret = false;
00381 } else {
00382 byte buff[8192];
00383 while (1) {
00384 int read = gzread(fin, buff, sizeof(buff));
00385 if (read == 0) {
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 int errnum;
00399 gzerror(fin, &errnum);
00400 if (errnum != 0 && errnum != Z_STREAM_END) ret = false;
00401 break;
00402 }
00403 if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
00404
00405 ret = false;
00406 break;
00407 }
00408
00409
00410 }
00411 }
00412
00413 if (fin != NULL) {
00414
00415 gzclose(fin);
00416 } else if (ftmp != NULL) {
00417
00418
00419 fclose(ftmp);
00420 }
00421 if (fout != NULL) fclose(fout);
00422
00423 return ret;
00424 #else
00425 NOT_REACHED();
00426 #endif
00427 }
00428
00429 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_CONTENT)
00430 {
00431 if (this->curFile == NULL) {
00432 delete this->curInfo;
00433
00434 this->curInfo = new ContentInfo;
00435 this->curInfo->type = (ContentType)p->Recv_uint8();
00436 this->curInfo->id = (ContentID)p->Recv_uint32();
00437 this->curInfo->filesize = p->Recv_uint32();
00438 p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
00439
00440 if (!this->BeforeDownload()) {
00441 this->Close();
00442 return false;
00443 }
00444 } else {
00445
00446 size_t toRead = (size_t)(p->size - p->pos);
00447 if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
00448 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00449 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00450 this->Close();
00451 fclose(this->curFile);
00452 this->curFile = NULL;
00453
00454 return false;
00455 }
00456
00457 this->OnDownloadProgress(this->curInfo, (uint)toRead);
00458
00459 if (toRead == 0) this->AfterDownload();
00460 }
00461
00462 return true;
00463 }
00464
00469 bool ClientNetworkContentSocketHandler::BeforeDownload()
00470 {
00471 if (!this->curInfo->IsValid()) {
00472 delete this->curInfo;
00473 this->curInfo = NULL;
00474 return false;
00475 }
00476
00477 if (this->curInfo->filesize != 0) {
00478
00479 const char *filename = GetFullFilename(this->curInfo, true);
00480 if (filename == NULL) {
00481
00482 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00483 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
00484 return false;
00485 }
00486
00487 this->curFile = fopen(filename, "wb");
00488 }
00489 return true;
00490 }
00491
00496 void ClientNetworkContentSocketHandler::AfterDownload()
00497 {
00498
00499
00500 fclose(this->curFile);
00501 this->curFile = NULL;
00502
00503 if (GunzipFile(this->curInfo)) {
00504 unlink(GetFullFilename(this->curInfo, true));
00505
00506 TarScanner ts;
00507 ts.AddFile(GetFullFilename(this->curInfo, false), 0);
00508
00509 if (this->curInfo->type == CONTENT_TYPE_BASE_MUSIC) {
00510
00511 ExtractTar(GetFullFilename(this->curInfo, false));
00512 unlink(GetFullFilename(this->curInfo, false));
00513 }
00514
00515 this->OnDownloadComplete(this->curInfo->id);
00516 } else {
00517 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, 0, 0);
00518 }
00519 }
00520
00521
00522 void ClientNetworkContentSocketHandler::OnFailure()
00523 {
00524
00525 uint files, bytes;
00526 this->DownloadSelectedContent(files, bytes, true);
00527
00528 this->http_response.Reset();
00529 this->http_response_index = -2;
00530
00531 if (this->curFile != NULL) {
00532 fclose(this->curFile);
00533 this->curFile = NULL;
00534 }
00535 }
00536
00537 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
00538 {
00539 assert(data == NULL || length != 0);
00540
00541
00542 if (this->http_response_index == -2) return;
00543
00544 if (this->http_response_index == -1) {
00545 if (data != NULL) {
00546
00547 memcpy(this->http_response.Append((uint)length), data, length);
00548 return;
00549 } else {
00550
00551 *this->http_response.Append() = '\0';
00552
00553
00554 this->http_response_index = 0;
00555 }
00556 }
00557
00558 if (data != NULL) {
00559
00560 if (fwrite(data, 1, length, this->curFile) != length) {
00561
00562 this->OnFailure();
00563 } else {
00564
00565 this->OnDownloadProgress(this->curInfo, (uint)length);
00566 }
00567
00568 return;
00569 }
00570
00571 if (this->curFile != NULL) {
00572
00573 this->AfterDownload();
00574 }
00575
00576 if ((uint)this->http_response_index >= this->http_response.Length()) {
00577
00578
00579
00580 this->OnFailure();
00581 return;
00582 }
00583
00584 delete this->curInfo;
00585
00586 this->curInfo = new ContentInfo;
00587
00589 #define check_not_null(p) { if ((p) == NULL) { this->OnFailure(); return; } }
00590
00591 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
00592
00593 for (;;) {
00594 char *str = this->http_response.Begin() + this->http_response_index;
00595 char *p = strchr(str, '\n');
00596 check_and_terminate(p);
00597
00598
00599 this->http_response_index += (int)strlen(str) + 1;
00600
00601
00602 p = strchr(str, ',');
00603 check_and_terminate(p);
00604 this->curInfo->id = (ContentID)atoi(str);
00605
00606
00607 str = p + 1;
00608 p = strchr(str, ',');
00609 check_and_terminate(p);
00610 this->curInfo->type = (ContentType)atoi(str);
00611
00612
00613 str = p + 1;
00614 p = strchr(str, ',');
00615 check_and_terminate(p);
00616 this->curInfo->filesize = atoi(str);
00617
00618
00619 str = p + 1;
00620
00621 if (strncmp(str, "ottd", 4) == 0) {
00622 if ((uint)this->http_response_index >= this->http_response.Length()) {
00623
00624 this->OnFailure();
00625 return;
00626 }
00627 continue;
00628 }
00629
00630 p = strrchr(str, '/');
00631 check_not_null(p);
00632 p++;
00633
00634 char tmp[MAX_PATH];
00635 if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
00636 this->OnFailure();
00637 return;
00638 }
00639
00640 for (uint i = 0; i < 2; i++) {
00641 p = strrchr(tmp, '.');
00642 check_and_terminate(p);
00643 }
00644
00645
00646 strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
00647
00648
00649 if (!this->BeforeDownload()) {
00650 this->OnFailure();
00651 return;
00652 }
00653
00654 NetworkHTTPSocketHandler::Connect(str, this);
00655 return;
00656 }
00657
00658 #undef check
00659 #undef check_and_terminate
00660 }
00661
00667 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
00668 NetworkContentSocketHandler(),
00669 http_response_index(-2),
00670 curFile(NULL),
00671 curInfo(NULL),
00672 isConnecting(false)
00673 {
00674 }
00675
00677 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
00678 {
00679 delete this->curInfo;
00680 if (this->curFile != NULL) fclose(this->curFile);
00681
00682 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00683 }
00684
00685 class NetworkContentConnecter : TCPConnecter {
00686 public:
00687 NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00688
00689 virtual void OnFailure()
00690 {
00691 _network_content_client.isConnecting = false;
00692 _network_content_client.OnConnect(false);
00693 }
00694
00695 virtual void OnConnect(SOCKET s)
00696 {
00697 assert(_network_content_client.sock == INVALID_SOCKET);
00698 _network_content_client.isConnecting = false;
00699 _network_content_client.sock = s;
00700 _network_content_client.Reopen();
00701 _network_content_client.OnConnect(true);
00702 }
00703 };
00704
00708 void ClientNetworkContentSocketHandler::Connect()
00709 {
00710 this->lastActivity = _realtime_tick;
00711
00712 if (this->sock != INVALID_SOCKET || this->isConnecting) return;
00713 this->isConnecting = true;
00714 new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
00715 }
00716
00720 void ClientNetworkContentSocketHandler::Close()
00721 {
00722 if (this->sock == INVALID_SOCKET) return;
00723 NetworkContentSocketHandler::Close();
00724
00725 this->OnDisconnect();
00726 }
00727
00732 void ClientNetworkContentSocketHandler::SendReceive()
00733 {
00734 if (this->sock == INVALID_SOCKET || this->isConnecting) return;
00735
00736 if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
00737 this->Close();
00738 return;
00739 }
00740
00741 fd_set read_fd, write_fd;
00742 struct timeval tv;
00743
00744 FD_ZERO(&read_fd);
00745 FD_ZERO(&write_fd);
00746
00747 FD_SET(this->sock, &read_fd);
00748 FD_SET(this->sock, &write_fd);
00749
00750 tv.tv_sec = tv.tv_usec = 0;
00751 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
00752 select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
00753 #else
00754 WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
00755 #endif
00756 if (FD_ISSET(this->sock, &read_fd)) {
00757 this->Recv_Packets();
00758 this->lastActivity = _realtime_tick;
00759 }
00760
00761 this->writable = !!FD_ISSET(this->sock, &write_fd);
00762 this->Send_Packets();
00763 }
00764
00769 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
00770 {
00771
00772 if (this->requested.Contains(cid)) return;
00773
00774 *this->requested.Append() = cid;
00775 assert(this->requested.Contains(cid));
00776 this->RequestContentList(1, &cid);
00777 }
00778
00784 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
00785 {
00786 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00787 ContentInfo *ci = *iter;
00788 if (ci->id == cid) return ci;
00789 }
00790 return NULL;
00791 }
00792
00793
00798 void ClientNetworkContentSocketHandler::Select(ContentID cid)
00799 {
00800 ContentInfo *ci = this->GetContent(cid);
00801 if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
00802
00803 ci->state = ContentInfo::SELECTED;
00804 this->CheckDependencyState(ci);
00805 }
00806
00811 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
00812 {
00813 ContentInfo *ci = this->GetContent(cid);
00814 if (ci == NULL || !ci->IsSelected()) return;
00815
00816 ci->state = ContentInfo::UNSELECTED;
00817 this->CheckDependencyState(ci);
00818 }
00819
00821 void ClientNetworkContentSocketHandler::SelectAll()
00822 {
00823 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00824 ContentInfo *ci = *iter;
00825 if (ci->state == ContentInfo::UNSELECTED) {
00826 ci->state = ContentInfo::SELECTED;
00827 this->CheckDependencyState(ci);
00828 }
00829 }
00830 }
00831
00833 void ClientNetworkContentSocketHandler::SelectUpgrade()
00834 {
00835 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00836 ContentInfo *ci = *iter;
00837 if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
00838 ci->state = ContentInfo::SELECTED;
00839 this->CheckDependencyState(ci);
00840 }
00841 }
00842 }
00843
00845 void ClientNetworkContentSocketHandler::UnselectAll()
00846 {
00847 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00848 ContentInfo *ci = *iter;
00849 if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
00850 }
00851 }
00852
00854 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
00855 {
00856 switch (ci->state) {
00857 case ContentInfo::SELECTED:
00858 case ContentInfo::AUTOSELECTED:
00859 this->Unselect(ci->id);
00860 break;
00861
00862 case ContentInfo::UNSELECTED:
00863 this->Select(ci->id);
00864 break;
00865
00866 default:
00867 break;
00868 }
00869 }
00870
00876 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
00877 {
00878 for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00879 const ContentInfo *ci = *iter;
00880 if (ci == child) continue;
00881
00882 for (uint i = 0; i < ci->dependency_count; i++) {
00883 if (ci->dependencies[i] == child->id) {
00884 *parents.Append() = ci;
00885 break;
00886 }
00887 }
00888 }
00889 }
00890
00896 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
00897 {
00898 *tree.Append() = child;
00899
00900
00901 for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
00902 ConstContentVector parents;
00903 this->ReverseLookupDependency(parents, *iter);
00904
00905 for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
00906 tree.Include(*piter);
00907 }
00908 }
00909 }
00910
00915 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
00916 {
00917 if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
00918
00919
00920
00921 for (uint i = 0; i < ci->dependency_count; i++) {
00922 ContentInfo *c = this->GetContent(ci->dependencies[i]);
00923 if (c == NULL) {
00924 this->DownloadContentInfo(ci->dependencies[i]);
00925 } else if (c->state == ContentInfo::UNSELECTED) {
00926 c->state = ContentInfo::AUTOSELECTED;
00927 this->CheckDependencyState(c);
00928 }
00929 }
00930 return;
00931 }
00932
00933 if (ci->state != ContentInfo::UNSELECTED) return;
00934
00935
00936
00937
00938
00939 ConstContentVector parents;
00940 this->ReverseLookupDependency(parents, ci);
00941 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00942 const ContentInfo *c = *iter;
00943 if (!c->IsSelected()) continue;
00944
00945 this->Unselect(c->id);
00946 }
00947
00948 for (uint i = 0; i < ci->dependency_count; i++) {
00949 const ContentInfo *c = this->GetContent(ci->dependencies[i]);
00950 if (c == NULL) {
00951 DownloadContentInfo(ci->dependencies[i]);
00952 continue;
00953 }
00954 if (c->state != ContentInfo::AUTOSELECTED) continue;
00955
00956
00957 parents.Clear();
00958 this->ReverseLookupDependency(parents, c);
00959
00960
00961 int sel_count = 0;
00962 bool force_selection = false;
00963 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00964 if ((*iter)->IsSelected()) sel_count++;
00965 if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
00966 }
00967 if (sel_count == 0) {
00968
00969 this->Unselect(c->id);
00970 continue;
00971 }
00972
00973 if (force_selection) continue;
00974
00975
00976 parents.Clear();
00977 this->ReverseLookupTreeDependency(parents, c);
00978
00979
00980 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00981 if ((*iter)->state != ContentInfo::SELECTED) continue;
00982
00983 force_selection = true;
00984 break;
00985 }
00986
00987
00988 if (force_selection) continue;
00989
00990
00991
00992
00993
00994 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00995 const ContentInfo *c = *iter;
00996 if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
00997 }
00998 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00999 this->CheckDependencyState(this->GetContent((*iter)->id));
01000 }
01001 }
01002 }
01003
01004 void ClientNetworkContentSocketHandler::Clear()
01005 {
01006 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
01007
01008 this->infos.Clear();
01009 this->requested.Clear();
01010 }
01011
01012
01013
01014 void ClientNetworkContentSocketHandler::OnConnect(bool success)
01015 {
01016 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01017 ContentCallback *cb = *iter;
01018 cb->OnConnect(success);
01019 if (iter != this->callbacks.End() && *iter == cb) iter++;
01020 }
01021 }
01022
01023 void ClientNetworkContentSocketHandler::OnDisconnect()
01024 {
01025 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01026 ContentCallback *cb = *iter;
01027 cb->OnDisconnect();
01028 if (iter != this->callbacks.End() && *iter == cb) iter++;
01029 }
01030 }
01031
01032 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
01033 {
01034 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01035 ContentCallback *cb = *iter;
01036 cb->OnReceiveContentInfo(ci);
01037 if (iter != this->callbacks.End() && *iter == cb) iter++;
01038 }
01039 }
01040
01041 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, uint bytes)
01042 {
01043 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01044 ContentCallback *cb = *iter;
01045 cb->OnDownloadProgress(ci, bytes);
01046 if (iter != this->callbacks.End() && *iter == cb) iter++;
01047 }
01048 }
01049
01050 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
01051 {
01052 ContentInfo *ci = this->GetContent(cid);
01053 if (ci != NULL) {
01054 ci->state = ContentInfo::ALREADY_HERE;
01055 }
01056
01057 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01058 ContentCallback *cb = *iter;
01059 cb->OnDownloadComplete(cid);
01060 if (iter != this->callbacks.End() && *iter == cb) iter++;
01061 }
01062 }
01063
01064 #endif