feat(housekeeping): add find-user-by-name packet pair

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.
This commit is contained in:
simoleo89
2026-05-24 10:39:04 +02:00
committed by simoleo89
parent ad60861a3f
commit 7726691cde
5 changed files with 103 additions and 0 deletions
@@ -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);
}
}
@@ -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;
}
@@ -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));
}
}
@@ -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;
}
@@ -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 : "";
}
}