From c5609ed00bdf656c3567e189c563701ee90fff18 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 9 Apr 2026 15:20:31 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20Added=20packet=20rate=20limit=20?= =?UTF-8?q?=3D=3D>=2050=20p/s=20&=20youtube=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Database Updates/007_PackageRateLimit.sql | 1 + .../com/eu/habbo/habbohotel/rooms/Room.java | 136 ++++++++++-------- .../rooms/youtube/YouTubeRoomPlayEvent.java | 43 ++++-- .../youtube/YouTubeRoomWatchingEvent.java | 21 ++- .../decoders/GameMessageRateLimit.java | 25 +++- 5 files changed, 143 insertions(+), 83 deletions(-) create mode 100644 Database Updates/007_PackageRateLimit.sql diff --git a/Database Updates/007_PackageRateLimit.sql b/Database Updates/007_PackageRateLimit.sql new file mode 100644 index 00000000..00ca631d --- /dev/null +++ b/Database Updates/007_PackageRateLimit.sql @@ -0,0 +1 @@ +INSERT INTO emulator_settings (`key`, `value`) VALUES ('packet.global.rate.limit', '50'); \ No newline at end of file diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java index 010cd15d..ac11d120 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/Room.java @@ -176,6 +176,8 @@ public class Room implements Comparable, ISerialize, Runnable { private RoomSpecialTypes roomSpecialTypes; private TraxManager traxManager; + // YouTube room broadcast state: tracks the current video being broadcast + // by the room owner, the owner's playlist, and which users have the player open. private String youtubeCurrentVideo = ""; private String youtubeSenderName = ""; private final java.util.List youtubePlaylist = new java.util.concurrent.CopyOnWriteArrayList<>(); @@ -187,16 +189,16 @@ public class Room implements Comparable, ISerialize, Runnable { public java.util.Set getYoutubeWatchers() { return this.youtubeWatchers; } public void setYoutubeVideo(String videoId, String senderName, java.util.List playlist) { - this.youtubeCurrentVideo = videoId; - this.youtubeSenderName = senderName; - this.youtubePlaylist.clear(); - if (playlist != null) this.youtubePlaylist.addAll(playlist); + this.youtubeCurrentVideo = videoId; + this.youtubeSenderName = senderName; + this.youtubePlaylist.clear(); + if (playlist != null) this.youtubePlaylist.addAll(playlist); } public void clearYoutubeVideo() { - this.youtubeCurrentVideo = ""; - this.youtubeSenderName = ""; - this.youtubePlaylist.clear(); + this.youtubeCurrentVideo = ""; + this.youtubeSenderName = ""; + this.youtubePlaylist.clear(); } public final THashMap cache; @@ -408,7 +410,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (this.loaded || this.loadingInProgress || !this.preLoaded) { return; } - + this.loadingInProgress = true; this.loadingFuture = CompletableFuture.runAsync(() -> { this.loadDataInternal(); @@ -428,7 +430,7 @@ public class Room implements Comparable, ISerialize, Runnable { } future = this.loadingFuture; } - + if (future != null) { try { future.join(); @@ -443,7 +445,7 @@ public class Room implements Comparable, ISerialize, Runnable { public void loadData() { CompletableFuture futureToWait = null; boolean shouldLoad = false; - + synchronized (this.loadLock) { if (this.loadingInProgress) { // Get the future to wait on outside the lock @@ -453,7 +455,7 @@ public class Room implements Comparable, ISerialize, Runnable { shouldLoad = true; } } - + // Wait for existing load outside the lock if (futureToWait != null) { try { @@ -463,7 +465,7 @@ public class Room implements Comparable, ISerialize, Runnable { } return; } - + // Load if needed if (shouldLoad) { this.loadDataInternal(); @@ -503,7 +505,7 @@ public class Room implements Comparable, ISerialize, Runnable { CompletableFuture.runAsync(() -> { try (Connection promoConnection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement stmt = promoConnection.prepareStatement( - "SELECT * FROM room_promotions WHERE room_id = ? AND end_timestamp > ? LIMIT 1")) { + "SELECT * FROM room_promotions WHERE room_id = ? AND end_timestamp > ? LIMIT 1")) { stmt.setInt(1, this.id); stmt.setInt(2, Emulator.getIntUnixTimestamp()); try (ResultSet promoSet = stmt.executeQuery()) { @@ -598,7 +600,7 @@ public class Room implements Comparable, ISerialize, Runnable { } this.roomCycleTask = Emulator.getThreading().getService() - .scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS); + .scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS); } catch (Exception e) { LOGGER.error("Caught exception during room load", e); } @@ -617,7 +619,7 @@ public class Room implements Comparable, ISerialize, Runnable { item.setExtradata("1"); this.updateItem(item); } - + // Set loaded flag with lock synchronized (this.loadLock) { this.loaded = true; @@ -634,7 +636,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.layout = Emulator.getGameEnvironment().getRoomManager().loadCustomLayout(this); } else { this.layout = Emulator.getGameEnvironment().getRoomManager() - .loadLayout(this.layoutName, this); + .loadLayout(this.layoutName, this); } } } @@ -666,7 +668,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.unitManager.clearBots(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) { + "SELECT users.username AS owner_name, bots.* FROM bots INNER JOIN users ON bots.user_id = users.id WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -677,11 +679,11 @@ public class Room implements Comparable, ISerialize, Runnable { b.setRoomUnit(new RoomUnit()); b.getRoomUnit().setPathFinderRoom(this); b.getRoomUnit() - .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); + .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); if (b.getRoomUnit().getCurrentLocation() == null) { b.getRoomUnit().setLocation(this.getLayout().getDoorTile()); b.getRoomUnit() - .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); + .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); } else { b.getRoomUnit().setZ(set.getDouble("z")); b.getRoomUnit().setPreviousLocationZ(set.getDouble("z")); @@ -705,7 +707,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.unitManager.clearPets(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) { + "SELECT users.username as pet_owner_name, users_pets.* FROM users_pets INNER JOIN users ON users_pets.user_id = users.id WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -715,11 +717,11 @@ public class Room implements Comparable, ISerialize, Runnable { pet.setRoomUnit(new RoomUnit()); pet.getRoomUnit().setPathFinderRoom(this); pet.getRoomUnit() - .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); + .setLocation(this.layout.getTile((short) set.getInt("x"), (short) set.getInt("y"))); if (pet.getRoomUnit().getCurrentLocation() == null) { pet.getRoomUnit().setLocation(this.getLayout().getDoorTile()); pet.getRoomUnit() - .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); + .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); } else { pet.getRoomUnit().setZ(set.getDouble("z")); pet.getRoomUnit().setRotation(RoomUserRotation.values()[set.getInt("rot")]); @@ -791,7 +793,7 @@ public class Room implements Comparable, ISerialize, Runnable { THashSet updatedTiles = new THashSet<>(); Rectangle rectangle = RoomLayout.getRectangle(item.getX(), item.getY(), - item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getRotation()); for (short x = (short) rectangle.x; x < rectangle.x + rectangle.getWidth(); x++) { for (short y = (short) rectangle.y; y < rectangle.y + rectangle.getHeight(); y++) { @@ -815,7 +817,7 @@ public class Room implements Comparable, ISerialize, Runnable { } Habbo habbo = (picker != null && picker.getHabboInfo().getId() == item.getId() ? picker - : Emulator.getGameServer().getGameClientManager().getHabbo(item.getUserId())); + : Emulator.getGameServer().getGameClientManager().getHabbo(item.getUserId())); if (habbo != null) { habbo.getInventory().getItemsComponent().addItem(item); habbo.getClient().sendResponse(new AddHabboItemComposer(item)); @@ -1053,7 +1055,7 @@ public class Room implements Comparable, ISerialize, Runnable { message.appendInt(this.category); String[] tags = Arrays.stream(this.tags.split(";")).filter(t -> !t.isEmpty()) - .toArray(String[]::new); + .toArray(String[]::new); message.appendInt(tags.length); for (String s : tags) { message.appendString(s); @@ -1117,8 +1119,8 @@ public class Room implements Comparable, ISerialize, Runnable { public void save() { if (this.needsUpdate) { try (Connection connection = Emulator.getDatabase().getDataSource() - .getConnection(); PreparedStatement statement = connection.prepareStatement( - "UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ?, allow_underpass = ? WHERE id = ?")) { + .getConnection(); PreparedStatement statement = connection.prepareStatement( + "UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ?, allow_underpass = ? WHERE id = ?")) { statement.setString(1, this.name); statement.setString(2, this.description); statement.setString(3, this.password); @@ -1183,8 +1185,8 @@ public class Room implements Comparable, ISerialize, Runnable { */ public void updateDatabaseUserCount() { try (Connection connection = Emulator.getDatabase().getDataSource() - .getConnection(); PreparedStatement statement = connection.prepareStatement( - "UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) { + .getConnection(); PreparedStatement statement = connection.prepareStatement( + "UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) { statement.setInt(1, this.getUserCount()); statement.setInt(2, this.id); statement.executeUpdate(); @@ -1498,7 +1500,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (extraData.length == 4) { if (extraData[0].equalsIgnoreCase("1")) { return Color.getHSBColor(Integer.parseInt(extraData[1]), - Integer.parseInt(extraData[2]), Integer.parseInt(extraData[3])); + Integer.parseInt(extraData[2]), Integer.parseInt(extraData[3])); } } } @@ -1605,7 +1607,7 @@ public class Room implements Comparable, ISerialize, Runnable { public String[] filterAnything() { return new String[]{this.getOwnerName(), this.getGuildName(), this.getDescription(), - this.getPromotionDesc()}; + this.getPromotionDesc()}; } public long getCycleTimestamp() { @@ -1797,13 +1799,21 @@ public class Room implements Comparable, ISerialize, Runnable { } public void removeHabbo(Habbo habbo) { + this.cleanupYoutubeWatcher(habbo); this.unitManager.removeHabbo(habbo); } public void removeHabbo(Habbo habbo, boolean sendRemovePacket) { + this.cleanupYoutubeWatcher(habbo); this.unitManager.removeHabbo(habbo, sendRemovePacket); } + private void cleanupYoutubeWatcher(Habbo habbo) { + if (habbo != null && this.youtubeWatchers.remove(habbo.getHabboInfo().getId())) { + this.sendComposer(new com.eu.habbo.messages.outgoing.rooms.youtube.YouTubeRoomWatchersComposer(this.youtubeWatchers).compose()); + } + } + public void addBot(Bot bot) { this.unitManager.addBot(bot); } @@ -1939,7 +1949,7 @@ public class Room implements Comparable, ISerialize, Runnable { } public void talk(final Habbo habbo, final RoomChatMessage roomChatMessage, RoomChatType chatType, - boolean ignoreWired) { + boolean ignoreWired) { this.chatManager.talk(habbo, roomChatMessage, chatType, ignoreWired); } @@ -2084,7 +2094,7 @@ public class Room implements Comparable, ISerialize, Runnable { private void loadRights(Connection connection) { this.rights.clear(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT user_id FROM room_rights WHERE room_id = ?")) { + "SELECT user_id FROM room_rights WHERE room_id = ?")) { statement.setInt(1, this.id); try (ResultSet set = statement.executeQuery()) { while (set.next()) { @@ -2100,7 +2110,7 @@ public class Room implements Comparable, ISerialize, Runnable { this.bannedHabbos.clear(); try (PreparedStatement statement = connection.prepareStatement( - "SELECT users.username, users.id, room_bans.* FROM room_bans INNER JOIN users ON room_bans.user_id = users.id WHERE ends > ? AND room_bans.room_id = ?")) { + "SELECT users.username, users.id, room_bans.* FROM room_bans INNER JOIN users ON room_bans.user_id = users.id WHERE ends > ? AND room_bans.room_id = ?")) { statement.setInt(1, Emulator.getIntUnixTimestamp()); statement.setInt(2, this.id); try (ResultSet set = statement.executeQuery()) { @@ -2122,7 +2132,7 @@ public class Room implements Comparable, ISerialize, Runnable { Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(this.guild); if (Emulator.getGameEnvironment().getGuildManager().getOnlyAdmins(guild) - .get(habbo.getHabboInfo().getId()) != null) { + .get(habbo.getHabboInfo().getId()) != null) { return RoomRightLevels.GUILD_ADMIN; } @@ -2200,15 +2210,15 @@ public class Room implements Comparable, ISerialize, Runnable { } if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) || !habbo.getRoomUnit() - .canForcePosture()) { + .canForcePosture()) { return; } this.dance(habbo, DanceType.NONE); habbo.getRoomUnit().cmdSit = true; habbo.getRoomUnit().setBodyRotation( - RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() - - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); + RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() + - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + ""); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1); @@ -2222,11 +2232,11 @@ public class Room implements Comparable, ISerialize, Runnable { HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY()); if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) { boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) - || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); + || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); habbo.getRoomUnit().cmdStand = true; habbo.getRoomUnit().setBodyRotation( - RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() - - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); + RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() + - habbo.getRoomUnit().getBodyRotation().getValue() % 2]); habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT); habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); @@ -2260,9 +2270,9 @@ public class Room implements Comparable, ISerialize, Runnable { if (item.getBaseItem().getType() == FurnitureType.FLOOR) { this.sendComposer(new FloorItemUpdateComposer(item).compose()); this.updateTiles(this.getLayout() - .getTilesAt(this.layout.getTile(item.getX(), item.getY()), - item.getBaseItem().getWidth(), item.getBaseItem().getLength(), - item.getRotation())); + .getTilesAt(this.layout.getTile(item.getX(), item.getY()), + item.getBaseItem().getWidth(), item.getBaseItem().getLength(), + item.getRotation())); } else if (item.getBaseItem().getType() == FurnitureType.WALL) { this.sendComposer(new WallItemUpdateComposer(item).compose()); } @@ -2284,8 +2294,8 @@ public class Room implements Comparable, ISerialize, Runnable { } this.updateTiles(this.getLayout() - .getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), - item.getBaseItem().getLength(), item.getRotation())); + .getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), + item.getBaseItem().getLength(), item.getRotation())); if (item instanceof InteractionMultiHeight) { ((InteractionMultiHeight) item).updateUnitsOnItem(this); @@ -2322,18 +2332,18 @@ public class Room implements Comparable, ISerialize, Runnable { public void refreshGuild(Guild guild) { if (guild.getRoomId() == this.id) { THashSet members = Emulator.getGameEnvironment().getGuildManager() - .getGuildMembers(guild.getId()); + .getGuildMembers(guild.getId()); for (Habbo habbo : this.getHabbos()) { Optional member = members.stream() - .filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny(); + .filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny(); if (!member.isPresent()) { continue; } habbo.getClient() - .sendResponse(new GuildInfoComposer(guild, habbo.getClient(), false, member.get())); + .sendResponse(new GuildInfoComposer(guild, habbo.getClient(), false, member.get())); } } @@ -2368,7 +2378,7 @@ public class Room implements Comparable, ISerialize, Runnable { if (habbo.getHabboInfo().getCurrentRoom() == this) { if (habbo.getHabboInfo().getId() != this.ownerId) { if (!(habbo.hasPermission(Permission.ACC_ANYROOMOWNER) || habbo.hasPermission( - Permission.ACC_MOVEROTATE))) { + Permission.ACC_MOVEROTATE))) { this.refreshRightsForHabbo(habbo); } } @@ -2454,18 +2464,18 @@ public class Room implements Comparable, ISerialize, Runnable { } } else { this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getTriggers()).compose()); + this.roomSpecialTypes.getTriggers()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getEffects()).compose()); + this.roomSpecialTypes.getEffects()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getConditions()).compose()); + this.roomSpecialTypes.getConditions()).compose()); this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), - this.roomSpecialTypes.getExtras()).compose()); + this.roomSpecialTypes.getExtras()).compose()); } } public FurnitureMovementError canPlaceFurnitureAt(HabboItem item, Habbo habbo, RoomTile tile, - int rotation) { + int rotation) { return this.itemManager.canPlaceFurnitureAt(item, habbo, tile, rotation); } @@ -2474,17 +2484,17 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError furnitureFitsAt(RoomTile tile, HabboItem item, int rotation, - boolean checkForUnits) { + boolean checkForUnits) { return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits); } public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, - boolean checkForUnits, WiredMovementPhysics physics) { + boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics); } public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, - Habbo owner) { + Habbo owner) { return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner); } @@ -2493,17 +2503,17 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor) { + Habbo actor) { return this.itemManager.moveFurniTo(item, tile, rotation, actor); } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates) { + Habbo actor, boolean sendUpdates) { return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates); } public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates, boolean checkForUnits) { + Habbo actor, boolean sendUpdates, boolean checkForUnits) { return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates, checkForUnits); } @@ -2520,12 +2530,12 @@ public class Room implements Comparable, ISerialize, Runnable { } public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, - Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, actor, sendUpdates, checkForUnits, physics); } public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, - Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { + Habbo actor, boolean sendUpdates, boolean checkForUnits, WiredMovementPhysics physics) { return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, z, actor, sendUpdates, checkForUnits, physics); } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomPlayEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomPlayEvent.java index 892d68ff..d5c5edf1 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomPlayEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomPlayEvent.java @@ -9,6 +9,17 @@ import java.util.ArrayList; import java.util.List; public class YouTubeRoomPlayEvent extends MessageHandler { + + private static final int MAX_VIDEO_ID_LENGTH = 100; + private static final int MAX_PLAYLIST_ITEM_LENGTH = 200; + private static final int MAX_PLAYLIST_SIZE = 50; + + @Override + public int getRatelimit() { + // Max 1 broadcast every 2 seconds per client + return 2000; + } + @Override public void handle() throws Exception { Habbo habbo = this.client.getHabbo(); @@ -21,19 +32,33 @@ public class YouTubeRoomPlayEvent extends MessageHandler { if (!room.isOwner(habbo) && !room.hasRights(habbo)) return; String videoId = this.packet.readString(); - - int playlistCount = this.packet.readInt(); - List playlist = new ArrayList<>(); - for (int i = 0; i < playlistCount && i < 50; i++) { - playlist.add(this.packet.readString()); + if (videoId.length() > MAX_VIDEO_ID_LENGTH) { + videoId = videoId.substring(0, MAX_VIDEO_ID_LENGTH); } - // Store the current video + playlist on the room - room.setYoutubeVideo(videoId, habbo.getHabboInfo().getUsername(), playlist); + int playlistCount = this.packet.readInt(); + if (playlistCount > MAX_PLAYLIST_SIZE) playlistCount = MAX_PLAYLIST_SIZE; + if (playlistCount < 0) playlistCount = 0; - // Broadcast to everyone in the room + List playlist = new ArrayList<>(); + for (int i = 0; i < playlistCount; i++) { + String item = this.packet.readString(); + if (item.length() > MAX_PLAYLIST_ITEM_LENGTH) { + item = item.substring(0, MAX_PLAYLIST_ITEM_LENGTH); + } + playlist.add(item); + } + + // Store the current video + playlist on the room, or clear if empty + if (videoId.isEmpty()) { + room.clearYoutubeVideo(); + } else { + room.setYoutubeVideo(videoId, habbo.getHabboInfo().getUsername(), playlist); + } + + // Broadcast to everyone in the room (empty videoId = stop) room.sendComposer( - new YouTubeRoomBroadcastComposer(videoId, habbo.getHabboInfo().getUsername(), playlist).compose() + new YouTubeRoomBroadcastComposer(videoId, habbo.getHabboInfo().getUsername(), playlist).compose() ); } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomWatchingEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomWatchingEvent.java index 670d9497..3de11651 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomWatchingEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/youtube/YouTubeRoomWatchingEvent.java @@ -6,6 +6,12 @@ import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.outgoing.rooms.youtube.YouTubeRoomWatchersComposer; public class YouTubeRoomWatchingEvent extends MessageHandler { + + @Override + public int getRatelimit() { + return 500; + } + @Override public void handle() throws Exception { Habbo habbo = this.client.getHabbo(); @@ -14,17 +20,20 @@ public class YouTubeRoomWatchingEvent extends MessageHandler { Room room = habbo.getHabboInfo().getCurrentRoom(); if (room == null) return; - boolean watching = this.packet.readBoolean(); + boolean watching = this.packet.readInt() == 1; int userId = habbo.getHabboInfo().getId(); + boolean changed; if (watching) { - room.getYoutubeWatchers().add(userId); + changed = room.getYoutubeWatchers().add(userId); } else { - room.getYoutubeWatchers().remove(userId); + changed = room.getYoutubeWatchers().remove(userId); } - room.sendComposer( - new YouTubeRoomWatchersComposer(room.getYoutubeWatchers()).compose() - ); + if (changed) { + room.sendComposer( + new YouTubeRoomWatchersComposer(room.getYoutubeWatchers()).compose() + ); + } } } diff --git a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageRateLimit.java b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageRateLimit.java index 7a4c8de2..1b83a3ee 100644 --- a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageRateLimit.java +++ b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/decoders/GameMessageRateLimit.java @@ -6,13 +6,18 @@ import com.eu.habbo.messages.ClientMessage; import com.eu.habbo.networking.gameserver.GameServerAttributes; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; public class GameMessageRateLimit extends MessageToMessageDecoder { + private static final Logger LOGGER = LoggerFactory.getLogger(GameMessageRateLimit.class); + private static final int RESET_TIME = 1; private static final int MAX_COUNTER = 10; + private static final int DEFAULT_GLOBAL_MAX = 50; @Override protected void decode(ChannelHandlerContext ctx, ClientMessage message, List out) throws Exception { @@ -23,26 +28,36 @@ public class GameMessageRateLimit extends MessageToMessageDecoder } int count = 0; + int globalCount = 0; - // Check if reset time has passed. int timestamp = Emulator.getIntUnixTimestamp(); if (timestamp - client.lastPacketCounterCleared > RESET_TIME) { - // Reset counter. client.incomingPacketCounter.clear(); client.lastPacketCounterCleared = timestamp; } else { - // Get stored count for message id. count = client.incomingPacketCounter.getOrDefault(message.getMessageId(), 0); + for (int c : client.incomingPacketCounter.values()) { + globalCount += c; + } } - // If we exceeded the counter, drop the packet. if (count > MAX_COUNTER) { return; } + int globalMax = Emulator.getConfig().getInt("packet.global.rate.limit", DEFAULT_GLOBAL_MAX); + if (globalCount > globalMax) { + if (globalCount == globalMax + 1) { + String username = (client.getHabbo() != null && client.getHabbo().getHabboInfo() != null) + ? client.getHabbo().getHabboInfo().getUsername() : "unauthenticated"; + LOGGER.warn("Global packet rate limit exceeded for {} ({} packets/sec) — dropping excess packets", + username, globalCount); + } + return; + } + client.incomingPacketCounter.put(message.getMessageId(), ++count); - // Continue processing. out.add(message); }