Merge pull request #98 from duckietm/dev

Dev
This commit is contained in:
DuckieTM
2026-05-04 15:37:38 +02:00
committed by GitHub
5 changed files with 451 additions and 13 deletions
@@ -23,6 +23,7 @@ import com.eu.habbo.habbohotel.rooms.RoomChatBubbleManager;
import com.eu.habbo.habbohotel.rooms.RoomManager;
import com.eu.habbo.habbohotel.users.HabboManager;
import com.eu.habbo.habbohotel.users.custombadge.CustomBadgeManager;
import com.eu.habbo.habbohotel.users.infostand.InfostandBackgroundManager;
import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionManager;
import com.eu.habbo.habbohotel.users.subscriptions.SubscriptionScheduler;
import org.slf4j.Logger;
@@ -60,6 +61,7 @@ public class GameEnvironment {
private CalendarManager calendarManager;
private RoomChatBubbleManager roomChatBubbleManager;
private CustomBadgeManager customBadgeManager;
private InfostandBackgroundManager infostandBackgroundManager;
public void load() throws Exception {
LOGGER.info("GameEnvironment -> Loading...");
@@ -87,6 +89,7 @@ public class GameEnvironment {
this.calendarManager = new CalendarManager();
this.roomChatBubbleManager = new RoomChatBubbleManager();
this.customBadgeManager = new CustomBadgeManager();
this.infostandBackgroundManager = new InfostandBackgroundManager();
this.roomManager.loadPublicRooms();
this.navigatorManager.loadNavigator();
@@ -226,4 +229,8 @@ public class GameEnvironment {
public CustomBadgeManager getCustomBadgeManager() {
return this.customBadgeManager;
}
public InfostandBackgroundManager getInfostandBackgroundManager() {
return this.infostandBackgroundManager;
}
}
@@ -0,0 +1,136 @@
package com.eu.habbo.habbohotel.users.infostand;
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.HabboStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class InfostandBackgroundManager {
private static final Logger LOGGER = LoggerFactory.getLogger(InfostandBackgroundManager.class);
public enum Category {
BACKGROUND("background"),
STAND("stand"),
OVERLAY("overlay"),
CARD("card");
public final String dbValue;
Category(String dbValue) {
this.dbValue = dbValue;
}
public static Category fromDbValue(String value) {
for (Category category : values()) {
if (category.dbValue.equalsIgnoreCase(value)) return category;
}
return null;
}
}
private final Map<Category, Map<Integer, Entry>> entries = new EnumMap<>(Category.class);
private boolean enforce = false;
public InfostandBackgroundManager() {
for (Category category : Category.values()) {
this.entries.put(category, Collections.emptyMap());
}
this.reload();
}
public void reload() {
Map<Category, Map<Integer, Entry>> next = new EnumMap<>(Category.class);
for (Category category : Category.values()) {
next.put(category, new HashMap<>());
}
int loaded = 0;
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT id, category, min_rank, is_hc_only, is_ambassador_only FROM infostand_backgrounds");
ResultSet set = statement.executeQuery()) {
while (set.next()) {
Category category = Category.fromDbValue(set.getString("category"));
if (category == null) continue;
int id = set.getInt("id");
int minRank = set.getInt("min_rank");
boolean isHcOnly = set.getBoolean("is_hc_only");
boolean isAmbassadorOnly = set.getBoolean("is_ambassador_only");
next.get(category).put(id, new Entry(minRank, isHcOnly, isAmbassadorOnly));
loaded++;
}
} catch (SQLException e) {
this.enforce = false;
for (Category category : Category.values()) {
this.entries.put(category, Collections.emptyMap());
}
LOGGER.error("InfostandBackgroundManager -> Failed to load infostand_backgrounds, server-side validation disabled.", e);
return;
}
for (Category category : Category.values()) {
this.entries.put(category, next.get(category));
}
this.enforce = loaded > 0;
if (this.enforce) {
LOGGER.info("InfostandBackgroundManager -> Loaded {} backgrounds, {} stands, {} overlays, {} cards from infostand_backgrounds.",
this.entries.get(Category.BACKGROUND).size(),
this.entries.get(Category.STAND).size(),
this.entries.get(Category.OVERLAY).size(),
this.entries.get(Category.CARD).size());
} else {
LOGGER.info("InfostandBackgroundManager -> infostand_backgrounds is empty, server-side validation disabled (only range clamp will apply).");
}
}
public boolean canUse(Habbo habbo, Category category, int id) {
if (id == 0) return true;
if (!this.enforce) return true;
if (habbo == null) return false;
Map<Integer, Entry> categoryEntries = this.entries.get(category);
if (categoryEntries == null) return false;
Entry entry = categoryEntries.get(id);
if (entry == null) return false;
HabboInfo info = habbo.getHabboInfo();
int rankId = (info != null && info.getRank() != null) ? info.getRank().getId() : 0;
HabboStats stats = habbo.getHabboStats();
boolean hasClub = stats != null && stats.hasActiveClub();
if (entry.isHcOnly && !hasClub) return false;
if (entry.isAmbassadorOnly && !habbo.hasPermission(Permission.ACC_AMBASSADOR)) return false;
if (rankId < entry.minRank) return false;
return true;
}
public static final class Entry {
public final int minRank;
public final boolean isHcOnly;
public final boolean isAmbassadorOnly;
public Entry(int minRank, boolean isHcOnly, boolean isAmbassadorOnly) {
this.minRank = minRank;
this.isHcOnly = isHcOnly;
this.isAmbassadorOnly = isAmbassadorOnly;
}
}
}
@@ -1,26 +1,77 @@
package com.eu.habbo.messages.incoming.users;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboStats;
import com.eu.habbo.habbohotel.users.infostand.InfostandBackgroundManager;
import com.eu.habbo.habbohotel.users.infostand.InfostandBackgroundManager.Category;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer;
public class ChangeInfostandBgEvent extends MessageHandler {
private static final String COOLDOWN_KEY = "infostand_bg_cooldown";
private static final long COOLDOWN_MS = 500L;
private static final int MIN_ID = 0;
private static final int MAX_ID = 9999;
@Override
public void handle() throws Exception {
int backgroundImage = this.packet.readInt();
int backgroundStand = this.packet.readInt();
int backgroundOverlay = this.packet.readInt();
int backgroundCard = this.packet.bytesAvailable() >= 4 ? this.packet.readInt() : 0;
Habbo habbo = this.client.getHabbo();
if (habbo == null) return;
this.client.getHabbo().getHabboInfo().setInfostandBg(backgroundImage);
this.client.getHabbo().getHabboInfo().setInfostandStand(backgroundStand);
this.client.getHabbo().getHabboInfo().setInfostandOverlay(backgroundOverlay);
this.client.getHabbo().getHabboInfo().setInfostandCardBg(backgroundCard);
this.client.getHabbo().getHabboInfo().run();
HabboInfo info = habbo.getHabboInfo();
if (info == null) return;
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != null) {
this.client.getHabbo().getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(this.client.getHabbo()).compose());
HabboStats stats = habbo.getHabboStats();
if (stats != null) {
long now = System.currentTimeMillis();
Object last = stats.cache.get(COOLDOWN_KEY);
if (last instanceof Long && (now - (Long) last) < COOLDOWN_MS) {
return;
}
stats.cache.put(COOLDOWN_KEY, now);
}
int requestedBg = sanitize(this.packet.readInt());
int requestedStand = sanitize(this.packet.readInt());
int requestedOverlay = sanitize(this.packet.readInt());
int requestedCard = this.packet.bytesAvailable() >= 4 ? sanitize(this.packet.readInt()) : 0;
InfostandBackgroundManager manager = Emulator.getGameEnvironment() != null ? Emulator.getGameEnvironment().getInfostandBackgroundManager() : null;
int backgroundImage = resolve(manager, habbo, Category.BACKGROUND, requestedBg, info.getInfostandBg());
int backgroundStand = resolve(manager, habbo, Category.STAND, requestedStand, info.getInfostandStand());
int backgroundOverlay = resolve(manager, habbo, Category.OVERLAY, requestedOverlay, info.getInfostandOverlay());
int backgroundCard = resolve(manager, habbo, Category.CARD, requestedCard, info.getInfostandCardBg());
if (info.getInfostandBg() == backgroundImage
&& info.getInfostandStand() == backgroundStand
&& info.getInfostandOverlay() == backgroundOverlay
&& info.getInfostandCardBg() == backgroundCard) {
return;
}
info.setInfostandBg(backgroundImage);
info.setInfostandStand(backgroundStand);
info.setInfostandOverlay(backgroundOverlay);
info.setInfostandCardBg(backgroundCard);
info.run();
if (info.getCurrentRoom() != null) {
info.getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose());
} else {
this.client.sendResponse(new RoomUserDataComposer(this.client.getHabbo()));
this.client.sendResponse(new RoomUserDataComposer(habbo));
}
}
}
private static int sanitize(int value) {
if (value < MIN_ID || value > MAX_ID) return 0;
return value;
}
private static int resolve(InfostandBackgroundManager manager, Habbo habbo, Category category, int requested, int current) {
if (manager == null) return requested;
return manager.canUse(habbo, category, requested) ? requested : current;
}
}