diff --git a/Database Updates/017_Profile_Backgrounds.sql b/Database Updates/017_Profile_Backgrounds.sql new file mode 100644 index 00000000..f68f93b5 --- /dev/null +++ b/Database Updates/017_Profile_Backgrounds.sql @@ -0,0 +1,244 @@ +CREATE TABLE IF NOT EXISTS `infostand_backgrounds` ( + `id` int(11) NOT NULL, + `category` enum('background','stand','overlay','card') NOT NULL, + `min_rank` int(11) NOT NULL DEFAULT 0, + `is_hc_only` tinyint(1) NOT NULL DEFAULT 0, + `is_ambassador_only` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`,`category`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +INSERT INTO `infostand_backgrounds` (`id`, `category`, `min_rank`, `is_hc_only`, `is_ambassador_only`) VALUES + (0, 'background', 0, 0, 0), + (1, 'background', 0, 0, 0), + (2, 'background', 0, 0, 0), + (3, 'background', 0, 0, 0), + (4, 'background', 0, 0, 0), + (5, 'background', 0, 0, 0), + (6, 'background', 0, 0, 0), + (7, 'background', 0, 0, 0), + (8, 'background', 0, 0, 0), + (9, 'background', 0, 0, 0), + (10, 'background', 0, 0, 0), + (11, 'background', 0, 0, 0), + (12, 'background', 0, 0, 0), + (13, 'background', 0, 0, 0), + (14, 'background', 0, 0, 0), + (15, 'background', 0, 0, 0), + (16, 'background', 0, 0, 0), + (17, 'background', 0, 0, 0), + (18, 'background', 0, 0, 0), + (19, 'background', 0, 0, 0), + (20, 'background', 0, 0, 0), + (21, 'background', 0, 0, 0), + (22, 'background', 0, 0, 0), + (23, 'background', 0, 0, 0), + (24, 'background', 0, 0, 0), + (25, 'background', 0, 0, 0), + (26, 'background', 0, 0, 0), + (27, 'background', 0, 0, 0), + (28, 'background', 0, 0, 0), + (29, 'background', 0, 0, 0), + (30, 'background', 0, 0, 0), + (31, 'background', 0, 0, 0), + (32, 'background', 0, 0, 0), + (33, 'background', 0, 0, 0), + (34, 'background', 0, 0, 0), + (35, 'background', 0, 0, 0), + (36, 'background', 0, 0, 0), + (37, 'background', 0, 0, 0), + (38, 'background', 0, 0, 0), + (39, 'background', 0, 0, 0), + (40, 'background', 0, 0, 0), + (41, 'background', 0, 0, 0), + (42, 'background', 0, 1, 0), + (43, 'background', 0, 1, 0), + (44, 'background', 0, 1, 0), + (45, 'background', 0, 1, 0), + (46, 'background', 0, 1, 0), + (47, 'background', 0, 1, 0), + (48, 'background', 0, 1, 0), + (49, 'background', 0, 1, 0), + (50, 'background', 0, 1, 0), + (51, 'background', 0, 1, 0), + (52, 'background', 0, 1, 0), + (53, 'background', 0, 1, 0), + (54, 'background', 0, 1, 0), + (55, 'background', 0, 1, 0), + (56, 'background', 0, 1, 0), + (57, 'background', 0, 1, 0), + (58, 'background', 0, 1, 0), + (59, 'background', 0, 1, 0), + (60, 'background', 0, 1, 0), + (61, 'background', 0, 1, 0), + (62, 'background', 0, 1, 0), + (63, 'background', 0, 1, 0), + (64, 'background', 0, 1, 0), + (65, 'background', 0, 1, 0), + (66, 'background', 0, 1, 0), + (67, 'background', 0, 1, 0), + (68, 'background', 0, 1, 0), + (69, 'background', 0, 1, 0), + (70, 'background', 0, 1, 0), + (71, 'background', 0, 1, 0), + (72, 'background', 0, 1, 0), + (73, 'background', 0, 1, 0), + (74, 'background', 0, 1, 0), + (75, 'background', 0, 1, 0), + (76, 'background', 0, 1, 0), + (77, 'background', 0, 1, 0), + (78, 'background', 0, 1, 0), + (79, 'background', 0, 1, 0), + (80, 'background', 0, 1, 0), + (81, 'background', 0, 1, 0), + (82, 'background', 0, 1, 0), + (83, 'background', 0, 1, 0), + (84, 'background', 0, 1, 0), + (85, 'background', 0, 1, 0), + (86, 'background', 0, 1, 0), + (87, 'background', 0, 1, 0), + (88, 'background', 0, 1, 0), + (89, 'background', 0, 1, 0), + (90, 'background', 0, 1, 0), + (91, 'background', 0, 1, 0), + (92, 'background', 0, 1, 0), + (93, 'background', 0, 1, 0), + (94, 'background', 0, 1, 0), + (95, 'background', 0, 1, 0), + (96, 'background', 0, 1, 0), + (97, 'background', 0, 1, 0), + (98, 'background', 0, 1, 0), + (99, 'background', 0, 1, 0), + (100, 'background', 0, 1, 0), + (101, 'background', 2, 0, 0), + (102, 'background', 0, 1, 0), + (103, 'background', 0, 1, 0), + (104, 'background', 0, 1, 0), + (105, 'background', 0, 1, 0), + (106, 'background', 0, 1, 0), + (107, 'background', 0, 1, 0), + (108, 'background', 0, 1, 0), + (109, 'background', 0, 1, 0), + (110, 'background', 0, 1, 0), + (111, 'background', 0, 1, 0), + (112, 'background', 0, 1, 0), + (113, 'background', 0, 1, 0), + (114, 'background', 0, 1, 0), + (115, 'background', 0, 1, 0), + (116, 'background', 0, 1, 0), + (117, 'background', 0, 1, 0), + (118, 'background', 0, 1, 0), + (119, 'background', 0, 1, 0), + (120, 'background', 0, 1, 0), + (121, 'background', 0, 1, 0), + (122, 'background', 0, 1, 0), + (123, 'background', 0, 1, 0), + (124, 'background', 0, 1, 0), + (125, 'background', 0, 1, 0), + (126, 'background', 0, 1, 0), + (127, 'background', 0, 1, 0), + (128, 'background', 0, 1, 0), + (129, 'background', 0, 1, 0), + (130, 'background', 0, 1, 0), + (131, 'background', 0, 1, 0), + (132, 'background', 0, 1, 0), + (133, 'background', 0, 1, 0), + (134, 'background', 0, 1, 0), + (135, 'background', 0, 1, 0), + (136, 'background', 0, 1, 0), + (137, 'background', 0, 1, 0), + (138, 'background', 0, 1, 0), + (139, 'background', 0, 1, 0), + (140, 'background', 0, 1, 0), + (141, 'background', 0, 1, 0), + (142, 'background', 0, 1, 0), + (143, 'background', 0, 1, 0), + (144, 'background', 0, 1, 0), + (145, 'background', 0, 1, 0), + (146, 'background', 0, 1, 0), + (147, 'background', 0, 1, 0), + (148, 'background', 0, 1, 0), + (149, 'background', 0, 1, 0), + (150, 'background', 0, 1, 0), + (151, 'background', 0, 1, 0), + (152, 'background', 0, 1, 0), + (153, 'background', 0, 1, 0), + (154, 'background', 0, 1, 0), + (155, 'background', 0, 1, 0), + (156, 'background', 0, 1, 0), + (157, 'background', 0, 1, 0), + (158, 'background', 0, 1, 0), + (159, 'background', 0, 1, 0), + (160, 'background', 0, 1, 0), + (161, 'background', 0, 1, 0), + (162, 'background', 0, 1, 0), + (163, 'background', 0, 1, 0), + (164, 'background', 0, 1, 0), + (165, 'background', 0, 1, 0), + (166, 'background', 0, 1, 0), + (167, 'background', 0, 1, 0), + (168, 'background', 0, 1, 0), + (169, 'background', 0, 1, 0), + (170, 'background', 0, 1, 0), + (171, 'background', 0, 1, 0), + (172, 'background', 0, 1, 0), + (173, 'background', 0, 1, 0), + (174, 'background', 0, 1, 0), + (175, 'background', 0, 1, 0), + (176, 'background', 0, 1, 0), + (177, 'background', 0, 1, 0), + (178, 'background', 0, 1, 0), + (179, 'background', 0, 1, 0), + (180, 'background', 0, 1, 0), + (181, 'background', 0, 1, 0), + (182, 'background', 0, 1, 0), + (183, 'background', 0, 1, 0), + (184, 'background', 0, 1, 0), + (185, 'background', 0, 1, 0), + (186, 'background', 0, 1, 0), + (187, 'background', 0, 1, 0), + (0, 'stand', 0, 0, 0), + (1, 'stand', 0, 0, 0), + (2, 'stand', 0, 0, 0), + (3, 'stand', 0, 0, 0), + (4, 'stand', 0, 0, 0), + (5, 'stand', 0, 0, 0), + (6, 'stand', 0, 0, 0), + (7, 'stand', 0, 0, 0), + (8, 'stand', 0, 0, 0), + (9, 'stand', 0, 0, 0), + (10, 'stand', 0, 0, 0), + (11, 'stand', 0, 0, 0), + (12, 'stand', 0, 0, 0), + (13, 'stand', 0, 0, 0), + (14, 'stand', 0, 0, 0), + (15, 'stand', 0, 0, 0), + (16, 'stand', 0, 1, 0), + (17, 'stand', 0, 1, 0), + (18, 'stand', 0, 1, 0), + (19, 'stand', 0, 1, 0), + (20, 'stand', 0, 1, 0), + (21, 'stand', 0, 1, 0), + (0, 'overlay', 0, 0, 0), + (1, 'overlay', 0, 0, 0), + (2, 'overlay', 0, 1, 0), + (3, 'overlay', 0, 1, 0), + (4, 'overlay', 0, 1, 0), + (5, 'overlay', 0, 1, 0), + (6, 'overlay', 0, 1, 0), + (7, 'overlay', 0, 1, 0), + (8, 'overlay', 0, 1, 0), + (1, 'card', 0, 0, 0), + (2, 'card', 0, 0, 0), + (3, 'card', 0, 0, 0), + (4, 'card', 0, 0, 0), + (5, 'card', 0, 0, 0), + (6, 'card', 0, 0, 0), + (7, 'card', 0, 0, 0), + (8, 'card', 0, 0, 0), + (9, 'card', 0, 0, 0), + (10, 'card', 0, 0, 0), + (11, 'card', 0, 0, 0), + (12, 'card', 0, 0, 0), + (13, 'card', 0, 0, 0), + (14, 'card', 0, 0, 0), + (15, 'card', 0, 0, 0); \ No newline at end of file diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java index ac401c1f..d8eec628 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/GameEnvironment.java @@ -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; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java new file mode 100644 index 00000000..60ce0419 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/users/infostand/InfostandBackgroundManager.java @@ -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> 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> 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 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; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/users/ChangeInfostandBgEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/users/ChangeInfostandBgEvent.java index 84262778..74485096 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/users/ChangeInfostandBgEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/users/ChangeInfostandBgEvent.java @@ -1,8 +1,11 @@ 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; @@ -30,10 +33,17 @@ public class ChangeInfostandBgEvent extends MessageHandler { stats.cache.put(COOLDOWN_KEY, now); } - int backgroundImage = sanitize(this.packet.readInt()); - int backgroundStand = sanitize(this.packet.readInt()); - int backgroundOverlay = sanitize(this.packet.readInt()); - int backgroundCard = this.packet.bytesAvailable() >= 4 ? sanitize(this.packet.readInt()) : 0; + 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 @@ -59,4 +69,9 @@ public class ChangeInfostandBgEvent extends MessageHandler { 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; + } } diff --git a/Latest_Compiled_Version/Habbo-4.1.2-jar-with-dependencies.jar b/Latest_Compiled_Version/Habbo-4.1.2-jar-with-dependencies.jar index 1de4428a..c15dee6a 100644 Binary files a/Latest_Compiled_Version/Habbo-4.1.2-jar-with-dependencies.jar and b/Latest_Compiled_Version/Habbo-4.1.2-jar-with-dependencies.jar differ