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:
committed by
Michał Janiszewski
parent
c018cff5a3
commit
efdac19f3b
@@ -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--;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user