Extends the snapshot pattern to the room's user list. The React client
currently has many widgets each calling `getUserDataByIndex(idx)` in a
loop (chat, doorbell, room player list, infostand …) — every render
walks the underlying Map and rebuilds an array. With
`getRoomUserListSnapshot(): ReadonlyArray<IRoomUserData>` consumers can
memoize on the array reference and only rebuild when something actually
changed.
Invalidation fires on every state-changing path:
- updateUserData (add/replace)
- removeUserData (leave)
- updateFigure / updateName / updateMotto / updateNickIcon /
updateCustomization / updateBackground / updateAchievementScore /
updatePetLevel / updatePetBreedingStatus
The inner IRoomUserData objects keep their existing in-place mutation
semantics (deep-clone would be too expensive for 30+ avatars on every
single status push). Consumers should treat each entry as a
snapshot-at-time-of-read and not retain references across an
invalidation.
New event: NitroEventType.ROOM_USER_LIST_UPDATED. Interface and event
additions are backwards-compatible; no existing accessors changed.
Also tidied: `updatePetLevel` now uses the explicit `if(!userData)
return;` guard pattern matching the rest of the methods (was a one-line
inline conditional).
Extends the v2.1.0 React-friendly snapshot pattern (originally on
SessionDataManager / RoomSessionManager) to two more session-state
holders the React client reads frequently:
- IgnoredUsersManager.getIgnoredUsersSnapshot(): ReadonlyArray<string>
- GroupInformationManager.getGroupBadgesSnapshot(): ReadonlyMap<number, string>
Both follow the same shape: lazy-frozen snapshot, cached until the
underlying state mutates, then invalidated and a dispatched event lets
the React client rebuild via useSyncExternalStore.
Two new NitroEventType members carry the invalidation signal:
- IGNORED_USERS_UPDATED — dispatched by IgnoredUsersManager whenever
the list changes (initial load, add, remove, queue-truncate case 2).
- GROUP_BADGES_UPDATED — dispatched by GroupInformationManager only
when the incoming HabboGroupBadges payload contains at least one
new or changed mapping (no-op refresh stays quiet).
This lets the user-info popup, profile page, friend/guild filtering,
and any other consumer share a single read through useSyncExternalStore
instead of each subscribing to the underlying message events
independently.
API additions are interface-respecting and backwards-compatible — the
existing `isIgnored(name)` / `getGroupBadge(groupId)` accessors stay
untouched.
Each workspace package was still pinning `typescript: ~5.5.x` or
`~5.8.2` in its own devDependencies even though the root bumped to 6.0.3
in 60b1143. The pins were dead (yarn 1 hoists from root) but they're
misleading when reading a single package.json. Bring them all to
`^6.0.3` to match the root.
Other:
- @thumbmarkjs/thumbmarkjs 1.8.1 → 1.9.0 (root + communication package)
- yarn.lock regenerated from a clean install (vitest 4 hoisting was
flaking on the patch vite bump; reverted vite to ^8.0.10)
Adds CLAUDE.md at the repo root: short project context for future
sessions — stack, the 12-workspace layout, the React-friendly v2.1.0
additions (`subscribe()`, `subscribeMessage()`, snapshot getters), build
scripts, and known gotchas (`SessionDataManager.getUserData` does NOT
exist; sendChat* expects 3 args; dispatchEvent is sync).