diff --git a/Emulator/.idea/misc.xml b/Emulator/.idea/misc.xml
index aacacf55..5ddb3b31 100644
--- a/Emulator/.idea/misc.xml
+++ b/Emulator/.idea/misc.xml
@@ -8,5 +8,5 @@
-
+
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/SetSpeedCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/SetSpeedCommand.java
index ab9c1fe2..46d52be9 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/SetSpeedCommand.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/SetSpeedCommand.java
@@ -26,14 +26,27 @@ public class SetSpeedCommand extends Command {
return true;
}
- if (newSpeed < -1 || newSpeed > Emulator.getConfig().getInt("hotel.rollers.speed.maximum")) {
+ // First check against the config bounds
+ int configMax = Emulator.getConfig().getInt("hotel.rollers.speed.maximum");
+ if (newSpeed < -1 || newSpeed > configMax) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_setspeed.bounds"), RoomChatMessageBubbles.ALERT);
return true;
}
+ // Enforce maximum speed of 10 regardless of config.
+ if (newSpeed > 10) {
+ newSpeed = 10;
+ gameClient.getHabbo().whisper("Speed cannot be set above 10. Setting speed to 10.", RoomChatMessageBubbles.ALERT);
+ }
+
room.setRollerSpeed(newSpeed);
- gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_setspeed").replace("%oldspeed%", oldSpeed + "").replace("%newspeed%", newSpeed + ""), RoomChatMessageBubbles.ALERT);
+ gameClient.getHabbo().whisper(
+ Emulator.getTexts().getValue("commands.succes.cmd_setspeed")
+ .replace("%oldspeed%", oldSpeed + "")
+ .replace("%newspeed%", newSpeed + ""),
+ RoomChatMessageBubbles.ALERT
+ );
return true;
}
}
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 6eb526e7..7156da80 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
@@ -1237,6 +1237,32 @@ public class Room implements Comparable, ISerialize, Runnable {
final long millis = System.currentTimeMillis();
for (Habbo habbo : this.currentHabbos.values()) {
+ RoomUnit unit = habbo.getRoomUnit();
+ // Only run loop detection if the unit is on a roller.
+ if (!unit.hasStatus(RoomUnitStatus.MOVE) && isOnRoller(unit)) {
+ double curX = unit.getX();
+ double curY = unit.getY();
+ double curZ = unit.getZ();
+
+ // Get or create a loop tracker for this unit.
+ LoopTracker tracker = loopTrackers.computeIfAbsent(unit, u -> new LoopTracker(curX, curY, curZ));
+
+ if (tracker.isSamePosition(curX, curY, curZ)) {
+ tracker.counter++;
+ // Compute threshold based on roller speed (max speed = 10).
+ int loopThreshold = Math.max(10, 3 + this.getRollerSpeed());
+ if (tracker.counter >= loopThreshold) {
+ LOGGER.warn("Loop detected for unit id " + unit.getId() +
+ " at position: x=" + curX + ", y=" + curY + ", z=" + curZ);
+ // Teleport unit back to the door tile.
+ RoomTile doorTile = this.getLayout().getDoorTile();
+ this.teleportRoomUnitToLocation(unit, doorTile.x, doorTile.y, doorTile.z);
+ tracker.counter = 0;
+ }
+ } else {
+ tracker.update(curX, curY, curZ);
+ }
+ }
if (!foundRightHolder[0]) {
foundRightHolder[0] = habbo.getRoomUnit().getRightsLevel() != RoomRightLevels.NONE;
}
@@ -1368,6 +1394,26 @@ public class Room implements Comparable, ISerialize, Runnable {
}
}
+ List rollers = new ArrayList<>(this.roomSpecialTypes.getRollers().values());
+
+ // Sort rollers using a custom comparator that uses a projection of the roller's position
+ rollers.sort((r1, r2) -> {
+ // Convert the roller's rotation into an angle in radians.
+ double angle1 = Math.toRadians(r1.getRotation() * 45);
+ double angle2 = Math.toRadians(r2.getRotation() * 45);
+
+ // Compute the movement vector for each roller (a unit vector in its direction)
+ double vx1 = Math.cos(angle1);
+ double vy1 = Math.sin(angle1);
+ double vx2 = Math.cos(angle2);
+ double vy2 = Math.sin(angle2);
+
+ // Calculate the projection of the roller's position along its movement vector
+ double proj1 = r1.getX() * vx1 + r1.getY() * vy1;
+ double proj2 = r2.getX() * vx2 + r2.getY() * vy2;
+
+ return Double.compare(proj1, proj2);
+ });
if (this.rollerSpeed != -1 && this.rollerCycle >= this.rollerSpeed) {
this.rollerCycle = 0;
@@ -4980,4 +5026,42 @@ public class Room implements Comparable, ISerialize, Runnable {
THashSet roomUnits = getRoomUnits();
return roomUnits.stream().filter(unit -> unit.getCurrentLocation() == tile).collect(Collectors.toSet());
}
-}
+
+ private static class LoopTracker {
+ double x, y, z;
+ int counter;
+
+ public LoopTracker(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.counter = 0;
+ }
+
+ public boolean isSamePosition(double newX, double newY, double newZ) {
+ // Use an epsilon for double comparison if necessary.
+ final double EPSILON = 0.001;
+ return Math.abs(newX - x) < EPSILON && Math.abs(newY - y) < EPSILON && Math.abs(newZ - z) < EPSILON;
+ }
+
+ public void update(double newX, double newY, double newZ) {
+ this.x = newX;
+ this.y = newY;
+ this.z = newZ;
+ this.counter = 0;
+ }
+ }
+
+ private boolean isOnRoller(RoomUnit unit) {
+ RoomTile currentTile = unit.getCurrentLocation();
+ // Iterate over all rollers in the room.
+ for (HabboItem roller : this.roomSpecialTypes.getRollers().values()) {
+ if (roller.getX() == currentTile.x && roller.getY() == currentTile.y) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final Map loopTrackers = new ConcurrentHashMap<>();
+}
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java
index a2a317f1..9c4a444b 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreManager.java
@@ -136,6 +136,19 @@ public class WiredHighscoreManager {
.collect(Collectors.toList());
}
+ if (scoreType == WiredHighscoreScoreType.LONGESTTIME) {
+ return highscores
+ .collect(Collectors.groupingBy(h -> h.getUsers().hashCode()))
+ .entrySet()
+ .stream()
+ .map(e -> e.getValue().stream()
+ .max(Comparator.comparingInt(WiredHighscoreRow::getValue))
+ .orElse(null))
+ .filter(Objects::nonNull)
+ .sorted(Comparator.comparingInt(WiredHighscoreRow::getValue).reversed())
+ .collect(Collectors.toList());
+ }
+
return null;
}
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreScoreType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreScoreType.java
index d70e07ed..d7f925a5 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreScoreType.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/highscores/WiredHighscoreScoreType.java
@@ -3,7 +3,8 @@ package com.eu.habbo.habbohotel.wired.highscores;
public enum WiredHighscoreScoreType {
PERTEAM(0),
MOSTWIN(1),
- CLASSIC(2);
+ CLASSIC(2),
+ LONGESTTIME(3);
public final int type;
diff --git a/Latest_Compiled_Version/Habbo-3.6.0-jar-with-dependencies.jar b/Latest_Compiled_Version/Habbo-3.6.0-jar-with-dependencies.jar
index f5eec3ae..ee98f69e 100644
Binary files a/Latest_Compiled_Version/Habbo-3.6.0-jar-with-dependencies.jar and b/Latest_Compiled_Version/Habbo-3.6.0-jar-with-dependencies.jar differ