1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-23 06:44:38 +01:00

Extract game commands execution​ from within network_update. (#5642)

* Moved out command processing from network_update into its own function. This should fix some false positives calling scenario_rand from a command callback.

* Fix Server_Handle_GAMECMD to use the command queue instead of executing directly.

* Exit the queue processing to still check for desync
This commit is contained in:
ZehM4tt
2017-06-18 08:03:37 +02:00
committed by Michał Janiszewski
parent c018cff5a3
commit efdac19f3b
3 changed files with 65 additions and 47 deletions

View File

@@ -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--;

View File

@@ -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; }

View File

@@ -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<std::unique_ptr<NetworkPlayer>>::iterator GetPlayerIteratorByID(uint8 id);
NetworkPlayer* GetPlayerByID(uint8 id);
std::vector<std::unique_ptr<NetworkGroup>>::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<NetworkConnection>& 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();