🆙 Update Rare-Value page

This commit is contained in:
duckietm
2026-05-29 08:28:01 +02:00
parent 1ba2e43d4d
commit b7915884b6
4 changed files with 65 additions and 17 deletions
@@ -202,8 +202,8 @@ public class CatalogManager {
public final Item ecotronItem;
public final THashMap<Integer, CatalogLimitedConfiguration> limitedNumbers;
private final List<Voucher> vouchers;
// spriteId -> [credits, points, pointsType], derived from catalog_items (see loadFurnitureValues)
public final TIntObjectMap<int[]> 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<int[]> 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<int[]> 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)) {
@@ -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()));
}
}
@@ -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<int[]> values;
private final byte[] snapshot;
public RareValuesComposer(byte[] snapshot) {
this.values = null;
this.snapshot = snapshot;
}
public RareValuesComposer(TIntObjectMap<int[]> 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<int[]> iterator = this.values.iterator();
@@ -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 {