Prevent undefined behaviour if too many players joined battle or soccer

This commit is contained in:
Benau 2020-08-27 00:58:09 +08:00
parent f99e13cb78
commit 875c2b8eb8
3 changed files with 58 additions and 9 deletions

View File

@ -2456,7 +2456,7 @@ void ServerLobby::startSelection(const Event *event)
if (!has_peer_plays_game)
{
for (STKPeer* peer : always_spectate_peers)
peer->setAlwaysSpectate(false);
peer->setAlwaysSpectate(ASM_NONE);
always_spectate_peers.clear();
}
else
@ -2469,6 +2469,40 @@ void ServerLobby::startSelection(const Event *event)
peer->setWaitingForGame(true);
}
unsigned max_player = 0;
STKHost::get()->updatePlayers(&max_player);
if (max_player > 10 && (RaceManager::get()->isBattleMode() ||
RaceManager::get()->isSoccerMode()))
{
// Set late coming player to spectate if too many players in battle or
// soccer
std::sort(peers.begin(), peers.end(),
[](const std::shared_ptr<STKPeer>& a,
const std::shared_ptr<STKPeer>& b)
{ return a->getHostId() > b->getHostId(); });
int remove_player = max_player - 10;
for (unsigned i = 0; i < peers.size(); i++)
{
auto& peer = peers[i];
if (!peer->isValidated() || peer->isWaitingForGame())
continue;
peer->setAlwaysSpectate(ASM_FULL);
peer->setWaitingForGame(true);
always_spectate_peers.insert(peer.get());
remove_player -= (int)peer->getPlayerProfiles().size();
if (remove_player <= 0)
break;
// In case something goes wrong (all players need spectate)
if (i == peers.size() - 1)
{
Log::error("ServerLobby", "Too many players and cannot set "
"spectate for late coming players!");
return;
}
}
}
for (const std::string& kart_erase : karts_erase)
{
m_available_kts.first.erase(kart_erase);
@ -2478,7 +2512,7 @@ void ServerLobby::startSelection(const Event *event)
m_available_kts.second.erase(track_erase);
}
unsigned max_player = 0;
max_player = 0;
STKHost::get()->updatePlayers(&max_player);
if (auto ai = m_ai_peer.lock())
{
@ -4684,6 +4718,7 @@ void ServerLobby::addWaitingPlayersToGame()
if (!peer || !peer->isValidated())
continue;
peer->resetAlwaysSpectateFull();
peer->setWaitingForGame(false);
peer->setSpectator(false);
if (m_peers_ready.find(peer) == m_peers_ready.end())
@ -5522,10 +5557,10 @@ void ServerLobby::handleServerCommand(Event* event,
delete chat;
return;
}
peer->setAlwaysSpectate(true);
peer->setAlwaysSpectate(ASM_COMMAND);
}
else
peer->setAlwaysSpectate(false);
peer->setAlwaysSpectate(ASM_NONE);
updatePlayerList();
}
else if (argv[0] == "listserveraddon")

View File

@ -44,7 +44,7 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
m_host_id = host_id;
m_connected_time = StkTime::getMonoTimeMs();
m_validated.store(false);
m_always_spectate.store(false);
m_always_spectate.store(ASM_NONE);
m_average_ping.store(0);
m_packet_loss.store(0);
m_waiting_for_game.store(true);

View File

@ -61,6 +61,13 @@ enum AddonScore : int
AS_TOTAL = 4,
}; // AddonScore
enum AlwaysSpectateMode : uint8_t
{
ASM_NONE = 0, //!< Default, not spectating at all
ASM_COMMAND = 1, //!< Set by player through command
ASM_FULL = 2, //!< Set by server because too many players joined
}; // AlwaysSpectateMode
/*! \class STKPeer
* \brief Represents a peer.
* This class is used to interface the ENetPeer structure.
@ -85,7 +92,7 @@ protected:
std::atomic_bool m_warned_for_high_ping;
std::atomic_bool m_always_spectate;
std::atomic<uint8_t> m_always_spectate;
/** Host id of this peer. */
uint32_t m_host_id;
@ -292,10 +299,17 @@ public:
// ------------------------------------------------------------------------
const SocketAddress& getAddress() const { return *m_socket_address.get(); }
// ------------------------------------------------------------------------
void setAlwaysSpectate(bool val) { m_always_spectate.store(val); }
void setAlwaysSpectate(AlwaysSpectateMode mode)
{ m_always_spectate.store(mode); }
// ------------------------------------------------------------------------
bool alwaysSpectate() const { return m_always_spectate.load(); }
bool alwaysSpectate() const
{ return m_always_spectate.load() != ASM_NONE; }
// ------------------------------------------------------------------------
void resetAlwaysSpectateFull()
{
if (m_always_spectate.load() == ASM_FULL)
m_always_spectate.store(ASM_NONE);
}
}; // STKPeer
#endif // STK_PEER_HPP