OpenTTD
network_client.cpp
Go to the documentation of this file.
1 /* $Id: network_client.cpp 27653 2016-09-04 16:06:50Z alberth $ */
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 #ifdef ENABLE_NETWORK
13 
14 #include "../stdafx.h"
15 #include "network_gui.h"
16 #include "../saveload/saveload.h"
17 #include "../saveload/saveload_filter.h"
18 #include "../command_func.h"
19 #include "../console_func.h"
20 #include "../strings_func.h"
21 #include "../window_func.h"
22 #include "../company_func.h"
23 #include "../company_base.h"
24 #include "../company_gui.h"
25 #include "../core/random_func.hpp"
26 #include "../date_func.h"
27 #include "../gfx_func.h"
28 #include "../error.h"
29 #include "../rev.h"
30 #include "network.h"
31 #include "network_base.h"
32 #include "network_client.h"
33 #include "../core/backup_type.hpp"
34 
35 #include "table/strings.h"
36 
37 #include "../safeguards.h"
38 
39 /* This file handles all the client-commands */
40 
41 
44  static const size_t CHUNK = 32 * 1024;
45 
47  byte *buf;
48  byte *bufe;
49  byte **block;
50  size_t written_bytes;
51  size_t read_bytes;
52 
54  PacketReader() : LoadFilter(NULL), buf(NULL), bufe(NULL), block(NULL), written_bytes(0), read_bytes(0)
55  {
56  }
57 
62  void AddPacket(const Packet *p)
63  {
64  assert(this->read_bytes == 0);
65 
66  size_t in_packet = p->size - p->pos;
67  size_t to_write = min((size_t)(this->bufe - this->buf), in_packet);
68  const byte *pbuf = p->buffer + p->pos;
69 
70  this->written_bytes += in_packet;
71  if (to_write != 0) {
72  memcpy(this->buf, pbuf, to_write);
73  this->buf += to_write;
74  }
75 
76  /* Did everything fit in the current chunk, then we're done. */
77  if (to_write == in_packet) return;
78 
79  /* Allocate a new chunk and add the remaining data. */
80  pbuf += to_write;
81  to_write = in_packet - to_write;
82  this->buf = *this->blocks.Append() = CallocT<byte>(CHUNK);
83  this->bufe = this->buf + CHUNK;
84 
85  memcpy(this->buf, pbuf, to_write);
86  this->buf += to_write;
87  }
88 
89  /* virtual */ size_t Read(byte *rbuf, size_t size)
90  {
91  /* Limit the amount to read to whatever we still have. */
92  size_t ret_size = size = min(this->written_bytes - this->read_bytes, size);
93  this->read_bytes += ret_size;
94  const byte *rbufe = rbuf + ret_size;
95 
96  while (rbuf != rbufe) {
97  if (this->buf == this->bufe) {
98  this->buf = *this->block++;
99  this->bufe = this->buf + CHUNK;
100  }
101 
102  size_t to_write = min(this->bufe - this->buf, rbufe - rbuf);
103  memcpy(rbuf, this->buf, to_write);
104  rbuf += to_write;
105  this->buf += to_write;
106  }
107 
108  return ret_size;
109  }
110 
111  /* virtual */ void Reset()
112  {
113  this->read_bytes = 0;
114 
115  this->block = this->blocks.Begin();
116  this->buf = *this->block++;
117  this->bufe = this->buf + CHUNK;
118  }
119 };
120 
121 
127 {
130 }
131 
134 {
137 
138  delete this->savegame;
139 }
140 
142 {
143  assert(status != NETWORK_RECV_STATUS_OKAY);
144  /*
145  * Sending a message just before leaving the game calls cs->SendPackets.
146  * This might invoke this function, which means that when we close the
147  * connection after cs->SendPackets we will close an already closed
148  * connection. This handles that case gracefully without having to make
149  * that code any more complex or more aware of the validity of the socket.
150  */
151  if (this->sock == INVALID_SOCKET) return status;
152 
153  DEBUG(net, 1, "Closed client connection %d", this->client_id);
154 
155  this->SendPackets(true);
156 
157  /* Wait a number of ticks so our leave message can reach the server.
158  * This is especially needed for Windows servers as they seem to get
159  * the "socket is closed" message before receiving our leave message,
160  * which would trigger the server to close the connection as well. */
161  CSleep(3 * MILLISECONDS_PER_TICK);
162 
163  delete this->GetInfo();
164  delete this;
165 
166  return status;
167 }
168 
174 {
175  /* First, send a CLIENT_ERROR to the server, so he knows we are
176  * disconnection (and why!) */
177  NetworkErrorCode errorno;
178 
179  /* We just want to close the connection.. */
180  if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
182  this->CloseConnection(res);
183  _networking = false;
184 
186  return;
187  }
188 
189  switch (res) {
190  case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break;
191  case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
192  case NETWORK_RECV_STATUS_NEWGRF_MISMATCH: errorno = NETWORK_ERROR_NEWGRF_MISMATCH; break;
193  default: errorno = NETWORK_ERROR_GENERAL; break;
194  }
195 
196  /* This means we fucked up and the server closed the connection */
199  SendError(errorno);
200  }
201 
203  this->CloseConnection(res);
204  _networking = false;
205 }
206 
207 
214 {
215  if (my_client->CanSendReceive()) {
217  if (res != NETWORK_RECV_STATUS_OKAY) {
218  /* The client made an error of which we can not recover.
219  * Close the connection and drop back to the main menu. */
220  my_client->ClientError(res);
221  return false;
222  }
223  }
224  return _networking;
225 }
226 
229 {
232 }
233 
239 {
240  _frame_counter++;
241 
243 
244  extern void StateGameLoop();
245  StateGameLoop();
246 
247  /* Check if we are in sync! */
248  if (_sync_frame != 0) {
249  if (_sync_frame == _frame_counter) {
250 #ifdef NETWORK_SEND_DOUBLE_SEED
251  if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
252 #else
253  if (_sync_seed_1 != _random.state[0]) {
254 #endif
255  NetworkError(STR_NETWORK_ERROR_DESYNC);
256  DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract);
257  DEBUG(net, 0, "Sync error detected!");
259  return false;
260  }
261 
262  /* If this is the first time we have a sync-frame, we
263  * need to let the server know that we are ready and at the same
264  * frame as he is.. so we can start playing! */
265  if (_network_first_time) {
266  _network_first_time = false;
267  SendAck();
268  }
269 
270  _sync_frame = 0;
271  } else if (_sync_frame < _frame_counter) {
272  DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
273  _sync_frame = 0;
274  }
275  }
276 
277  return true;
278 }
279 
280 
283 
285 static uint32 last_ack_frame;
286 
288 static uint32 _password_game_seed;
291 
296 
299 
301 const char *_network_join_server_password = NULL;
304 
306 assert_compile(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
307 
308 /***********
309  * Sending functions
310  * DEF_CLIENT_SEND_COMMAND has no parameters
311  ************/
312 
315 {
317  _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
319 
321  my_client->SendPacket(p);
323 }
324 
327 {
329  _network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING;
331 
332  Packet *p = new Packet(PACKET_CLIENT_JOIN);
333  p->Send_string(_openttd_revision);
334  p->Send_uint32(_openttd_newgrf_version);
335  p->Send_string(_settings_client.network.client_name); // Client name
336  p->Send_uint8 (_network_join_as); // PlayAs
337  p->Send_uint8 (NETLANG_ANY); // Language
338  my_client->SendPacket(p);
340 }
341 
344 {
346  my_client->SendPacket(p);
348 }
349 
355 {
357  p->Send_string(password);
358  my_client->SendPacket(p);
360 }
361 
367 {
369  p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
370  my_client->SendPacket(p);
372 }
373 
376 {
378 
380  my_client->SendPacket(p);
382 }
383 
386 {
388 
390  my_client->SendPacket(p);
392 }
393 
396 {
397  Packet *p = new Packet(PACKET_CLIENT_ACK);
398 
400  p->Send_uint8 (my_client->token);
401  my_client->SendPacket(p);
403 }
404 
410 {
412  my_client->NetworkGameSocketHandler::SendCommand(p, cp);
413 
414  my_client->SendPacket(p);
416 }
417 
419 NetworkRecvStatus ClientNetworkGameSocketHandler::SendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data)
420 {
421  Packet *p = new Packet(PACKET_CLIENT_CHAT);
422 
423  p->Send_uint8 (action);
424  p->Send_uint8 (type);
425  p->Send_uint32(dest);
426  p->Send_string(msg);
427  p->Send_uint64(data);
428 
429  my_client->SendPacket(p);
431 }
432 
435 {
437 
438  p->Send_uint8(errorno);
439  my_client->SendPacket(p);
441 }
442 
448 {
450 
451  p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
452  my_client->SendPacket(p);
454 }
455 
461 {
463 
464  p->Send_string(name);
465  my_client->SendPacket(p);
467 }
468 
473 {
474  Packet *p = new Packet(PACKET_CLIENT_QUIT);
475 
476  my_client->SendPacket(p);
478 }
479 
485 NetworkRecvStatus ClientNetworkGameSocketHandler::SendRCon(const char *pass, const char *command)
486 {
487  Packet *p = new Packet(PACKET_CLIENT_RCON);
488  p->Send_string(pass);
489  p->Send_string(command);
490  my_client->SendPacket(p);
492 }
493 
500 {
501  Packet *p = new Packet(PACKET_CLIENT_MOVE);
502  p->Send_uint8(company);
503  p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
504  my_client->SendPacket(p);
506 }
507 
513 {
514  return my_client != NULL && my_client->status == STATUS_ACTIVE;
515 }
516 
517 
518 /***********
519  * Receiving functions
520  * DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p
521  ************/
522 
523 extern bool SafeLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL);
524 
526 {
527  /* We try to join a server which is full */
528  ShowErrorMessage(STR_NETWORK_ERROR_SERVER_FULL, INVALID_STRING_ID, WL_CRITICAL);
530 
532 }
533 
535 {
536  /* We try to join a server where we are banned */
537  ShowErrorMessage(STR_NETWORK_ERROR_SERVER_BANNED, INVALID_STRING_ID, WL_CRITICAL);
539 
541 }
542 
544 {
546 
547  byte company_info_version = p->Recv_uint8();
548 
549  if (!this->HasClientQuit() && company_info_version == NETWORK_COMPANY_INFO_VERSION) {
550  /* We have received all data... (there are no more packets coming) */
551  if (!p->Recv_bool()) return NETWORK_RECV_STATUS_CLOSE_QUERY;
552 
553  CompanyID current = (Owner)p->Recv_uint8();
554  if (current >= MAX_COMPANIES) return NETWORK_RECV_STATUS_CLOSE_QUERY;
555 
556  NetworkCompanyInfo *company_info = GetLobbyCompanyInfo(current);
557  if (company_info == NULL) return NETWORK_RECV_STATUS_CLOSE_QUERY;
558 
559  p->Recv_string(company_info->company_name, sizeof(company_info->company_name));
560  company_info->inaugurated_year = p->Recv_uint32();
561  company_info->company_value = p->Recv_uint64();
562  company_info->money = p->Recv_uint64();
563  company_info->income = p->Recv_uint64();
564  company_info->performance = p->Recv_uint16();
565  company_info->use_password = p->Recv_bool();
566  for (uint i = 0; i < NETWORK_VEH_END; i++) {
567  company_info->num_vehicle[i] = p->Recv_uint16();
568  }
569  for (uint i = 0; i < NETWORK_VEH_END; i++) {
570  company_info->num_station[i] = p->Recv_uint16();
571  }
572  company_info->ai = p->Recv_bool();
573 
574  p->Recv_string(company_info->clients, sizeof(company_info->clients));
575 
577 
579  }
580 
582 }
583 
584 /* This packet contains info about the client (playas and name)
585  * as client we save this in NetworkClientInfo, linked via 'client_id'
586  * which is always an unique number on a server. */
588 {
589  NetworkClientInfo *ci;
591  CompanyID playas = (CompanyID)p->Recv_uint8();
592  char name[NETWORK_NAME_LENGTH];
593 
594  p->Recv_string(name, sizeof(name));
595 
597  if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
598 
599  ci = NetworkClientInfo::GetByClientID(client_id);
600  if (ci != NULL) {
601  if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
602  /* Client name changed, display the change */
603  NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, name);
604  } else if (playas != ci->client_playas) {
605  /* The client changed from client-player..
606  * Do not display that for now */
607  }
608 
609  /* Make sure we're in the company the server tells us to be in,
610  * for the rare case that we get moved while joining. */
611  if (client_id == _network_own_client_id) SetLocalCompany(!Company::IsValidID(playas) ? COMPANY_SPECTATOR : playas);
612 
613  ci->client_playas = playas;
614  strecpy(ci->client_name, name, lastof(ci->client_name));
615 
617 
619  }
620 
621  /* There are at most as many ClientInfo as ClientSocket objects in a
622  * server. Having more info than a server can have means something
623  * has gone wrong somewhere, i.e. the server has more info than it
624  * has actual clients. That means the server is feeding us an invalid
625  * state. So, bail out! This server is broken. */
627 
628  /* We don't have this client_id yet, find an empty client_id, and put the data there */
629  ci = new NetworkClientInfo(client_id);
630  ci->client_playas = playas;
631  if (client_id == _network_own_client_id) this->SetInfo(ci);
632 
633  strecpy(ci->client_name, name, lastof(ci->client_name));
634 
636 
638 }
639 
641 {
642  static const StringID network_error_strings[] = {
643  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL
644  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC
645  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED
646  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST
647  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET
648  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH
649  STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED
650  STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED
651  STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION
652  STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE
653  STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD
654  STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH
655  STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED
656  STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER
657  STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL
658  STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS
659  STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD
660  STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER
661  STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP
662  STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN
663  };
664  assert_compile(lengthof(network_error_strings) == NETWORK_ERROR_END);
665 
667 
668  StringID err = STR_NETWORK_ERROR_LOSTCONNECTION;
669  if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error];
670 
672 
674 
676 }
677 
679 {
681 
682  uint grf_count = p->Recv_uint8();
684 
685  /* Check all GRFs */
686  for (; grf_count > 0; grf_count--) {
687  GRFIdentifier c;
688  this->ReceiveGRFIdentifier(p, &c);
689 
690  /* Check whether we know this GRF */
691  const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum);
692  if (f == NULL) {
693  /* We do not know this GRF, bail out of initialization */
694  char buf[sizeof(c.md5sum) * 2 + 1];
695  md5sumToString(buf, lastof(buf), c.md5sum);
696  DEBUG(grf, 0, "NewGRF %08X not found; checksum %s", BSWAP32(c.grfid), buf);
698  }
699  }
700 
701  if (ret == NETWORK_RECV_STATUS_OKAY) {
702  /* Start receiving the map */
703  return SendNewGRFsOk();
704  }
705 
706  /* NewGRF mismatch, bail out */
707  ShowErrorMessage(STR_NETWORK_ERROR_NEWGRF_MISMATCH, INVALID_STRING_ID, WL_CRITICAL);
708  return ret;
709 }
710 
712 {
713  if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
714  this->status = STATUS_AUTH_GAME;
715 
716  const char *password = _network_join_server_password;
717  if (!StrEmpty(password)) {
718  return SendGamePassword(password);
719  }
720 
721  ShowNetworkNeedPassword(NETWORK_GAME_PASSWORD);
722 
724 }
725 
727 {
728  if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_COMPANY) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
729  this->status = STATUS_AUTH_COMPANY;
730 
731  _password_game_seed = p->Recv_uint32();
732  p->Recv_string(_password_server_id, sizeof(_password_server_id));
734 
735  const char *password = _network_join_company_password;
736  if (!StrEmpty(password)) {
737  return SendCompanyPassword(password);
738  }
739 
740  ShowNetworkNeedPassword(NETWORK_COMPANY_PASSWORD);
741 
743 }
744 
746 {
747  if (this->status < STATUS_JOIN || this->status >= STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
748  this->status = STATUS_AUTHORIZED;
749 
751 
752  /* Initialize the password hash salting variables, even if they were previously. */
753  _password_game_seed = p->Recv_uint32();
754  p->Recv_string(_password_server_id, sizeof(_password_server_id));
755 
756  /* Start receiving the map */
757  return SendGetMap();
758 }
759 
761 {
762  /* We set the internal wait state when requesting the map. */
764 
765  /* But... only now we set the join status to waiting, instead of requesting. */
766  _network_join_status = NETWORK_JOIN_STATUS_WAITING;
769 
771 }
772 
774 {
775  if (this->status < STATUS_AUTHORIZED || this->status >= STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
776  this->status = STATUS_MAP;
777 
778  if (this->savegame != NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
779 
780  this->savegame = new PacketReader();
781 
783 
786 
787  _network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
789 
791 }
792 
794 {
796  if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
797 
800 
802 }
803 
805 {
807  if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
808 
809  /* We are still receiving data, put it to the file */
810  this->savegame->AddPacket(p);
811 
812  _network_join_bytes = (uint32)this->savegame->written_bytes;
814 
816 }
817 
819 {
821  if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
822 
823  _network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
825 
826  /*
827  * Make sure everything is set for reading.
828  *
829  * We need the local copy and reset this->savegame because when
830  * loading fails the network gets reset upon loading the intro
831  * game, which would cause us to free this->savegame twice.
832  */
833  LoadFilter *lf = this->savegame;
834  this->savegame = NULL;
835  lf->Reset();
836 
837  /* The map is done downloading, load it */
839  bool load_success = SafeLoad(NULL, SLO_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, lf);
840 
841  /* Long savegame loads shouldn't affect the lag calculation! */
842  this->last_packet = _realtime_tick;
843 
844  if (!load_success) {
846  ShowErrorMessage(STR_NETWORK_ERROR_SAVEGAMEERROR, INVALID_STRING_ID, WL_CRITICAL);
848  }
849  /* If the savegame has successfully loaded, ALL windows have been removed,
850  * only toolbar/statusbar and gamefield are visible */
851 
852  /* Say we received the map and loaded it correctly! */
853  SendMapOk();
854 
855  /* New company/spectator (invalid company) or company we want to join is not active
856  * Switch local company to spectator and await the server's judgement */
857  if (_network_join_as == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join_as)) {
859 
860  if (_network_join_as != COMPANY_SPECTATOR) {
861  /* We have arrived and ready to start playing; send a command to make a new company;
862  * the server will give us a client-id and let us in */
863  _network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
864  ShowJoinStatusWindow();
865  NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company);
866  }
867  } else {
868  /* take control over an existing company */
869  SetLocalCompany(_network_join_as);
870  }
871 
873 }
874 
876 {
878 
881 #ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
882  /* Test if the server supports this option
883  * and if we are at the frame the server is */
884  if (p->pos + 1 < p->size) {
886  _sync_seed_1 = p->Recv_uint32();
887 #ifdef NETWORK_SEND_DOUBLE_SEED
888  _sync_seed_2 = p->Recv_uint32();
889 #endif
890  }
891 #endif
892  /* Receive the token. */
893  if (p->pos != p->size) this->token = p->Recv_uint8();
894 
895  DEBUG(net, 5, "Received FRAME %d", _frame_counter_server);
896 
897  /* Let the server know that we received this frame correctly
898  * We do this only once per day, to save some bandwidth ;) */
899  if (!_network_first_time && last_ack_frame < _frame_counter) {
900  last_ack_frame = _frame_counter + DAY_TICKS;
901  DEBUG(net, 4, "Sent ACK at %d", _frame_counter);
902  SendAck();
903  }
904 
906 }
907 
909 {
911 
912  _sync_frame = p->Recv_uint32();
913  _sync_seed_1 = p->Recv_uint32();
914 #ifdef NETWORK_SEND_DOUBLE_SEED
915  _sync_seed_2 = p->Recv_uint32();
916 #endif
917 
919 }
920 
922 {
924 
925  CommandPacket cp;
926  const char *err = this->ReceiveCommand(p, &cp);
927  cp.frame = p->Recv_uint32();
928  cp.my_cmd = p->Recv_bool();
929 
930  if (err != NULL) {
931  IConsolePrintF(CC_ERROR, "WARNING: %s from server, dropping...", err);
933  }
934 
935  this->incoming_queue.Append(&cp);
936 
938 }
939 
941 {
943 
944  char name[NETWORK_NAME_LENGTH], msg[NETWORK_CHAT_LENGTH];
945  const NetworkClientInfo *ci = NULL, *ci_to;
946 
947  NetworkAction action = (NetworkAction)p->Recv_uint8();
949  bool self_send = p->Recv_bool();
951  int64 data = p->Recv_uint64();
952 
953  ci_to = NetworkClientInfo::GetByClientID(client_id);
954  if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY;
955 
956  /* Did we initiate the action locally? */
957  if (self_send) {
958  switch (action) {
959  case NETWORK_ACTION_CHAT_CLIENT:
960  /* For speaking to client we need the client-name */
961  seprintf(name, lastof(name), "%s", ci_to->client_name);
963  break;
964 
965  /* For speaking to company or giving money, we need the company-name */
966  case NETWORK_ACTION_GIVE_MONEY:
967  if (!Company::IsValidID(ci_to->client_playas)) return NETWORK_RECV_STATUS_OKAY;
968  /* FALL THROUGH */
969  case NETWORK_ACTION_CHAT_COMPANY: {
970  StringID str = Company::IsValidID(ci_to->client_playas) ? STR_COMPANY_NAME : STR_NETWORK_SPECTATORS;
971  SetDParam(0, ci_to->client_playas);
972 
973  GetString(name, str, lastof(name));
975  break;
976  }
977 
978  default: return NETWORK_RECV_STATUS_MALFORMED_PACKET;
979  }
980  } else {
981  /* Display message from somebody else */
982  seprintf(name, lastof(name), "%s", ci_to->client_name);
983  ci = ci_to;
984  }
985 
986  if (ci != NULL) {
987  NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), self_send, name, msg, data);
988  }
990 }
991 
993 {
995 
997 
999  if (ci != NULL) {
1000  NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, ci->client_name, NULL, GetNetworkErrorMsg((NetworkErrorCode)p->Recv_uint8()));
1001  delete ci;
1002  }
1003 
1005 
1006  return NETWORK_RECV_STATUS_OKAY;
1007 }
1008 
1010 {
1012 
1014 
1016  if (ci != NULL) {
1017  NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, ci->client_name, NULL, STR_NETWORK_MESSAGE_CLIENT_LEAVING);
1018  delete ci;
1019  } else {
1020  DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id);
1021  }
1022 
1024 
1025  /* If we come here it means we could not locate the client.. strange :s */
1026  return NETWORK_RECV_STATUS_OKAY;
1027 }
1028 
1030 {
1032 
1034 
1036  if (ci != NULL) {
1037  NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, ci->client_name);
1038  }
1039 
1041 
1042  return NETWORK_RECV_STATUS_OKAY;
1043 }
1044 
1046 {
1047  /* Only when we're trying to join we really
1048  * care about the server shutting down. */
1049  if (this->status >= STATUS_JOIN) {
1050  ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_SHUTDOWN, INVALID_STRING_ID, WL_CRITICAL);
1051  }
1052 
1054 }
1055 
1057 {
1058  /* Only when we're trying to join we really
1059  * care about the server shutting down. */
1060  if (this->status >= STATUS_JOIN) {
1061  /* To throttle the reconnects a bit, every clients waits its
1062  * Client ID modulo 16. This way reconnects should be spread
1063  * out a bit. */
1065  ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL);
1066  }
1067 
1069 }
1070 
1072 {
1074 
1075  TextColour colour_code = (TextColour)p->Recv_uint16();
1077 
1078  char rcon_out[NETWORK_RCONCOMMAND_LENGTH];
1079  p->Recv_string(rcon_out, sizeof(rcon_out));
1080 
1081  IConsolePrint(colour_code, rcon_out);
1082 
1083  return NETWORK_RECV_STATUS_OKAY;
1084 }
1085 
1087 {
1089 
1090  /* Nothing more in this packet... */
1092  CompanyID company_id = (CompanyID)p->Recv_uint8();
1093 
1094  if (client_id == 0) {
1095  /* definitely an invalid client id, debug message and do nothing. */
1096  DEBUG(net, 0, "[move] received invalid client index = 0");
1098  }
1099 
1100  const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
1101  /* Just make sure we do not try to use a client_index that does not exist */
1102  if (ci == NULL) return NETWORK_RECV_STATUS_OKAY;
1103 
1104  /* if not valid player, force spectator, else check player exists */
1105  if (!Company::IsValidID(company_id)) company_id = COMPANY_SPECTATOR;
1106 
1107  if (client_id == _network_own_client_id) {
1108  SetLocalCompany(company_id);
1109  }
1110 
1111  return NETWORK_RECV_STATUS_OKAY;
1112 }
1113 
1115 {
1117 
1118  _network_server_max_companies = p->Recv_uint8();
1119  _network_server_max_spectators = p->Recv_uint8();
1120 
1121  return NETWORK_RECV_STATUS_OKAY;
1122 }
1123 
1125 {
1127 
1130 
1131  return NETWORK_RECV_STATUS_OKAY;
1132 }
1133 
1138 {
1139  /* Only once we're authorized we can expect a steady stream of packets. */
1140  if (this->status < STATUS_AUTHORIZED) return;
1141 
1142  /* It might... sometimes occur that the realtime ticker overflows. */
1143  if (_realtime_tick < this->last_packet) this->last_packet = _realtime_tick;
1144 
1145  /* Lag is in milliseconds; 5 seconds are roughly twice the
1146  * server's "you're slow" threshold (1 game day). */
1147  uint lag = (_realtime_tick - this->last_packet) / 1000;
1148  if (lag < 5) return;
1149 
1150  /* 20 seconds are (way) more than 4 game days after which
1151  * the server will forcefully disconnect you. */
1152  if (lag > 20) {
1154  ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL);
1155  return;
1156  }
1157 
1158  /* Prevent showing the lag message every tick; just update it when needed. */
1159  static uint last_lag = 0;
1160  if (last_lag == lag) return;
1161 
1162  last_lag = lag;
1163  SetDParam(0, lag);
1164  ShowErrorMessage(STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION, STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION, WL_INFO);
1165 }
1166 
1167 
1170 {
1171  /* Set the frame-counter to 0 so nothing happens till we are ready */
1172  _frame_counter = 0;
1174  last_ack_frame = 0;
1175  /* Request the game-info */
1177 }
1178 
1184 void NetworkClientSendRcon(const char *password, const char *command)
1185 {
1186  MyClient::SendRCon(password, command);
1187 }
1188 
1195 void NetworkClientRequestMove(CompanyID company_id, const char *pass)
1196 {
1197  MyClient::SendMove(company_id, pass);
1198 }
1199 
1205 {
1206  Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1207  /* If our company is changing owner, go to spectators */
1209 
1210  NetworkClientInfo *ci;
1211  FOR_ALL_CLIENT_INFOS(ci) {
1212  if (ci->client_playas != cid) continue;
1213  NetworkTextMessage(NETWORK_ACTION_COMPANY_SPECTATOR, CC_DEFAULT, false, ci->client_name);
1215  }
1216 
1217  cur_company.Restore();
1218 }
1219 
1224 {
1226 
1227  if (ci == NULL) return;
1228 
1229  /* Don't change the name if it is the same as the old name */
1230  if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) {
1231  if (!_network_server) {
1233  } else {
1235  NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, _settings_client.network.client_name);
1238  }
1239  }
1240  }
1241 }
1242 
1251 void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data)
1252 {
1253  MyClient::SendChat(action, type, dest, msg, data);
1254 }
1255 
1260 void NetworkClientSetCompanyPassword(const char *password)
1261 {
1262  MyClient::SendSetPassword(password);
1263 }
1264 
1271 {
1272  /* Only companies actually playing can speak to team. Eg spectators cannot */
1274 
1275  const NetworkClientInfo *ci;
1276  FOR_ALL_CLIENT_INFOS(ci) {
1277  if (ci->client_playas == cio->client_playas && ci != cio) return true;
1278  }
1279 
1280  return false;
1281 }
1282 
1288 {
1290 }
1291 
1297 {
1299 }
1300 
1301 #endif /* ENABLE_NETWORK */