Skip to content

Commit 3c91131

Browse files
authored
Server: Win*::Match::Update(): Wrap game start code in mutex
Additionally, `Win*::Match::DisconnectedPlayer()` now also uses a try/catch to free mutex on exception.
1 parent d2e0679 commit 3c91131

File tree

2 files changed

+155
-101
lines changed

2 files changed

+155
-101
lines changed

InternetGamesServer/Win7/Match.cpp

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -159,43 +159,49 @@ Match::DisconnectedPlayer(PlayerSocket& player)
159159
default:
160160
throw MutexError("Win7::Match::DisconnectedPlayer(): An error occured waiting for mutex: " + std::to_string(GetLastError()));
161161
}
162-
163-
RemovePlayer(player);
164-
165-
// End the match on no players, marking it as to-be-removed from MatchManager
166-
if (m_players.empty())
167-
{
168-
m_state = STATE_ENDED;
169-
}
170-
// Originally, servers replaced players who have left the game with computer (AI) players.
171-
// Based on the game, either replace with computer player, or end the game directly by disconnecting everyone.
172-
else if (m_state == STATE_PLAYING)
162+
try
173163
{
174-
if ((g_config.allowSinglePlayer || m_players.size() > 1) && SupportsComputerPlayers())
164+
RemovePlayer(player);
165+
166+
// End the match on no players, marking it as to-be-removed from MatchManager
167+
if (m_players.empty())
168+
{
169+
m_state = STATE_ENDED;
170+
}
171+
// Originally, servers replaced players who have left the game with computer (AI) players.
172+
// Based on the game, either replace with computer player, or end the game directly by disconnecting everyone.
173+
else if (m_state == STATE_PLAYING)
175174
{
176-
const std::string replaceWithAIXML =
177-
StateSTag::ConstructMethodMessage("GameManagement", "ReplaceWithAI",
178-
std::to_string(player.m_role) + ",");
179-
for (PlayerSocket* p : m_players)
180-
p->OnEventReceive(replaceWithAIXML);
175+
if ((g_config.allowSinglePlayer || m_players.size() > 1) && SupportsComputerPlayers())
176+
{
177+
const std::string replaceWithAIXML =
178+
StateSTag::ConstructMethodMessage("GameManagement", "ReplaceWithAI",
179+
std::to_string(player.m_role) + ",");
180+
for (PlayerSocket* p : m_players)
181+
p->OnEventReceive(replaceWithAIXML);
181182

182-
m_playerSeatsComputer[player.m_role] = true;
183+
m_playerSeatsComputer[player.m_role] = true;
183184

184-
OnReplacePlayer(player);
185-
}
185+
OnReplacePlayer(player);
186+
}
186187
#if not MATCH_NO_DISCONNECT_ON_PLAYER_LEAVE
187-
else
188-
{
189-
// Disconnect any remaining players
190-
for (PlayerSocket* p : m_players)
191-
p->OnMatchDisconnect();
192-
m_players.clear();
188+
else
189+
{
190+
// Disconnect any remaining players
191+
for (PlayerSocket* p : m_players)
192+
p->OnMatchDisconnect();
193+
m_players.clear();
193194

194-
m_state = STATE_ENDED;
195-
}
195+
m_state = STATE_ENDED;
196+
}
196197
#endif
198+
}
199+
}
200+
catch (...)
201+
{
202+
ReleaseMutex(m_mutex);
203+
throw;
197204
}
198-
199205
if (!ReleaseMutex(m_mutex))
200206
throw MutexError("Win7::Match::JoinPlayer(): Couldn't release mutex: " + std::to_string(GetLastError()));
201207
}
@@ -208,23 +214,44 @@ Match::Update()
208214
{
209215
case STATE_WAITINGFORPLAYERS:
210216
{
211-
// Start the game, if all players are waiting for opponents
212-
if (m_players.size() == GetRequiredPlayerCount() &&
213-
std::all_of(m_players.begin(), m_players.end(),
214-
[](const auto& player) { return player->GetState() == PlayerSocket::STATE_WAITINGFOROPPONENTS; }))
217+
switch (WaitForSingleObject(m_mutex, MATCH_MUTEX_TIMEOUT_MS))
218+
{
219+
case WAIT_OBJECT_0: // Acquired ownership of the mutex
220+
break;
221+
case WAIT_TIMEOUT:
222+
throw MutexError("Win7::Match::Update(): Timed out waiting for mutex: " + std::to_string(GetLastError()));
223+
case WAIT_ABANDONED: // Acquired ownership of an abandoned mutex
224+
throw MutexError("Win7::Match::Update(): Got ownership of an abandoned mutex: " + std::to_string(GetLastError()));
225+
default:
226+
throw MutexError("Win7::Match::Update(): An error occured waiting for mutex: " + std::to_string(GetLastError()));
227+
}
228+
try
215229
{
216-
// Distribute unique role IDs for each player, starting from 0
217-
const int playerCount = static_cast<int>(m_players.size());
218-
const std::vector<int> roles = GenerateUniqueRandomNums(0, playerCount - 1);
219-
for (int i = 0; i < playerCount; i++)
220-
const_cast<int8_t&>(m_players[i]->m_role) = static_cast<int8_t>(roles[i]);
230+
// Start the game, if all players are waiting for opponents
231+
if (m_players.size() == GetRequiredPlayerCount() &&
232+
std::all_of(m_players.begin(), m_players.end(),
233+
[](const auto& player) { return player->GetState() == PlayerSocket::STATE_WAITINGFOROPPONENTS; }))
234+
{
235+
// Distribute unique role IDs for each player, starting from 0
236+
const int playerCount = static_cast<int>(m_players.size());
237+
const std::vector<int> roles = GenerateUniqueRandomNums(0, playerCount - 1);
238+
for (int i = 0; i < playerCount; i++)
239+
const_cast<int8_t&>(m_players[i]->m_role) = static_cast<int8_t>(roles[i]);
221240

222-
for (PlayerSocket* p : m_players)
223-
p->OnGameStart();
241+
for (PlayerSocket* p : m_players)
242+
p->OnGameStart();
224243

225-
SessionLog() << "[MATCH] " << m_guid << ": Started match!" << std::endl;
226-
m_state = STATE_PLAYING;
244+
SessionLog() << "[MATCH] " << m_guid << ": Started match!" << std::endl;
245+
m_state = STATE_PLAYING;
246+
}
247+
}
248+
catch (...)
249+
{
250+
ReleaseMutex(m_mutex);
251+
throw;
227252
}
253+
if (!ReleaseMutex(m_mutex))
254+
throw MutexError("Win7::Match::Update(): Couldn't release mutex: " + std::to_string(GetLastError()));
228255
break;
229256
}
230257
case STATE_GAMEOVER:

InternetGamesServer/WinXP/Match.cpp

Lines changed: 86 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -158,64 +158,70 @@ Match::DisconnectedPlayer(PlayerSocket& player)
158158
default:
159159
throw MutexError("WinXP::Match::DisconnectedPlayer(): An error occured waiting for mutex: " + std::to_string(GetLastError()));
160160
}
161-
162-
RemovePlayer(player);
163-
164-
// End the match on no players, marking it as to-be-removed from MatchManager
165-
if (m_players.empty())
166-
{
167-
m_state = STATE_ENDED;
168-
}
169-
else if (m_state == STATE_WAITINGFORPLAYERS)
170-
{
171-
MsgServerStatus msgServerStatus;
172-
msgServerStatus.playersWaiting = static_cast<int>(m_players.size());
173-
BroadcastGenericMessage<MessageServerStatus>(msgServerStatus);
174-
}
175-
// Originally, servers replaced players who have left the game with computer (AI) players.
176-
// Based on the game, either replace with computer player, or end the game directly by disconnecting everyone.
177-
else if (m_state == STATE_PLAYING || m_state == STATE_GAMEOVER)
161+
try
178162
{
179-
if ((g_config.allowSinglePlayer || m_players.size() > 1) && SupportsComputerPlayers())
163+
RemovePlayer(player);
164+
165+
// End the match on no players, marking it as to-be-removed from MatchManager
166+
if (m_players.empty())
167+
{
168+
m_state = STATE_ENDED;
169+
}
170+
else if (m_state == STATE_WAITINGFORPLAYERS)
180171
{
181-
const uint32 userID = std::uniform_int_distribution<uint32>{}(g_rng);
172+
MsgServerStatus msgServerStatus;
173+
msgServerStatus.playersWaiting = static_cast<int>(m_players.size());
174+
BroadcastGenericMessage<MessageServerStatus>(msgServerStatus);
175+
}
176+
// Originally, servers replaced players who have left the game with computer (AI) players.
177+
// Based on the game, either replace with computer player, or end the game directly by disconnecting everyone.
178+
else if (m_state == STATE_PLAYING || m_state == STATE_GAMEOVER)
179+
{
180+
if ((g_config.allowSinglePlayer || m_players.size() > 1) && SupportsComputerPlayers())
181+
{
182+
const uint32 userID = std::uniform_int_distribution<uint32>{}(g_rng);
182183

183-
MsgPlayerReplaced msgPlayerReplaced;
184-
msgPlayerReplaced.userIDOld = player.GetID();
185-
msgPlayerReplaced.userIDNew = userID;
186-
BroadcastGenericMessage<MessagePlayerReplaced>(msgPlayerReplaced);
184+
MsgPlayerReplaced msgPlayerReplaced;
185+
msgPlayerReplaced.userIDOld = player.GetID();
186+
msgPlayerReplaced.userIDNew = userID;
187+
BroadcastGenericMessage<MessagePlayerReplaced>(msgPlayerReplaced);
187188

188-
m_playerSeatsComputer[player.m_seat] = true;
189-
m_playerComputerIDs[player.m_seat] = userID;
189+
m_playerSeatsComputer[player.m_seat] = true;
190+
m_playerComputerIDs[player.m_seat] = userID;
190191

191-
OnReplacePlayer(player, userID);
192-
}
192+
OnReplacePlayer(player, userID);
193+
}
193194
#if not MATCH_NO_DISCONNECT_ON_PLAYER_LEAVE
194-
else
195-
{
196-
SessionLog() << "[MATCH] " << m_guid << ": A player left, closing match!" << std::endl;
197-
198-
for (PlayerSocket* p : m_players)
195+
else
199196
{
200-
try
201-
{
202-
p->OnMatchDisconnect();
203-
}
204-
catch (const std::exception& err)
197+
SessionLog() << "[MATCH] " << m_guid << ": A player left, closing match!" << std::endl;
198+
199+
for (PlayerSocket* p : m_players)
205200
{
206-
SessionLog() << "[MATCH] " << m_guid
207-
<< ": Couldn't disconnect socket from this match! Disconnecting from server instead. Error: "
208-
<< err.what() << std::endl;
209-
p->Disconnect();
201+
try
202+
{
203+
p->OnMatchDisconnect();
204+
}
205+
catch (const std::exception& err)
206+
{
207+
SessionLog() << "[MATCH] " << m_guid
208+
<< ": Couldn't disconnect socket from this match! Disconnecting from server instead. Error: "
209+
<< err.what() << std::endl;
210+
p->Disconnect();
211+
}
210212
}
211-
}
212-
m_players.clear();
213+
m_players.clear();
213214

214-
m_state = STATE_ENDED;
215-
}
215+
m_state = STATE_ENDED;
216+
}
216217
#endif
218+
}
219+
}
220+
catch (...)
221+
{
222+
ReleaseMutex(m_mutex);
223+
throw;
217224
}
218-
219225
if (!ReleaseMutex(m_mutex))
220226
throw MutexError("WinXP::Match::DisconnectedPlayer(): Couldn't release mutex: " + std::to_string(GetLastError()));
221227
}
@@ -228,23 +234,44 @@ Match::Update()
228234
{
229235
case STATE_WAITINGFORPLAYERS:
230236
{
231-
// Start the game, if all players are waiting for opponents
232-
if (m_players.size() == GetRequiredPlayerCount() &&
233-
std::all_of(m_players.begin(), m_players.end(),
234-
[](const auto& player) { return player->GetState() == PlayerSocket::STATE_WAITINGFOROPPONENTS; }))
237+
switch (WaitForSingleObject(m_mutex, MATCH_MUTEX_TIMEOUT_MS))
238+
{
239+
case WAIT_OBJECT_0: // Acquired ownership of the mutex
240+
break;
241+
case WAIT_TIMEOUT:
242+
throw MutexError("WinXP::Match::Update(): Timed out waiting for mutex: " + std::to_string(GetLastError()));
243+
case WAIT_ABANDONED: // Acquired ownership of an abandoned mutex
244+
throw MutexError("WinXP::Match::Update(): Got ownership of an abandoned mutex: " + std::to_string(GetLastError()));
245+
default:
246+
throw MutexError("WinXP::Match::Update(): An error occured waiting for mutex: " + std::to_string(GetLastError()));
247+
}
248+
try
235249
{
236-
// Distribute unique IDs for each player, starting from 0
237-
const int playerCount = static_cast<int>(m_players.size());
238-
const std::vector<int> seats = GenerateUniqueRandomNums(0, playerCount - 1);
239-
for (int i = 0; i < playerCount; i++)
240-
const_cast<int16&>(m_players[i]->m_seat) = seats[i];
250+
// Start the game, if all players are waiting for opponents
251+
if (m_players.size() == GetRequiredPlayerCount() &&
252+
std::all_of(m_players.begin(), m_players.end(),
253+
[](const auto& player) { return player->GetState() == PlayerSocket::STATE_WAITINGFOROPPONENTS; }))
254+
{
255+
// Distribute unique IDs for each player, starting from 0
256+
const int playerCount = static_cast<int>(m_players.size());
257+
const std::vector<int> seats = GenerateUniqueRandomNums(0, playerCount - 1);
258+
for (int i = 0; i < playerCount; i++)
259+
const_cast<int16&>(m_players[i]->m_seat) = seats[i];
241260

242-
for (PlayerSocket* p : m_players)
243-
p->OnGameStart(m_players);
261+
for (PlayerSocket* p : m_players)
262+
p->OnGameStart(m_players);
244263

245-
SessionLog() << "[MATCH] " << m_guid << ": Started match!" << std::endl;
246-
m_state = STATE_PLAYING;
264+
SessionLog() << "[MATCH] " << m_guid << ": Started match!" << std::endl;
265+
m_state = STATE_PLAYING;
266+
}
267+
}
268+
catch (...)
269+
{
270+
ReleaseMutex(m_mutex);
271+
throw;
247272
}
273+
if (!ReleaseMutex(m_mutex))
274+
throw MutexError("WinXP::Match::Update(): Couldn't release mutex: " + std::to_string(GetLastError()));
248275
break;
249276
}
250277
case STATE_PLAYING:

0 commit comments

Comments
 (0)