Final hardening: online=0 in finally + auto-save race fix

CRITICAL-1: online=0 moved to finally block in logout handler.
If store() threw an exception, online=0 was never written and the
player was permanently locked out of all servers.

CRITICAL-2: Same fix for shutdown handler. Any save failure during
shutdown left the player permanently stuck as online=1.

IMPORTANT: Auto-save background DB write now acquires tryLock()
before writing. If logout already saved newer data and holds/held
the lock, the stale auto-save snapshot is skipped. Prevents
overwriting correct logout data with an older snapshot.

Vyrriox
This commit is contained in:
laforetbrut 2026-03-26 22:06:38 +01:00
parent 1bf2a67e8d
commit e511414463

View File

@ -731,11 +731,17 @@ public class VanillaSync {
if (ModList.get().isLoaded("refinedstorage")) {
ModsSupport.storeRefinedStorageDisks(player);
}
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", puuid);
PlayerSync.LOGGER.info("Saved player {} data on server shutdown", player.getUUID());
} catch (Exception e) {
PlayerSync.LOGGER.error("Error saving player {} on shutdown", player.getUUID(), e);
} finally {
// CRITICAL: online=0 MUST be in finally - if any save throws,
// player gets permanently locked as online=1
try {
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", puuid);
} catch (Exception e2) {
PlayerSync.LOGGER.error("CRITICAL: Failed to mark player {} offline on shutdown", puuid, e2);
}
lock.unlock();
}
}
@ -794,10 +800,16 @@ public class VanillaSync {
ModCompatSync.storeAll(player);
// Save main inventory + effects + advancements (main thread - safe)
store(player, false);
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player_uuid);
} catch (Exception e) {
PlayerSync.LOGGER.error("Error during player logout save for {}", player_uuid, e);
} finally {
// CRITICAL: online=0 MUST be in finally - if store() throws, player gets
// permanently locked as online=1 and can never reconnect.
try {
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player_uuid);
} catch (Exception e2) {
PlayerSync.LOGGER.error("CRITICAL: Failed to mark player {} offline", player_uuid, e2);
}
lock.unlock();
removePlayerLock(player_uuid);
}
@ -1137,11 +1149,16 @@ public class VanillaSync {
}
// === BACKGROUND THREAD: Write main snapshot to DB (slow, off main thread) ===
// Use tryLock in the background task to skip if logout already saved newer data
executorService.submit(() -> {
ReentrantLock bgLock = getPlayerLock(puuid);
if (!bgLock.tryLock()) return; // logout won the race, skip stale snapshot
try {
writeSnapshotToDB(snapshot);
} catch (Exception e) {
PlayerSync.LOGGER.error("Error auto-saving player {}", puuid, e);
} finally {
bgLock.unlock();
}
});
} catch (Exception e) {