feat(housekeeping): force-disconnect-user packet

Incoming 9106 HousekeepingForceDisconnectUserEvent — (userId, reason).
Sends the optional reason as a Habbo.alert, dispatches the action ack
BEFORE calling target.disconnect() so the result lands on the wire
before the target's socket closes, then drops the session. Online-only;
offline target returns `user_offline`.

`mvn compile` clean.
This commit is contained in:
simoleo89
2026-05-24 11:03:32 +02:00
committed by simoleo89
parent 418c753e6c
commit c4b3295a45
3 changed files with 56 additions and 0 deletions
@@ -724,5 +724,6 @@ public class PacketManager {
this.registerHandler(Incoming.HousekeepingUnbanUserEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingUnbanUserEvent.class);
this.registerHandler(Incoming.HousekeepingMuteUserEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingMuteUserEvent.class);
this.registerHandler(Incoming.HousekeepingKickUserEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingKickUserEvent.class);
this.registerHandler(Incoming.HousekeepingForceDisconnectUserEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingForceDisconnectUserEvent.class);
}
}
@@ -468,4 +468,5 @@ public class Incoming {
public static final int HousekeepingUnbanUserEvent = 9103;
public static final int HousekeepingMuteUserEvent = 9104;
public static final int HousekeepingKickUserEvent = 9105;
public static final int HousekeepingForceDisconnectUserEvent = 9106;
}
@@ -0,0 +1,54 @@
package com.eu.habbo.messages.incoming.housekeeping;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.housekeeping.HousekeepingActionResultComposer;
/**
* Force-close the session of an online user. Unlike kick (which only
* removes them from the current room), this drops their socket. Equivalent
* to /disconnect in command form but issued through the HK panel.
*/
public class HousekeepingForceDisconnectUserEvent extends MessageHandler {
private static final String ACTION_KEY = "user.disconnect";
@Override
public int getRatelimit() {
return 1000;
}
@Override
public void handle() throws Exception {
if (!this.client.getHabbo().hasPermission(Permission.ACC_HOUSEKEEPING)) {
return;
}
int userId = this.packet.readInt();
String reason = this.packet.readString();
if (userId <= 0) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "invalid_input"));
return;
}
Habbo target = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
if (target == null) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "user_offline"));
return;
}
if (reason != null && !reason.isEmpty()) {
target.alert(reason);
}
// ACK first so the action result lands before the target's socket
// closes (otherwise an alerted user on the same emulator thread may
// already be torn down when we try to write).
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, true, userId, ""));
target.disconnect();
}
}