You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 15:36:17 +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.guilds.forums.ForumThread;
|
||||||
import com.eu.habbo.habbohotel.users.Habbo;
|
import com.eu.habbo.habbohotel.users.Habbo;
|
||||||
import com.eu.habbo.messages.incoming.friends.SearchUserEvent;
|
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.messages.outgoing.users.UserDataComposer;
|
||||||
import com.eu.habbo.threading.runnables.AchievementUpdater;
|
import com.eu.habbo.threading.runnables.AchievementUpdater;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -101,8 +100,7 @@ public class CleanerThread implements Runnable {
|
|||||||
LAST_HABBO_CACHE_CLEARED = time;
|
LAST_HABBO_CACHE_CLEARED = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchRoomsEvent.cachedResults.clear();
|
SearchUserEvent.cleanExpiredCache();
|
||||||
SearchUserEvent.cachedResults.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
|||||||
private String password;
|
private String password;
|
||||||
private RoomState state;
|
private RoomState state;
|
||||||
private int usersMax;
|
private int usersMax;
|
||||||
private volatile int score;
|
private int score;
|
||||||
private volatile int category;
|
private int category;
|
||||||
private String floorPaint;
|
private String floorPaint;
|
||||||
private String wallPaint;
|
private String wallPaint;
|
||||||
private String backgroundPaint;
|
private String backgroundPaint;
|
||||||
@@ -140,37 +140,37 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
|
|||||||
private int floorSize;
|
private int floorSize;
|
||||||
private int guild;
|
private int guild;
|
||||||
private String tags;
|
private String tags;
|
||||||
private volatile boolean publicRoom;
|
private boolean publicRoom;
|
||||||
private volatile boolean staffPromotedRoom;
|
private boolean staffPromotedRoom;
|
||||||
private volatile boolean allowPets;
|
private boolean allowPets;
|
||||||
private volatile boolean allowPetsEat;
|
private boolean allowPetsEat;
|
||||||
private volatile boolean allowWalkthrough;
|
private boolean allowWalkthrough;
|
||||||
private volatile boolean allowBotsWalk;
|
private boolean allowBotsWalk;
|
||||||
private volatile boolean allowEffects;
|
private boolean allowEffects;
|
||||||
private volatile boolean hideWall;
|
private boolean hideWall;
|
||||||
private volatile int chatMode;
|
private int chatMode;
|
||||||
private volatile int chatWeight;
|
private int chatWeight;
|
||||||
private volatile int chatSpeed;
|
private int chatSpeed;
|
||||||
private volatile int chatDistance;
|
private int chatDistance;
|
||||||
private volatile int chatProtection;
|
private int chatProtection;
|
||||||
private volatile int muteOption;
|
private int muteOption;
|
||||||
private volatile int kickOption;
|
private int kickOption;
|
||||||
private volatile int banOption;
|
private int banOption;
|
||||||
private volatile int pollId;
|
private int pollId;
|
||||||
private volatile boolean promoted;
|
private boolean promoted;
|
||||||
private volatile int tradeMode;
|
private int tradeMode;
|
||||||
private volatile boolean moveDiagonally;
|
private boolean moveDiagonally;
|
||||||
private volatile boolean allowUnderpass;
|
private boolean allowUnderpass;
|
||||||
private volatile boolean jukeboxActive;
|
private boolean jukeboxActive;
|
||||||
private volatile boolean hideWired;
|
private boolean hideWired;
|
||||||
private RoomPromotion promotion;
|
private RoomPromotion promotion;
|
||||||
private volatile boolean needsUpdate;
|
private volatile boolean needsUpdate;
|
||||||
private volatile boolean loaded;
|
private volatile boolean loaded;
|
||||||
private volatile boolean preLoaded;
|
private volatile boolean preLoaded;
|
||||||
private volatile boolean loadingInProgress;
|
private volatile boolean loadingInProgress;
|
||||||
private volatile CompletableFuture<Void> loadingFuture;
|
private volatile CompletableFuture<Void> loadingFuture;
|
||||||
private volatile int rollerSpeed;
|
private int rollerSpeed;
|
||||||
private volatile int lastTimerReset = Emulator.getIntUnixTimestamp();
|
private int lastTimerReset = Emulator.getIntUnixTimestamp();
|
||||||
private volatile boolean muted;
|
private volatile boolean muted;
|
||||||
private RoomSpecialTypes roomSpecialTypes;
|
private RoomSpecialTypes roomSpecialTypes;
|
||||||
private TraxManager traxManager;
|
private TraxManager traxManager;
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public class RoomManager {
|
|||||||
private final THashMap<Integer, RoomCategory> roomCategories;
|
private final THashMap<Integer, RoomCategory> roomCategories;
|
||||||
private final List<String> mapNames;
|
private final List<String> mapNames;
|
||||||
private final ConcurrentHashMap<Integer, Room> activeRooms;
|
private final ConcurrentHashMap<Integer, Room> activeRooms;
|
||||||
|
private final ConcurrentHashMap<Integer, Set<Integer>> roomsByOwner;
|
||||||
private final ArrayList<Class<? extends Game>> gameTypes;
|
private final ArrayList<Class<? extends Game>> gameTypes;
|
||||||
|
|
||||||
public RoomManager() {
|
public RoomManager() {
|
||||||
@@ -79,6 +80,7 @@ public class RoomManager {
|
|||||||
this.roomCategories = new THashMap<>();
|
this.roomCategories = new THashMap<>();
|
||||||
this.mapNames = new ArrayList<>();
|
this.mapNames = new ArrayList<>();
|
||||||
this.activeRooms = new ConcurrentHashMap<>();
|
this.activeRooms = new ConcurrentHashMap<>();
|
||||||
|
this.roomsByOwner = new ConcurrentHashMap<>();
|
||||||
this.loadRoomCategories();
|
this.loadRoomCategories();
|
||||||
this.loadRoomModels();
|
this.loadRoomModels();
|
||||||
|
|
||||||
@@ -95,6 +97,20 @@ public class RoomManager {
|
|||||||
LOGGER.info("Room Manager -> Loaded! ({} MS)", System.currentTimeMillis() - millis);
|
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() {
|
public void loadRoomModels() {
|
||||||
this.mapNames.clear();
|
this.mapNames.clear();
|
||||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM room_models")) {
|
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 room = new Room(set);
|
||||||
room.preventUncaching = true;
|
room.preventUncaching = true;
|
||||||
this.activeRooms.put(set.getInt("id"), room);
|
this.activeRooms.put(set.getInt("id"), room);
|
||||||
|
this.trackRoomOwner(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -162,6 +179,7 @@ public class RoomManager {
|
|||||||
if (room == null) {
|
if (room == null) {
|
||||||
room = new Room(set);
|
room = new Room(set);
|
||||||
this.activeRooms.put(set.getInt("id"), room);
|
this.activeRooms.put(set.getInt("id"), room);
|
||||||
|
this.trackRoomOwner(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rooms.containsKey(set.getInt("category"))) {
|
if (!rooms.containsKey(set.getInt("category"))) {
|
||||||
@@ -321,6 +339,7 @@ public class RoomManager {
|
|||||||
|
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
this.activeRooms.put(room.getId(), room);
|
this.activeRooms.put(room.getId(), room);
|
||||||
|
this.trackRoomOwner(room);
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOGGER.error("Caught SQL exception", e);
|
LOGGER.error("Caught SQL exception", e);
|
||||||
@@ -368,8 +387,11 @@ public class RoomManager {
|
|||||||
statement.setInt(1, habbo.getHabboInfo().getId());
|
statement.setInt(1, habbo.getHabboInfo().getId());
|
||||||
try (ResultSet set = statement.executeQuery()) {
|
try (ResultSet set = statement.executeQuery()) {
|
||||||
while (set.next()) {
|
while (set.next()) {
|
||||||
if (!this.activeRooms.containsKey(set.getInt("id")))
|
if (!this.activeRooms.containsKey(set.getInt("id"))) {
|
||||||
this.activeRooms.put(set.getInt("id"), new Room(set));
|
Room room = new Room(set);
|
||||||
|
this.activeRooms.put(room.getId(), room);
|
||||||
|
this.trackRoomOwner(room);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -390,24 +412,33 @@ public class RoomManager {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
room.dispose();
|
room.dispose();
|
||||||
|
this.untrackRoomOwner(room);
|
||||||
this.activeRooms.remove(room.getId());
|
this.activeRooms.remove(room.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearInactiveRooms() {
|
public void clearInactiveRooms() {
|
||||||
THashSet<Room> roomsToDispose = new THashSet<>();
|
THashSet<Room> roomsToDispose = new THashSet<>();
|
||||||
for (Room room : this.activeRooms.values()) {
|
for (Map.Entry<Integer, Set<Integer>> entry : this.roomsByOwner.entrySet()) {
|
||||||
if (!room.isPublicRoom() && !room.isStaffPromotedRoom() && !Emulator.getGameServer().getGameClientManager().containsHabbo(room.getOwnerId()) && room.isPreLoaded()) {
|
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);
|
roomsToDispose.add(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (Room room : roomsToDispose) {
|
for (Room room : roomsToDispose) {
|
||||||
room.dispose();
|
room.dispose();
|
||||||
if (room.getUserCount() == 0)
|
if (room.getUserCount() == 0) {
|
||||||
|
this.untrackRoomOwner(room);
|
||||||
this.activeRooms.remove(room.getId());
|
this.activeRooms.remove(room.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean layoutExists(String name) {
|
public boolean layoutExists(String name) {
|
||||||
return this.mapNames.contains(name);
|
return this.mapNames.contains(name);
|
||||||
@@ -434,6 +465,7 @@ public class RoomManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void uncacheRoom(Room room) {
|
public void uncacheRoom(Room room) {
|
||||||
|
this.untrackRoomOwner(room);
|
||||||
this.activeRooms.remove(room.getId());
|
this.activeRooms.remove(room.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1125,6 +1157,7 @@ public class RoomManager {
|
|||||||
Room r = new Room(set);
|
Room r = new Room(set);
|
||||||
rooms.add(r);
|
rooms.add(r);
|
||||||
this.activeRooms.put(r.getId(), r);
|
this.activeRooms.put(r.getId(), r);
|
||||||
|
this.trackRoomOwner(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -1185,6 +1218,7 @@ public class RoomManager {
|
|||||||
rooms.add(r);
|
rooms.add(r);
|
||||||
|
|
||||||
this.activeRooms.put(r.getId(), r);
|
this.activeRooms.put(r.getId(), r);
|
||||||
|
this.trackRoomOwner(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -1248,6 +1282,7 @@ public class RoomManager {
|
|||||||
room = new Room(set);
|
room = new Room(set);
|
||||||
|
|
||||||
this.activeRooms.put(room.getId(), room);
|
this.activeRooms.put(room.getId(), room);
|
||||||
|
this.trackRoomOwner(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
rooms.add(room);
|
rooms.add(room);
|
||||||
@@ -1489,6 +1524,7 @@ public class RoomManager {
|
|||||||
room.dispose();
|
room.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.roomsByOwner.clear();
|
||||||
this.activeRooms.clear();
|
this.activeRooms.clear();
|
||||||
|
|
||||||
LOGGER.info("Room Manager -> Disposed!");
|
LOGGER.info("Room Manager -> Disposed!");
|
||||||
|
|||||||
@@ -9,7 +9,20 @@ import gnu.trove.set.hash.THashSet;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class SearchUserEvent extends MessageHandler {
|
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
|
@Override
|
||||||
public void handle() throws Exception {
|
public void handle() throws Exception {
|
||||||
@@ -31,6 +44,7 @@ public class SearchUserEvent extends MessageHandler {
|
|||||||
if (buddies == null) {
|
if (buddies == null) {
|
||||||
buddies = Messenger.searchUsers(username);
|
buddies = Messenger.searchUsers(username);
|
||||||
cachedResults.put(username, buddies);
|
cachedResults.put(username, buddies);
|
||||||
|
cacheTimestamps.put(username, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.client.sendResponse(new UserSearchResultComposer(buddies, this.client.getHabbo().getMessenger().getFriends(username), this.client.getHabbo()));
|
this.client.sendResponse(new UserSearchResultComposer(buddies, this.client.getHabbo().getMessenger().getFriends(username), this.client.getHabbo()));
|
||||||
|
|||||||
Reference in New Issue
Block a user