OpenTTD
network_content.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../rev.h"
14 #include "../ai/ai.hpp"
15 #include "../game/game.hpp"
16 #include "../window_func.h"
17 #include "../error.h"
18 #include "../base_media_base.h"
19 #include "../settings_type.h"
20 #include "network_content.h"
21 
22 #include "table/strings.h"
23 
24 #if defined(WITH_ZLIB)
25 #include <zlib.h>
26 #endif
27 
28 #include "../safeguards.h"
29 
30 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
31 
34 
36 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
37 {
38  return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? ci->md5sum : nullptr) != nullptr;
39 }
40 
48 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
49 
51 {
52  ContentInfo *ci = new ContentInfo();
53  ci->type = (ContentType)p->Recv_uint8();
54  ci->id = (ContentID)p->Recv_uint32();
55  ci->filesize = p->Recv_uint32();
56 
57  p->Recv_string(ci->name, lengthof(ci->name));
58  p->Recv_string(ci->version, lengthof(ci->version));
59  p->Recv_string(ci->url, lengthof(ci->url));
61 
62  ci->unique_id = p->Recv_uint32();
63  for (uint j = 0; j < sizeof(ci->md5sum); j++) {
64  ci->md5sum[j] = p->Recv_uint8();
65  }
66 
67  ci->dependency_count = p->Recv_uint8();
68  ci->dependencies = MallocT<ContentID>(ci->dependency_count);
69  for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
70 
71  ci->tag_count = p->Recv_uint8();
72  ci->tags = MallocT<char[32]>(ci->tag_count);
73  for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
74 
75  if (!ci->IsValid()) {
76  delete ci;
77  this->Close();
78  return false;
79  }
80 
81  /* Find the appropriate check function */
82  HasProc proc = nullptr;
83  switch (ci->type) {
85  proc = HasGRFConfig;
86  break;
87 
89  proc = BaseGraphics::HasSet;
90  break;
91 
93  proc = BaseMusic::HasSet;
94  break;
95 
97  proc = BaseSounds::HasSet;
98  break;
99 
100  case CONTENT_TYPE_AI:
101  proc = AI::HasAI; break;
102  break;
103 
105  proc = AI::HasAILibrary; break;
106  break;
107 
108  case CONTENT_TYPE_GAME:
109  proc = Game::HasGame; break;
110  break;
111 
113  proc = Game::HasGameLibrary; break;
114  break;
115 
118  proc = HasScenario;
119  break;
120 
121  default:
122  break;
123  }
124 
125  if (proc != nullptr) {
126  if (proc(ci, true)) {
128  } else {
130  if (proc(ci, false)) ci->upgrade = true;
131  }
132  } else {
134  }
135 
136  /* Something we don't have and has filesize 0 does not exist in the system */
138 
139  /* Do we already have a stub for this? */
140  for (ContentInfo *ici : this->infos) {
141  if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
142  memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
143  /* Preserve the name if possible */
144  if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
145  if (ici->IsSelected()) ci->state = ici->state;
146 
147  /*
148  * As ici might be selected by the content window we cannot delete that.
149  * However, we want to keep most of the values of ci, except the values
150  * we (just) already preserved.
151  * So transfer data and ownership of allocated memory from ci to ici.
152  */
153  ici->TransferFrom(ci);
154  delete ci;
155 
156  this->OnReceiveContentInfo(ici);
157  return true;
158  }
159  }
160 
161  /* Missing content info? Don't list it */
162  if (ci->filesize == 0) {
163  delete ci;
164  return true;
165  }
166 
167  this->infos.push_back(ci);
168 
169  /* Incoming data means that we might need to reconsider dependencies */
170  for (ContentInfo *ici : this->infos) {
171  this->CheckDependencyState(ici);
172  }
173 
174  this->OnReceiveContentInfo(ci);
175 
176  return true;
177 }
178 
184 {
185  if (type == CONTENT_TYPE_END) {
196  return;
197  }
198 
199  this->Connect();
200 
202  p->Send_uint8 ((byte)type);
203  p->Send_uint32(_openttd_newgrf_version);
204 
205  this->SendPacket(p);
206 }
207 
214 {
215  this->Connect();
216 
217  while (count > 0) {
218  /* We can "only" send a limited number of IDs in a single packet.
219  * A packet begins with the packet size and a byte for the type.
220  * Then this packet adds a uint16 for the count in this packet.
221  * The rest of the packet can be used for the IDs. */
222  uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
223 
225  p->Send_uint16(p_count);
226 
227  for (uint i = 0; i < p_count; i++) {
228  p->Send_uint32(content_ids[i]);
229  }
230 
231  this->SendPacket(p);
232  count -= p_count;
233  content_ids += p_count;
234  }
235 }
236 
243 {
244  if (cv == nullptr) return;
245 
246  this->Connect();
247 
248  assert(cv->size() < 255);
249  assert(cv->size() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) /
250  (sizeof(uint8) + sizeof(uint32) + (send_md5sum ? /*sizeof(ContentInfo::md5sum)*/16 : 0)));
251 
253  p->Send_uint8((uint8)cv->size());
254 
255  for (const ContentInfo *ci : *cv) {
256  p->Send_uint8((byte)ci->type);
257  p->Send_uint32(ci->unique_id);
258  if (!send_md5sum) continue;
259 
260  for (uint j = 0; j < sizeof(ci->md5sum); j++) {
261  p->Send_uint8(ci->md5sum[j]);
262  }
263  }
264 
265  this->SendPacket(p);
266 
267  for (ContentInfo *ci : *cv) {
268  bool found = false;
269  for (ContentInfo *ci2 : this->infos) {
270  if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
271  (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
272  found = true;
273  break;
274  }
275  }
276  if (!found) {
277  this->infos.push_back(ci);
278  } else {
279  delete ci;
280  }
281  }
282 }
283 
290 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
291 {
292  bytes = 0;
293 
294  ContentIDList content;
295  for (const ContentInfo *ci : this->infos) {
296  if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
297 
298  content.push_back(ci->id);
299  bytes += ci->filesize;
300  }
301 
302  files = (uint)content.size();
303 
304  /* If there's nothing to download, do nothing. */
305  if (files == 0) return;
306 
308  this->DownloadSelectedContentFallback(content);
309  } else {
310  this->DownloadSelectedContentHTTP(content);
311  }
312 }
313 
319 {
320  uint count = (uint)content.size();
321 
322  /* Allocate memory for the whole request.
323  * Requests are "id\nid\n..." (as strings), so assume the maximum ID,
324  * which is uint32 so 10 characters long. Then the newlines and
325  * multiply that all with the count and then add the '\0'. */
326  uint bytes = (10 + 1) * count + 1;
327  char *content_request = MallocT<char>(bytes);
328  const char *lastof = content_request + bytes - 1;
329 
330  char *p = content_request;
331  for (const ContentID &id : content) {
332  p += seprintf(p, lastof, "%d\n", id);
333  }
334 
335  this->http_response_index = -1;
336 
338  new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
339  /* NetworkHTTPContentConnecter takes over freeing of content_request! */
340 }
341 
347 {
348  uint count = (uint)content.size();
349  const ContentID *content_ids = content.data();
350  this->Connect();
351 
352  while (count > 0) {
353  /* We can "only" send a limited number of IDs in a single packet.
354  * A packet begins with the packet size and a byte for the type.
355  * Then this packet adds a uint16 for the count in this packet.
356  * The rest of the packet can be used for the IDs. */
357  uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
358 
360  p->Send_uint16(p_count);
361 
362  for (uint i = 0; i < p_count; i++) {
363  p->Send_uint32(content_ids[i]);
364  }
365 
366  this->SendPacket(p);
367  count -= p_count;
368  content_ids += p_count;
369  }
370 }
371 
379 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
380 {
382  if (dir == NO_DIRECTORY) return nullptr;
383 
384  static char buf[MAX_PATH];
385  FioGetFullPath(buf, lastof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
386  strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
387 
388  return buf;
389 }
390 
396 static bool GunzipFile(const ContentInfo *ci)
397 {
398 #if defined(WITH_ZLIB)
399  bool ret = true;
400 
401  /* Need to open the file with fopen() to support non-ASCII on Windows. */
402  FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
403  if (ftmp == nullptr) return false;
404  /* Duplicate the handle, and close the FILE*, to avoid double-closing the handle later. */
405  gzFile fin = gzdopen(dup(fileno(ftmp)), "rb");
406  fclose(ftmp);
407 
408  FILE *fout = fopen(GetFullFilename(ci, false), "wb");
409 
410  if (fin == nullptr || fout == nullptr) {
411  ret = false;
412  } else {
413  byte buff[8192];
414  for (;;) {
415  int read = gzread(fin, buff, sizeof(buff));
416  if (read == 0) {
417  /* If gzread() returns 0, either the end-of-file has been
418  * reached or an underlying read error has occurred.
419  *
420  * gzeof() can't be used, because:
421  * 1.2.5 - it is safe, 1 means 'everything was OK'
422  * 1.2.3.5, 1.2.4 - 0 or 1 is returned 'randomly'
423  * 1.2.3.3 - 1 is returned for truncated archive
424  *
425  * So we use gzerror(). When proper end of archive
426  * has been reached, then:
427  * errnum == Z_STREAM_END in 1.2.3.3,
428  * errnum == 0 in 1.2.4 and 1.2.5 */
429  int errnum;
430  gzerror(fin, &errnum);
431  if (errnum != 0 && errnum != Z_STREAM_END) ret = false;
432  break;
433  }
434  if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
435  /* If gzread() returns -1, there was an error in archive */
436  ret = false;
437  break;
438  }
439  /* DO NOT DO THIS! It will fail to detect broken archive with 1.2.3.3!
440  * if (read < sizeof(buff)) break; */
441  }
442  }
443 
444  if (fin != nullptr) gzclose(fin);
445  if (fout != nullptr) fclose(fout);
446 
447  return ret;
448 #else
449  NOT_REACHED();
450 #endif /* defined(WITH_ZLIB) */
451 }
452 
454 {
455  if (this->curFile == nullptr) {
456  delete this->curInfo;
457  /* When we haven't opened a file this must be our first packet with metadata. */
458  this->curInfo = new ContentInfo;
459  this->curInfo->type = (ContentType)p->Recv_uint8();
460  this->curInfo->id = (ContentID)p->Recv_uint32();
461  this->curInfo->filesize = p->Recv_uint32();
462  p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
463 
464  if (!this->BeforeDownload()) {
465  this->Close();
466  return false;
467  }
468  } else {
469  /* We have a file opened, thus are downloading internal content */
470  size_t toRead = (size_t)(p->size - p->pos);
471  if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
473  ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
474  this->Close();
475  fclose(this->curFile);
476  this->curFile = nullptr;
477 
478  return false;
479  }
480 
481  this->OnDownloadProgress(this->curInfo, (int)toRead);
482 
483  if (toRead == 0) this->AfterDownload();
484  }
485 
486  return true;
487 }
488 
494 {
495  if (!this->curInfo->IsValid()) {
496  delete this->curInfo;
497  this->curInfo = nullptr;
498  return false;
499  }
500 
501  if (this->curInfo->filesize != 0) {
502  /* The filesize is > 0, so we are going to download it */
503  const char *filename = GetFullFilename(this->curInfo, true);
504  if (filename == nullptr || (this->curFile = fopen(filename, "wb")) == nullptr) {
505  /* Unless that fails of course... */
507  ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
508  return false;
509  }
510  }
511  return true;
512 }
513 
519 {
520  /* We read nothing; that's our marker for end-of-stream.
521  * Now gunzip the tar and make it known. */
522  fclose(this->curFile);
523  this->curFile = nullptr;
524 
525  if (GunzipFile(this->curInfo)) {
526  unlink(GetFullFilename(this->curInfo, true));
527 
529  if (sd == NO_DIRECTORY) NOT_REACHED();
530 
531  TarScanner ts;
532  ts.AddFile(sd, GetFullFilename(this->curInfo, false));
533 
534  if (this->curInfo->type == CONTENT_TYPE_BASE_MUSIC) {
535  /* Music can't be in a tar. So extract the tar! */
537  unlink(GetFullFilename(this->curInfo, false));
538  }
539 
540  this->OnDownloadComplete(this->curInfo->id);
541  } else {
542  ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);
543  }
544 }
545 
546 /* Also called to just clean up the mess. */
548 {
549  /* If we fail, download the rest via the 'old' system. */
550  uint files, bytes;
551  this->DownloadSelectedContent(files, bytes, true);
552 
553  this->http_response.clear();
554  this->http_response.shrink_to_fit();
555  this->http_response_index = -2;
556 
557  if (this->curFile != nullptr) {
558  /* Revert the download progress when we are going for the old system. */
559  long size = ftell(this->curFile);
560  if (size > 0) this->OnDownloadProgress(this->curInfo, (int)-size);
561 
562  fclose(this->curFile);
563  this->curFile = nullptr;
564  }
565 }
566 
567 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
568 {
569  assert(data == nullptr || length != 0);
570 
571  /* Ignore any latent data coming from a connection we closed. */
572  if (this->http_response_index == -2) return;
573 
574  if (this->http_response_index == -1) {
575  if (data != nullptr) {
576  /* Append the rest of the response. */
577  this->http_response.insert(this->http_response.end(), data, data + length);
578  return;
579  } else {
580  /* Make sure the response is properly terminated. */
581  this->http_response.push_back('\0');
582 
583  /* And prepare for receiving the rest of the data. */
584  this->http_response_index = 0;
585  }
586  }
587 
588  if (data != nullptr) {
589  /* We have data, so write it to the file. */
590  if (fwrite(data, 1, length, this->curFile) != length) {
591  /* Writing failed somehow, let try via the old method. */
592  this->OnFailure();
593  } else {
594  /* Just received the data. */
595  this->OnDownloadProgress(this->curInfo, (int)length);
596  }
597  /* Nothing more to do now. */
598  return;
599  }
600 
601  if (this->curFile != nullptr) {
602  /* We've finished downloading a file. */
603  this->AfterDownload();
604  }
605 
606  if ((uint)this->http_response_index >= this->http_response.size()) {
607  /* It's not a real failure, but if there's
608  * nothing more to download it helps with
609  * cleaning up the stuff we allocated. */
610  this->OnFailure();
611  return;
612  }
613 
614  delete this->curInfo;
615  /* When we haven't opened a file this must be our first packet with metadata. */
616  this->curInfo = new ContentInfo;
617 
619 #define check_not_null(p) { if ((p) == nullptr) { this->OnFailure(); return; } }
620 
621 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
622 
623  for (;;) {
624  char *str = this->http_response.data() + this->http_response_index;
625  char *p = strchr(str, '\n');
626  check_and_terminate(p);
627 
628  /* Update the index for the next one */
629  this->http_response_index += (int)strlen(str) + 1;
630 
631  /* Read the ID */
632  p = strchr(str, ',');
633  check_and_terminate(p);
634  this->curInfo->id = (ContentID)atoi(str);
635 
636  /* Read the type */
637  str = p + 1;
638  p = strchr(str, ',');
639  check_and_terminate(p);
640  this->curInfo->type = (ContentType)atoi(str);
641 
642  /* Read the file size */
643  str = p + 1;
644  p = strchr(str, ',');
645  check_and_terminate(p);
646  this->curInfo->filesize = atoi(str);
647 
648  /* Read the URL */
649  str = p + 1;
650  /* Is it a fallback URL? If so, just continue with the next one. */
651  if (strncmp(str, "ottd", 4) == 0) {
652  if ((uint)this->http_response_index >= this->http_response.size()) {
653  /* Have we gone through all lines? */
654  this->OnFailure();
655  return;
656  }
657  continue;
658  }
659 
660  p = strrchr(str, '/');
661  check_not_null(p);
662  p++; // Start after the '/'
663 
664  char tmp[MAX_PATH];
665  if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
666  this->OnFailure();
667  return;
668  }
669  /* Remove the extension from the string. */
670  for (uint i = 0; i < 2; i++) {
671  p = strrchr(tmp, '.');
672  check_and_terminate(p);
673  }
674 
675  /* Copy the string, without extension, to the filename. */
676  strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
677 
678  /* Request the next file. */
679  if (!this->BeforeDownload()) {
680  this->OnFailure();
681  return;
682  }
683 
685  return;
686  }
687 
688 #undef check
689 #undef check_and_terminate
690 }
691 
698  curFile(nullptr),
699  curInfo(nullptr),
700  isConnecting(false),
702 {
703 }
704 
707 {
708  delete this->curInfo;
709  if (this->curFile != nullptr) fclose(this->curFile);
710 
711  for (ContentInfo *ci : this->infos) delete ci;
712 }
713 
716 public:
722 
723  void OnFailure() override
724  {
725  _network_content_client.isConnecting = false;
726  _network_content_client.OnConnect(false);
727  }
728 
729  void OnConnect(SOCKET s) override
730  {
731  assert(_network_content_client.sock == INVALID_SOCKET);
732  _network_content_client.isConnecting = false;
733  _network_content_client.sock = s;
734  _network_content_client.Reopen();
735  _network_content_client.OnConnect(true);
736  }
737 };
738 
743 {
745 
746  if (this->sock != INVALID_SOCKET || this->isConnecting) return;
747  this->isConnecting = true;
749 }
750 
755 {
756  if (this->sock == INVALID_SOCKET) return;
758 
759  this->OnDisconnect();
760 }
761 
767 {
768  if (this->sock == INVALID_SOCKET || this->isConnecting) return;
769 
770  if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
771  this->Close();
772  return;
773  }
774 
775  if (this->CanSendReceive()) {
776  if (this->ReceivePackets()) {
777  /* Only update activity once a packet is received, instead of every time we try it. */
779  }
780  }
781 
782  this->SendPackets();
783 }
784 
790 {
791  /* When we tried to download it already, don't try again */
792  if (std::find(this->requested.begin(), this->requested.end(), cid) != this->requested.end()) return;
793 
794  this->requested.push_back(cid);
795  this->RequestContentList(1, &cid);
796 }
797 
804 {
805  for (ContentInfo *ci : this->infos) {
806  if (ci->id == cid) return ci;
807  }
808  return nullptr;
809 }
810 
811 
817 {
818  ContentInfo *ci = this->GetContent(cid);
819  if (ci == nullptr || ci->state != ContentInfo::UNSELECTED) return;
820 
822  this->CheckDependencyState(ci);
823 }
824 
830 {
831  ContentInfo *ci = this->GetContent(cid);
832  if (ci == nullptr || !ci->IsSelected()) return;
833 
835  this->CheckDependencyState(ci);
836 }
837 
840 {
841  for (ContentInfo *ci : this->infos) {
842  if (ci->state == ContentInfo::UNSELECTED) {
843  ci->state = ContentInfo::SELECTED;
844  this->CheckDependencyState(ci);
845  }
846  }
847 }
848 
851 {
852  for (ContentInfo *ci : this->infos) {
853  if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
854  ci->state = ContentInfo::SELECTED;
855  this->CheckDependencyState(ci);
856  }
857  }
858 }
859 
862 {
863  for (ContentInfo *ci : this->infos) {
864  if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
865  }
866 }
867 
870 {
871  switch (ci->state) {
874  this->Unselect(ci->id);
875  break;
876 
878  this->Select(ci->id);
879  break;
880 
881  default:
882  break;
883  }
884 }
885 
892 {
893  for (const ContentInfo * const &ci : this->infos) {
894  if (ci == child) continue;
895 
896  for (uint i = 0; i < ci->dependency_count; i++) {
897  if (ci->dependencies[i] == child->id) {
898  parents.push_back(ci);
899  break;
900  }
901  }
902  }
903 }
904 
911 {
912  tree.push_back(child);
913 
914  /* First find all direct parents. We can't use the "normal" iterator as
915  * we are including stuff into the vector and as such the vector's data
916  * store can be reallocated (and thus move), which means out iterating
917  * pointer gets invalid. So fall back to the indices. */
918  for (uint i = 0; i < tree.size(); i++) {
919  ConstContentVector parents;
920  this->ReverseLookupDependency(parents, tree[i]);
921 
922  for (const ContentInfo *ci : parents) {
923  include(tree, ci);
924  }
925  }
926 }
927 
933 {
934  if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
935  /* Selection is easy; just walk all children and set the
936  * autoselected state. That way we can see what we automatically
937  * selected and thus can unselect when a dependency is removed. */
938  for (uint i = 0; i < ci->dependency_count; i++) {
939  ContentInfo *c = this->GetContent(ci->dependencies[i]);
940  if (c == nullptr) {
941  this->DownloadContentInfo(ci->dependencies[i]);
942  } else if (c->state == ContentInfo::UNSELECTED) {
944  this->CheckDependencyState(c);
945  }
946  }
947  return;
948  }
949 
950  if (ci->state != ContentInfo::UNSELECTED) return;
951 
952  /* For unselection we need to find the parents of us. We need to
953  * unselect them. After that we unselect all children that we
954  * depend on and are not used as dependency for us, but only when
955  * we automatically selected them. */
956  ConstContentVector parents;
957  this->ReverseLookupDependency(parents, ci);
958  for (const ContentInfo *c : parents) {
959  if (!c->IsSelected()) continue;
960 
961  this->Unselect(c->id);
962  }
963 
964  for (uint i = 0; i < ci->dependency_count; i++) {
965  const ContentInfo *c = this->GetContent(ci->dependencies[i]);
966  if (c == nullptr) {
968  continue;
969  }
970  if (c->state != ContentInfo::AUTOSELECTED) continue;
971 
972  /* Only unselect when WE are the only parent. */
973  parents.clear();
974  this->ReverseLookupDependency(parents, c);
975 
976  /* First check whether anything depends on us */
977  int sel_count = 0;
978  bool force_selection = false;
979  for (const ContentInfo *ci : parents) {
980  if (ci->IsSelected()) sel_count++;
981  if (ci->state == ContentInfo::SELECTED) force_selection = true;
982  }
983  if (sel_count == 0) {
984  /* Nothing depends on us */
985  this->Unselect(c->id);
986  continue;
987  }
988  /* Something manually selected depends directly on us */
989  if (force_selection) continue;
990 
991  /* "Flood" search to find all items in the dependency graph*/
992  parents.clear();
993  this->ReverseLookupTreeDependency(parents, c);
994 
995  /* Is there anything that is "force" selected?, if so... we're done. */
996  for (const ContentInfo *ci : parents) {
997  if (ci->state != ContentInfo::SELECTED) continue;
998 
999  force_selection = true;
1000  break;
1001  }
1002 
1003  /* So something depended directly on us */
1004  if (force_selection) continue;
1005 
1006  /* Nothing depends on us, mark the whole graph as unselected.
1007  * After that's done run over them once again to test their children
1008  * to unselect. Don't do it immediately because it'll do exactly what
1009  * we're doing now. */
1010  for (const ContentInfo *c : parents) {
1011  if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
1012  }
1013  for (const ContentInfo *c : parents) {
1014  this->CheckDependencyState(this->GetContent(c->id));
1015  }
1016  }
1017 }
1018 
1021 {
1022  for (ContentInfo *c : this->infos) delete c;
1023 
1024  this->infos.clear();
1025  this->requested.clear();
1026 }
1027 
1028 /*** CALLBACK ***/
1029 
1031 {
1032  for (size_t i = 0; i < this->callbacks.size(); /* nothing */) {
1033  ContentCallback *cb = this->callbacks[i];
1034  /* the callback may remove itself from this->callbacks */
1035  cb->OnConnect(success);
1036  if (i != this->callbacks.size() && this->callbacks[i] == cb) i++;
1037  }
1038 }
1039 
1041 {
1042  for (size_t i = 0; i < this->callbacks.size(); /* nothing */) {
1043  ContentCallback *cb = this->callbacks[i];
1044  cb->OnDisconnect();
1045  if (i != this->callbacks.size() && this->callbacks[i] == cb) i++;
1046  }
1047 }
1048 
1050 {
1051  for (size_t i = 0; i < this->callbacks.size(); /* nothing */) {
1052  ContentCallback *cb = this->callbacks[i];
1053  /* the callback may add items and/or remove itself from this->callbacks */
1054  cb->OnReceiveContentInfo(ci);
1055  if (i != this->callbacks.size() && this->callbacks[i] == cb) i++;
1056  }
1057 }
1058 
1060 {
1061  for (size_t i = 0; i < this->callbacks.size(); /* nothing */) {
1062  ContentCallback *cb = this->callbacks[i];
1063  cb->OnDownloadProgress(ci, bytes);
1064  if (i != this->callbacks.size() && this->callbacks[i] == cb) i++;
1065  }
1066 }
1067 
1069 {
1070  ContentInfo *ci = this->GetContent(cid);
1071  if (ci != nullptr) {
1073  }
1074 
1075  for (size_t i = 0; i < this->callbacks.size(); /* nothing */) {
1076  ContentCallback *cb = this->callbacks[i];
1077  /* the callback may remove itself from this->callbacks */
1078  cb->OnDownloadComplete(cid);
1079  if (i != this->callbacks.size() && this->callbacks[i] == cb) i++;
1080  }
1081 }
void Close() override
Disconnect from the content server.
Helper to mark the end of the types.
Definition: tcp_content.h:35
ContentVector infos
All content info we received.
void CheckDependencyState(ContentInfo *ci)
Check the dependencies (recursively) of this content info.
The content consists of base graphics.
Definition: tcp_content.h:25
bool IsSelected() const
Is the state either selected or autoselected?
Definition: tcp_content.cpp:75
Connect with a HTTP server and do ONE query.
Definition: tcp_http.h:77
void DownloadSelectedContent(uint &files, uint &bytes, bool fallback=false)
Actually begin downloading the content we selected.
char filename[48]
Filename (for the .tar.gz; only valid on download)
Definition: tcp_content.h:70
SOCKET sock
The socket currently connected to.
Definition: tcp.h:34
uint32 unique_id
Unique ID; either GRF ID or shortname.
Definition: tcp_content.h:75
uint32 _realtime_tick
The real time in the game.
Definition: debug.cpp:50
Internal entity of a packet.
Definition: packet.h:42
std::vector< char > http_response
The HTTP response to the requests we&#39;ve been doing.
Queries the content server for information about a list of external IDs.
Definition: tcp_content.h:42
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: depend.cpp:99
bool Receive_SERVER_INFO(Packet *p) override
Server sending list of content info: byte type (invalid ID == does not exist) uint32 id uint32 file_s...
Socket handler for the content server connection.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:110
uint32 Recv_uint32()
Read a 32 bits integer from the packet.
Definition: packet.cpp:248
PacketSize pos
The current read/write position in the packet.
Definition: packet.h:52
"Helper" class for creating TCP connections in a non-blocking manner
Definition: tcp.h:64
static bool GunzipFile(const ContentInfo *ci)
Gunzip a given file and remove the .gz if successful.
Base socket handler for all Content TCP sockets.
Definition: tcp_content.h:98
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:409
std::vector< ContentCallback * > callbacks
Callbacks to notify "the world".
Callbacks for notifying others about incoming data.
uint32 filesize
Size of the file.
Definition: tcp_content.h:69
void Connect()
Connect with the content server.
The content consists of a scenario.
Definition: tcp_content.h:29
void DownloadContentInfo(ContentID cid)
Download information of a given Content ID if not already tried.
void OnDownloadComplete(ContentID cid) override
We have finished downloading a file.
uint8 dependency_count
Number of dependencies.
Definition: tcp_content.h:77
void Send_uint8(uint8 data)
Package a 8 bits integer in the packet.
Definition: packet.cpp:98
char(* tags)[32]
Malloced array of tags (strings)
Definition: tcp_content.h:80
static bool HasGame(const struct ContentInfo *ci, bool md5sum)
Wrapper function for GameScanner::HasGame.
Definition: game_core.cpp:266
bool(* HasProc)(const ContentInfo *ci, bool md5sum)
Check whether a function piece of content is locally known.
void SendReceive()
Check whether we received/can send some data from/to the content server and when that&#39;s the case hand...
void ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
Reverse lookup the dependencies of all parents over a given child.
void OnDisconnect() override
Callback for when the connection got disconnected.
Wrapper for (un)resolved network addresses; there&#39;s no reason to transform a numeric IP to a string a...
Definition: address.h:29
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=nullptr, uint textref_stack_size=0, const uint32 *textref_stack=nullptr)
Display an error message in a window.
Definition: error_gui.cpp:382
ContentID * dependencies
Malloced array of dependencies (unique server side ids)
Definition: tcp_content.h:78
Helper for scanning for files with tar as extension.
Definition: fileio_func.h:94
The content has been selected as dependency.
Definition: tcp_content.h:61
void Send_uint32(uint32 data)
Package a 32 bits integer in the packet.
Definition: packet.cpp:119
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
void OnDownloadProgress(const ContentInfo *ci, int bytes) override
We have progress in the download of a file.
void OnConnect(bool success) override
Callback for when the connection has finished.
const GRFConfig * FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const uint8 *md5sum, uint32 desired_version)
Find a NewGRF in the scanned list.
void OnFailure() override
An error has occurred and the connection has been closed.
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:118
ContentID
Unique identifier for the content.
Definition: tcp_content.h:51
The content has not been selected.
Definition: tcp_content.h:59
Queries the content server for information about a list of internal IDs.
Definition: tcp_content.h:41
void Reopen()
Reopen the socket so we can send/receive stuff again.
Definition: core.h:74
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition: tcp.cpp:97
void Unselect(ContentID cid)
Unselect a specific content id.
The content does not exist in the content system.
Definition: tcp_content.h:63
bool ReceivePackets()
Receive a packet at TCP level.
void UnselectAll()
Unselect everything that we&#39;ve not downloaded so far.
bool isConnecting
Whether we&#39;re connecting.
static int Connect(char *uri, HTTPCallback *callback, const char *data=nullptr, int depth=0)
Connect to the given URI.
Definition: tcp_http.cpp:195
char name[32]
Name of the content.
Definition: tcp_content.h:71
virtual void OnDisconnect()
Callback for when the connection got disconnected.
The content consists of a game script.
Definition: tcp_content.h:33
NetworkSettings network
settings related to the network
std::vector< ContentID > ContentIDList
List of content IDs to (possibly) select.
~ClientNetworkContentSocketHandler()
Clear up the mess ;)
byte * buffer
The buffer of this packet, of basically variable length up to SEND_MTU.
Definition: packet.h:54
std::vector< ContentInfo * > ContentVector
Vector with content info.
uint8 tag_count
Number of tags.
Definition: tcp_content.h:79
char version[16]
Version of the content.
Definition: tcp_content.h:72
void SelectUpgrade()
Select everything that&#39;s an update for something we&#39;ve got.
void Send_uint16(uint16 data)
Package a 16 bits integer in the packet.
Definition: packet.cpp:108
uint32 lastActivity
The last time there was network activity.
static const char *const NETWORK_CONTENT_SERVER_HOST
DNS hostname of the content server.
Definition: config.h:20
virtual void OnDownloadProgress(const ContentInfo *ci, int bytes)
We have progress in the download of a file.
static char * GetFullFilename(const ContentInfo *ci, bool compressed)
Determine the full filename of a piece of content information.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:80
A path without any base directory.
Definition: fileio_type.h:127
The content is already at the client side.
Definition: tcp_content.h:62
ContentIDList requested
ContentIDs we already requested (so we don&#39;t do it again)
Allow newlines.
Definition: string_type.h:53
ContentID id
Unique (server side) ID for the content.
Definition: tcp_content.h:68
Connect to the content server.
void Clear()
Clear all downloaded content information.
void RequestContentList(ContentType type)
Request the content list for the given type.
static const char *const NETWORK_CONTENT_MIRROR_HOST
DNS hostname of the HTTP-content mirror server.
Definition: config.h:22
State state
Whether the content info is selected (for download)
Definition: tcp_content.h:81
byte * buf
Buffer we&#39;re going to write to/read from.
Search within the autodownload directory.
Definition: fileio_type.h:144
Part of the network protocol handling content distribution.
PacketSize size
The size of the whole packet for received packets.
Definition: packet.h:50
void OnReceiveContentInfo(const ContentInfo *ci) override
We received a content info.
void Close() override
Really close the socket.
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:42
static const int IDLE_TIMEOUT
The idle timeout; when to close the connection because it&#39;s idle.
Queries the content server for a list of info of a given content type.
Definition: tcp_content.h:40
byte md5sum[16]
The MD5 checksum.
Definition: tcp_content.h:76
The content consists of a GS library.
Definition: tcp_content.h:34
bool BeforeDownload()
Handle the opening of the file before downloading.
static bool HasAI(const struct ContentInfo *ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition: ai_core.cpp:371
ContentInfo * GetContent(ContentID cid)
Get the content info based on a ContentID.
The content consists of a NewGRF.
Definition: tcp_content.h:26
bool Receive_SERVER_CONTENT(Packet *p) override
Server sending list of content info: uint32 unique id uint32 file size (0 == does not exist) string f...
bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename=nullptr) override
Add a file with the given filename.
Definition: fileio.cpp:658
ContentType
The values in the enum are important; they are used as database &#39;keys&#39;.
Definition: tcp_content.h:23
Network status window; Window numbers:
Definition: window_type.h:487
static bool HasSet(const ContentInfo *ci, bool md5sum)
Check whether we have an set with the exact characteristics as ci.
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
Delete a window by its class and window number (if it is open).
Definition: window.cpp:1146
Queries the content server for information about a list of external IDs and MD5.
Definition: tcp_content.h:43
void OnConnect(SOCKET s) override
Callback when the connection succeeded.
void Select(ContentID cid)
Select a specific content id.
The content consists of an AI library.
Definition: tcp_content.h:28
uint8 Recv_uint8()
Read a 8 bits integer from the packet.
Definition: packet.cpp:219
void SelectAll()
Select everything we can select.
void ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
Reverse lookup the dependencies of (direct) parents over a given child.
Request a content file given an internal ID.
Definition: tcp_content.h:45
static const char *const NETWORK_CONTENT_MIRROR_URL
URL of the HTTP mirror system.
Definition: config.h:24
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:59
Replace the unknown/bad bits with question marks.
Definition: string_type.h:52
The content consists of a heightmap.
Definition: tcp_content.h:30
The content consists of an AI.
Definition: tcp_content.h:27
bool include(std::vector< T > &vec, const T &item)
Helper function to append an item to a vector if it is not already contained Consider using std::set...
ContentType type
Type of content.
Definition: tcp_content.h:67
bool upgrade
This item is an upgrade.
Definition: tcp_content.h:82
void DownloadSelectedContentFallback(const ContentIDList &content)
Initiate downloading the content over the fallback protocol.
void OnFailure() override
Callback for when the connection attempt failed.
void AfterDownload()
Handle the closing and extracting of a file after downloading it has been done.
static const uint16 NETWORK_CONTENT_MIRROR_PORT
The default port of the content mirror (TCP)
Definition: config.h:30
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:68
The content consists of base music.
Definition: tcp_content.h:32
bool no_http_content_downloads
do not do content downloads over HTTP
virtual void OnReceiveContentInfo(const ContentInfo *ci)
We received a content info.
bool HasScenario(const ContentInfo *ci, bool md5sum)
Check whether we&#39;ve got a given scenario based on its unique ID.
Definition: fios.cpp:763
ClientNetworkContentSocketHandler()
Create a socket handler to handle the connection.
static const uint16 SEND_MTU
Number of bytes we can pack in a single packet.
Definition: config.h:35
NetworkContentConnecter(const NetworkAddress &address)
Initiate the connecting.
ContentInfo * curInfo
Information about the currently downloaded file.
virtual void OnConnect(bool success)
Callback for when the connection has finished.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition: strings_type.h:19
bool ExtractTar(const char *tar_filename, Subdirectory subdir)
Extract the tar with the given filename in the directory where the tar resides.
Definition: fileio.cpp:888
The content has been manually selected.
Definition: tcp_content.h:60
virtual void SendPacket(Packet *packet)
This function puts the packet in the send-queue and it is send as soon as possible.
Definition: tcp.cpp:63
static const uint16 NETWORK_CONTENT_SERVER_PORT
The default port of the content server (TCP)
Definition: config.h:29
int http_response_index
Where we are, in the response, with handling it.
virtual void OnDownloadComplete(ContentID cid)
We have finished downloading a file.
void OnReceiveData(const char *data, size_t length) override
We&#39;re receiving data.
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition: tcp.cpp:227
static uint32 BSWAP32(uint32 x)
Perform a 32 bits endianness bitswap on x.
Only find Grfs matching md5sum.
Subdirectory GetContentInfoSubDir(ContentType type)
Helper to get the subdirectory a ContentInfo is located in.
char url[96]
URL related to the content.
Definition: tcp_content.h:73
static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
Wrapper function for the HasProc.
Errors (eg. saving/loading failed)
Definition: error.h:25
void DownloadSelectedContentHTTP(const ContentIDList &content)
Initiate downloading the content over HTTP.
char description[512]
Description of the content.
Definition: tcp_content.h:74
uint16 PacketSize
Size of the whole packet.
Definition: packet.h:21
FILE * curFile
Currently downloaded file.
Container for all important information about a piece of content.
Definition: tcp_content.h:56
bool IsValid() const
Is the information from this content info valid?
Definition: tcp_content.cpp:92
Network content download status.
Definition: window_type.h:35
ClientNetworkContentSocketHandler _network_content_client
The client we use to connect to the server.
Use first found.
void Recv_string(char *buffer, size_t size, StringValidationSettings settings=SVS_REPLACE_WITH_QUESTION_MARK)
Reads a string till it finds a &#39;\0&#39; in the stream.
Definition: packet.cpp:288
std::vector< const ContentInfo * > ConstContentVector
Vector with constant content info.
void ToggleSelectedState(const ContentInfo *ci)
Toggle the state of a content info and check its dependencies.
The content consists of base sounds.
Definition: tcp_content.h:31