You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
🆙 Stage 1 reconnect
This commit is contained in:
@@ -7,6 +7,7 @@ import com.eu.habbo.core.*;
|
||||
import com.eu.habbo.core.consolecommands.ConsoleCommand;
|
||||
import com.eu.habbo.database.Database;
|
||||
import com.eu.habbo.habbohotel.GameEnvironment;
|
||||
import com.eu.habbo.habbohotel.gameclients.SessionResumeManager;
|
||||
import com.eu.habbo.networking.gameserver.GameServer;
|
||||
import com.eu.habbo.networking.rconserver.RCONServer;
|
||||
import com.eu.habbo.plugin.PluginManager;
|
||||
@@ -319,6 +320,7 @@ public final class Emulator {
|
||||
if (Emulator.pluginManager != null)
|
||||
tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStartShutdownEvent()));
|
||||
if (Emulator.rconServer != null) tryShutdown(() -> Emulator.rconServer.stop());
|
||||
tryShutdown(() -> SessionResumeManager.getInstance().disposeAll());
|
||||
if (Emulator.gameEnvironment != null) tryShutdown(() -> Emulator.gameEnvironment.dispose());
|
||||
if (Emulator.pluginManager != null)
|
||||
tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStoppedEvent()));
|
||||
|
||||
@@ -26,6 +26,7 @@ public class GameClient {
|
||||
private Habbo habbo;
|
||||
private boolean handshakeFinished;
|
||||
private String machineId = "";
|
||||
private String ssoTicket = "";
|
||||
|
||||
public final ConcurrentHashMap<Integer, Integer> incomingPacketCounter = new ConcurrentHashMap<>(25);
|
||||
public final ConcurrentHashMap<Class<? extends MessageHandler>, Long> messageTimestamps = new ConcurrentHashMap<>();
|
||||
@@ -82,6 +83,14 @@ public class GameClient {
|
||||
this.machineId = machineId;
|
||||
}
|
||||
|
||||
public String getSsoTicket() {
|
||||
return this.ssoTicket;
|
||||
}
|
||||
|
||||
public void setSsoTicket(String ssoTicket) {
|
||||
this.ssoTicket = ssoTicket != null ? ssoTicket : "";
|
||||
}
|
||||
|
||||
public void sendResponse(MessageComposer composer) {
|
||||
this.sendResponse(composer.compose());
|
||||
}
|
||||
@@ -145,8 +154,15 @@ public class GameClient {
|
||||
|
||||
if (this.habbo != null) {
|
||||
if (this.habbo.isOnline()) {
|
||||
this.habbo.getHabboInfo().setOnline(false);
|
||||
this.habbo.disconnect();
|
||||
// Try to park the habbo in the grace period instead of immediate disconnect
|
||||
boolean parked = SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket);
|
||||
|
||||
if (!parked) {
|
||||
// No grace period configured — immediate disconnect as before
|
||||
this.habbo.getHabboInfo().setOnline(false);
|
||||
this.habbo.disconnect();
|
||||
}
|
||||
// If parked, do NOT call disconnect() — the habbo stays in the room
|
||||
}
|
||||
|
||||
this.habbo = null;
|
||||
|
||||
@@ -116,6 +116,22 @@ public class GameClientManager {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find an existing GameClient that authenticated with the given SSO ticket.
|
||||
* Used to detect reconnections where the old connection hasn't been closed yet.
|
||||
*/
|
||||
public GameClient findClientBySsoTicket(String ssoTicket) {
|
||||
if (ssoTicket == null || ssoTicket.isEmpty()) return null;
|
||||
|
||||
for (GameClient client : this.clients.values()) {
|
||||
if (ssoTicket.equals(client.getSsoTicket()) && client.getHabbo() != null) {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public List<Habbo> getHabbosWithMachineId(String machineId) {
|
||||
List<Habbo> habbos = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.eu.habbo.habbohotel.gameclients;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
public class SessionResumeManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SessionResumeManager.class);
|
||||
|
||||
private static SessionResumeManager instance;
|
||||
|
||||
private final ConcurrentHashMap<Integer, GhostSession> ghostSessions = new ConcurrentHashMap<>();
|
||||
|
||||
public static SessionResumeManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new SessionResumeManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public int getGracePeriodSeconds() {
|
||||
return Emulator.getConfig().getInt("session.reconnect.grace.seconds", 30);
|
||||
}
|
||||
|
||||
public boolean parkHabbo(Habbo habbo, String ssoTicket) {
|
||||
int graceSeconds = getGracePeriodSeconds();
|
||||
if (graceSeconds <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int userId = habbo.getHabboInfo().getId();
|
||||
|
||||
GhostSession existing = ghostSessions.remove(userId);
|
||||
if (existing != null && existing.disposeFuture != null) {
|
||||
existing.disposeFuture.cancel(false);
|
||||
}
|
||||
|
||||
LOGGER.info("[SessionResume] Parking {} (id={}) for {}s grace period",
|
||||
habbo.getHabboInfo().getUsername(), userId, graceSeconds);
|
||||
|
||||
if (ssoTicket != null && !ssoTicket.isEmpty()) {
|
||||
restoreSsoTicket(userId, ssoTicket);
|
||||
}
|
||||
|
||||
ScheduledFuture<?> future = Emulator.getThreading().run(() -> {
|
||||
GhostSession ghost = ghostSessions.remove(userId);
|
||||
if (ghost != null) {
|
||||
LOGGER.info("[SessionResume] Grace period expired for {} (id={}) - performing full disconnect",
|
||||
ghost.habbo.getHabboInfo().getUsername(), userId);
|
||||
performFullDisconnect(ghost.habbo);
|
||||
}
|
||||
}, graceSeconds * 1000);
|
||||
|
||||
ghostSessions.put(userId, new GhostSession(habbo, ssoTicket, future));
|
||||
return true;
|
||||
}
|
||||
|
||||
public Habbo resumeSession(int userId) {
|
||||
GhostSession ghost = ghostSessions.remove(userId);
|
||||
if (ghost == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ghost.disposeFuture != null) {
|
||||
ghost.disposeFuture.cancel(false);
|
||||
}
|
||||
|
||||
LOGGER.info("[SessionResume] Resuming session for {} (id={})",
|
||||
ghost.habbo.getHabboInfo().getUsername(), userId);
|
||||
|
||||
return ghost.habbo;
|
||||
}
|
||||
|
||||
public boolean hasGhostSession(int userId) {
|
||||
return ghostSessions.containsKey(userId);
|
||||
}
|
||||
|
||||
public void disposeAll() {
|
||||
for (GhostSession ghost : ghostSessions.values()) {
|
||||
if (ghost.disposeFuture != null) {
|
||||
ghost.disposeFuture.cancel(false);
|
||||
}
|
||||
performFullDisconnect(ghost.habbo);
|
||||
}
|
||||
ghostSessions.clear();
|
||||
}
|
||||
|
||||
private void performFullDisconnect(Habbo habbo) {
|
||||
try {
|
||||
habbo.getHabboInfo().setOnline(false);
|
||||
habbo.disconnect();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("[SessionResume] Error during deferred disconnect", e);
|
||||
}
|
||||
clearSsoTicket(habbo.getHabboInfo().getId());
|
||||
}
|
||||
|
||||
private void restoreSsoTicket(int userId, String ssoTicket) {
|
||||
try (var connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
|
||||
statement.setString(1, ssoTicket);
|
||||
statement.setInt(2, userId);
|
||||
statement.execute();
|
||||
LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("[SessionResume] Failed to restore SSO ticket for user " + userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearSsoTicket(int userId) {
|
||||
try (var connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
|
||||
statement.setString(1, "");
|
||||
statement.setInt(2, userId);
|
||||
statement.execute();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("[SessionResume] Failed to clear SSO ticket for user " + userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GhostSession {
|
||||
final Habbo habbo;
|
||||
final String ssoTicket;
|
||||
final ScheduledFuture<?> disposeFuture;
|
||||
|
||||
GhostSession(Habbo habbo, String ssoTicket, ScheduledFuture<?> disposeFuture) {
|
||||
this.habbo = habbo;
|
||||
this.ssoTicket = ssoTicket;
|
||||
this.disposeFuture = disposeFuture;
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
-52
@@ -1,11 +1,14 @@
|
||||
package com.eu.habbo.messages.incoming.handshake;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.gameclients.GameClient;
|
||||
import com.eu.habbo.habbohotel.gameclients.SessionResumeManager;
|
||||
import com.eu.habbo.habbohotel.messenger.Messenger;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctionItem;
|
||||
import com.eu.habbo.habbohotel.modtool.ModToolSanctions;
|
||||
import com.eu.habbo.habbohotel.navigation.NavigatorSavedSearch;
|
||||
import com.eu.habbo.habbohotel.permissions.Permission;
|
||||
import com.eu.habbo.habbohotel.rooms.Room;
|
||||
import com.eu.habbo.habbohotel.rooms.RoomManager;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.habbohotel.users.HabboManager;
|
||||
@@ -81,31 +84,83 @@ public class SecureLoginEvent extends MessageHandler {
|
||||
}
|
||||
|
||||
if (this.client.getHabbo() == null) {
|
||||
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso);
|
||||
this.client.setSsoTicket(sso);
|
||||
|
||||
GameClient existingClient = Emulator.getGameServer().getGameClientManager().findClientBySsoTicket(sso);
|
||||
if (existingClient != null && existingClient != this.client) {
|
||||
LOGGER.info("[SessionResume] Found existing client with same SSO ticket — disposing old connection to trigger parking");
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(existingClient);
|
||||
}
|
||||
|
||||
int lookupUserId = 0;
|
||||
try (java.sql.Connection conn = Emulator.getDatabase().getDataSource().getConnection();
|
||||
java.sql.PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE auth_ticket = ? LIMIT 1")) {
|
||||
stmt.setString(1, sso);
|
||||
try (java.sql.ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
lookupUserId = rs.getInt("id");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Caught exception looking up user for session resume", e);
|
||||
}
|
||||
|
||||
Habbo habbo = null;
|
||||
boolean isSessionResume = false;
|
||||
|
||||
if (lookupUserId > 0) {
|
||||
habbo = SessionResumeManager.getInstance().resumeSession(lookupUserId);
|
||||
}
|
||||
|
||||
if (habbo != null) {
|
||||
try {
|
||||
habbo.setClient(this.client);
|
||||
this.client.setHabbo(habbo);
|
||||
if(!this.client.getHabbo().connect()) {
|
||||
isSessionResume = true;
|
||||
LOGGER.info("[SessionResume] Resuming session for {} (id={})",
|
||||
habbo.getHabboInfo().getUsername(), habbo.getHabboInfo().getId());
|
||||
|
||||
habbo.setClient(this.client);
|
||||
this.client.setHabbo(habbo);
|
||||
this.client.setMachineId(habbo.getHabboInfo().getMachineID());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso);
|
||||
}
|
||||
|
||||
if (habbo != null) {
|
||||
if (!isSessionResume) {
|
||||
try {
|
||||
habbo.setClient(this.client);
|
||||
this.client.setHabbo(habbo);
|
||||
if(!this.client.getHabbo().connect()) {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo() == null) {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo().getRank() == null) {
|
||||
throw new NullPointerException(habbo.getHabboInfo().getUsername() + " has a NON EXISTING RANK!");
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(habbo);
|
||||
Emulator.getGameEnvironment().getHabboManager().addHabbo(habbo);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Caught exception", e);
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo() == null) {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client.getHabbo().getHabboInfo().getRank() == null) {
|
||||
throw new NullPointerException(habbo.getHabboInfo().getUsername() + " has a NON EXISTING RANK!");
|
||||
}
|
||||
|
||||
Emulator.getThreading().run(habbo);
|
||||
Emulator.getGameEnvironment().getHabboManager().addHabbo(habbo);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Caught exception", e);
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ClothingValidationManager.VALIDATE_ON_LOGIN) {
|
||||
@@ -121,7 +176,13 @@ public class SecureLoginEvent extends MessageHandler {
|
||||
|
||||
int roomIdToEnter = 0;
|
||||
|
||||
if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && this.client.getHabbo().getHabboInfo().getHomeRoom() != 0)
|
||||
if (isSessionResume) {
|
||||
Room currentRoom = habbo.getHabboInfo().getCurrentRoom();
|
||||
if (currentRoom != null) {
|
||||
LOGGER.info("[SessionResume] {} is still in room {} — client will resume in-place",
|
||||
habbo.getHabboInfo().getUsername(), currentRoom.getId());
|
||||
}
|
||||
} else if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && this.client.getHabbo().getHabboInfo().getHomeRoom() != 0)
|
||||
roomIdToEnter = this.client.getHabbo().getHabboInfo().getHomeRoom();
|
||||
else if (!this.client.getHabbo().getHabboStats().nux || Emulator.getConfig().getBoolean("retro.style.homeroom") && RoomManager.HOME_ROOM_ID > 0)
|
||||
roomIdToEnter = RoomManager.HOME_ROOM_ID;
|
||||
@@ -152,8 +213,6 @@ public class SecureLoginEvent extends MessageHandler {
|
||||
|
||||
this.client.sendResponses(messages);
|
||||
|
||||
//Hardcoded
|
||||
//this.client.sendResponse(new ForumsTestComposer());
|
||||
this.client.sendResponse(new InventoryAchievementsComposer());
|
||||
|
||||
ModToolSanctions modToolSanctions = Emulator.getGameEnvironment().getModToolSanctions();
|
||||
@@ -189,42 +248,44 @@ public class SecureLoginEvent extends MessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
UserLoginEvent userLoginEvent = new UserLoginEvent(habbo, this.client.getHabbo().getHabboInfo().getIpLogin());
|
||||
Emulator.getPluginManager().fireEvent(userLoginEvent);
|
||||
if (!isSessionResume) {
|
||||
UserLoginEvent userLoginEvent = new UserLoginEvent(habbo, this.client.getHabbo().getHabboInfo().getIpLogin());
|
||||
Emulator.getPluginManager().fireEvent(userLoginEvent);
|
||||
|
||||
if(userLoginEvent.isCancelled()) {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
if(userLoginEvent.isCancelled()) {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Emulator.getConfig().getBoolean("hotel.welcome.alert.enabled")) {
|
||||
final Habbo finalHabbo = habbo;
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (Emulator.getConfig().getBoolean("hotel.welcome.alert.oldstyle")) {
|
||||
SecureLoginEvent.this.client.sendResponse(new MessagesForYouComposer(HabboManager.WELCOME_MESSAGE.replace("%username%", finalHabbo.getHabboInfo().getUsername()).replace("%user%", finalHabbo.getHabboInfo().getUsername()).split("<br/>")));
|
||||
} else {
|
||||
SecureLoginEvent.this.client.sendResponse(new GenericAlertComposer(HabboManager.WELCOME_MESSAGE.replace("%username%", finalHabbo.getHabboInfo().getUsername()).replace("%user%", finalHabbo.getHabboInfo().getUsername())));
|
||||
}
|
||||
}, Emulator.getConfig().getInt("hotel.welcome.alert.delay", 5000));
|
||||
}
|
||||
if (Emulator.getConfig().getBoolean("hotel.welcome.alert.enabled")) {
|
||||
final Habbo finalHabbo = habbo;
|
||||
Emulator.getThreading().run(() -> {
|
||||
if (Emulator.getConfig().getBoolean("hotel.welcome.alert.oldstyle")) {
|
||||
SecureLoginEvent.this.client.sendResponse(new MessagesForYouComposer(HabboManager.WELCOME_MESSAGE.replace("%username%", finalHabbo.getHabboInfo().getUsername()).replace("%user%", finalHabbo.getHabboInfo().getUsername()).split("<br/>")));
|
||||
} else {
|
||||
SecureLoginEvent.this.client.sendResponse(new GenericAlertComposer(HabboManager.WELCOME_MESSAGE.replace("%username%", finalHabbo.getHabboInfo().getUsername()).replace("%user%", finalHabbo.getHabboInfo().getUsername())));
|
||||
}
|
||||
}, Emulator.getConfig().getInt("hotel.welcome.alert.delay", 5000));
|
||||
}
|
||||
|
||||
if(SubscriptionHabboClub.HC_PAYDAY_ENABLED) {
|
||||
SubscriptionHabboClub.processUnclaimed(habbo);
|
||||
}
|
||||
if(SubscriptionHabboClub.HC_PAYDAY_ENABLED) {
|
||||
SubscriptionHabboClub.processUnclaimed(habbo);
|
||||
}
|
||||
|
||||
SubscriptionHabboClub.processClubBadge(habbo);
|
||||
SubscriptionHabboClub.processClubBadge(habbo);
|
||||
|
||||
Messenger.checkFriendSizeProgress(habbo);
|
||||
Messenger.checkFriendSizeProgress(habbo);
|
||||
|
||||
if (!habbo.getHabboStats().hasGottenDefaultSavedSearches) {
|
||||
habbo.getHabboStats().hasGottenDefaultSavedSearches = true;
|
||||
Emulator.getThreading().run(habbo.getHabboStats());
|
||||
if (!habbo.getHabboStats().hasGottenDefaultSavedSearches) {
|
||||
habbo.getHabboStats().hasGottenDefaultSavedSearches = true;
|
||||
Emulator.getThreading().run(habbo.getHabboStats());
|
||||
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("official-root", ""));
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("my", ""));
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("favorites", ""));
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("official-root", ""));
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("my", ""));
|
||||
habbo.getHabboInfo().addSavedSearch(new NavigatorSavedSearch("favorites", ""));
|
||||
|
||||
this.client.sendResponse(new NewNavigatorSavedSearchesComposer(this.client.getHabbo().getHabboInfo().getSavedSearches()));
|
||||
this.client.sendResponse(new NewNavigatorSavedSearchesComposer(this.client.getHabbo().getHabboInfo().getSavedSearches()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Emulator.getGameServer().getGameClientManager().disposeClient(this.client);
|
||||
|
||||
Reference in New Issue
Block a user