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"
77 #ifdef NETWORK_SEND_DOUBLE_SEED
107 NetworkClientSocket *cs;
132 if (ci->
client_id == client_id)
return ci;
145 NetworkClientSocket *cs;
148 if (cs->client_id == client_id)
return cs;
154 byte NetworkSpectatorCount()
164 if (_network_dedicated) count--;
177 if (strcmp(password,
"*") == 0) password =
"";
179 if (_network_server) {
197 if (
StrEmpty(password))
return password;
201 memset(salted_password, 0,
sizeof(salted_password));
202 seprintf(salted_password,
lastof(salted_password),
"%s", password);
205 salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32));
213 checksum.Append(salted_password,
sizeof(salted_password) - 1);
214 checksum.Finish(digest);
216 for (
int di = 0; di < 16; di++)
seprintf(hashed_password + di * 2,
lastof(hashed_password),
"%02x", digest[di]);
218 return hashed_password;
228 return HasBit(_network_company_passworded, company_id);
234 void NetworkTextMessage(
NetworkAction action,
TextColour colour,
bool self_send,
const char *name,
const char *str, int64 data)
238 case NETWORK_ACTION_SERVER_MESSAGE:
240 strid = STR_NETWORK_SERVER_MESSAGE;
243 case NETWORK_ACTION_COMPANY_SPECTATOR:
245 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE;
247 case NETWORK_ACTION_COMPANY_JOIN:
249 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN;
251 case NETWORK_ACTION_COMPANY_NEW:
253 strid = STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW;
255 case NETWORK_ACTION_JOIN:
257 strid = _network_server ? STR_NETWORK_MESSAGE_CLIENT_JOINED_ID : STR_NETWORK_MESSAGE_CLIENT_JOINED;
259 case NETWORK_ACTION_LEAVE: strid = STR_NETWORK_MESSAGE_CLIENT_LEFT;
break;
260 case NETWORK_ACTION_NAME_CHANGE: strid = STR_NETWORK_MESSAGE_NAME_CHANGE;
break;
261 case NETWORK_ACTION_GIVE_MONEY: strid = self_send ? STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY : STR_NETWORK_MESSAGE_GIVE_MONEY;
break;
262 case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY;
break;
263 case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT;
break;
264 default: strid = STR_NETWORK_CHAT_ALL;
break;
277 GetString(msg_ptr, strid,
lastof(message));
285 uint NetworkCalculateLag(
const NetworkClientSocket *cs)
287 int lag = cs->last_frame_server - cs->last_frame;
300 void NetworkError(
StringID error_string)
315 static const StringID network_error_strings[] = {
316 STR_NETWORK_ERROR_CLIENT_GENERAL,
317 STR_NETWORK_ERROR_CLIENT_DESYNC,
318 STR_NETWORK_ERROR_CLIENT_SAVEGAME,
319 STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST,
320 STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR,
321 STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH,
322 STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED,
323 STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED,
324 STR_NETWORK_ERROR_CLIENT_WRONG_REVISION,
325 STR_NETWORK_ERROR_CLIENT_NAME_IN_USE,
326 STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD,
327 STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH,
328 STR_NETWORK_ERROR_CLIENT_KICKED,
329 STR_NETWORK_ERROR_CLIENT_CHEATER,
330 STR_NETWORK_ERROR_CLIENT_SERVER_FULL,
331 STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS,
332 STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD,
333 STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER,
334 STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP,
335 STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN,
337 assert_compile(
lengthof(network_error_strings) == NETWORK_ERROR_END);
339 if (err >= (ptrdiff_t)
lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
341 return network_error_strings[err];
351 if (!_networking)
return;
353 switch (changed_mode) {
360 if (!paused && !changed)
return;
370 str = STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 + i;
372 switch (changed_mode) {
377 default: NOT_REACHED();
379 str = paused ? STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED : STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED;
383 GetString(buffer, str,
lastof(buffer));
384 NetworkTextMessage(NETWORK_ACTION_SERVER_MESSAGE,
CC_DEFAULT,
false, NULL, buffer);
416 const NetworkClientSocket *cs;
420 if (cs->status != NetworkClientSocket::STATUS_ACTIVE)
continue;
434 !_network_dedicated ||
447 const NetworkClientSocket *cs;
449 if (cs->status >= NetworkClientSocket::STATUS_AUTHORIZED && cs->status < NetworkClientSocket::STATUS_ACTIVE)
return true;
477 bool ipv6 = (strchr(connection_string,
':') != strrchr(connection_string,
':'));
479 for (p = connection_string; *p !=
'\0'; p++) {
511 _network_clients_connected++;
533 if (_network_server) {
541 NetworkClientSocket *cs;
555 _network_server =
false;
566 static void NetworkInitialize(
bool close_admins =
true)
572 _network_first_time =
true;
574 _network_reconnect = 0;
582 virtual void OnFailure()
587 virtual void OnConnect(SOCKET s)
600 if (!_network_available)
return;
611 void NetworkAddServer(
const char *b)
614 const char *port = NULL;
615 const char *company = NULL;
625 if (port != NULL) rport = atoi(port);
638 for (
char **iter = _network_bind_list.
Begin(); iter != _network_bind_list.
End(); iter++) {
643 if (addresses->
Length() == 0) {
651 void NetworkRebuildHostList()
653 _network_host_list.
Clear();
656 if (item->manually) *_network_host_list.
Append() =
stredup(item->address.GetAddressAsString(
false));
665 virtual void OnFailure()
667 NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
670 virtual void OnConnect(SOCKET s)
681 void NetworkClientConnectGame(
NetworkAddress address,
CompanyID join_as,
const char *join_server_password,
const char *join_company_password)
683 if (!_network_available)
return;
685 if (address.
GetPort() == 0)
return;
697 ShowJoinStatusWindow();
702 static void NetworkInitGameInfo()
709 _network_game_info.
clients_on = _network_dedicated ? 0 : 1;
719 bool NetworkServerStart()
721 if (!_network_available)
return false;
725 if (_network_dedicated)
IConsoleCmdExec(
"exec scripts/pre_dedicated.scr 0");
728 NetworkInitialize(
false);
729 DEBUG(net, 1,
"starting listeners for clients");
734 DEBUG(net, 1,
"starting listeners for admins");
739 DEBUG(net, 1,
"starting listeners for incoming server queries");
740 _network_udp_server = _udp_server_socket->
Listen();
743 _network_server =
true;
746 _frame_counter_server = 0;
747 _frame_counter_max = 0;
748 _last_sync_frame = 0;
751 _network_clients_connected = 0;
752 _network_company_passworded = 0;
754 NetworkInitGameInfo();
759 if (_network_dedicated)
IConsoleCmdExec(
"exec scripts/on_dedicated.scr 0");
762 _network_last_advertise_frame = 0;
763 _network_need_advertise =
true;
776 if (_network_server) {
777 NetworkClientSocket *cs;
802 if (_network_server) {
803 NetworkClientSocket *cs;
834 if (_network_server) {
843 static void NetworkSend()
845 if (_network_server) {
869 void NetworkGameLoop()
871 if (!_networking)
return;
875 if (_network_server) {
879 static Date last_log;
880 if (last_log !=
_date) {
886 #ifdef DEBUG_DUMP_COMMANDS
889 static Date next_date = 0;
890 static uint32 next_date_fract;
892 static bool check_sync_state =
false;
893 static uint32 sync_state[2];
894 if (f == NULL && next_date == 0) {
895 DEBUG(net, 0,
"Cannot open commands.log");
899 while (f != NULL && !feof(f)) {
903 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));
907 if (check_sync_state) {
911 DEBUG(net, 0,
"sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}",
915 check_sync_state =
false;
919 if (cp != NULL || check_sync_state)
break;
922 if (fgets(buff,
lengthof(buff), f) == NULL)
break;
928 if (p == NULL)
break;
932 if (strncmp(p,
"cmd: ", 5) == 0
933 #ifdef DEBUG_FAILED_DUMP_COMMANDS
934 || strncmp(p,
"cmdf: ", 6) == 0
939 cp = CallocT<CommandPacket>(1);
941 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);
945 assert(ret == 8 || ret == 7);
947 }
else if (strncmp(p,
"join: ", 6) == 0) {
949 int ret = sscanf(p + 6,
"%x; %x", &next_date, &next_date_fract);
951 DEBUG(net, 0,
"injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract);
952 cp = CallocT<CommandPacket>(1);
958 }
else if (strncmp(p,
"sync: ", 6) == 0) {
959 int ret = sscanf(p + 6,
"%x; %x; %x; %x", &next_date, &next_date_fract, &sync_state[0], &sync_state[1]);
961 check_sync_state =
true;
962 }
else if (strncmp(p,
"msg: ", 5) == 0 || strncmp(p,
"client: ", 8) == 0 ||
963 strncmp(p,
"load: ", 6) == 0 || strncmp(p,
"save: ", 6) == 0) {
965 #ifndef DEBUG_FAILED_DUMP_COMMANDS
966 }
else if (strncmp(p,
"cmdf: ", 6) == 0) {
967 DEBUG(net, 0,
"Skipping replay of failed command: %s", p + 6);
971 DEBUG(net, 0,
"trying to parse: %s", p);
975 if (f != NULL && feof(f)) {
976 DEBUG(net, 0,
"End of commands.log");
981 if (_frame_counter >= _frame_counter_max) {
990 bool send_frame =
false;
995 if (_frame_counter > _frame_counter_max) {
1006 #ifdef NETWORK_SEND_DOUBLE_SEED
1015 if (_frame_counter_server > _frame_counter) {
1017 while (_frame_counter_server > _frame_counter) {
1022 if (_frame_counter_max > _frame_counter) {
1032 static void NetworkGenerateServerId()
1036 char hex_output[16 * 2 + 1];
1040 seprintf(coding_string,
lastof(coding_string),
"%d%s", (uint)Random(),
"OpenTTD Server ID");
1043 checksum.Append((
const uint8*)coding_string, strlen(coding_string));
1044 checksum.Finish(digest);
1046 for (di = 0; di < 16; ++di) {
1047 seprintf(hex_output + di * 2,
lastof(hex_output),
"%02x", digest[di]);
1056 extern SOCKET _debug_socket;
1061 if (s == INVALID_SOCKET) {
1062 DEBUG(net, 0,
"Failed to open socket for redirection DEBUG()");
1068 DEBUG(net, 0,
"DEBUG() is now redirected");
1074 DEBUG(net, 3,
"[core] starting network...");
1078 _network_dedicated =
false;
1079 _network_last_advertise_frame = 0;
1080 _network_need_advertise =
true;
1081 _network_advertise_retries = 0;
1086 memset(&_network_game_info, 0,
sizeof(_network_game_info));
1088 NetworkInitialize();
1089 DEBUG(net, 3,
"[core] network online, multiplayer available");
1099 DEBUG(net, 3,
"[core] shutting down network");
1101 _network_available =
false;