From 5c0f2d2855d4f593f8a52fc8f2d0661d9b0ac62c Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Tue, 9 Jun 2026 22:02:07 +0200 Subject: [PATCH] fix(session): separate forced disconnects from resume parking Add a forced dispose path for bans, RCON disconnects, logout/account endpoints, plugin-cancelled login, duplicate login replacement, and late MAC-ban enforcement. Soft channel closes still park a session for reconnect, while security-driven closes now bypass session resume. Also null-guard client/channel disposal and cover the contract with focused tests. --- .../habbohotel/gameclients/GameClient.java | 8 ++++-- .../gameclients/GameClientManager.java | 26 ++++++++++++++++--- .../habbo/habbohotel/users/HabboManager.java | 2 +- .../incoming/handshake/MachineIDEvent.java | 4 +-- .../incoming/handshake/SecureLoginEvent.java | 2 +- .../habbo/messages/rcon/DisconnectUser.java | 4 +-- .../auth/AccountChangeEndpoints.java | 2 +- .../gameserver/auth/SessionEndpoints.java | 2 +- .../GameClientManagerContractTest.java | 22 ++++++++++++++++ 9 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 Emulator/src/test/java/com/eu/habbo/habbohotel/gameclients/GameClientManagerContractTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java index 7ffe5228..87d5fb3d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java @@ -149,6 +149,10 @@ public class GameClient { } public void dispose() { + this.dispose(true); + } + + public void dispose(boolean allowSessionResume) { try { this.channel.close(); @@ -161,7 +165,7 @@ public class GameClient { // appena ripristinata (era la causa del "Bye"/kick al 2° reconnect). if (this.habbo.getClient() == this && this.habbo.isOnline()) { // Try to park the habbo in the grace period instead of immediate disconnect - boolean parked = SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket); + boolean parked = allowSessionResume && SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket); if (!parked) { // No grace period configured — immediate disconnect as before @@ -177,4 +181,4 @@ public class GameClient { LOGGER.error("Caught exception", e); } } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java index cd0602cb..d07e33b7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClientManager.java @@ -43,14 +43,34 @@ public class GameClientManager { public void disposeClient(GameClient client) { - this.disposeClient(client.getChannel()); + if (client == null) { + return; + } + + this.disposeClient(client.getChannel(), true); + } + + public void forceDisposeClient(GameClient client) { + if (client == null) { + return; + } + + this.disposeClient(client.getChannel(), false); } private void disposeClient(Channel channel) { + this.disposeClient(channel, true); + } + + private void disposeClient(Channel channel, boolean allowSessionResume) { + if (channel == null) { + return; + } + GameClient client = channel.attr(GameServerAttributes.CLIENT).get(); if (client != null) { - client.dispose(); + client.dispose(allowSessionResume); } channel.deregister(); channel.attr(GameServerAttributes.CLIENT).set(null); @@ -190,4 +210,4 @@ public class GameClientManager { CFKeepAlive(); }, 30000); } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java index 48261f66..ef602679 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/HabboManager.java @@ -111,7 +111,7 @@ public class HabboManager { habbo = this.cloneCheck(userId); if (habbo != null) { habbo.alert(Emulator.getTexts().getValue("loggedin.elsewhere")); - Emulator.getGameServer().getGameClientManager().disposeClient(habbo.getClient()); + Emulator.getGameServer().getGameClientManager().forceDisposeClient(habbo.getClient()); habbo = null; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java index b39fda3f..b9c7fd71 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java @@ -37,10 +37,10 @@ public class MachineIDEvent extends MessageHandler { // SSO ticket), so Habbo.connect() may have skipped the MAC-ban check for // lack of a machineId. Enforce it now that the fingerprint is known. if (Emulator.getGameEnvironment().getModToolManager().hasMACBan(this.client)) { - Emulator.getGameServer().getGameClientManager().disposeClient(this.client); + Emulator.getGameServer().getGameClientManager().forceDisposeClient(this.client); } } LOGGER.debug("Setting client MachineId to {}", storedMachineId); } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java index cdebe27d..f9704bec 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java @@ -306,7 +306,7 @@ public class SecureLoginEvent extends MessageHandler { Emulator.getPluginManager().fireEvent(userLoginEvent); if(userLoginEvent.isCancelled()) { - Emulator.getGameServer().getGameClientManager().disposeClient(this.client); + Emulator.getGameServer().getGameClientManager().forceDisposeClient(this.client); return; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/rcon/DisconnectUser.java b/Emulator/src/main/java/com/eu/habbo/messages/rcon/DisconnectUser.java index 8b768ac0..8f8a51ed 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/rcon/DisconnectUser.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/rcon/DisconnectUser.java @@ -29,7 +29,7 @@ public class DisconnectUser extends RCONMessage GameClient.class.getDeclaredMethod("dispose", boolean.class)); + assertDoesNotThrow(() -> GameClientManager.class.getDeclaredMethod("forceDisposeClient", GameClient.class)); + } + + @Test + void disposeMethodsIgnoreNullClient() { + GameClientManager manager = new GameClientManager(); + + assertDoesNotThrow(() -> manager.disposeClient(null)); + assertDoesNotThrow(() -> manager.forceDisposeClient(null)); + } +}