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 "../game/game.hpp"
00018 #include "../window_func.h"
00019 #include "../error.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
00033 ClientNetworkContentSocketHandler _network_content_client;
00034
00036 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
00037 {
00038 return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? ci->md5sum : NULL) != NULL;
00039 }
00040
00048 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
00049
00050 bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
00051 {
00052 ContentInfo *ci = new ContentInfo();
00053 ci->type = (ContentType)p->Recv_uint8();
00054 ci->id = (ContentID)p->Recv_uint32();
00055 ci->filesize = p->Recv_uint32();
00056
00057 p->Recv_string(ci->name, lengthof(ci->name));
00058 p->Recv_string(ci->version, lengthof(ci->name));
00059 p->Recv_string(ci->url, lengthof(ci->url));
00060 p->Recv_string(ci->description, lengthof(ci->description), SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
00061
00062 ci->unique_id = p->Recv_uint32();
00063 for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00064 ci->md5sum[j] = p->Recv_uint8();
00065 }
00066
00067 ci->dependency_count = p->Recv_uint8();
00068 ci->dependencies = MallocT<ContentID>(ci->dependency_count);
00069 for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
00070
00071 ci->tag_count = p->Recv_uint8();
00072 ci->tags = MallocT<char[32]>(ci->tag_count);
00073 for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
00074
00075 if (!ci->IsValid()) {
00076 delete ci;
00077 this->Close();
00078 return false;
00079 }
00080
00081
00082 HasProc proc = NULL;
00083 switch (ci->type) {
00084 case CONTENT_TYPE_NEWGRF:
00085 proc = HasGRFConfig;
00086 break;
00087
00088 case CONTENT_TYPE_BASE_GRAPHICS:
00089 proc = BaseGraphics::HasSet;
00090 break;
00091
00092 case CONTENT_TYPE_BASE_MUSIC:
00093 proc = BaseMusic::HasSet;
00094 break;
00095
00096 case CONTENT_TYPE_BASE_SOUNDS:
00097 proc = BaseSounds::HasSet;
00098 break;
00099
00100 case CONTENT_TYPE_AI:
00101 proc = AI::HasAI; break;
00102 break;
00103
00104 case CONTENT_TYPE_AI_LIBRARY:
00105 proc = AI::HasAILibrary; break;
00106 break;
00107
00108 case CONTENT_TYPE_GAME:
00109 proc = Game::HasGame; break;
00110 break;
00111
00112 case CONTENT_TYPE_GAME_LIBRARY:
00113 proc = Game::HasGameLibrary; break;
00114 break;
00115
00116 case CONTENT_TYPE_SCENARIO:
00117 case CONTENT_TYPE_HEIGHTMAP:
00118 proc = HasScenario;
00119 break;
00120
00121 default:
00122 break;
00123 }
00124
00125 if (proc != NULL) {
00126 if (proc(ci, true)) {
00127 ci->state = ContentInfo::ALREADY_HERE;
00128 } else {
00129 ci->state = ContentInfo::UNSELECTED;
00130 if (proc(ci, false)) ci->upgrade = true;
00131 }
00132 } else {
00133 ci->state = ContentInfo::UNSELECTED;
00134 }
00135
00136
00137 if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
00138
00139
00140 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00141 ContentInfo *ici = *iter;
00142 if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
00143 memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
00144
00145 if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
00146 if (ici->IsSelected()) ci->state = ici->state;
00147
00148
00149
00150
00151
00152
00153
00154 ici->TransferFrom(ci);
00155 delete ci;
00156
00157 this->OnReceiveContentInfo(ici);
00158 return true;
00159 }
00160 }
00161
00162
00163 if (ci->filesize == 0) {
00164 delete ci;
00165 return true;
00166 }
00167
00168 *this->infos.Append() = ci;
00169
00170
00171 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00172 this->CheckDependencyState(*iter);
00173 }
00174
00175 this->OnReceiveContentInfo(ci);
00176
00177 return true;
00178 }
00179
00184 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
00185 {
00186 if (type == CONTENT_TYPE_END) {
00187 this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
00188 this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
00189 this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
00190 this->RequestContentList(CONTENT_TYPE_SCENARIO);
00191 this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
00192 this->RequestContentList(CONTENT_TYPE_AI);
00193 this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
00194 this->RequestContentList(CONTENT_TYPE_GAME);
00195 this->RequestContentList(CONTENT_TYPE_GAME_LIBRARY);
00196 this->RequestContentList(CONTENT_TYPE_NEWGRF);
00197 return;
00198 }
00199
00200 this->Connect();
00201
00202 Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
00203 p->Send_uint8 ((byte)type);
00204 p->Send_uint32(_openttd_newgrf_version);
00205
00206 this->SendPacket(p);
00207 }
00208
00214 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
00215 {
00216 this->Connect();
00217
00218 while (count > 0) {
00219
00220
00221
00222
00223
00224 uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00225
00226 Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
00227 p->Send_uint16(p_count);
00228
00229 for (uint i = 0; i < p_count; i++) {
00230 p->Send_uint32(content_ids[i]);
00231 }
00232
00233 this->SendPacket(p);
00234 count -= p_count;
00235 content_ids += p_count;
00236 }
00237 }
00238
00244 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
00245 {
00246 if (cv == NULL) return;
00247
00248 this->Connect();
00249
00250
00251 assert(cv->Length() < 255);
00252 assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
00253
00254 Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
00255 p->Send_uint8(cv->Length());
00256
00257 for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00258 const ContentInfo *ci = *iter;
00259 p->Send_uint8((byte)ci->type);
00260 p->Send_uint32(ci->unique_id);
00261 if (!send_md5sum) continue;
00262
00263 for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00264 p->Send_uint8(ci->md5sum[j]);
00265 }
00266 }
00267
00268 this->SendPacket(p);
00269
00270 for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00271 ContentInfo *ci = *iter;
00272 bool found = false;
00273 for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
00274 ContentInfo *ci2 = *iter2;
00275 if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
00276 (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
00277 found = true;
00278 break;
00279 }
00280 }
00281 if (!found) {
00282 *this->infos.Append() = ci;
00283 } else {
00284 delete ci;
00285 }
00286 }
00287 }
00288
00295 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
00296 {
00297 bytes = 0;
00298
00299 ContentIDList content;
00300 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00301 const ContentInfo *ci = *iter;
00302 if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
00303
00304 *content.Append() = ci->id;
00305 bytes += ci->filesize;
00306 }
00307
00308 files = content.Length();
00309
00310
00311 if (files == 0) return;
00312
00313 if (_settings_client.network.no_http_content_downloads || fallback) {
00314 this->DownloadSelectedContentFallback(content);
00315 } else {
00316 this->DownloadSelectedContentHTTP(content);
00317 }
00318 }
00319
00324 void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const ContentIDList &content)
00325 {
00326 uint count = content.Length();
00327
00328
00329
00330
00331
00332 uint bytes = (10 + 1) * count + 1;
00333 char *content_request = MallocT<char>(bytes);
00334 const char *lastof = content_request + bytes - 1;
00335
00336 char *p = content_request;
00337 for (const ContentID *id = content.Begin(); id != content.End(); id++) {
00338 p += seprintf(p, lastof, "%d\n", *id);
00339 }
00340
00341 this->http_response_index = -1;
00342
00343 NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
00344 new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
00345
00346 }
00347
00352 void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const ContentIDList &content)
00353 {
00354 uint count = content.Length();
00355 const ContentID *content_ids = content.Begin();
00356 this->Connect();
00357
00358 while (count > 0) {
00359
00360
00361
00362
00363 uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00364
00365 Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
00366 p->Send_uint16(p_count);
00367
00368 for (uint i = 0; i < p_count; i++) {
00369 p->Send_uint32(content_ids[i]);
00370 }
00371
00372 this->SendPacket(p);
00373 count -= p_count;
00374 content_ids += p_count;
00375 }
00376 }
00377
00385 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
00386 {
00387 Subdirectory dir;
00388 switch (ci->type) {
00389 default: return NULL;
00390 case CONTENT_TYPE_BASE_GRAPHICS: dir = BASESET_DIR; break;
00391 case CONTENT_TYPE_BASE_MUSIC: dir = BASESET_DIR; break;
00392 case CONTENT_TYPE_BASE_SOUNDS: dir = BASESET_DIR; break;
00393 case CONTENT_TYPE_NEWGRF: dir = NEWGRF_DIR; break;
00394 case CONTENT_TYPE_AI: dir = AI_DIR; break;
00395 case CONTENT_TYPE_AI_LIBRARY: dir = AI_LIBRARY_DIR; break;
00396 case CONTENT_TYPE_SCENARIO: dir = SCENARIO_DIR; break;
00397 case CONTENT_TYPE_HEIGHTMAP: dir = HEIGHTMAP_DIR; break;
00398 case CONTENT_TYPE_GAME: dir = GAME_DIR; break;
00399 case CONTENT_TYPE_GAME_LIBRARY: dir = GAME_LIBRARY_DIR; break;
00400 }
00401
00402 static char buf[MAX_PATH];
00403 FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
00404 strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
00405
00406 return buf;
00407 }
00408
00414 static bool GunzipFile(const ContentInfo *ci)
00415 {
00416 #if defined(WITH_ZLIB)
00417 bool ret = true;
00418 FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
00419 gzFile fin = gzdopen(fileno(ftmp), "rb");
00420 FILE *fout = fopen(GetFullFilename(ci, false), "wb");
00421
00422 if (fin == NULL || fout == NULL) {
00423 ret = false;
00424 } else {
00425 byte buff[8192];
00426 for (;;) {
00427 int read = gzread(fin, buff, sizeof(buff));
00428 if (read == 0) {
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441 int errnum;
00442 gzerror(fin, &errnum);
00443 if (errnum != 0 && errnum != Z_STREAM_END) ret = false;
00444 break;
00445 }
00446 if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
00447
00448 ret = false;
00449 break;
00450 }
00451
00452
00453 }
00454 }
00455
00456 if (fin != NULL) {
00457
00458 gzclose(fin);
00459 } else if (ftmp != NULL) {
00460
00461
00462 fclose(ftmp);
00463 }
00464 if (fout != NULL) fclose(fout);
00465
00466 return ret;
00467 #else
00468 NOT_REACHED();
00469 #endif
00470 }
00471
00472 bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p)
00473 {
00474 if (this->curFile == NULL) {
00475 delete this->curInfo;
00476
00477 this->curInfo = new ContentInfo;
00478 this->curInfo->type = (ContentType)p->Recv_uint8();
00479 this->curInfo->id = (ContentID)p->Recv_uint32();
00480 this->curInfo->filesize = p->Recv_uint32();
00481 p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
00482
00483 if (!this->BeforeDownload()) {
00484 this->Close();
00485 return false;
00486 }
00487 } else {
00488
00489 size_t toRead = (size_t)(p->size - p->pos);
00490 if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
00491 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD);
00492 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
00493 this->Close();
00494 fclose(this->curFile);
00495 this->curFile = NULL;
00496
00497 return false;
00498 }
00499
00500 this->OnDownloadProgress(this->curInfo, (int)toRead);
00501
00502 if (toRead == 0) this->AfterDownload();
00503 }
00504
00505 return true;
00506 }
00507
00512 bool ClientNetworkContentSocketHandler::BeforeDownload()
00513 {
00514 if (!this->curInfo->IsValid()) {
00515 delete this->curInfo;
00516 this->curInfo = NULL;
00517 return false;
00518 }
00519
00520 if (this->curInfo->filesize != 0) {
00521
00522 const char *filename = GetFullFilename(this->curInfo, true);
00523 if (filename == NULL || (this->curFile = fopen(filename, "wb")) == NULL) {
00524
00525 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD);
00526 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
00527 return false;
00528 }
00529 }
00530 return true;
00531 }
00532
00537 void ClientNetworkContentSocketHandler::AfterDownload()
00538 {
00539
00540
00541 fclose(this->curFile);
00542 this->curFile = NULL;
00543
00544 if (GunzipFile(this->curInfo)) {
00545 unlink(GetFullFilename(this->curInfo, true));
00546
00547 if (this->curInfo->type == CONTENT_TYPE_BASE_MUSIC) {
00548
00549 ExtractTar(GetFullFilename(this->curInfo, false), BASESET_DIR);
00550 unlink(GetFullFilename(this->curInfo, false));
00551 } else {
00552 Subdirectory sd = NO_DIRECTORY;
00553 switch (this->curInfo->type) {
00554 case CONTENT_TYPE_AI:
00555 sd = AI_DIR;
00556 break;
00557
00558 case CONTENT_TYPE_AI_LIBRARY:
00559 sd = AI_LIBRARY_DIR;
00560 break;
00561
00562 case CONTENT_TYPE_GAME:
00563 sd = GAME_DIR;
00564 break;
00565
00566 case CONTENT_TYPE_GAME_LIBRARY:
00567 sd = GAME_LIBRARY_DIR;
00568 break;
00569
00570 case CONTENT_TYPE_BASE_GRAPHICS:
00571 case CONTENT_TYPE_BASE_SOUNDS:
00572 case CONTENT_TYPE_BASE_MUSIC:
00573 sd = BASESET_DIR;
00574 break;
00575
00576 case CONTENT_TYPE_NEWGRF:
00577 sd = NEWGRF_DIR;
00578 break;
00579
00580 case CONTENT_TYPE_SCENARIO:
00581 case CONTENT_TYPE_HEIGHTMAP:
00582 sd = SCENARIO_DIR;
00583 break;
00584
00585 default: NOT_REACHED();
00586 }
00587
00588 TarScanner ts;
00589 ts.AddFile(sd, GetFullFilename(this->curInfo, false));
00590 }
00591
00592 this->OnDownloadComplete(this->curInfo->id);
00593 } else {
00594 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);
00595 }
00596 }
00597
00598
00599 void ClientNetworkContentSocketHandler::OnFailure()
00600 {
00601
00602 uint files, bytes;
00603 this->DownloadSelectedContent(files, bytes, true);
00604
00605 this->http_response.Reset();
00606 this->http_response_index = -2;
00607
00608 if (this->curFile != NULL) {
00609
00610 long size = ftell(this->curFile);
00611 if (size > 0) this->OnDownloadProgress(this->curInfo, (int)-size);
00612
00613 fclose(this->curFile);
00614 this->curFile = NULL;
00615 }
00616 }
00617
00618 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
00619 {
00620 assert(data == NULL || length != 0);
00621
00622
00623 if (this->http_response_index == -2) return;
00624
00625 if (this->http_response_index == -1) {
00626 if (data != NULL) {
00627
00628 memcpy(this->http_response.Append((uint)length), data, length);
00629 return;
00630 } else {
00631
00632 *this->http_response.Append() = '\0';
00633
00634
00635 this->http_response_index = 0;
00636 }
00637 }
00638
00639 if (data != NULL) {
00640
00641 if (fwrite(data, 1, length, this->curFile) != length) {
00642
00643 this->OnFailure();
00644 } else {
00645
00646 this->OnDownloadProgress(this->curInfo, (int)length);
00647 }
00648
00649 return;
00650 }
00651
00652 if (this->curFile != NULL) {
00653
00654 this->AfterDownload();
00655 }
00656
00657 if ((uint)this->http_response_index >= this->http_response.Length()) {
00658
00659
00660
00661 this->OnFailure();
00662 return;
00663 }
00664
00665 delete this->curInfo;
00666
00667 this->curInfo = new ContentInfo;
00668
00670 #define check_not_null(p) { if ((p) == NULL) { this->OnFailure(); return; } }
00671
00672 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
00673
00674 for (;;) {
00675 char *str = this->http_response.Begin() + this->http_response_index;
00676 char *p = strchr(str, '\n');
00677 check_and_terminate(p);
00678
00679
00680 this->http_response_index += (int)strlen(str) + 1;
00681
00682
00683 p = strchr(str, ',');
00684 check_and_terminate(p);
00685 this->curInfo->id = (ContentID)atoi(str);
00686
00687
00688 str = p + 1;
00689 p = strchr(str, ',');
00690 check_and_terminate(p);
00691 this->curInfo->type = (ContentType)atoi(str);
00692
00693
00694 str = p + 1;
00695 p = strchr(str, ',');
00696 check_and_terminate(p);
00697 this->curInfo->filesize = atoi(str);
00698
00699
00700 str = p + 1;
00701
00702 if (strncmp(str, "ottd", 4) == 0) {
00703 if ((uint)this->http_response_index >= this->http_response.Length()) {
00704
00705 this->OnFailure();
00706 return;
00707 }
00708 continue;
00709 }
00710
00711 p = strrchr(str, '/');
00712 check_not_null(p);
00713 p++;
00714
00715 char tmp[MAX_PATH];
00716 if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
00717 this->OnFailure();
00718 return;
00719 }
00720
00721 for (uint i = 0; i < 2; i++) {
00722 p = strrchr(tmp, '.');
00723 check_and_terminate(p);
00724 }
00725
00726
00727 strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
00728
00729
00730 if (!this->BeforeDownload()) {
00731 this->OnFailure();
00732 return;
00733 }
00734
00735 NetworkHTTPSocketHandler::Connect(str, this);
00736 return;
00737 }
00738
00739 #undef check
00740 #undef check_and_terminate
00741 }
00742
00746 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
00747 NetworkContentSocketHandler(),
00748 http_response_index(-2),
00749 curFile(NULL),
00750 curInfo(NULL),
00751 isConnecting(false)
00752 {
00753 }
00754
00756 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
00757 {
00758 delete this->curInfo;
00759 if (this->curFile != NULL) fclose(this->curFile);
00760
00761 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00762 }
00763
00765 class NetworkContentConnecter : TCPConnecter {
00766 public:
00771 NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00772
00773 virtual void OnFailure()
00774 {
00775 _network_content_client.isConnecting = false;
00776 _network_content_client.OnConnect(false);
00777 }
00778
00779 virtual void OnConnect(SOCKET s)
00780 {
00781 assert(_network_content_client.sock == INVALID_SOCKET);
00782 _network_content_client.isConnecting = false;
00783 _network_content_client.sock = s;
00784 _network_content_client.Reopen();
00785 _network_content_client.OnConnect(true);
00786 }
00787 };
00788
00792 void ClientNetworkContentSocketHandler::Connect()
00793 {
00794 this->lastActivity = _realtime_tick;
00795
00796 if (this->sock != INVALID_SOCKET || this->isConnecting) return;
00797 this->isConnecting = true;
00798 new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
00799 }
00800
00804 void ClientNetworkContentSocketHandler::Close()
00805 {
00806 if (this->sock == INVALID_SOCKET) return;
00807 NetworkContentSocketHandler::Close();
00808
00809 this->OnDisconnect();
00810 }
00811
00816 void ClientNetworkContentSocketHandler::SendReceive()
00817 {
00818 if (this->sock == INVALID_SOCKET || this->isConnecting) return;
00819
00820 if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
00821 this->Close();
00822 return;
00823 }
00824
00825 if (this->CanSendReceive()) {
00826 this->ReceivePackets();
00827 this->lastActivity = _realtime_tick;
00828 }
00829
00830 this->SendPackets();
00831 }
00832
00837 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
00838 {
00839
00840 if (this->requested.Contains(cid)) return;
00841
00842 *this->requested.Append() = cid;
00843 assert(this->requested.Contains(cid));
00844 this->RequestContentList(1, &cid);
00845 }
00846
00852 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
00853 {
00854 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00855 ContentInfo *ci = *iter;
00856 if (ci->id == cid) return ci;
00857 }
00858 return NULL;
00859 }
00860
00861
00866 void ClientNetworkContentSocketHandler::Select(ContentID cid)
00867 {
00868 ContentInfo *ci = this->GetContent(cid);
00869 if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
00870
00871 ci->state = ContentInfo::SELECTED;
00872 this->CheckDependencyState(ci);
00873 }
00874
00879 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
00880 {
00881 ContentInfo *ci = this->GetContent(cid);
00882 if (ci == NULL || !ci->IsSelected()) return;
00883
00884 ci->state = ContentInfo::UNSELECTED;
00885 this->CheckDependencyState(ci);
00886 }
00887
00889 void ClientNetworkContentSocketHandler::SelectAll()
00890 {
00891 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00892 ContentInfo *ci = *iter;
00893 if (ci->state == ContentInfo::UNSELECTED) {
00894 ci->state = ContentInfo::SELECTED;
00895 this->CheckDependencyState(ci);
00896 }
00897 }
00898 }
00899
00901 void ClientNetworkContentSocketHandler::SelectUpgrade()
00902 {
00903 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00904 ContentInfo *ci = *iter;
00905 if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
00906 ci->state = ContentInfo::SELECTED;
00907 this->CheckDependencyState(ci);
00908 }
00909 }
00910 }
00911
00913 void ClientNetworkContentSocketHandler::UnselectAll()
00914 {
00915 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00916 ContentInfo *ci = *iter;
00917 if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
00918 }
00919 }
00920
00922 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
00923 {
00924 switch (ci->state) {
00925 case ContentInfo::SELECTED:
00926 case ContentInfo::AUTOSELECTED:
00927 this->Unselect(ci->id);
00928 break;
00929
00930 case ContentInfo::UNSELECTED:
00931 this->Select(ci->id);
00932 break;
00933
00934 default:
00935 break;
00936 }
00937 }
00938
00944 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
00945 {
00946 for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00947 const ContentInfo *ci = *iter;
00948 if (ci == child) continue;
00949
00950 for (uint i = 0; i < ci->dependency_count; i++) {
00951 if (ci->dependencies[i] == child->id) {
00952 *parents.Append() = ci;
00953 break;
00954 }
00955 }
00956 }
00957 }
00958
00964 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
00965 {
00966 *tree.Append() = child;
00967
00968
00969
00970
00971
00972 for (uint i = 0; i < tree.Length(); i++) {
00973 ConstContentVector parents;
00974 this->ReverseLookupDependency(parents, tree[i]);
00975
00976 for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
00977 tree.Include(*piter);
00978 }
00979 }
00980 }
00981
00986 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
00987 {
00988 if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
00989
00990
00991
00992 for (uint i = 0; i < ci->dependency_count; i++) {
00993 ContentInfo *c = this->GetContent(ci->dependencies[i]);
00994 if (c == NULL) {
00995 this->DownloadContentInfo(ci->dependencies[i]);
00996 } else if (c->state == ContentInfo::UNSELECTED) {
00997 c->state = ContentInfo::AUTOSELECTED;
00998 this->CheckDependencyState(c);
00999 }
01000 }
01001 return;
01002 }
01003
01004 if (ci->state != ContentInfo::UNSELECTED) return;
01005
01006
01007
01008
01009
01010 ConstContentVector parents;
01011 this->ReverseLookupDependency(parents, ci);
01012 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01013 const ContentInfo *c = *iter;
01014 if (!c->IsSelected()) continue;
01015
01016 this->Unselect(c->id);
01017 }
01018
01019 for (uint i = 0; i < ci->dependency_count; i++) {
01020 const ContentInfo *c = this->GetContent(ci->dependencies[i]);
01021 if (c == NULL) {
01022 DownloadContentInfo(ci->dependencies[i]);
01023 continue;
01024 }
01025 if (c->state != ContentInfo::AUTOSELECTED) continue;
01026
01027
01028 parents.Clear();
01029 this->ReverseLookupDependency(parents, c);
01030
01031
01032 int sel_count = 0;
01033 bool force_selection = false;
01034 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01035 if ((*iter)->IsSelected()) sel_count++;
01036 if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
01037 }
01038 if (sel_count == 0) {
01039
01040 this->Unselect(c->id);
01041 continue;
01042 }
01043
01044 if (force_selection) continue;
01045
01046
01047 parents.Clear();
01048 this->ReverseLookupTreeDependency(parents, c);
01049
01050
01051 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01052 if ((*iter)->state != ContentInfo::SELECTED) continue;
01053
01054 force_selection = true;
01055 break;
01056 }
01057
01058
01059 if (force_selection) continue;
01060
01061
01062
01063
01064
01065 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01066 const ContentInfo *c = *iter;
01067 if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
01068 }
01069 for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01070 this->CheckDependencyState(this->GetContent((*iter)->id));
01071 }
01072 }
01073 }
01074
01076 void ClientNetworkContentSocketHandler::Clear()
01077 {
01078 for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
01079
01080 this->infos.Clear();
01081 this->requested.Clear();
01082 }
01083
01084
01085
01086 void ClientNetworkContentSocketHandler::OnConnect(bool success)
01087 {
01088 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01089 ContentCallback *cb = *iter;
01090 cb->OnConnect(success);
01091 if (iter != this->callbacks.End() && *iter == cb) iter++;
01092 }
01093 }
01094
01095 void ClientNetworkContentSocketHandler::OnDisconnect()
01096 {
01097 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01098 ContentCallback *cb = *iter;
01099 cb->OnDisconnect();
01100 if (iter != this->callbacks.End() && *iter == cb) iter++;
01101 }
01102 }
01103
01104 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
01105 {
01106 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01107 ContentCallback *cb = *iter;
01108 cb->OnReceiveContentInfo(ci);
01109 if (iter != this->callbacks.End() && *iter == cb) iter++;
01110 }
01111 }
01112
01113 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, int bytes)
01114 {
01115 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01116 ContentCallback *cb = *iter;
01117 cb->OnDownloadProgress(ci, bytes);
01118 if (iter != this->callbacks.End() && *iter == cb) iter++;
01119 }
01120 }
01121
01122 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
01123 {
01124 ContentInfo *ci = this->GetContent(cid);
01125 if (ci != NULL) {
01126 ci->state = ContentInfo::ALREADY_HERE;
01127 }
01128
01129 for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); ) {
01130 ContentCallback *cb = *iter;
01131 cb->OnDownloadComplete(cid);
01132 if (iter != this->callbacks.End() && *iter == cb) iter++;
01133 }
01134 }
01135
01136 #endif