1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 11:03:00 +01:00

Fix #9124: Disconnected clients can crash the server

This commit is contained in:
ζeh Matt
2019-05-15 19:55:16 +02:00
committed by Michael Steenbeek
parent 1fcedae3bc
commit 1a01a49002
3 changed files with 167 additions and 107 deletions

View File

@@ -38,6 +38,7 @@
- Fix: [#8947] Detection of AVX2 support.
- Fix: [#8988] Character sprite lookup noticeably slows down drawing.
- Fix: [#9000] Show correct error message if not enough money available.
- Fix: [#9124] Disconnected clients can crash the server.
- Fix: [#9132] System file browser cannot open SV4 files.
- Fix: [#9152] Spectators can modify ride colours.
- Fix: [#9202] Artefacts show when changing ride type as client or using in-game console.

View File

@@ -33,7 +33,7 @@
// This string 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 "30"
#define NETWORK_STREAM_VERSION "31"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;
@@ -133,6 +133,7 @@ public:
void ProcessPending();
void ProcessPlayerList();
void ProcessPlayerInfo();
void ProcessDisconnectedClients();
void ProcessGameCommands();
void EnqueueGameAction(const GameAction* action);
std::vector<std::unique_ptr<NetworkPlayer>>::iterator GetPlayerIteratorByID(uint8_t id);
@@ -148,7 +149,7 @@ public:
NetworkServerState_t GetServerState() const;
void KickPlayer(int32_t playerId);
void SetPassword(const char* password);
void ShutdownClient();
void ServerClientDisconnected();
NetworkGroup* AddGroup();
void RemoveGroup(uint8_t id);
uint8_t GetDefaultGroup();
@@ -224,8 +225,9 @@ private:
bool ProcessConnection(NetworkConnection& connection);
void ProcessPacket(NetworkConnection& connection, NetworkPacket& packet);
void AddClient(std::unique_ptr<ITcpSocket>&& socket);
void RemoveClient(std::unique_ptr<NetworkConnection>& connection);
void ServerClientDisconnected(std::unique_ptr<NetworkConnection>& connection);
void RemovePlayer(std::unique_ptr<NetworkConnection>& connection);
NetworkPlayer* AddPlayer(const std::string& name, const std::string& keyhash);
std::string MakePlayerNameUnique(const std::string& name);
@@ -286,18 +288,9 @@ private:
struct PlayerListUpdate
{
uint32_t tick = 0;
std::vector<NetworkPlayer> players;
void reset()
{
tick = 0;
players.clear();
}
};
PlayerListUpdate _pendingPlayerList;
struct ServerTickData_t
{
uint32_t srand0;
@@ -306,7 +299,9 @@ private:
};
std::map<uint32_t, ServerTickData_t> _serverTickData;
std::map<uint32_t, PlayerListUpdate> _pendingPlayerLists;
std::multimap<uint32_t, NetworkPlayer> _pendingPlayerInfo;
bool _playerListInvalidated = false;
int32_t mode = NETWORK_MODE_NONE;
int32_t status = NETWORK_STATUS_NONE;
bool _closeLock = false;
@@ -484,7 +479,8 @@ void Network::Close()
game_command_queue.clear();
player_list.clear();
group_list.clear();
_pendingPlayerList.reset();
_serverTickData.clear();
_pendingPlayerLists.clear();
_pendingPlayerInfo.clear();
gfx_invalidate_screen();
@@ -774,18 +770,19 @@ void Network::Flush()
void Network::UpdateServer()
{
auto it = client_connection_list.begin();
while (it != client_connection_list.end())
for (auto& connection : client_connection_list)
{
if (!ProcessConnection(*(*it)))
// This can be called multiple times before the connection is removed.
if (connection->IsDisconnected)
continue;
if (!ProcessConnection(*connection))
{
RemoveClient((*it));
it = client_connection_list.begin();
connection->IsDisconnected = true;
}
else
{
DecayCooldown((*it)->Player);
it++;
DecayCooldown(connection->Player);
}
}
@@ -983,6 +980,12 @@ void Network::SendPacketToClients(NetworkPacket& packet, bool front, bool gameCm
{
for (auto& client_connection : client_connection_list)
{
if (client_connection->IsDisconnected)
{
// Client will be removed at the end of the tick, don't bother.
continue;
}
if (gameCmd)
{
// If marked as game command we can not send the packet to connections that are not fully connected.
@@ -1097,7 +1100,7 @@ void Network::SetPassword(const char* password)
_password = password == nullptr ? "" : password;
}
void Network::ShutdownClient()
void Network::ServerClientDisconnected()
{
if (GetMode() == NETWORK_MODE_CLIENT)
{
@@ -1956,63 +1959,89 @@ void Network::ProcessPacket(NetworkConnection& connection, NetworkPacket& packet
packet.Clear();
}
// This is called at the end of each game tick, this where things should be processed that affects the game state.
void Network::ProcessPending()
{
ProcessGameCommands();
if (GetMode() == NETWORK_MODE_SERVER)
{
ProcessDisconnectedClients();
}
else if (GetMode() == NETWORK_MODE_CLIENT)
{
ProcessPlayerInfo();
}
ProcessPlayerList();
ProcessPlayerInfo();
}
void Network::ProcessPlayerList()
{
if (_pendingPlayerList.tick == 0 || _pendingPlayerList.tick < gCurrentTicks)
if (GetMode() == NETWORK_MODE_SERVER)
{
return;
}
// List of active players found in the list.
std::vector<uint8_t> activePlayerIds;
for (auto&& pendingPlayer : _pendingPlayerList.players)
{
activePlayerIds.push_back(pendingPlayer.Id);
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
// Avoid sending multiple times the player list, we mark the list invalidated on modifications
// and then send at the end of the tick the final player list.
if (_playerListInvalidated)
{
// Add new player.
player = AddPlayer("", "");
if (player)
_playerListInvalidated = false;
Server_Send_PLAYERLIST();
}
}
else
{
// As client we have to keep things in order so the update is tick bound.
// Commands/Actions reference players and so this list needs to be in sync with those.
auto itPending = _pendingPlayerLists.begin();
while (itPending != _pendingPlayerLists.end())
{
if (itPending->first > gCurrentTicks)
break;
// List of active players found in the list.
std::vector<uint8_t> activePlayerIds;
for (auto&& pendingPlayer : itPending->second.players)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
activePlayerIds.push_back(pendingPlayer.Id);
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
_serverConnection->Player = player;
// Add new player.
player = AddPlayer("", "");
if (player)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
}
}
else
{
// Update.
*player = pendingPlayer;
}
}
}
else
{
// Update.
*player = pendingPlayer;
// Remove any players that are not in newly received list
auto it = player_list.begin();
while (it != player_list.end())
{
if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end())
{
it = player_list.erase(it);
}
else
{
it++;
}
}
_pendingPlayerLists.erase(itPending);
itPending = _pendingPlayerLists.begin();
}
}
// Remove any players that are not in newly received list
auto it = player_list.begin();
while (it != player_list.end())
{
if (std::find(activePlayerIds.begin(), activePlayerIds.end(), (*it)->Id) == activePlayerIds.end())
{
it = player_list.erase(it);
}
else
{
it++;
}
}
_pendingPlayerList.reset();
}
void Network::ProcessPlayerInfo()
@@ -2035,6 +2064,31 @@ void Network::ProcessPlayerInfo()
_pendingPlayerInfo.erase(gCurrentTicks);
}
void Network::ProcessDisconnectedClients()
{
for (auto it = client_connection_list.begin(); it != client_connection_list.end();)
{
auto& connection = *it;
if (connection->IsDisconnected)
{
ServerClientDisconnected(connection);
RemovePlayer(connection);
it = client_connection_list.erase(it);
}
else
{
it++;
}
}
if (gConfigNetwork.pause_server_if_no_clients && game_is_not_paused() && client_connection_list.empty())
{
auto pauseToggleAction = PauseToggleAction();
GameActions::Execute(&pauseToggleAction);
}
}
void Network::ProcessGameCommands()
{
while (game_command_queue.begin() != game_command_queue.end())
@@ -2168,53 +2222,57 @@ void Network::AddClient(std::unique_ptr<ITcpSocket>&& socket)
client_connection_list.push_back(std::move(connection));
}
void Network::RemoveClient(std::unique_ptr<NetworkConnection>& connection)
void Network::ServerClientDisconnected(std::unique_ptr<NetworkConnection>& connection)
{
NetworkPlayer* connection_player = connection->Player;
if (connection_player)
if (connection_player == nullptr)
return;
char text[256];
const char* has_disconnected_args[2] = {
connection_player->Name.c_str(),
connection->GetLastDisconnectReason(),
};
if (has_disconnected_args[1])
{
char text[256];
const char* has_disconnected_args[2] = {
connection_player->Name.c_str(),
connection->GetLastDisconnectReason(),
};
if (has_disconnected_args[1])
{
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_WITH_REASON, has_disconnected_args);
}
else
{
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_NO_REASON, &(has_disconnected_args[0]));
}
chat_history_add(text);
Peep* pickup_peep = network_get_pickup_peep(connection_player->Id);
if (pickup_peep)
{
game_command_playerid = connection_player->Id;
game_do_command(
pickup_peep->sprite_index, GAME_COMMAND_FLAG_APPLY, 1, 0,
pickup_peep->type == PEEP_TYPE_GUEST ? GAME_COMMAND_PICKUP_GUEST : GAME_COMMAND_PICKUP_STAFF,
network_get_pickup_peep_old_x(connection_player->Id), 0);
}
gNetwork.Server_Send_EVENT_PLAYER_DISCONNECTED(
(char*)connection_player->Name.c_str(), connection->GetLastDisconnectReason());
// Log player disconnected event
AppendServerLog(text);
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_WITH_REASON, has_disconnected_args);
}
else
{
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_DISCONNECTED_NO_REASON, &(has_disconnected_args[0]));
}
chat_history_add(text);
Peep* pickup_peep = network_get_pickup_peep(connection_player->Id);
if (pickup_peep)
{
game_command_playerid = connection_player->Id;
game_do_command(
pickup_peep->sprite_index, GAME_COMMAND_FLAG_APPLY, 1, 0,
pickup_peep->type == PEEP_TYPE_GUEST ? GAME_COMMAND_PICKUP_GUEST : GAME_COMMAND_PICKUP_STAFF,
network_get_pickup_peep_old_x(connection_player->Id), 0);
}
gNetwork.Server_Send_EVENT_PLAYER_DISCONNECTED(
(char*)connection_player->Name.c_str(), connection->GetLastDisconnectReason());
// Log player disconnected event
AppendServerLog(text);
}
void Network::RemovePlayer(std::unique_ptr<NetworkConnection>& connection)
{
NetworkPlayer* connection_player = connection->Player;
if (connection_player == nullptr)
return;
player_list.erase(
std::remove_if(
player_list.begin(), player_list.end(),
[connection_player](std::unique_ptr<NetworkPlayer>& player) { return player.get() == connection_player; }),
player_list.end());
client_connection_list.remove(connection);
if (gConfigNetwork.pause_server_if_no_clients && game_is_not_paused() && client_connection_list.empty())
{
auto pauseToggleAction = PauseToggleAction();
GameActions::Execute(&pauseToggleAction);
}
Server_Send_PLAYERLIST();
// Send new player list.
_playerListInvalidated = true;
}
NetworkPlayer* Network::AddPlayer(const std::string& name, const std::string& keyhash)
@@ -2267,6 +2325,9 @@ NetworkPlayer* Network::AddPlayer(const std::string& name, const std::string& ke
player->Group = networkUser->GroupId.GetValueOrDefault(GetDefaultGroup());
player->SetName(networkUser->Name);
}
// Send new player list.
_playerListInvalidated = true;
}
else
{
@@ -2482,8 +2543,6 @@ void Network::Server_Client_Joined(const char* name, const std::string& keyhash,
player_name = (const char*)playerNameHash.c_str();
format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name);
AppendServerLog(text);
Server_Send_PLAYERLIST();
}
}
@@ -2645,7 +2704,6 @@ void Network::Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket
Server_Send_MAP(&connection);
Server_Send_EVENT_PLAYER_JOINED(player_name);
Server_Send_GROUPLIST(connection);
Server_Send_PLAYERLIST();
}
void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet)
@@ -3190,15 +3248,15 @@ void Network::Client_Handle_PLAYERLIST([[maybe_unused]] NetworkConnection& conne
uint8_t size;
packet >> tick >> size;
_pendingPlayerList.reset();
_pendingPlayerList.tick = tick;
auto& pending = _pendingPlayerLists[tick];
pending.players.clear();
for (uint32_t i = 0; i < size; i++)
{
NetworkPlayer tempplayer;
tempplayer.Read(packet);
_pendingPlayerList.players.push_back(tempplayer);
pending.players.push_back(tempplayer);
}
}
@@ -3364,7 +3422,7 @@ void network_reconnect()
void network_shutdown_client()
{
gNetwork.ShutdownClient();
gNetwork.ServerClientDisconnected();
}
int32_t network_begin_client(const std::string& host, int32_t port)

View File

@@ -35,6 +35,7 @@ public:
NetworkKey Key;
std::vector<uint8_t> Challenge;
std::vector<const ObjectRepositoryItem*> RequestedObjects;
bool IsDisconnected = false;
NetworkConnection();
~NetworkConnection();