From 7726691cde5ef6773fcc64d99900075ae34d845b Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 24 May 2026 10:39:04 +0200 Subject: [PATCH] feat(housekeeping): add find-user-by-name packet pair MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First wire-level packet for the in-client housekeeping admin panel. Adds an incoming handler (Incoming 9100) that resolves a username to a HabboInfo (online via the HabboManager hashmap, offline via the users-table SQL fallback) and replies with HousekeepingUserDetail (Outgoing 9200) containing id / username / motto / look / rank / rank name / online / lastOnline / credits / duckets / diamonds / email / ipLogin / isBanned. Active-mute and active-trade-lock are written as trailing booleans (currently false) so the renderer parser can pick them up later behind a bytesAvailable guard once those manager APIs are surfaced offline-side. Permission gate is ACC_SUPPORTTOOL — same one ModTools already uses. Avoids adding a new column to the permissions table on this slice; a dedicated ACC_HOUSEKEEPING permission can be introduced later when the destructive HK operations (give-credits, delete-room, reset-pwd) go in. Reserves the 9100..9199 / 9200..9299 ID blocks for the rest of the HK packet surface (search-prefix, find-by-id, ban/mute/kick with arbitrary duration, room actions, economy, catalog admin, dashboard aggregate, audit log read). `mvn compile` clean on Habbo 4.2.12. --- .../com/eu/habbo/messages/PacketManager.java | 3 + .../eu/habbo/messages/incoming/Incoming.java | 3 + .../HousekeepingFindUserByNameEvent.java | 35 +++++++++++ .../eu/habbo/messages/outgoing/Outgoing.java | 3 + .../HousekeepingUserDetailComposer.java | 59 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingFindUserByNameEvent.java create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/outgoing/housekeeping/HousekeepingUserDetailComposer.java diff --git a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java index 0d38b908..9b421f10 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java @@ -716,5 +716,8 @@ public class PacketManager { this.registerHandler(Incoming.YouTubeRoomPlayEvent, com.eu.habbo.messages.incoming.rooms.youtube.YouTubeRoomPlayEvent.class); this.registerHandler(Incoming.YouTubeRoomWatchingEvent, com.eu.habbo.messages.incoming.rooms.youtube.YouTubeRoomWatchingEvent.class); this.registerHandler(Incoming.YouTubeRoomSettingsEvent, com.eu.habbo.messages.incoming.rooms.youtube.YouTubeRoomSettingsEvent.class); + + // Housekeeping (in-client admin panel) + this.registerHandler(Incoming.HousekeepingFindUserByNameEvent, com.eu.habbo.messages.incoming.housekeeping.HousekeepingFindUserByNameEvent.class); } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java index 77ef0064..5264de12 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java @@ -460,4 +460,7 @@ public class Incoming { public static final int YouTubeRoomPlayEvent = 8001; public static final int YouTubeRoomWatchingEvent = 8002; public static final int YouTubeRoomSettingsEvent = 8003; + + // Housekeeping (in-client admin panel) — IDs 9100..9199 reserved + public static final int HousekeepingFindUserByNameEvent = 9100; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingFindUserByNameEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingFindUserByNameEvent.java new file mode 100644 index 00000000..acae3c6e --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingFindUserByNameEvent.java @@ -0,0 +1,35 @@ +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.habbohotel.users.HabboInfo; +import com.eu.habbo.habbohotel.users.HabboManager; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.housekeeping.HousekeepingUserDetailComposer; + +public class HousekeepingFindUserByNameEvent extends MessageHandler { + @Override + public int getRatelimit() { + return 500; + } + + @Override + public void handle() throws Exception { + if (!this.client.getHabbo().hasPermission(Permission.ACC_SUPPORTTOOL)) { + return; + } + + String username = this.packet.readString(); + + if (username == null || username.isEmpty()) { + this.client.sendResponse(new HousekeepingUserDetailComposer(null)); + return; + } + + Habbo online = Emulator.getGameEnvironment().getHabboManager().getHabbo(username); + HabboInfo info = online != null ? online.getHabboInfo() : HabboManager.getOfflineHabboInfo(username); + + this.client.sendResponse(new HousekeepingUserDetailComposer(info)); + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java index 45d8c986..902421c5 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java @@ -586,4 +586,7 @@ public class Outgoing { public static final int YouTubeRoomWatchersComposer = 8002; public static final int YouTubeRoomSettingsComposer = 8003; + // Housekeeping (in-client admin panel) — IDs 9200..9299 reserved + public static final int HousekeepingUserDetailComposer = 9200; + } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/housekeeping/HousekeepingUserDetailComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/housekeeping/HousekeepingUserDetailComposer.java new file mode 100644 index 00000000..8718e7f3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/housekeeping/HousekeepingUserDetailComposer.java @@ -0,0 +1,59 @@ +package com.eu.habbo.messages.outgoing.housekeeping; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.modtool.ModToolBan; +import com.eu.habbo.habbohotel.permissions.Rank; +import com.eu.habbo.habbohotel.users.HabboInfo; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +public class HousekeepingUserDetailComposer extends MessageComposer { + private static final int CURRENCY_DUCKETS = 0; + private static final int CURRENCY_DIAMONDS = 5; + + private final HabboInfo info; + + public HousekeepingUserDetailComposer(HabboInfo info) { + this.info = info; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.HousekeepingUserDetailComposer); + + if (this.info == null) { + this.response.appendBoolean(false); + return this.response; + } + + Rank rank = this.info.getRank(); + ModToolBan ban = Emulator.getGameEnvironment().getModToolManager().checkForBan(this.info.getId()); + + this.response.appendBoolean(true); + this.response.appendInt(this.info.getId()); + this.response.appendString(safe(this.info.getUsername())); + this.response.appendString(safe(this.info.getMotto())); + this.response.appendString(safe(this.info.getLook())); + this.response.appendInt(rank != null ? rank.getId() : 0); + this.response.appendString(rank != null ? safe(rank.getName()) : ""); + this.response.appendBoolean(this.info.isOnline()); + this.response.appendInt(this.info.getLastOnline()); + this.response.appendInt(this.info.getCredits()); + this.response.appendInt(this.info.getCurrencyAmount(CURRENCY_DUCKETS)); + this.response.appendInt(this.info.getCurrencyAmount(CURRENCY_DIAMONDS)); + this.response.appendString(safe(this.info.getMail())); + this.response.appendString(safe(this.info.getIpLogin())); + this.response.appendBoolean(ban != null); + // Mute / trade-lock surface as future packet extensions; see the + // optional-trailing-field parser pattern on the renderer side. + this.response.appendBoolean(false); + this.response.appendBoolean(false); + + return this.response; + } + + private static String safe(String value) { + return value != null ? value : ""; + } +}