From b1563cc9aee4765d10532910032edf5889ff733d Mon Sep 17 00:00:00 2001 From: laforetbrut Date: Thu, 26 Mar 2026 18:27:29 +0100 Subject: [PATCH] Fix duplicate login kick bypass - logout was resetting online flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE: When Server B kicks a player for being already online on Server A, the onPlayerLogout handler on Server B fires and sets online=0 in the DB. The player then immediately reconnects to Server B, the DB says online=0, and the kick check passes - player is now on BOTH servers simultaneously. FIX: New kickedForDuplicateLogin set tracks players being kicked for duplicate login. onPlayerLogout checks this set FIRST and skips the online=0 update entirely. The player's DB record correctly stays online=1 with last_server=A, preventing reconnect bypass. Flow: 1. Player on Server A (online=1, last_server=A) 2. Player tries Server B → kick check → online=1, A alive → KICK 3. kickedForDuplicateLogin.add(uuid) BEFORE disconnect 4. onPlayerLogout fires → sees kickedForDuplicateLogin → skips online=0 5. Player retries Server B → online=1 still → KICKED AGAIN Vyrriox --- .../vip/fubuki/playersync/sync/VanillaSync.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java b/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java index fd08f5b..6fad305 100644 --- a/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java +++ b/src/main/java/vip/fubuki/playersync/sync/VanillaSync.java @@ -217,6 +217,8 @@ public class VanillaSync { // Use string uuid as key public static Set deadPlayerWhileLogging = ConcurrentHashMap.newKeySet(); public static Set syncNotCompletedPlayer = ConcurrentHashMap.newKeySet(); + // Players kicked for being already online on another server - their logout must NOT set online=0 + public static Set kickedForDuplicateLogin = ConcurrentHashMap.newKeySet(); public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { ServerPlayer serverPlayer = (ServerPlayer) event.getEntity(); @@ -466,6 +468,9 @@ public class VanillaSync { boolean enable = rs2.getBoolean("enable"); if (enable && System.currentTimeMillis() < lastUpdate + 300000L) { // Other server is alive → KICK using ServerPlayer.connection which works reliably + // CRITICAL: Mark as kicked BEFORE disconnect so onPlayerLogout does NOT set online=0. + // Without this, the logout handler resets online=0, allowing immediate reconnect bypass. + kickedForDuplicateLogin.add(player_uuid); PlayerSync.LOGGER.warn("Kicking player {} - already online on server {}", player_uuid, lastServer); player.connection.disconnect(Component.translatableWithFallback( "playersync.already_online", @@ -739,7 +744,15 @@ public class VanillaSync { @SubscribeEvent public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) { String player_uuid = event.getEntity().getUUID().toString(); - if (deadPlayerWhileLogging.contains(player_uuid)) { + // FIX: Players kicked for duplicate login must NOT set online=0. + // They are still online on the OTHER server. Setting online=0 here would allow + // them to bypass the kick by immediately reconnecting (DB says offline while + // they're still on the other server). + if (kickedForDuplicateLogin.contains(player_uuid)) { + PlayerSync.LOGGER.info("Player {} was kicked for duplicate login, NOT marking offline (still on other server)", player_uuid); + kickedForDuplicateLogin.remove(player_uuid); + return; + } else if (deadPlayerWhileLogging.contains(player_uuid)) { PlayerSync.LOGGER.warn("A dead or dying player was kicked, uuid: {}", player_uuid); try { JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player_uuid);