fix: dedicated server thread safety, disconnect deadlock, and console freeze

- Protect PlayerList and ServerConnection players vectors with critical
  sections; all iterations use copy-on-read snapshots to prevent iterator
  invalidation during concurrent join/leave
- Add null check on player bounding box in movement validation to prevent
  crash when player is removed mid-tick
- Re-validate socket player pointer immediately before SendData to narrow
  the TOCTOU race window on disconnect
- Replace inline disconnect cleanup with a queued system drained on the
  main tick thread, eliminating the done_cs -> m_playersCS lock inversion
  that caused deadlocks under load
- Disable Windows QuickEdit mode at server startup to prevent console
  input selection from freezing the process
- Move chunk priority sort behind ServerConnection::sortPlayersByChunkPriority()
  to keep the players vector lock-protected
This commit is contained in:
itsRevela
2026-04-10 01:12:59 -05:00
parent 055bce517d
commit 20229fc07b
9 changed files with 309 additions and 158 deletions

View File

@@ -20,6 +20,7 @@ ServerConnection::ServerConnection(MinecraftServer *server)
// 4J - added initialiser
connectionCounter = 0;
InitializeCriticalSection(&pending_cs);
InitializeCriticalSection(&players_cs);
this->server = server;
}
@@ -27,6 +28,7 @@ ServerConnection::ServerConnection(MinecraftServer *server)
ServerConnection::~ServerConnection()
{
DeleteCriticalSection(&pending_cs);
DeleteCriticalSection(&players_cs);
}
// 4J - added to handle incoming connections, to replace thread that original used to have
@@ -38,7 +40,9 @@ void ServerConnection::NewIncomingSocket(Socket *socket)
void ServerConnection::addPlayerConnection(shared_ptr<PlayerConnection> uc)
{
EnterCriticalSection(&players_cs);
players.push_back(uc);
LeaveCriticalSection(&players_cs);
}
void ServerConnection::handleConnection(shared_ptr<PendingConnection> uc)
@@ -76,7 +80,9 @@ void ServerConnection::stop()
}
// Snapshot to avoid iterator invalidation if disconnect modifies the vector.
EnterCriticalSection(&players_cs);
std::vector<shared_ptr<PlayerConnection> > playerSnapshot = players;
LeaveCriticalSection(&players_cs);
for (unsigned int i = 0; i < playerSnapshot.size(); i++)
{
shared_ptr<PlayerConnection> player = playerSnapshot[i];
@@ -118,26 +124,34 @@ void ServerConnection::tick()
}
LeaveCriticalSection(&pending_cs);
for (unsigned int i = 0; i < players.size(); i++)
{
shared_ptr<PlayerConnection> player = players[i];
shared_ptr<ServerPlayer> serverPlayer = player->getPlayer();
if( serverPlayer )
EnterCriticalSection(&players_cs);
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
LeaveCriticalSection(&players_cs);
for (unsigned int i = 0; i < tempPlayers.size(); i++)
{
serverPlayer->updateFrameTick();
serverPlayer->doChunkSendingTick(false);
shared_ptr<PlayerConnection> player = tempPlayers[i];
shared_ptr<ServerPlayer> serverPlayer = player->getPlayer();
if( serverPlayer )
{
serverPlayer->updateFrameTick();
serverPlayer->doChunkSendingTick(false);
}
player->tick();
if (player->done)
{
EnterCriticalSection(&players_cs);
auto it = find(players.begin(), players.end(), player);
if (it != players.end()) players.erase(it);
LeaveCriticalSection(&players_cs);
}
else
{
player->connection->flush();
}
}
player->tick();
if (player->done)
{
players.erase(players.begin()+i);
i--;
}
else
{
player->connection->flush();
}
}
}
}
@@ -163,7 +177,10 @@ void ServerConnection::handleTextureReceived(const wstring &textureName)
{
m_pendingTextureRequests.erase(it);
}
for (auto& player : players)
EnterCriticalSection(&players_cs);
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
LeaveCriticalSection(&players_cs);
for (auto& player : tempPlayers)
{
if (!player->done)
{
@@ -179,7 +196,10 @@ void ServerConnection::handleTextureAndGeometryReceived(const wstring &textureNa
{
m_pendingTextureRequests.erase(it);
}
for (auto& player : players)
EnterCriticalSection(&players_cs);
vector< shared_ptr<PlayerConnection> > tempPlayers = players;
LeaveCriticalSection(&players_cs);
for (auto& player : tempPlayers)
{
if (!player->done)
{
@@ -231,4 +251,46 @@ void ServerConnection::handleServerSettingsChanged(shared_ptr<ServerSettingsChan
vector< shared_ptr<PlayerConnection> > * ServerConnection::getPlayers()
{
return &players;
}
vector< shared_ptr<PlayerConnection> > ServerConnection::getPlayersSnapshot()
{
EnterCriticalSection(&players_cs);
vector< shared_ptr<PlayerConnection> > snapshot = players;
LeaveCriticalSection(&players_cs);
return snapshot;
}
void ServerConnection::sortPlayersByChunkPriority()
{
EnterCriticalSection(&players_cs);
if( players.size() )
{
vector< shared_ptr<PlayerConnection> > playersOrig = players;
players.clear();
do
{
int longestTime = 0;
auto playerConnectionBest = playersOrig.begin();
for( auto it = playersOrig.begin(); it != playersOrig.end(); it++)
{
int thisTime = 0;
INetworkPlayer *np = (*it)->getNetworkPlayer();
if( np )
{
thisTime = np->GetTimeSinceLastChunkPacket_ms();
}
if( thisTime > longestTime )
{
playerConnectionBest = it;
longestTime = thisTime;
}
}
players.push_back(*playerConnectionBest);
playersOrig.erase(playerConnectionBest);
} while ( playersOrig.size() > 0 );
}
LeaveCriticalSection(&players_cs);
}