The getCloneAccounts query had an operator-precedence bug:
WHERE ip_register = ? OR ip_current = ? AND id != ?
-- parsed as:
WHERE ip_register = ? OR (ip_current = ? AND id != ?)
So a user whose ip_register matched themselves would show up in their own clone list. Added parentheses:
WHERE (ip_register = ? OR ip_current = ?) AND id != ?
So please run the 006_HabboManager_fix before you run the update Emulator !
- add wf_xtra_text_output_furni_name and expand text placeholders for furni, users, bots and pets
- include room, entry, teleport and item metadata needed by the new :wired tools flow
- animate furniture position updates through the wired movement path
- fix teleport pair persistence/lookups for items_teleports with explicit column inserts
- Layout Cache (eliminates 1 DB query per room load)
Standard room models (model_a, model_b, etc.) are loaded once at startup and cached in memory
RoomLayout gets a new constructor from cached data instead of ResultSet
~99% of rooms use standard models, so this saves a DB round-trip on nearly every room load
- Better Parallel Pipeline (reduced critical path)
Before: layout → [items|rights|wordfilter] → heightmap → [bots|pets|wired]
After: layout → [items|rights|wordfilter|bots|pets] → [heightmap|wired]
Bots and pets only need layout for positioning, not items - so they now start immediately
Wired only needs items loaded (not heightmap) - so it now runs parallel with heightmap
- Deferred Promotion Query (faster Room instantiation)
Moved room_promotions DB query from constructor to loadDataInternal() as an async task
Room constructor now only runs bans query (needed for entry check)
Saves ~20ms per Room instantiation for promoted rooms
- Smart Heightmap (reduced tile iterations by 80-95%)
Instead of updating ALL tiles (1024 for 32x32 room), only updates tiles with items on them
Uses getTilesAt() for correct rotation-aware multi-tile coverage
For a room with 100 items on a 32x32 grid: ~200 tile updates instead of 1024
se this instead of this.client.getHabbo() since giveCredits() is already called on the Habbo instance itself. No need for the round-trip through the client.
- 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.
1. HabboManager - O(1) username lookup
Before: getHabbo(String) held a synchronized lock and iterated ALL online users every call
After: Secondary ConcurrentHashMap<String, Habbo> keyed by lowercase username → instant get() lookup, no lock contention
2. ItemsComponent - Batch DB saves
Before: dispose() spawned a separate thread per dirty item, each opening its own DB connection
After: Single connection, JDBC addBatch()/executeBatch() for both UPDATE and DELETE, flushed every 100 items. A user with 500 dirty items now does 5 batch executions instead of 500 thread spawns + 500 connections.
3. AcceptFriendRequestEvent - N+1 elimination
Before: For each offline user: query 1 (getOfflineHabboInfo by ID) → query 2 (load full Habbo by username) = 2 queries × up to 100 users = 200 queries
After: Single query by user ID directly = 1 query × up to 100 users = 100 queries (50% reduction)
4. RoomManager - Direct HashMap lookup
Before: getCategory(int id) iterated all categories checking getId() == id even though the map is already keyed by ID
After: Direct roomCategories.get(id) → O(1) instead of O(n)