fix: server save-on-exit reliability and logging

This commit is contained in:
itsRevela
2026-04-29 13:28:20 -05:00
parent 17fdc78567
commit ff3241cfee
5 changed files with 153 additions and 50 deletions
+6
View File
@@ -1544,6 +1544,12 @@ void MinecraftServer::stopServer(bool didInit)
}
}
}
// connection->stop() enqueued disconnects; the run loop is gone, so
// drain now or DisconnectPacket + sendAndQuit() never fire.
if (players != nullptr)
{
players->drainPendingDisconnects();
}
// reset the primary player signout flag
m_bPrimaryPlayerSignedOut=false;
s_bServerHalted = false;
+40 -37
View File
@@ -121,6 +121,45 @@ void PlayerList::queueDisconnect(shared_ptr<ServerPlayer> player, int reason, co
LeaveCriticalSection(&m_disconnectCS);
}
void PlayerList::drainPendingDisconnects()
{
std::deque<PendingDisconnect> dcCopy;
EnterCriticalSection(&m_disconnectCS);
dcCopy.swap(m_pendingDisconnects);
LeaveCriticalSection(&m_disconnectCS);
while (!dcCopy.empty())
{
PendingDisconnect pd = dcCopy.front();
dcCopy.pop_front();
server->getPlayers()->removePlayerFromReceiving(pd.player);
if (pd.player->connection != nullptr)
{
pd.player->connection->send(std::make_shared<DisconnectPacket>(static_cast<DisconnectPacket::eDisconnectReason>(pd.reason)));
pd.player->connection->connection->sendAndQuit();
}
if (!pd.kickMessage.empty())
{
broadcastAll(std::make_shared<ChatPacket>(pd.kickMessage));
}
else if (!pd.fourKitHandledQuit)
{
if (pd.wasKicked)
{
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerKickedFromGame));
}
else
{
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerLeftGame));
}
}
remove(pd.player);
}
}
bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer> player, shared_ptr<LoginPacket> packet)
{
CompoundTag *playerTag = load(player);
@@ -1300,43 +1339,7 @@ void PlayerList::tick()
// Drain the pending disconnect queue. disconnect() enqueues here so it
// can release done_cs before the heavy cleanup runs on the tick thread.
{
std::deque<PendingDisconnect> dcCopy;
EnterCriticalSection(&m_disconnectCS);
dcCopy.swap(m_pendingDisconnects);
LeaveCriticalSection(&m_disconnectCS);
while (!dcCopy.empty())
{
PendingDisconnect pd = dcCopy.front();
dcCopy.pop_front();
server->getPlayers()->removePlayerFromReceiving(pd.player);
if (pd.player->connection != nullptr)
{
pd.player->connection->send(std::make_shared<DisconnectPacket>(static_cast<DisconnectPacket::eDisconnectReason>(pd.reason)));
pd.player->connection->connection->sendAndQuit();
}
if (!pd.kickMessage.empty())
{
broadcastAll(std::make_shared<ChatPacket>(pd.kickMessage));
}
else if (!pd.fourKitHandledQuit)
{
if (pd.wasKicked)
{
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerKickedFromGame));
}
else
{
broadcastAll(std::make_shared<ChatPacket>(pd.player->name, ChatPacket::e_ChatPlayerLeftGame));
}
}
remove(pd.player);
}
}
drainPendingDisconnects();
// Drain the close queue: snapshot the deque, then release the CS before
// calling disconnect() which may itself try to acquire other locks.
+1
View File
@@ -98,6 +98,7 @@ public:
void move(shared_ptr<ServerPlayer> player);
void remove(shared_ptr<ServerPlayer> player);
void queueDisconnect(shared_ptr<ServerPlayer> player, int reason, const wstring& kickMessage, bool wasKicked, bool fourKitHandledQuit);
void drainPendingDisconnects();
shared_ptr<ServerPlayer> getPlayerForLogin(PendingConnection *pendingConnection, const wstring& userName, PlayerUID xuid, PlayerUID OnlineXuid);
shared_ptr<ServerPlayer> respawn(shared_ptr<ServerPlayer> serverPlayer, int targetDimension, bool keepAllPlayerData);
void toggleDimension(shared_ptr<ServerPlayer> player, int targetDimension);