From b7915884b665bf0c96ffa70d99fc3c6e15a77f77 Mon Sep 17 00:00:00 2001 From: duckietm Date: Fri, 29 May 2026 08:28:01 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20Update=20Rare-Value=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../habbohotel/catalog/CatalogManager.java | 37 ++++++++++++++----- .../rarevalues/RequestRareValuesEvent.java | 16 +++++--- .../rarevalues/RareValuesComposer.java | 15 +++++++- .../decoders/GameMessageHandler.java | 14 +++++++ 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java index c89bf959..f0dbccec 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/catalog/CatalogManager.java @@ -202,8 +202,8 @@ public class CatalogManager { public final Item ecotronItem; public final THashMap limitedNumbers; private final List vouchers; - // spriteId -> [credits, points, pointsType], derived from catalog_items (see loadFurnitureValues) public final TIntObjectMap furnitureValues; + private volatile byte[] rareValuesPayloadCache; public CatalogManager() { long millis = System.currentTimeMillis(); @@ -249,10 +249,6 @@ public class CatalogManager { this.loadFurnitureValues(); } - // Builds spriteId -> [credits, points, pointsType] from catalog_items so the - // client can show a furni's "value" (toolbar price guide + infostand line). - // Only single-item, single-amount FLOOR/WALL sales are considered, so bundles - // and multi-packs don't pollute the per-rare price. First clean entry wins. private synchronized void loadFurnitureValues() { this.furnitureValues.clear(); final int diamondType = Emulator.getConfig().getInt("seasonal.currency.diamond", 5); @@ -266,8 +262,6 @@ public class CatalogManager { int points = catalogItem.getPoints(); int pointsType = catalogItem.getPointsType(); - // Only diamond-priced items — both the "Valore Rari" panel and the - // infostand value line show diamonds only. if (points <= 0 || pointsType != diamondType) continue; @@ -291,13 +285,39 @@ public class CatalogManager { } } + this.rebuildRareValuesPayloadCache(); + LOGGER.info("Furniture Values -> Loaded! ({} entries)", this.furnitureValues.size()); } + private void rebuildRareValuesPayloadCache() { + try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(this.furnitureValues.size() * 16 + 8); + java.io.DataOutputStream out = new java.io.DataOutputStream(baos)) { + out.writeInt(this.furnitureValues.size()); + TIntObjectIterator iterator = this.furnitureValues.iterator(); + while (iterator.hasNext()) { + iterator.advance(); + int[] value = iterator.value(); + out.writeInt(iterator.key()); // spriteId + out.writeInt(value[0]); // credits + out.writeInt(value[1]); // points + out.writeInt(value[2]); // pointsType + } + this.rareValuesPayloadCache = baos.toByteArray(); + } catch (java.io.IOException e) { + LOGGER.error("Failed to build rare values payload cache", e); + this.rareValuesPayloadCache = null; + } + } + public TIntObjectMap getFurnitureValues() { return this.furnitureValues; } + public byte[] getRareValuesPayloadSnapshot() { + return this.rareValuesPayloadCache; + } + private synchronized void loadLimitedNumbers() { this.limitedNumbers.clear(); @@ -1104,9 +1124,6 @@ public class CatalogManager { type = type.replace("bot_", ""); type = type.replace("visitor_logger", "visitor_log"); - // Permission gate keyed on the canonical base-item name - // (admin-controlled but stable), not the catalog page name - // which can be renamed and bypass the check. if (("bot_" + com.eu.habbo.habbohotel.bots.FrankBot.BOT_TYPE).equals(baseName) || ("rentable_bot_" + com.eu.habbo.habbohotel.bots.FrankBot.BOT_TYPE).equals(baseName)) { if (!habbo.getClient().getHabbo().hasPermission(com.eu.habbo.habbohotel.bots.FrankBot.PERMISSION_USE)) { diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rarevalues/RequestRareValuesEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rarevalues/RequestRareValuesEvent.java index 117043a4..0ca00ad0 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rarevalues/RequestRareValuesEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rarevalues/RequestRareValuesEvent.java @@ -1,11 +1,10 @@ package com.eu.habbo.messages.incoming.rarevalues; import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.catalog.CatalogManager; import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rarevalues.RareValuesComposer; -// Client requests the furni value map once on load. Public info (catalog prices), -// no permission gate. Rate limited since the payload is large. public class RequestRareValuesEvent extends MessageHandler { @Override public int getRatelimit() { @@ -14,8 +13,15 @@ public class RequestRareValuesEvent extends MessageHandler { @Override public void handle() throws Exception { - this.client.sendResponse(new RareValuesComposer( - Emulator.getGameEnvironment().getCatalogManager().getFurnitureValues() - )); + if (this.client.getHabbo() == null) return; + + CatalogManager catalog = Emulator.getGameEnvironment().getCatalogManager(); + byte[] snapshot = catalog.getRareValuesPayloadSnapshot(); + if (snapshot != null) { + this.client.sendResponse(new RareValuesComposer(snapshot)); + return; + } + + this.client.sendResponse(new RareValuesComposer(catalog.getFurnitureValues())); } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rarevalues/RareValuesComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rarevalues/RareValuesComposer.java index f713c8c9..85778e77 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rarevalues/RareValuesComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/rarevalues/RareValuesComposer.java @@ -6,18 +6,29 @@ import com.eu.habbo.messages.outgoing.Outgoing; import gnu.trove.iterator.TIntObjectIterator; import gnu.trove.map.TIntObjectMap; -// Sends the full spriteId -> value map to the client. Consumed by the toolbar -// price guide and the furni infostand "value" line. See CatalogManager#loadFurnitureValues. public class RareValuesComposer extends MessageComposer { private final TIntObjectMap values; + private final byte[] snapshot; + + public RareValuesComposer(byte[] snapshot) { + this.values = null; + this.snapshot = snapshot; + } public RareValuesComposer(TIntObjectMap values) { this.values = values; + this.snapshot = null; } @Override protected ServerMessage composeInternal() { this.response.init(Outgoing.RareValuesComposer); + + if (this.snapshot != null) { + this.response.appendRawBytes(this.snapshot); + return this.response; + } + this.response.appendInt(this.values.size()); TIntObjectIterator iterator = this.values.iterator(); diff --git a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageHandler.java b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageHandler.java index bbe9540f..a80be5a5 100644 --- a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageHandler.java +++ b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageHandler.java @@ -11,6 +11,7 @@ import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.ssl.NotSslRecordException; +import io.netty.util.ReferenceCountUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +40,19 @@ public class GameMessageHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!(msg instanceof ClientMessage)) { + try { + if (Emulator.getConfig().getBoolean("debug.mode")) { + LOGGER.debug("Discarding non-game message {} from {}", + msg.getClass().getSimpleName(), ctx.channel().remoteAddress()); + } + } finally { + ReferenceCountUtil.release(msg); + ctx.channel().close(); + } + return; + } + ClientMessage message = (ClientMessage) msg; try {