You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 23:36:19 +00:00
🆙 Optimization for the gameserver
- Room Cleanup Optimization (RoomManager.java) Added roomsByOwner ConcurrentHashMap that tracks which rooms belong to which owner clearInactiveRooms() now iterates unique owners instead of ALL rooms Went from O(rooms × clients) to O(unique_owners × clients) every 120s - Volatile Fields (Room.java) Removed volatile from 27 room config fields (score, category, chatMode, allowPets, etc.) Kept volatile only on 8 fields that genuinely need cross-thread visibility (loaded, preLoaded, needsUpdate, muted, etc.) Reduces CPU cache line invalidation on every room cycle tick - Search Cache TTL (SearchUserEvent.java + CleanerThread.java) SearchUserEvent now has 30-second TTL per entry instead of full wipe every 10s SearchRoomsEvent already had LRU eviction (max 200) — removed redundant .clear() call Frequently searched users stay cached, only stale entries get cleaned - scheduledComposers/scheduledTasks — After reading the code, these are actually already handled correctly: processScheduledTasks() swaps the set with a fresh one before processing, and processScheduledComposers() calls .clear() after sending. No leak risk.
This commit is contained in:
@@ -4,7 +4,6 @@ import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.guilds.forums.ForumThread;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.eu.habbo.messages.incoming.friends.SearchUserEvent;
|
||||
import com.eu.habbo.messages.incoming.navigator.SearchRoomsEvent;
|
||||
import com.eu.habbo.messages.outgoing.users.UserDataComposer;
|
||||
import com.eu.habbo.threading.runnables.AchievementUpdater;
|
||||
import org.slf4j.Logger;
|
||||
@@ -101,8 +100,7 @@ public class CleanerThread implements Runnable {
|
||||
LAST_HABBO_CACHE_CLEARED = time;
|
||||
}
|
||||
|
||||
SearchRoomsEvent.cachedResults.clear();
|
||||
SearchUserEvent.cachedResults.clear();
|
||||
SearchUserEvent.cleanExpiredCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -130,8 +130,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
private String password;
|
||||
private RoomState state;
|
||||
private int usersMax;
|
||||
private volatile int score;
|
||||
private volatile int category;
|
||||
private int score;
|
||||
private int category;
|
||||
private String floorPaint;
|
||||
private String wallPaint;
|
||||
private String backgroundPaint;
|
||||
@@ -140,37 +140,37 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
||||
private int floorSize;
|
||||
private int guild;
|
||||
private String tags;
|
||||
private volatile boolean publicRoom;
|
||||
private volatile boolean staffPromotedRoom;
|
||||
private volatile boolean allowPets;
|
||||
private volatile boolean allowPetsEat;
|
||||
private volatile boolean allowWalkthrough;
|
||||
private volatile boolean allowBotsWalk;
|
||||
private volatile boolean allowEffects;
|
||||
private volatile boolean hideWall;
|
||||
private volatile int chatMode;
|
||||
private volatile int chatWeight;
|
||||
private volatile int chatSpeed;
|
||||
private volatile int chatDistance;
|
||||
private volatile int chatProtection;
|
||||
private volatile int muteOption;
|
||||
private volatile int kickOption;
|
||||
private volatile int banOption;
|
||||
private volatile int pollId;
|
||||
private volatile boolean promoted;
|
||||
private volatile int tradeMode;
|
||||
private volatile boolean moveDiagonally;
|
||||
private volatile boolean allowUnderpass;
|
||||
private volatile boolean jukeboxActive;
|
||||
private volatile boolean hideWired;
|
||||
private boolean publicRoom;
|
||||
private boolean staffPromotedRoom;
|
||||
private boolean allowPets;
|
||||
private boolean allowPetsEat;
|
||||
private boolean allowWalkthrough;
|
||||
private boolean allowBotsWalk;
|
||||
private boolean allowEffects;
|
||||
private boolean hideWall;
|
||||
private int chatMode;
|
||||
private int chatWeight;
|
||||
private int chatSpeed;
|
||||
private int chatDistance;
|
||||
private int chatProtection;
|
||||
private int muteOption;
|
||||
private int kickOption;
|
||||
private int banOption;
|
||||
private int pollId;
|
||||
private boolean promoted;
|
||||
private int tradeMode;
|
||||
private boolean moveDiagonally;
|
||||
private boolean allowUnderpass;
|
||||
private boolean jukeboxActive;
|
||||
private boolean hideWired;
|
||||
private RoomPromotion promotion;
|
||||
private volatile boolean needsUpdate;
|
||||
private volatile boolean loaded;
|
||||
private volatile boolean preLoaded;
|
||||
private volatile boolean loadingInProgress;
|
||||
private volatile CompletableFuture<Void> loadingFuture;
|
||||
private volatile int rollerSpeed;
|
||||
private volatile int lastTimerReset = Emulator.getIntUnixTimestamp();
|
||||
private int rollerSpeed;
|
||||
private int lastTimerReset = Emulator.getIntUnixTimestamp();
|
||||
private volatile boolean muted;
|
||||
private RoomSpecialTypes roomSpecialTypes;
|
||||
private TraxManager traxManager;
|
||||
|
||||
@@ -72,6 +72,7 @@ public class RoomManager {
|
||||
private final THashMap<Integer, RoomCategory> roomCategories;
|
||||
private final List<String> mapNames;
|
||||
private final ConcurrentHashMap<Integer, Room> activeRooms;
|
||||
private final ConcurrentHashMap<Integer, Set<Integer>> roomsByOwner;
|
||||
private final ArrayList<Class<? extends Game>> gameTypes;
|
||||
|
||||
public RoomManager() {
|
||||
@@ -79,6 +80,7 @@ public class RoomManager {
|
||||
this.roomCategories = new THashMap<>();
|
||||
this.mapNames = new ArrayList<>();
|
||||
this.activeRooms = new ConcurrentHashMap<>();
|
||||
this.roomsByOwner = new ConcurrentHashMap<>();
|
||||
this.loadRoomCategories();
|
||||
this.loadRoomModels();
|
||||
|
||||
@@ -95,6 +97,20 @@ public class RoomManager {
|
||||
LOGGER.info("Room Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis);
|
||||
}
|
||||
|
||||
private void trackRoomOwner(Room room) {
|
||||
this.roomsByOwner.computeIfAbsent(room.getOwnerId(), k -> ConcurrentHashMap.newKeySet()).add(room.getId());
|
||||
}
|
||||
|
||||
private void untrackRoomOwner(Room room) {
|
||||
Set<Integer> rooms = this.roomsByOwner.get(room.getOwnerId());
|
||||
if (rooms != null) {
|
||||
rooms.remove(room.getId());
|
||||
if (rooms.isEmpty()) {
|
||||
this.roomsByOwner.remove(room.getOwnerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadRoomModels() {
|
||||
this.mapNames.clear();
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM room_models")) {
|
||||
@@ -143,6 +159,7 @@ public class RoomManager {
|
||||
Room room = new Room(set);
|
||||
room.preventUncaching = true;
|
||||
this.activeRooms.put(set.getInt("id"), room);
|
||||
this.trackRoomOwner(room);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -162,6 +179,7 @@ public class RoomManager {
|
||||
if (room == null) {
|
||||
room = new Room(set);
|
||||
this.activeRooms.put(set.getInt("id"), room);
|
||||
this.trackRoomOwner(room);
|
||||
}
|
||||
|
||||
if (!rooms.containsKey(set.getInt("category"))) {
|
||||
@@ -321,6 +339,7 @@ public class RoomManager {
|
||||
|
||||
if (room != null) {
|
||||
this.activeRooms.put(room.getId(), room);
|
||||
this.trackRoomOwner(room);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
@@ -368,8 +387,11 @@ public class RoomManager {
|
||||
statement.setInt(1, habbo.getHabboInfo().getId());
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
if (!this.activeRooms.containsKey(set.getInt("id")))
|
||||
this.activeRooms.put(set.getInt("id"), new Room(set));
|
||||
if (!this.activeRooms.containsKey(set.getInt("id"))) {
|
||||
Room room = new Room(set);
|
||||
this.activeRooms.put(room.getId(), room);
|
||||
this.trackRoomOwner(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -390,24 +412,33 @@ public class RoomManager {
|
||||
continue;
|
||||
|
||||
room.dispose();
|
||||
this.untrackRoomOwner(room);
|
||||
this.activeRooms.remove(room.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void clearInactiveRooms() {
|
||||
THashSet<Room> roomsToDispose = new THashSet<>();
|
||||
for (Room room : this.activeRooms.values()) {
|
||||
if (!room.isPublicRoom() && !room.isStaffPromotedRoom() && !Emulator.getGameServer().getGameClientManager().containsHabbo(room.getOwnerId()) && room.isPreLoaded()) {
|
||||
for (Map.Entry<Integer, Set<Integer>> entry : this.roomsByOwner.entrySet()) {
|
||||
int ownerId = entry.getKey();
|
||||
if (!Emulator.getGameServer().getGameClientManager().containsHabbo(ownerId)) {
|
||||
for (int roomId : entry.getValue()) {
|
||||
Room room = this.activeRooms.get(roomId);
|
||||
if (room != null && !room.isPublicRoom() && !room.isStaffPromotedRoom() && room.isPreLoaded()) {
|
||||
roomsToDispose.add(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Room room : roomsToDispose) {
|
||||
room.dispose();
|
||||
if (room.getUserCount() == 0)
|
||||
if (room.getUserCount() == 0) {
|
||||
this.untrackRoomOwner(room);
|
||||
this.activeRooms.remove(room.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean layoutExists(String name) {
|
||||
return this.mapNames.contains(name);
|
||||
@@ -434,6 +465,7 @@ public class RoomManager {
|
||||
}
|
||||
|
||||
public void uncacheRoom(Room room) {
|
||||
this.untrackRoomOwner(room);
|
||||
this.activeRooms.remove(room.getId());
|
||||
}
|
||||
|
||||
@@ -1125,6 +1157,7 @@ public class RoomManager {
|
||||
Room r = new Room(set);
|
||||
rooms.add(r);
|
||||
this.activeRooms.put(r.getId(), r);
|
||||
this.trackRoomOwner(r);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -1185,6 +1218,7 @@ public class RoomManager {
|
||||
rooms.add(r);
|
||||
|
||||
this.activeRooms.put(r.getId(), r);
|
||||
this.trackRoomOwner(r);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -1248,6 +1282,7 @@ public class RoomManager {
|
||||
room = new Room(set);
|
||||
|
||||
this.activeRooms.put(room.getId(), room);
|
||||
this.trackRoomOwner(room);
|
||||
}
|
||||
|
||||
rooms.add(room);
|
||||
@@ -1489,6 +1524,7 @@ public class RoomManager {
|
||||
room.dispose();
|
||||
}
|
||||
|
||||
this.roomsByOwner.clear();
|
||||
this.activeRooms.clear();
|
||||
|
||||
LOGGER.info("Room Manager -> Disposed!");
|
||||
|
||||
@@ -9,7 +9,20 @@ import gnu.trove.set.hash.THashSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SearchUserEvent extends MessageHandler {
|
||||
public static ConcurrentHashMap<String, THashSet<MessengerBuddy>> cachedResults = new ConcurrentHashMap<>();
|
||||
private static final long CACHE_TTL_MS = 30_000; // 30 second TTL
|
||||
private static final ConcurrentHashMap<String, Long> cacheTimestamps = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentHashMap<String, THashSet<MessengerBuddy>> cachedResults = new ConcurrentHashMap<>();
|
||||
|
||||
public static void cleanExpiredCache() {
|
||||
long now = System.currentTimeMillis();
|
||||
cacheTimestamps.entrySet().removeIf(entry -> {
|
||||
if (now - entry.getValue() > CACHE_TTL_MS) {
|
||||
cachedResults.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() throws Exception {
|
||||
@@ -31,6 +44,7 @@ public class SearchUserEvent extends MessageHandler {
|
||||
if (buddies == null) {
|
||||
buddies = Messenger.searchUsers(username);
|
||||
cachedResults.put(username, buddies);
|
||||
cacheTimestamps.put(username, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
this.client.sendResponse(new UserSearchResultComposer(buddies, this.client.getHabbo().getMessenger().getFriends(username), this.client.getHabbo()));
|
||||
|
||||
Reference in New Issue
Block a user