diff --git a/src/openrct2/game.c b/src/openrct2/game.c index d7e80aecb1..9320254d4c 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -361,6 +361,10 @@ void game_logic_update() gInUpdateCode = true; /////////////////////////// + // Separated out processing commands in network_update which could call scenario_rand where gInUpdateCode is false. + // All commands that are received are first queued and then executed where gInUpdateCode is set to true. + network_process_game_commands(); + gScreenAge++; if (gScreenAge == 0) gScreenAge--; diff --git a/src/openrct2/network/network.cpp b/src/openrct2/network/network.cpp index f2fccb41d9..a7b049a18f 100644 --- a/src/openrct2/network/network.cpp +++ b/src/openrct2/network/network.cpp @@ -519,21 +519,6 @@ void Network::UpdateClient() } Close(); } - - // Check synchronisation - ProcessGameCommandQueue(); - - if (!_desynchronised && !CheckSRAND(gCurrentTicks, gScenarioSrand0)) { - _desynchronised = true; - - char str_desync[256]; - format_string(str_desync, 256, STR_MULTIPLAYER_DESYNC, NULL); - window_network_status_open(str_desync, NULL); - if (!gConfigNetwork.stay_connected) { - Close(); - } - } - break; } } @@ -1337,7 +1322,6 @@ void Network::ProcessGameCommandQueue() // run all the game commands at the current tick const GameCommand& gc = (*game_command_queue.begin()); - // If our tick is higher than the command tick we are in trouble. if (mode == NETWORK_MODE_CLIENT) { if (game_command_queue.begin()->tick < gCurrentTicks) { @@ -1354,29 +1338,70 @@ void Network::ProcessGameCommandQueue() continue; } + // exit the game command processing loop to still have a chance at finding desync. if (game_command_queue.begin()->tick != gCurrentTicks) - return; + break; } - // run all the game commands at the current tick if (GetPlayerID() == gc.playerid) { game_command_callback = game_command_callback_get_callback(gc.callback); } game_command_playerid = gc.playerid; + sint32 command = gc.esi; - money32 cost = game_do_command_p(command, (sint32*)&gc.eax, (sint32*)&gc.ebx, (sint32*)&gc.ecx, (sint32*)&gc.edx, (sint32*)&gc.esi, (sint32*)&gc.edi, (sint32*)&gc.ebp); - if (cost != MONEY32_UNDEFINED) { + sint32 flags = gc.ebx; + if (mode == NETWORK_MODE_SERVER) + flags |= GAME_COMMAND_FLAG_NETWORKED; + + money32 cost = game_do_command(gc.eax, flags, gc.ecx, gc.edx, gc.esi, gc.edi, gc.ebp); + + if (cost != MONEY32_UNDEFINED) + { game_commands_processed_this_tick++; NetworkPlayer* player = GetPlayerByID(gc.playerid); - if (player) { - player->LastAction = NetworkActions::FindCommand(command); - player->LastActionTime = platform_get_ticks(); - player->AddMoneySpent(cost); + if (!player) + return; + + player->LastAction = NetworkActions::FindCommand(command); + player->LastActionTime = gc.tick; + player->AddMoneySpent(cost); + + if (mode == NETWORK_MODE_SERVER) { + + if (command == GAME_COMMAND_PLACE_SCENERY) { + player->LastPlaceSceneryTime = player->LastActionTime; + } + else if (command == GAME_COMMAND_DEMOLISH_RIDE) { + player->LastDemolishRideTime = player->LastActionTime; + } + + Server_Send_GAMECMD(gc.eax, gc.ebx, gc.ecx, gc.edx, gc.esi, gc.edi, gc.ebp, gc.playerid, gc.callback); } } + game_command_queue.erase(game_command_queue.begin()); } + + // Check synchronisation + if (mode == NETWORK_MODE_CLIENT && !_desynchronised && !CheckSRAND(gCurrentTicks, gScenarioSrand0)) { + _desynchronised = true; + + char str_desync[256]; + format_string(str_desync, 256, STR_MULTIPLAYER_DESYNC, NULL); + window_network_status_open(str_desync, NULL); + if (!gConfigNetwork.stay_connected) { + Close(); + } + } + + if (mode == NETWORK_MODE_SERVER) + { + for (const auto& it : client_connection_list) + { + it->SendQueuedPackets(); + } + } } void Network::AddClient(ITcpSocket * socket) @@ -2028,31 +2053,13 @@ void Network::Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket } // Don't let clients send pause or quit else if (commandCommand == GAME_COMMAND_TOGGLE_PAUSE || - commandCommand == GAME_COMMAND_LOAD_OR_QUIT + commandCommand == GAME_COMMAND_LOAD_OR_QUIT ) { return; } - // Set this to reference inside of game command functions - game_command_playerid = playerid; - // Run game command, and if it is successful send to clients - money32 cost = game_do_command(args[0], args[1] | GAME_COMMAND_FLAG_NETWORKED, args[2], args[3], args[4], args[5], args[6]); - if (cost == MONEY32_UNDEFINED) { - return; - } - - connection.Player->LastAction = NetworkActions::FindCommand(commandCommand); - connection.Player->LastActionTime = platform_get_ticks(); - connection.Player->AddMoneySpent(cost); - - if (commandCommand == GAME_COMMAND_PLACE_SCENERY) { - connection.Player->LastPlaceSceneryTime = connection.Player->LastActionTime; - } - else if (commandCommand == GAME_COMMAND_DEMOLISH_RIDE) { - connection.Player->LastDemolishRideTime = connection.Player->LastActionTime; - } - - Server_Send_GAMECMD(args[0], args[1], args[2], args[3], args[4], args[5], args[6], playerid, callback); + GameCommand gc = GameCommand(tick, args, playerid, callback); + game_command_queue.insert(gc); } void Network::Client_Handle_TICK(NetworkConnection& connection, NetworkPacket& packet) @@ -2272,6 +2279,11 @@ void network_update() gNetwork.Update(); } +void network_process_game_commands() +{ + gNetwork.ProcessGameCommandQueue(); +} + sint32 network_get_mode() { return gNetwork.GetMode(); @@ -2879,6 +2891,7 @@ uint32 network_get_server_tick() { return gCurrentTicks; } void network_send_gamecmd(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp, uint8 callback) {} void network_send_map() {} void network_update() {} +void network_process_game_commands() {} sint32 network_begin_client(const char *host, sint32 port) { return 1; } sint32 network_begin_server(sint32 port, const char * address) { return 1; } sint32 network_get_num_players() { return 1; } diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 4abaaec6b5..e2f6132c56 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -56,7 +56,7 @@ extern "C" { // This define specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "15" +#define NETWORK_STREAM_VERSION "16" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION #ifdef __cplusplus @@ -108,6 +108,7 @@ public: uint32 GetServerTick(); uint8 GetPlayerID(); void Update(); + void ProcessGameCommandQueue(); std::vector>::iterator GetPlayerIteratorByID(uint8 id); NetworkPlayer* GetPlayerByID(uint8 id); std::vector>::iterator GetGroupIteratorByID(uint8 id); @@ -178,7 +179,6 @@ public: private: bool ProcessConnection(NetworkConnection& connection); void ProcessPacket(NetworkConnection& connection, NetworkPacket& packet); - void ProcessGameCommandQueue(); void AddClient(ITcpSocket * socket); void RemoveClient(std::unique_ptr& connection); NetworkPlayer* AddPlayer(const utf8 *name, const std::string &keyhash); @@ -287,6 +287,7 @@ sint32 network_begin_server(sint32 port, const char* address); sint32 network_get_mode(); sint32 network_get_status(); void network_update(); +void network_process_game_commands(); sint32 network_get_authstatus(); uint32 network_get_server_tick(); uint8 network_get_current_player_id();