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

@@ -506,7 +506,6 @@ void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int
INetworkPlayer *socketPlayer = m_socket->getPlayer();
if(socketPlayer == nullptr)
{
app.DebugPrintf("Trying to write to network, but the socketPlayer is nullptr\n");
return;
}
@@ -518,30 +517,20 @@ void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int
bool requireAck = ( ( flags & NON_QNET_SENDDATA_ACK_REQUIRED ) == NON_QNET_SENDDATA_ACK_REQUIRED );
#endif
// Re-validate the socket player immediately before use to minimize
// the TOCTOU window where the network layer could remove the player
// between our initial null-check and the SendData call.
if( m_queueIdx == SOCKET_SERVER_END )
{
//printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n",
//buffer.dwDataSize,
//hostPlayer->GetGamertag(),
//m_socket->networkPlayer->GetGamertag());
hostPlayer->SendData(socketPlayer, buffer.pbyData, buffer.dwDataSize, lowPriority, requireAck);
// DWORD queueSize = hostPlayer->GetSendQueueSize( nullptr, QNET_GETSENDQUEUESIZE_BYTES );
// if( queueSize > 24000 )
// {
// //printf("Queue size is: %d, forcing doWork()\n",queueSize);
// g_NetworkManager.DoWork();
// }
INetworkPlayer *validatedPlayer = m_socket->getPlayer();
if(validatedPlayer == nullptr) return;
hostPlayer->SendData(validatedPlayer, buffer.pbyData, buffer.dwDataSize, lowPriority, requireAck);
}
else
{
//printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n",
//buffer.dwDataSize,
//m_socket->networkPlayer->GetGamertag(),
//hostPlayer->GetGamertag());
socketPlayer->SendData(hostPlayer, buffer.pbyData, buffer.dwDataSize, lowPriority, requireAck);
INetworkPlayer *validatedPlayer = m_socket->getPlayer();
if(validatedPlayer == nullptr) return;
validatedPlayer->SendData(hostPlayer, buffer.pbyData, buffer.dwDataSize, lowPriority, requireAck);
}
}
}