🆙 Added packet rate limit ==> 50 p/s & youtube fixes

This commit is contained in:
duckietm
2026-04-09 15:20:31 +02:00
parent 5172a74f61
commit c5609ed00b
5 changed files with 143 additions and 83 deletions
@@ -0,0 +1 @@
INSERT INTO emulator_settings (`key`, `value`) VALUES ('packet.global.rate.limit', '50');
@@ -176,6 +176,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
private RoomSpecialTypes roomSpecialTypes; private RoomSpecialTypes roomSpecialTypes;
private TraxManager traxManager; 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 youtubeCurrentVideo = "";
private String youtubeSenderName = ""; private String youtubeSenderName = "";
private final java.util.List<String> youtubePlaylist = new java.util.concurrent.CopyOnWriteArrayList<>(); private final java.util.List<String> youtubePlaylist = new java.util.concurrent.CopyOnWriteArrayList<>();
@@ -187,16 +189,16 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
public java.util.Set<Integer> getYoutubeWatchers() { return this.youtubeWatchers; } public java.util.Set<Integer> getYoutubeWatchers() { return this.youtubeWatchers; }
public void setYoutubeVideo(String videoId, String senderName, java.util.List<String> playlist) { public void setYoutubeVideo(String videoId, String senderName, java.util.List<String> playlist) {
this.youtubeCurrentVideo = videoId; this.youtubeCurrentVideo = videoId;
this.youtubeSenderName = senderName; this.youtubeSenderName = senderName;
this.youtubePlaylist.clear(); this.youtubePlaylist.clear();
if (playlist != null) this.youtubePlaylist.addAll(playlist); if (playlist != null) this.youtubePlaylist.addAll(playlist);
} }
public void clearYoutubeVideo() { public void clearYoutubeVideo() {
this.youtubeCurrentVideo = ""; this.youtubeCurrentVideo = "";
this.youtubeSenderName = ""; this.youtubeSenderName = "";
this.youtubePlaylist.clear(); this.youtubePlaylist.clear();
} }
public final THashMap<String, Object> cache; public final THashMap<String, Object> cache;
@@ -503,7 +505,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try (Connection promoConnection = Emulator.getDatabase().getDataSource().getConnection(); try (Connection promoConnection = Emulator.getDatabase().getDataSource().getConnection();
PreparedStatement stmt = promoConnection.prepareStatement( 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(1, this.id);
stmt.setInt(2, Emulator.getIntUnixTimestamp()); stmt.setInt(2, Emulator.getIntUnixTimestamp());
try (ResultSet promoSet = stmt.executeQuery()) { try (ResultSet promoSet = stmt.executeQuery()) {
@@ -598,7 +600,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
this.roomCycleTask = Emulator.getThreading().getService() this.roomCycleTask = Emulator.getThreading().getService()
.scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS); .scheduleAtFixedRate(this, 500, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("Caught exception during room load", e); LOGGER.error("Caught exception during room load", e);
} }
@@ -634,7 +636,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.layout = Emulator.getGameEnvironment().getRoomManager().loadCustomLayout(this); this.layout = Emulator.getGameEnvironment().getRoomManager().loadCustomLayout(this);
} else { } else {
this.layout = Emulator.getGameEnvironment().getRoomManager() this.layout = Emulator.getGameEnvironment().getRoomManager()
.loadLayout(this.layoutName, this); .loadLayout(this.layoutName, this);
} }
} }
} }
@@ -666,7 +668,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.unitManager.clearBots(); this.unitManager.clearBots();
try (PreparedStatement statement = connection.prepareStatement( 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); statement.setInt(1, this.id);
try (ResultSet set = statement.executeQuery()) { try (ResultSet set = statement.executeQuery()) {
while (set.next()) { while (set.next()) {
@@ -677,11 +679,11 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
b.setRoomUnit(new RoomUnit()); b.setRoomUnit(new RoomUnit());
b.getRoomUnit().setPathFinderRoom(this); b.getRoomUnit().setPathFinderRoom(this);
b.getRoomUnit() 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) { if (b.getRoomUnit().getCurrentLocation() == null) {
b.getRoomUnit().setLocation(this.getLayout().getDoorTile()); b.getRoomUnit().setLocation(this.getLayout().getDoorTile());
b.getRoomUnit() b.getRoomUnit()
.setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection()));
} else { } else {
b.getRoomUnit().setZ(set.getDouble("z")); b.getRoomUnit().setZ(set.getDouble("z"));
b.getRoomUnit().setPreviousLocationZ(set.getDouble("z")); b.getRoomUnit().setPreviousLocationZ(set.getDouble("z"));
@@ -705,7 +707,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.unitManager.clearPets(); this.unitManager.clearPets();
try (PreparedStatement statement = connection.prepareStatement( 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); statement.setInt(1, this.id);
try (ResultSet set = statement.executeQuery()) { try (ResultSet set = statement.executeQuery()) {
while (set.next()) { while (set.next()) {
@@ -715,11 +717,11 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
pet.setRoomUnit(new RoomUnit()); pet.setRoomUnit(new RoomUnit());
pet.getRoomUnit().setPathFinderRoom(this); pet.getRoomUnit().setPathFinderRoom(this);
pet.getRoomUnit() 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) { if (pet.getRoomUnit().getCurrentLocation() == null) {
pet.getRoomUnit().setLocation(this.getLayout().getDoorTile()); pet.getRoomUnit().setLocation(this.getLayout().getDoorTile());
pet.getRoomUnit() pet.getRoomUnit()
.setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection())); .setRotation(RoomUserRotation.fromValue(this.getLayout().getDoorDirection()));
} else { } else {
pet.getRoomUnit().setZ(set.getDouble("z")); pet.getRoomUnit().setZ(set.getDouble("z"));
pet.getRoomUnit().setRotation(RoomUserRotation.values()[set.getInt("rot")]); pet.getRoomUnit().setRotation(RoomUserRotation.values()[set.getInt("rot")]);
@@ -791,7 +793,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
THashSet<RoomTile> updatedTiles = new THashSet<>(); THashSet<RoomTile> updatedTiles = new THashSet<>();
Rectangle rectangle = RoomLayout.getRectangle(item.getX(), item.getY(), 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 x = (short) rectangle.x; x < rectangle.x + rectangle.getWidth(); x++) {
for (short y = (short) rectangle.y; y < rectangle.y + rectangle.getHeight(); y++) { for (short y = (short) rectangle.y; y < rectangle.y + rectangle.getHeight(); y++) {
@@ -815,7 +817,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
Habbo habbo = (picker != null && picker.getHabboInfo().getId() == item.getId() ? picker 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) { if (habbo != null) {
habbo.getInventory().getItemsComponent().addItem(item); habbo.getInventory().getItemsComponent().addItem(item);
habbo.getClient().sendResponse(new AddHabboItemComposer(item)); habbo.getClient().sendResponse(new AddHabboItemComposer(item));
@@ -1053,7 +1055,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
message.appendInt(this.category); message.appendInt(this.category);
String[] tags = Arrays.stream(this.tags.split(";")).filter(t -> !t.isEmpty()) String[] tags = Arrays.stream(this.tags.split(";")).filter(t -> !t.isEmpty())
.toArray(String[]::new); .toArray(String[]::new);
message.appendInt(tags.length); message.appendInt(tags.length);
for (String s : tags) { for (String s : tags) {
message.appendString(s); message.appendString(s);
@@ -1117,8 +1119,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
public void save() { public void save() {
if (this.needsUpdate) { if (this.needsUpdate) {
try (Connection connection = Emulator.getDatabase().getDataSource() try (Connection connection = Emulator.getDatabase().getDataSource()
.getConnection(); PreparedStatement statement = connection.prepareStatement( .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 = ?")) { "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(1, this.name);
statement.setString(2, this.description); statement.setString(2, this.description);
statement.setString(3, this.password); statement.setString(3, this.password);
@@ -1183,8 +1185,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
*/ */
public void updateDatabaseUserCount() { public void updateDatabaseUserCount() {
try (Connection connection = Emulator.getDatabase().getDataSource() try (Connection connection = Emulator.getDatabase().getDataSource()
.getConnection(); PreparedStatement statement = connection.prepareStatement( .getConnection(); PreparedStatement statement = connection.prepareStatement(
"UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) { "UPDATE rooms SET users = ? WHERE id = ? LIMIT 1")) {
statement.setInt(1, this.getUserCount()); statement.setInt(1, this.getUserCount());
statement.setInt(2, this.id); statement.setInt(2, this.id);
statement.executeUpdate(); statement.executeUpdate();
@@ -1498,7 +1500,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
if (extraData.length == 4) { if (extraData.length == 4) {
if (extraData[0].equalsIgnoreCase("1")) { if (extraData[0].equalsIgnoreCase("1")) {
return Color.getHSBColor(Integer.parseInt(extraData[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<Room>, ISerialize, Runnable {
public String[] filterAnything() { public String[] filterAnything() {
return new String[]{this.getOwnerName(), this.getGuildName(), this.getDescription(), return new String[]{this.getOwnerName(), this.getGuildName(), this.getDescription(),
this.getPromotionDesc()}; this.getPromotionDesc()};
} }
public long getCycleTimestamp() { public long getCycleTimestamp() {
@@ -1797,13 +1799,21 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
public void removeHabbo(Habbo habbo) { public void removeHabbo(Habbo habbo) {
this.cleanupYoutubeWatcher(habbo);
this.unitManager.removeHabbo(habbo); this.unitManager.removeHabbo(habbo);
} }
public void removeHabbo(Habbo habbo, boolean sendRemovePacket) { public void removeHabbo(Habbo habbo, boolean sendRemovePacket) {
this.cleanupYoutubeWatcher(habbo);
this.unitManager.removeHabbo(habbo, sendRemovePacket); 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) { public void addBot(Bot bot) {
this.unitManager.addBot(bot); this.unitManager.addBot(bot);
} }
@@ -1939,7 +1949,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
public void talk(final Habbo habbo, final RoomChatMessage roomChatMessage, RoomChatType chatType, public void talk(final Habbo habbo, final RoomChatMessage roomChatMessage, RoomChatType chatType,
boolean ignoreWired) { boolean ignoreWired) {
this.chatManager.talk(habbo, roomChatMessage, chatType, ignoreWired); this.chatManager.talk(habbo, roomChatMessage, chatType, ignoreWired);
} }
@@ -2084,7 +2094,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
private void loadRights(Connection connection) { private void loadRights(Connection connection) {
this.rights.clear(); this.rights.clear();
try (PreparedStatement statement = connection.prepareStatement( 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); statement.setInt(1, this.id);
try (ResultSet set = statement.executeQuery()) { try (ResultSet set = statement.executeQuery()) {
while (set.next()) { while (set.next()) {
@@ -2100,7 +2110,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.bannedHabbos.clear(); this.bannedHabbos.clear();
try (PreparedStatement statement = connection.prepareStatement( 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(1, Emulator.getIntUnixTimestamp());
statement.setInt(2, this.id); statement.setInt(2, this.id);
try (ResultSet set = statement.executeQuery()) { try (ResultSet set = statement.executeQuery()) {
@@ -2122,7 +2132,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(this.guild); Guild guild = Emulator.getGameEnvironment().getGuildManager().getGuild(this.guild);
if (Emulator.getGameEnvironment().getGuildManager().getOnlyAdmins(guild) if (Emulator.getGameEnvironment().getGuildManager().getOnlyAdmins(guild)
.get(habbo.getHabboInfo().getId()) != null) { .get(habbo.getHabboInfo().getId()) != null) {
return RoomRightLevels.GUILD_ADMIN; return RoomRightLevels.GUILD_ADMIN;
} }
@@ -2200,15 +2210,15 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) || !habbo.getRoomUnit() if (habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) || !habbo.getRoomUnit()
.canForcePosture()) { .canForcePosture()) {
return; return;
} }
this.dance(habbo, DanceType.NONE); this.dance(habbo, DanceType.NONE);
habbo.getRoomUnit().cmdSit = true; habbo.getRoomUnit().cmdSit = true;
habbo.getRoomUnit().setBodyRotation( habbo.getRoomUnit().setBodyRotation(
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]); - habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + ""); habbo.getRoomUnit().setStatus(RoomUnitStatus.SIT, 0.5 + "");
this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1); WiredManager.triggerUserPerformsAction(this, habbo.getRoomUnit(), WiredUserActionType.SIT, -1);
@@ -2222,11 +2232,11 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY()); HabboItem item = this.getTopItemAt(habbo.getRoomUnit().getX(), habbo.getRoomUnit().getY());
if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) { if (item == null || !item.getBaseItem().allowSit() || !item.getBaseItem().allowLay()) {
boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT) boolean wasSittingOrLaying = habbo.getRoomUnit().hasStatus(RoomUnitStatus.SIT)
|| habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY); || habbo.getRoomUnit().hasStatus(RoomUnitStatus.LAY);
habbo.getRoomUnit().cmdStand = true; habbo.getRoomUnit().cmdStand = true;
habbo.getRoomUnit().setBodyRotation( habbo.getRoomUnit().setBodyRotation(
RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue() RoomUserRotation.values()[habbo.getRoomUnit().getBodyRotation().getValue()
- habbo.getRoomUnit().getBodyRotation().getValue() % 2]); - habbo.getRoomUnit().getBodyRotation().getValue() % 2]);
habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT); habbo.getRoomUnit().removeStatus(RoomUnitStatus.SIT);
habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY); habbo.getRoomUnit().removeStatus(RoomUnitStatus.LAY);
this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose()); this.sendComposer(new RoomUserStatusComposer(habbo.getRoomUnit()).compose());
@@ -2260,9 +2270,9 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
if (item.getBaseItem().getType() == FurnitureType.FLOOR) { if (item.getBaseItem().getType() == FurnitureType.FLOOR) {
this.sendComposer(new FloorItemUpdateComposer(item).compose()); this.sendComposer(new FloorItemUpdateComposer(item).compose());
this.updateTiles(this.getLayout() this.updateTiles(this.getLayout()
.getTilesAt(this.layout.getTile(item.getX(), item.getY()), .getTilesAt(this.layout.getTile(item.getX(), item.getY()),
item.getBaseItem().getWidth(), item.getBaseItem().getLength(), item.getBaseItem().getWidth(), item.getBaseItem().getLength(),
item.getRotation())); item.getRotation()));
} else if (item.getBaseItem().getType() == FurnitureType.WALL) { } else if (item.getBaseItem().getType() == FurnitureType.WALL) {
this.sendComposer(new WallItemUpdateComposer(item).compose()); this.sendComposer(new WallItemUpdateComposer(item).compose());
} }
@@ -2284,8 +2294,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
this.updateTiles(this.getLayout() this.updateTiles(this.getLayout()
.getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(), .getTilesAt(this.layout.getTile(item.getX(), item.getY()), item.getBaseItem().getWidth(),
item.getBaseItem().getLength(), item.getRotation())); item.getBaseItem().getLength(), item.getRotation()));
if (item instanceof InteractionMultiHeight) { if (item instanceof InteractionMultiHeight) {
((InteractionMultiHeight) item).updateUnitsOnItem(this); ((InteractionMultiHeight) item).updateUnitsOnItem(this);
@@ -2322,18 +2332,18 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
public void refreshGuild(Guild guild) { public void refreshGuild(Guild guild) {
if (guild.getRoomId() == this.id) { if (guild.getRoomId() == this.id) {
THashSet<GuildMember> members = Emulator.getGameEnvironment().getGuildManager() THashSet<GuildMember> members = Emulator.getGameEnvironment().getGuildManager()
.getGuildMembers(guild.getId()); .getGuildMembers(guild.getId());
for (Habbo habbo : this.getHabbos()) { for (Habbo habbo : this.getHabbos()) {
Optional<GuildMember> member = members.stream() Optional<GuildMember> member = members.stream()
.filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny(); .filter(m -> m.getUserId() == habbo.getHabboInfo().getId()).findAny();
if (!member.isPresent()) { if (!member.isPresent()) {
continue; continue;
} }
habbo.getClient() 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<Room>, ISerialize, Runnable {
if (habbo.getHabboInfo().getCurrentRoom() == this) { if (habbo.getHabboInfo().getCurrentRoom() == this) {
if (habbo.getHabboInfo().getId() != this.ownerId) { if (habbo.getHabboInfo().getId() != this.ownerId) {
if (!(habbo.hasPermission(Permission.ACC_ANYROOMOWNER) || habbo.hasPermission( if (!(habbo.hasPermission(Permission.ACC_ANYROOMOWNER) || habbo.hasPermission(
Permission.ACC_MOVEROTATE))) { Permission.ACC_MOVEROTATE))) {
this.refreshRightsForHabbo(habbo); this.refreshRightsForHabbo(habbo);
} }
} }
@@ -2454,18 +2464,18 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
} else { } else {
this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(),
this.roomSpecialTypes.getTriggers()).compose()); this.roomSpecialTypes.getTriggers()).compose());
this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(),
this.roomSpecialTypes.getEffects()).compose()); this.roomSpecialTypes.getEffects()).compose());
this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(),
this.roomSpecialTypes.getConditions()).compose()); this.roomSpecialTypes.getConditions()).compose());
this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(), this.sendComposer(new RoomFloorItemsComposer(this.itemManager.getFurniOwnerNames(),
this.roomSpecialTypes.getExtras()).compose()); this.roomSpecialTypes.getExtras()).compose());
} }
} }
public FurnitureMovementError canPlaceFurnitureAt(HabboItem item, Habbo habbo, RoomTile tile, public FurnitureMovementError canPlaceFurnitureAt(HabboItem item, Habbo habbo, RoomTile tile,
int rotation) { int rotation) {
return this.itemManager.canPlaceFurnitureAt(item, habbo, tile, rotation); return this.itemManager.canPlaceFurnitureAt(item, habbo, tile, rotation);
} }
@@ -2474,17 +2484,17 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
public FurnitureMovementError furnitureFitsAt(RoomTile tile, HabboItem item, int rotation, public FurnitureMovementError furnitureFitsAt(RoomTile tile, HabboItem item, int rotation,
boolean checkForUnits) { boolean checkForUnits) {
return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits); return this.itemManager.furnitureFitsAt(tile, item, rotation, checkForUnits);
} }
public FurnitureMovementError furnitureFitsAtWithPhysics(RoomTile tile, HabboItem item, int rotation, 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); return this.itemManager.furnitureFitsAtWithPhysics(tile, item, rotation, checkForUnits, physics);
} }
public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation,
Habbo owner) { Habbo owner) {
return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner); return this.itemManager.placeFloorFurniAt(item, tile, rotation, owner);
} }
@@ -2493,17 +2503,17 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation,
Habbo actor) { Habbo actor) {
return this.itemManager.moveFurniTo(item, tile, rotation, actor); return this.itemManager.moveFurniTo(item, tile, rotation, actor);
} }
public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, 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); return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates);
} }
public FurnitureMovementError moveFurniTo(HabboItem item, RoomTile tile, int rotation, 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); return this.itemManager.moveFurniTo(item, tile, rotation, actor, sendUpdates, checkForUnits);
} }
@@ -2520,12 +2530,12 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
} }
public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, 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); return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, actor, sendUpdates, checkForUnits, physics);
} }
public FurnitureMovementError moveFurniToWithPhysics(HabboItem item, RoomTile tile, int rotation, double z, 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); return this.itemManager.moveFurniToWithPhysics(item, tile, rotation, z, actor, sendUpdates, checkForUnits, physics);
} }
@@ -9,6 +9,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class YouTubeRoomPlayEvent extends MessageHandler { 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 @Override
public void handle() throws Exception { public void handle() throws Exception {
Habbo habbo = this.client.getHabbo(); Habbo habbo = this.client.getHabbo();
@@ -21,19 +32,33 @@ public class YouTubeRoomPlayEvent extends MessageHandler {
if (!room.isOwner(habbo) && !room.hasRights(habbo)) return; if (!room.isOwner(habbo) && !room.hasRights(habbo)) return;
String videoId = this.packet.readString(); String videoId = this.packet.readString();
if (videoId.length() > MAX_VIDEO_ID_LENGTH) {
int playlistCount = this.packet.readInt(); videoId = videoId.substring(0, MAX_VIDEO_ID_LENGTH);
List<String> playlist = new ArrayList<>();
for (int i = 0; i < playlistCount && i < 50; i++) {
playlist.add(this.packet.readString());
} }
// Store the current video + playlist on the room int playlistCount = this.packet.readInt();
room.setYoutubeVideo(videoId, habbo.getHabboInfo().getUsername(), playlist); if (playlistCount > MAX_PLAYLIST_SIZE) playlistCount = MAX_PLAYLIST_SIZE;
if (playlistCount < 0) playlistCount = 0;
// Broadcast to everyone in the room List<String> 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( room.sendComposer(
new YouTubeRoomBroadcastComposer(videoId, habbo.getHabboInfo().getUsername(), playlist).compose() new YouTubeRoomBroadcastComposer(videoId, habbo.getHabboInfo().getUsername(), playlist).compose()
); );
} }
} }
@@ -6,6 +6,12 @@ import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.rooms.youtube.YouTubeRoomWatchersComposer; import com.eu.habbo.messages.outgoing.rooms.youtube.YouTubeRoomWatchersComposer;
public class YouTubeRoomWatchingEvent extends MessageHandler { public class YouTubeRoomWatchingEvent extends MessageHandler {
@Override
public int getRatelimit() {
return 500;
}
@Override @Override
public void handle() throws Exception { public void handle() throws Exception {
Habbo habbo = this.client.getHabbo(); Habbo habbo = this.client.getHabbo();
@@ -14,17 +20,20 @@ public class YouTubeRoomWatchingEvent extends MessageHandler {
Room room = habbo.getHabboInfo().getCurrentRoom(); Room room = habbo.getHabboInfo().getCurrentRoom();
if (room == null) return; if (room == null) return;
boolean watching = this.packet.readBoolean(); boolean watching = this.packet.readInt() == 1;
int userId = habbo.getHabboInfo().getId(); int userId = habbo.getHabboInfo().getId();
boolean changed;
if (watching) { if (watching) {
room.getYoutubeWatchers().add(userId); changed = room.getYoutubeWatchers().add(userId);
} else { } else {
room.getYoutubeWatchers().remove(userId); changed = room.getYoutubeWatchers().remove(userId);
} }
room.sendComposer( if (changed) {
new YouTubeRoomWatchersComposer(room.getYoutubeWatchers()).compose() room.sendComposer(
); new YouTubeRoomWatchersComposer(room.getYoutubeWatchers()).compose()
);
}
} }
} }
@@ -6,13 +6,18 @@ import com.eu.habbo.messages.ClientMessage;
import com.eu.habbo.networking.gameserver.GameServerAttributes; import com.eu.habbo.networking.gameserver.GameServerAttributes;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
public class GameMessageRateLimit extends MessageToMessageDecoder<ClientMessage> { public class GameMessageRateLimit extends MessageToMessageDecoder<ClientMessage> {
private static final Logger LOGGER = LoggerFactory.getLogger(GameMessageRateLimit.class);
private static final int RESET_TIME = 1; private static final int RESET_TIME = 1;
private static final int MAX_COUNTER = 10; private static final int MAX_COUNTER = 10;
private static final int DEFAULT_GLOBAL_MAX = 50;
@Override @Override
protected void decode(ChannelHandlerContext ctx, ClientMessage message, List<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ClientMessage message, List<Object> out) throws Exception {
@@ -23,26 +28,36 @@ public class GameMessageRateLimit extends MessageToMessageDecoder<ClientMessage>
} }
int count = 0; int count = 0;
int globalCount = 0;
// Check if reset time has passed.
int timestamp = Emulator.getIntUnixTimestamp(); int timestamp = Emulator.getIntUnixTimestamp();
if (timestamp - client.lastPacketCounterCleared > RESET_TIME) { if (timestamp - client.lastPacketCounterCleared > RESET_TIME) {
// Reset counter.
client.incomingPacketCounter.clear(); client.incomingPacketCounter.clear();
client.lastPacketCounterCleared = timestamp; client.lastPacketCounterCleared = timestamp;
} else { } else {
// Get stored count for message id.
count = client.incomingPacketCounter.getOrDefault(message.getMessageId(), 0); 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) { if (count > MAX_COUNTER) {
return; 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); client.incomingPacketCounter.put(message.getMessageId(), ++count);
// Continue processing.
out.add(message); out.add(message);
} }