12 #include "../stdafx.h"
16 #include "../strings_func.h"
17 #include "../command_func.h"
18 #include "../date_func.h"
29 #include "../console_func.h"
30 #include "../3rdparty/md5/md5.h"
31 #include "../core/random_func.hpp"
32 #include "../window_func.h"
33 #include "../company_func.h"
34 #include "../company_base.h"
35 #include "../landscape_type.h"
37 #include "../core/pool_func.hpp"
38 #include "../gfx_func.h"
41 #include "../safeguards.h"
43 #ifdef DEBUG_DUMP_COMMANDS
44 #include "../fileio_func.h"
76 #ifdef NETWORK_SEND_DOUBLE_SEED
106 NetworkClientSocket *cs;
131 if (ci->
client_id == client_id)
return ci;
144 NetworkClientSocket *cs;
147 if (cs->client_id == client_id)
return cs;
153 byte NetworkSpectatorCount()
163 if (_network_dedicated) count--;
176 if (strcmp(password,
"*") == 0) password =
"";
178 if (_network_server) {
196 if (
StrEmpty(password))
return password;
200 memset(salted_password, 0,
sizeof(salted_password));
201 seprintf(salted_password,
lastof(salted_password),
"%s", password);
204 salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32));
212 checksum.Append(salted_password,
sizeof(salted_password) - 1);
213 checksum.Finish(digest);
215 for (
int di = 0; di < 16; di++)
seprintf(hashed_password + di * 2,
lastof(hashed_password),
"%02x", digest[di]);
217 return hashed_password;
227 return HasBit(_network_company_passworded, company_id);
233 void NetworkTextMessage(
NetworkAction action,
TextColour colour,
bool self_send,
const char *name,
const char *str, int64 data)
237 case NETWORK_ACTION_SERVER_MESSAGE:
239 strid = STR_NETWORK_SERVER_MESSAGE;
242 case NETWORK_ACTION_COMPANY_SPECTATOR:
244 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE;
246 case NETWORK_ACTION_COMPANY_JOIN:
248 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN;
250 case NETWORK_ACTION_COMPANY_NEW:
252 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW;
254 case NETWORK_ACTION_JOIN:
256 strid = _network_server ? STR_NETWORK_MESSAGE_CLIENT_JOINED_ID : STR_NETWORK_MESSAGE_CLIENT_JOINED;
258 case NETWORK_ACTION_LEAVE: strid = STR_NETWORK_MESSAGE_CLIENT_LEFT;
break;
259 case NETWORK_ACTION_NAME_CHANGE: strid = STR_NETWORK_MESSAGE_NAME_CHANGE;
break;
260 case NETWORK_ACTION_GIVE_MONEY: strid = self_send ? STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY : STR_NETWORK_MESSAGE_GIVE_MONEY;
break;
261 case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY;
break;
262 case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT;
break;
263 default: strid = STR_NETWORK_CHAT_ALL;
break;
276 GetString(msg_ptr, strid,
lastof(message));
284 uint NetworkCalculateLag(
const NetworkClientSocket *cs)
286 int lag = cs->last_frame_server - cs->last_frame;
299 void NetworkError(
StringID error_string)
314 static const StringID network_error_strings[] = {
315 STR_NETWORK_ERROR_CLIENT_GENERAL,
316 STR_NETWORK_ERROR_CLIENT_DESYNC,
317 STR_NETWORK_ERROR_CLIENT_SAVEGAME,
318 STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST,
319 STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR,
320 STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH,
321 STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED,
322 STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED,
323 STR_NETWORK_ERROR_CLIENT_WRONG_REVISION,
324 STR_NETWORK_ERROR_CLIENT_NAME_IN_USE,
325 STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD,
326 STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH,
327 STR_NETWORK_ERROR_CLIENT_KICKED,
328 STR_NETWORK_ERROR_CLIENT_CHEATER,
329 STR_NETWORK_ERROR_CLIENT_SERVER_FULL,
330 STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS,
331 STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD,
332 STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER,
333 STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP,
334 STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN,
336 assert_compile(
lengthof(network_error_strings) == NETWORK_ERROR_END);
338 if (err >= (ptrdiff_t)
lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
340 return network_error_strings[err];
350 if (!_networking)
return;
352 switch (changed_mode) {
359 if (!paused && !changed)
return;
369 str = STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 + i;
371 switch (changed_mode) {
376 default: NOT_REACHED();
378 str = paused ? STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED : STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED;
382 GetString(buffer, str,
lastof(buffer));
383 NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE,
CC_DEFAULT,
false, NULL, buffer);
415 const NetworkClientSocket *cs;
419 if (cs->status != NetworkClientSocket::STATUS_ACTIVE)
continue;
433 !_network_dedicated ||
446 const NetworkClientSocket *cs;
448 if (cs->status >= NetworkClientSocket::STATUS_AUTHORIZED && cs->status < NetworkClientSocket::STATUS_ACTIVE)
return true;
476 bool ipv6 = (strchr(connection_string,
':') != strrchr(connection_string,
':'));
478 for (p = connection_string; *p !=
'\0'; p++) {
510 _network_clients_connected++;
532 if (_network_server) {
540 NetworkClientSocket *cs;
554 _network_server =
false;
565 static void NetworkInitialize(
bool close_admins =
true)
571 _network_first_time =
true;
573 _network_reconnect = 0;
581 virtual void OnFailure()
586 virtual void OnConnect(SOCKET s)
599 if (!_network_available)
return;
610 void NetworkAddServer(
const char *b)
613 const char *port = NULL;
614 const char *company = NULL;
624 if (port != NULL) rport = atoi(port);
637 for (
char **iter = _network_bind_list.
Begin(); iter != _network_bind_list.
End(); iter++) {
642 if (addresses->
Length() == 0) {
650 void NetworkRebuildHostList()
652 _network_host_list.
Clear();
655 if (item->manually) *_network_host_list.
Append() =
stredup(item->address.GetAddressAsString(
false));
664 virtual void OnFailure()
666 NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
669 virtual void OnConnect(SOCKET s)
680 void NetworkClientConnectGame(
NetworkAddress address,
CompanyID join_as,
const char *join_server_password,
const char *join_company_password)
682 if (!_network_available)
return;
684 if (address.
GetPort() == 0)
return;
696 ShowJoinStatusWindow();
701 static void NetworkInitGameInfo()
708 _network_game_info.
clients_on = _network_dedicated ? 0 : 1;
718 bool NetworkServerStart()
720 if (!_network_available)
return false;
724 if (_network_dedicated)
IConsoleCmdExec(
"exec scripts/pre_dedicated.scr 0");
727 NetworkInitialize(
false);
728 DEBUG(net, 1,
"starting listeners for clients");
733 DEBUG(net, 1,
"starting listeners for admins");
738 DEBUG(net, 1,
"starting listeners for incoming server queries");
739 _network_udp_server = _udp_server_socket->
Listen();
742 _network_server =
true;
745 _frame_counter_server = 0;
746 _frame_counter_max = 0;
747 _last_sync_frame = 0;
750 _network_clients_connected = 0;
751 _network_company_passworded = 0;
753 NetworkInitGameInfo();
758 if (_network_dedicated)
IConsoleCmdExec(
"exec scripts/on_dedicated.scr 0");
761 _network_need_advertise =
true;
774 if (_network_server) {
775 NetworkClientSocket *cs;
800 if (_network_server) {
801 NetworkClientSocket *cs;
832 if (_network_server) {
841 static void NetworkSend()
843 if (_network_server) {
867 void NetworkGameLoop()
869 if (!_networking)
return;
873 if (_network_server) {
877 static Date last_log;
878 if (last_log !=
_date) {
884 #ifdef DEBUG_DUMP_COMMANDS
887 static Date next_date = 0;
888 static uint32 next_date_fract;
890 static bool check_sync_state =
false;
891 static uint32 sync_state[2];
892 if (f == NULL && next_date == 0) {
893 DEBUG(net, 0,
"Cannot open commands.log");
897 while (f != NULL && !feof(f)) {
901 DEBUG(net, 0,
"injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)",
_date,
_date_fract, (
int)
_current_company, cp->
tile, cp->
p1, cp->
p2, cp->
cmd, cp->
text,
GetCommandName(cp->
cmd));
905 if (check_sync_state) {
909 DEBUG(net, 0,
"sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}",
913 check_sync_state =
false;
917 if (cp != NULL || check_sync_state)
break;
920 if (fgets(buff,
lengthof(buff), f) == NULL)
break;
926 if (p == NULL)
break;
930 if (strncmp(p,
"cmd: ", 5) == 0
931 #ifdef DEBUG_FAILED_DUMP_COMMANDS
932 || strncmp(p,
"cmdf: ", 6) == 0
937 cp = CallocT<CommandPacket>(1);
939 int ret = sscanf(p,
"%x; %x; %x; %x; %x; %x; %x; \"%[^\"]\"", &next_date, &next_date_fract, &company, &cp->
tile, &cp->
p1, &cp->
p2, &cp->
cmd, cp->
text);
943 assert(ret == 8 || ret == 7);
945 }
else if (strncmp(p,
"join: ", 6) == 0) {
947 int ret = sscanf(p + 6,
"%x; %x", &next_date, &next_date_fract);
949 DEBUG(net, 0,
"injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract);
950 cp = CallocT<CommandPacket>(1);
956 }
else if (strncmp(p,
"sync: ", 6) == 0) {
957 int ret = sscanf(p + 6,
"%x; %x; %x; %x", &next_date, &next_date_fract, &sync_state[0], &sync_state[1]);
959 check_sync_state =
true;
960 }
else if (strncmp(p,
"msg: ", 5) == 0 || strncmp(p,
"client: ", 8) == 0 ||
961 strncmp(p,
"load: ", 6) == 0 || strncmp(p,
"save: ", 6) == 0) {
963 #ifndef DEBUG_FAILED_DUMP_COMMANDS
964 }
else if (strncmp(p,
"cmdf: ", 6) == 0) {
965 DEBUG(net, 0,
"Skipping replay of failed command: %s", p + 6);
969 DEBUG(net, 0,
"trying to parse: %s", p);
973 if (f != NULL && feof(f)) {
974 DEBUG(net, 0,
"End of commands.log");
979 if (_frame_counter >= _frame_counter_max) {
988 bool send_frame =
false;
993 if (_frame_counter > _frame_counter_max) {
1004 #ifdef NETWORK_SEND_DOUBLE_SEED
1013 if (_frame_counter_server > _frame_counter) {
1015 while (_frame_counter_server > _frame_counter) {
1020 if (_frame_counter_max > _frame_counter) {
1030 static void NetworkGenerateServerId()
1034 char hex_output[16 * 2 + 1];
1038 seprintf(coding_string,
lastof(coding_string),
"%d%s", (uint)Random(),
"OpenTTD Server ID");
1041 checksum.Append((
const uint8*)coding_string, strlen(coding_string));
1042 checksum.Finish(digest);
1044 for (di = 0; di < 16; ++di) {
1045 seprintf(hex_output + di * 2,
lastof(hex_output),
"%02x", digest[di]);
1054 extern SOCKET _debug_socket;
1059 if (s == INVALID_SOCKET) {
1060 DEBUG(net, 0,
"Failed to open socket for redirection DEBUG()");
1066 DEBUG(net, 0,
"DEBUG() is now redirected");
1072 DEBUG(net, 3,
"[core] starting network...");
1076 _network_dedicated =
false;
1077 _network_need_advertise =
true;
1078 _network_advertise_retries = 0;
1083 memset(&_network_game_info, 0,
sizeof(_network_game_info));
1085 NetworkInitialize();
1086 DEBUG(net, 3,
"[core] network online, multiplayer available");
1096 DEBUG(net, 3,
"[core] shutting down network");
1098 _network_available =
false;