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 f526c15b..7ffe5228 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 @@ -153,7 +153,13 @@ public class GameClient { this.channel.close(); if (this.habbo != null) { - if (this.habbo.isOnline()) { + // Agisci sull'Habbo SOLO se è ancora attaccato a QUESTO client. Su un + // reconnect veloce (drop Cloudflare → il client riconnette) l'Habbo può + // essere già stato riassegnato alla NUOVA connessione (session resume): + // in quel caso questo dispose della vecchia connessione NON deve + // parcheggiarlo né disconnetterlo, altrimenti ucciderebbe la sessione + // 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); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java index 1f654d35..e8099bbc 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/SessionResumeManager.java @@ -118,16 +118,32 @@ public class SessionResumeManager { LOGGER.error("[SessionResume] Error during deferred disconnect", e); } - clearSsoTicket(habbo.getHabboInfo().getId()); + // NON svuotare il ticket SSO qui. Dietro Cloudflare la pagina si ricarica + // lentamente (~15s) e la grace (5s) scade prima che la nuova connessione + // arrivi: svuotando il ticket si cancellava quello NUOVO appena scritto dal + // CMS per il refresh → "non-existing SSO token" → bisognava refreshare 2 volte. + // Il ticket vive col suo TTL (auth_ticket_expires_at) e viene sovrascritto dal + // CMS al prossimo /client o azzerato al logout. } private void restoreSsoTicket(int userId, String ssoTicket) { + // Restore the old ticket ONLY if no fresh ticket has been written in the + // meantime. On a hard-refresh the CMS writes a NEW auth_ticket for the same + // user before this parking restore runs; without the guard we'd clobber it + // with the old ticket, so the new connection's SSO wouldn't be found and the + // client would get "session expired" on the first attempt. The guard means: + // normal reconnect (ticket cleared to '' after login) -> restore; hard-refresh + // (CMS already wrote a new ticket) -> leave the new ticket untouched. try (var connection = Emulator.getDatabase().getDataSource().getConnection(); - var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) { + var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? AND (auth_ticket = '' OR auth_ticket IS NULL) LIMIT 1")) { statement.setString(1, ssoTicket); statement.setInt(2, userId); - statement.execute(); - LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId); + int updated = statement.executeUpdate(); + if (updated > 0) { + LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId); + } else { + LOGGER.info("[SessionResume] Skipped SSO restore for user {} — a newer ticket is already present (likely a fresh login/hard-refresh)", userId); + } } catch (Exception e) { LOGGER.error("[SessionResume] Failed to restore SSO ticket for user " + userId, e); } 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 4413163d..48261f66 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 @@ -132,15 +132,12 @@ public class HabboManager { Emulator.getPluginManager().fireEvent(new UserRegisteredEvent(habbo)); } - if (!Emulator.debugging) { - try (PreparedStatement stmt = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) { - stmt.setString(1, ""); - stmt.setInt(2, habbo.getHabboInfo().getId()); - stmt.execute(); - } catch (SQLException e) { - LOGGER.error("Caught SQL exception", e); - } - } + // NB: il ticket SSO NON viene svuotato qui di proposito. Dietro + // Cloudflare il WebSocket viene droppato e il client ritenta più + // volte con lo STESSO ticket: se lo consumassimo al primo uso, i + // retry (e l'hard-refresh) fallirebbero con "non-existing SSO token". + // Il ticket resta valido fino alla scadenza (auth_ticket_expires_at, + // TTL gestito dal CMS) o finché il CMS non ne scrive uno nuovo / logout. } } } catch (SQLException e) { 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 076a6795..7c7dcf3c 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 @@ -133,17 +133,10 @@ public class SecureLoginEvent extends MessageHandler { this.client.setHabbo(habbo); this.client.setMachineId(habbo.getHabboInfo().getMachineID()); - // Clear the SSO ticket now that session is resumed (prevent reuse) - if (!Emulator.debugging) { - try (java.sql.Connection conn = Emulator.getDatabase().getDataSource().getConnection(); - java.sql.PreparedStatement stmt = conn.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) { - stmt.setString(1, ""); - stmt.setInt(2, habbo.getHabboInfo().getId()); - stmt.execute(); - } catch (Exception e) { - LOGGER.error("Failed to clear SSO ticket after session resume", e); - } - } + // NB: NON svuotiamo il ticket SSO qui (vedi HabboManager.loadHabbo): + // dietro Cloudflare il client ritenta la connessione con lo stesso + // ticket, quindi deve restare valido fino alla scadenza TTL. Consumarlo + // farebbe fallire i retry / l'hard-refresh con "non-existing SSO token". } else { // Normal login — load from database habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateSoundboard.java b/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateSoundboard.java new file mode 100644 index 00000000..59b796c8 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateSoundboard.java @@ -0,0 +1,21 @@ +package com.eu.habbo.messages.rcon; + +import com.eu.habbo.Emulator; +import com.google.gson.Gson; + +// Ricarica i suoni della Soundboard dal DB (live), così i suoni aggiunti/caricati +// dal CMS (/admin/soundboard) si applicano senza riavviare l'emulatore. +public class UpdateSoundboard extends RCONMessage { + + public UpdateSoundboard() { + super(SoundboardJSON.class); + } + + @Override + public void handle(Gson gson, SoundboardJSON object) { + Emulator.getGameEnvironment().getSoundboardManager().reload(); + } + + static class SoundboardJSON { + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateWheel.java b/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateWheel.java new file mode 100644 index 00000000..aa9b2be4 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/rcon/UpdateWheel.java @@ -0,0 +1,21 @@ +package com.eu.habbo.messages.rcon; + +import com.eu.habbo.Emulator; +import com.google.gson.Gson; + +// Ricarica i premi/settings della Ruota della Fortuna dal DB (live), così le +// modifiche fatte dal CMS (/admin/wheel) si applicano senza riavviare l'emulatore. +public class UpdateWheel extends RCONMessage { + + public UpdateWheel() { + super(WheelJSON.class); + } + + @Override + public void handle(Gson gson, WheelJSON object) { + Emulator.getGameEnvironment().getWheelManager().reload(); + } + + static class WheelJSON { + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java b/Emulator/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java index c5c1ad8a..9eaeeef4 100644 --- a/Emulator/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java +++ b/Emulator/src/main/java/com/eu/habbo/networking/rconserver/RCONServer.java @@ -45,6 +45,8 @@ public class RCONServer extends Server { this.addRCONMessage("sendroombundle", SendRoomBundle.class); this.addRCONMessage("setrank", SetRank.class); this.addRCONMessage("updatewordfilter", UpdateWordfilter.class); + this.addRCONMessage("updatewheel", UpdateWheel.class); + this.addRCONMessage("updatesoundboard", UpdateSoundboard.class); this.addRCONMessage("updatecatalog", UpdateCatalog.class); this.addRCONMessage("executecommand", ExecuteCommand.class); this.addRCONMessage("progressachievement", ProgressAchievement.class);