From 842d8407e895ebc17ab1fed6848c39e93d506aab Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 20:13:08 +0200 Subject: [PATCH 1/6] =?UTF-8?q?docs(furni):=20spec=20=E2=80=94=20server-au?= =?UTF-8?q?thoritative=20furni=20names/descriptions=20from=20JSON=20+=20li?= =?UTF-8?q?ve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design for sourcing furni display names from furnidata JSON (DB keeps technical data), with a live delta-broadcast pipeline (emulator file-watch -> renderer patch -> client refresh) and a security hardening section. Cross-repo reference copy. --- ...-04-furni-names-from-json-server-design.md | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-04-furni-names-from-json-server-design.md diff --git a/docs/superpowers/specs/2026-06-04-furni-names-from-json-server-design.md b/docs/superpowers/specs/2026-06-04-furni-names-from-json-server-design.md new file mode 100644 index 0000000..dfe2e3e --- /dev/null +++ b/docs/superpowers/specs/2026-06-04-furni-names-from-json-server-design.md @@ -0,0 +1,264 @@ +# Furni names from JSON (server-authoritative) — Design + +- **Date:** 2026-06-04 +- **Status:** Draft for review +- **Scope:** Cross-repo — Arcturus (emulator), Nitro_Render_V3 (renderer), Nitro-V3 (client) +- **Out of scope:** furni-editor feature/packets, NitroV3-Housekeeping (CMS), server-side multi-language, description rendering in the infostand. + +## 1. Problem & motivation + +Today a furni's display name lives in **two independent places** that drift apart: + +- **DB** — `items_base.public_name` (`Item.fullName`), used by the emulator. +- **furnidata JSON** — used by the client (the client already resolves all visible furni + names/descriptions from furnidata, keyed by classname). + +This forces admins to maintain names twice and causes mismatches. We want **one source of +truth**: the **furnidata JSON owns display names & descriptions**, the **DB owns technical +data**. Editing furnidata should reflect everywhere — server-pronounced strings and every +connected client — **live**, with no DB edit and no restart. + +This is a single, unified refactor whose payoff is admin furni management: one place to edit, +consistent everywhere. + +## 2. Source-of-truth contract + +| Concern | Owner | Storage | Read by | +|---|---|---|---| +| `classname` (`item_name` / `Item.name`) | **DB** | `items_base.item_name` | join key → furnidata **and** `.nitro` asset; `isPet/isBot`; wired `wf_` fallback | +| technical data (dimensions, `stateCount`, flags, interaction, effects) | **DB** | `items_base.*` | emulator simulation | +| **display name** | **JSON** | furnidata (per classname) | emulator (`getDisplayName`) + client (furnidata, unchanged) | +| **description** | **JSON** | furnidata (per classname) | client only (catalog) — **no server consumer** | + +Invariants: + +1. The **bridge is `classname`**, not a numeric id. `Item.name` ↔ furnidata `classname`. +2. `public_name` (`Item.fullName`) is **NOT removed**: it remains (a) the fallback when a + classname is missing from furnidata, and (b) the technical token for wired furni + (`Item.java:107-116` reads `fullName.startsWith("wf_")`). No schema migration. No DROP. +3. There is **no `description` column** in `items_base`; description is JSON-only and has no + server consumer → the emulator gets **no** `getDescription()`. +4. **One furnidata artifact** is shared truth: the file the emulator indexes must be the same + furnidata the client loads (deploy invariant, §7). +5. Server emits names in the **base locale** of the furnidata file. Player-facing multi-language + stays a client localization-layer concern (unchanged). + +## 3. Architecture — two independent pieces + +The refactor is two pieces that share only the furnidata file and one new packet. They do not +depend on each other. + +- **Piece 1 — Server-authoritative names.** The emulator's pronounced names come from furnidata. +- **Piece 2 — Liveness via delta.** When the furnidata file changes, connected clients (and the + server index) update without reconnecting, via a minimal delta broadcast. + +## 4. Piece 1 — Emulator (server-authoritative names) + +### 4.1 `FurnidataReader` (new, package `com.eu.habbo.habbohotel.items`) + +A neutral, shared reader extracted so the editor is **not touched**. Responsibilities: + +- Resolve the furnidata source reusing the **same already-configured** path as the editor: + `furni.editor.renderer.config.path` → `furnidata.url` → `furni.editor.asset.base.path` + (see `FurniDataManager.resolveSource()` for the exact resolution we mirror). Default to those + values so admins configure **once**. +- Support both layouts the editor already supports: **single file** (`FurnitureData.json`) and + **split-tier directory** (`core/custom/seasonal`, `manifest.json5`, JSON5 with comments; + later tiers override earlier). Reuse the JSON5 strip logic (extract to the shared reader). +- Parse `roomitemtypes` (floor) and `wallitemtypes` (wall) → return a flat list of + `FurnidataEntry { int id, String classname, FurnitureType type, String name, String description }`. + +**Security requirements on the reader (furnidata is untrusted input):** + +- **Path-traversal guard.** When resolving split-tier manifest entries + (`tiers[]`, `files[]`) via `dir.resolve(name)`, normalize the result and **reject any path that + escapes the configured base dir** (absolute paths, `..`). The existing `FurniDataManager` lacks + this guard — the shared reader MUST add it (do not propagate the gap). +- **Size cap.** Refuse to load a furnidata file/dir above a configurable max (default e.g. 64 MB) + to bound parse cost. +- **Sanitization at the boundary.** Every `name`/`description` is sanitized on load: + truncate to **256 chars**, strip control characters and newlines, and **neutralize `%` tokens** + (so they cannot inject into `String.replace` placeholder chains, server- or wired-side). + Normal text/emoji/non-latin scripts pass through. +- **Fail-safe.** Any IO/parse error is caught and logged; the provider keeps the **last-good + index** (or empty on first load) and never throws — boot must not crash on a bad furnidata. + +### 4.2 `FurnitureTextProvider` (new, package `items`) + +- Holds `volatile Map`. +- `reindex()`: read via `FurnidataReader` → build a new immutable map → compute delta vs the + previous map (§5) → atomically swap the reference → return the delta. +- Initialized in `GameEnvironment.load` near `ItemManager`. Resolution is **lazy**, so boot order + is not critical and `Item` objects do not depend on the provider at load time. +- Toggle `items.furnidata.names.enabled` (default `true`). When `false`, `getDisplayName()` + returns the DB value (instant rollback, no recompile). + +### 4.3 `Item.getDisplayName()` + +``` +String getDisplayName(): + if !enabled: return fullName + FurniText t = FurnitureTextProvider.get(this.name /* classname, lowercased */) + return (t != null && t.name not blank) ? t.name : this.fullName // never null +``` + +No `getDescription()` on the server (no consumer). + +### 4.4 Swap list (exhaustive — verified) + +Replace `item.getFullName()` → `item.getDisplayName()` at exactly these 6 sites: + +| Site | Context | +|---|---| +| `CatalogBuyItemAsGiftEvent.java:251` | LTD daily-total alert (gift) | +| `CatalogBuyItemAsGiftEvent.java:262` | LTD daily-item alert (gift) | +| `CatalogManager.java:1057` | LTD daily-total alert (buy) | +| `CatalogManager.java:1063` | LTD daily-item alert (buy) | +| `WiredTextPlaceholderUtil.java:282` | wired `%furni.name%` (keep existing `getName()` ultimate fallback) | +| `WatchAndEarnRewardComposer.java:21` | `appendString(...)` — sends name in a packet | + +**Do NOT change** (technical, use `item_name`/classname): `PresentItemOpenedComposer:24`, +`GiftCommand:72`, `SendGift:82`, `SellItemEvent:37,45`, `CloseDiceEvent:34`, `isPet/isBot`, and the +wired `wf_` fallback in `Item.load`. The catalog offer/page serialization sends **no** display +name (`CatalogItem` serializes `catalog_name` + sprite only) — confirmed, nothing to change there. + +## 5. Piece 2 — Liveness via delta + +### 5.1 Server: file watcher + diff + broadcast + +- A `WatchService` watches the resolved furnidata location on a **single, serialized watcher + thread** (so reindex never races itself). For the **split-tier** layout, register the base dir + and each tier dir. **Debounce** (~750 ms) to coalesce burst writes, plus a **minimum interval + between broadcasts** (e.g. ≥5 s) to cap amplification. +- On settle → `FurnitureTextProvider.reindex()` → diff old vs new **by classname**: + - **added** (new classname) and **changed** (name **or** description differs) → included. + - **removed** classnames → **ignored** (rare; resolved on client reconnect). +- Broadcast decision (anti-DoS): + - delta empty → no broadcast. + - delta size ≤ **cap** (e.g. 500 entries) → broadcast `FurnitureDataReload` in **delta mode**. + - delta size > cap (mass replace) → broadcast in **reload-hint mode** (compact signal; clients + re-load furnidata at next opportunity) instead of a giant per-client payload. +- The broadcast is triggered **only** by the file watcher — there is **no client-initiated reload + path**. This is a security property to preserve (clients cannot induce reindex/broadcast). + +### 5.2 Wire contract — new packet `FurnitureDataReload` + +- **Composer (Arcturus):** `FurnitureDataReloadComposer`, new dedicated header id (pick a free id; + document on both sides). Two modes: + ``` + int mode // 0 = delta, 1 = reload-hint + // mode == 0 (delta): + int count // bounded by the server cap; the client MUST also bound it on read + count × { + string type // "S" (floor) | "I" (wall) + int id // furnidata numeric id (for localization-key + FurnitureData lookup) + string classname + string name // already sanitized server-side + string description + } + // mode == 1 (reload-hint): no further fields (optionally an int revision for cache-busting) + ``` +- **Parser/Event (renderer):** `FurnitureDataReloadEvent` + `FurnitureDataReloadParser` reading the + same shape. The parser **bounds `count`** (reject/clamp absurd values) and tolerates truncation + (`bytesAvailable` pattern) so a malformed/MITM payload cannot allocate unbounded memory. + Registered in `SessionDataManager.init()` via + `GetCommunication().registerMessageEvent(new FurnitureDataReloadEvent(...))` (same pattern as the + existing `FurniDataUpdatedEvent` registration, but a **distinct** handler). + +### 5.3 Renderer: separate patch path (no editor reuse) + +- New method, e.g. `SessionDataManager.applyFurnidataDelta(entries)` — **distinct** from the + editor's `applyLiveFurnitureNameUpdate(...)` (`SessionDataManager.ts:84`), which we leave intact. +- **Delta mode (0):** for each entry, patch the corresponding `FurnitureData` (floor/wall, by `id`) + — update `_localizedName` and `_description` — and re-register the localization keys + `roomItem.name/desc.{id}` / `wallItem.name/desc.{id}` (mirrors `FurnitureDataLoader:105-110`). +- **Reload-hint mode (1):** re-run the furnidata load (`FurnitureDataLoader`, re-fetching + `furnidata.url` with cache-bust) — the appropriate response to a mass change. +- In both modes, after the batch dispatch the window event **once**: + `window.dispatchEvent(new CustomEvent('nitro-localization-updated'))`. + +### 5.4 Client: zero changes + +All three furni surfaces already subscribe to `nitro-localization-updated` and re-derive: + +- catalog — `useCatalog.ts:919` +- inventory — `useInventoryFurni.ts:137` (→ `refreshGroupItemsLocalization`) +- infostand — `useAvatarInfoWidget.ts:425` (→ `getFurniInfo`, which reads `furnitureData.name`) + +No Nitro-V3 edits are required for Piece 2. + +## 6. Admin-facing outcome + +Edit one place — the **furnidata JSON** — and display names update **live** across: +server-pronounced strings (catalog LTD alerts, wired `%furni.name%`, Watch&Earn), and every +connected client's catalog, inventory, and furni infostand. No DB edit, no restart, no double +maintenance. + +## 7. Constraints, risks, invariants + +1. **Locale no-clobber.** If per-locale furni text override files are in use (they override + `roomItem.name.{id}` after furnidata load), a live delta that re-registers base names would + revert overridden ids to base. Mitigation options for the plan: re-apply active overrides after + the delta, or skip the localization-key patch for ids with an active override (still patch the + `FurnitureData` object). For single-furnidata setups (typical retro) there is no override and no + issue. **Document the limitation.** +2. **Deploy invariant.** `furni.editor.asset.base.path`/`furnidata.url` (what the emulator watches) + and the furnidata the client loaded must be the **same artifact**, else the server delta + references entries the client doesn't have. +3. **`public_name` fallback.** Wired `wf_` items absent from furnidata would show the raw `wf_…` + token as their display name (internal/invisible furni — acceptable). +4. **Split-layout watcher.** The watcher must register all tier dirs; missing a tier dir means live + updates from that tier are not detected (resolved on reconnect). +5. **Performance.** `getDisplayName()` is a single `HashMap` lookup on cold paths (catalog alerts, + wired text, Watch&Earn) — negligible. + +## 8. Security + +With this refactor the **furnidata becomes a security-relevant input**: its strings now flow into +server output (catalog LTD alerts, wired `%furni.name%`, the Watch&Earn packet) and into a +broadcast to every connected client. Regular players cannot influence names (names are admin-owned, +keyed by classname); the threat is **untrusted furnidata content** (third-party furni packs, +imports, a compromised editor/supply chain). Controls: + +1. **Boundary sanitization** (see §4.1): cap 256 chars, strip control/newline, **neutralize `%`**. + Neutralizing `%` at load makes every `String.replace("%itemname%", name)` / + `%furni.name%` site injection-safe; as defense-in-depth, substitute the (untrusted) furni name + **last** in any placeholder chain. +2. **Path-traversal guard** in the shared reader (§4.1) — reject manifest paths escaping the base + dir. Closes a gap the current editor reader does not cover. +3. **DoS / amplification controls** (§5.1): single serialized watcher thread, debounce + minimum + broadcast interval, delta-size cap with **reload-hint fallback** for mass changes, furnidata + file-size cap. +4. **Fail-safe loading** (§4.1): bad/corrupt furnidata never crashes boot; last-good index is kept; + `getDisplayName()` falls back to `public_name`. +5. **Robust client parser** (§5.2): bound `count`, tolerate truncation — a malformed/MITM + `FurnitureDataReload` cannot allocate unbounded memory client-side. +6. **No client-triggered reload** (§5.1): only the file watcher broadcasts. Do not add any + client→server reload request. Preserve this property. +7. **Minimal disclosure**: the delta carries **only** `name`/`description` (already public via + furnidata) — never other fields from the server-side file. +8. **Concurrency**: `volatile` index reference + atomic swap + single reindex thread → no torn reads. + +## 9. Testing + +- **Emulator (JUnit):** `FurnidataReader` parses single-file and split-tier (JSON5, tier override); + `FurnitureTextProvider` lookup by lowercased classname, **fallback to `public_name`** when absent, + atomic reindex; `reindex()` diff produces correct added/changed delta and ignores removals; + `Item.getDisplayName()` honors the enable toggle. +- **Renderer (Vitest):** `FurnitureDataReloadParser` reads the payload shape; `applyFurnidataDelta` + patches floor/wall `FurnitureData` by id, re-registers localization keys, dispatches + `nitro-localization-updated` once. +- **Client (Vitest):** existing subscribers (`useCatalog`, `useInventoryFurni`, `useAvatarInfoWidget`) + refresh on `nitro-localization-updated` (regression guard; no new code). +- **Manual acceptance:** edit a furni name in furnidata → live update in catalog + inventory + + infostand without refresh; a wired `%furni.name%` sign and a Watch&Earn reward show the new name. +- **Security tests:** reader rejects a split-tier manifest with `../` traversal; a name containing + `%limit%`/`%user.name%` does not inject into catalog alerts or wired text (`%` neutralized); + oversized furnidata is refused; corrupt furnidata keeps last-good index and does not crash; + a mass change emits a reload-hint (not a giant delta); the client parser clamps an absurd `count`. + +## 10. Open questions + +- Free header id for `FurnitureDataReload` (assign during implementation; document both sides). +- Whether any retro on this stack actually ships per-locale furni override files (governs whether + constraint §7.1 is live or moot). From c0f6504d9c9de241d9821525be8b451991efaa3f Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 21:36:15 +0200 Subject: [PATCH 2/6] docs(furni): liveness implementation plan (Piece 2) --- ...4-furni-names-from-json-server-liveness.md | 779 ++++++++++++++++++ 1 file changed, 779 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-04-furni-names-from-json-server-liveness.md diff --git a/docs/superpowers/plans/2026-06-04-furni-names-from-json-server-liveness.md b/docs/superpowers/plans/2026-06-04-furni-names-from-json-server-liveness.md new file mode 100644 index 0000000..5ed0584 --- /dev/null +++ b/docs/superpowers/plans/2026-06-04-furni-names-from-json-server-liveness.md @@ -0,0 +1,779 @@ +# Furni Names from JSON — Liveness (Piece 2) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** When the furnidata file changes, push only the changed names to every connected client so catalog/inventory/infostand update live, without a giant re-download and without a reconnect. + +**Architecture:** The emulator (building on Piece 1) computes a **delta** during reindex, a single-threaded **file watcher** (debounced + throttled) broadcasts a new `FurnitureDataReload` packet (delta, or a compact reload-hint above a cap). The renderer parses it, patches `FurnitureData` + the localization keys, and dispatches the existing `nitro-localization-updated` window event — so the three already-subscribed client surfaces refresh. **No Nitro-V3 client code changes.** + +**Tech Stack:** Java (Arcturus, Gson, java.nio WatchService) + TypeScript (Nitro_Render_V3, Vitest). + +**Depends on:** Piece 1 plan (`FurnitureTextProvider`, `FurnidataReader`, `FurnidataEntry`, `Item.getDisplayName()`) merged on the same branch. + +**Spec:** `docs/superpowers/specs/2026-06-04-furni-names-from-json-server-design.md` (§5, §7, §8). + +**Repos / branch:** `feat/furni-names-from-json-server` in both `Arcturus-Morningstar-Extended` and `Nitro_Render_V3`. + +**Wire contract — packet `FurnitureDataReload` (server → client), header `10047`:** +``` +int mode // 0 = delta, 1 = reload-hint +// mode == 0 (delta): +int count // server-capped; client clamps on read +count × { string type ("S"|"I"); int id; string classname; string name; string description } +// mode == 1: no further fields +``` +`10046` is taken (editor `FURNI_DATA_UPDATED`); `10047` is the new id. Task R1 Step 2 verifies it is free. + +--- + +## Part A — Renderer (Nitro_Render_V3) + +All paths relative to `Nitro_Render_V3/`. Build/test: `yarn build`, `yarn test`. + +### Task R1: IncomingHeader + parser + event + +**Files:** +- Modify: `packages/communication/src/messages/incoming/IncomingHeader.ts:498` +- Create: `packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts` +- Create: `packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts` + +- [ ] **Step 1: Add the incoming header constant** + +In `packages/communication/src/messages/incoming/IncomingHeader.ts`, right after line 498 (`public static FURNI_DATA_UPDATED = 10046;`), add: + +```ts + public static FURNITURE_DATA_RELOAD = 10047; +``` + +- [ ] **Step 2: Verify the id is free** + +Run: `grep -rn "10047" packages/communication/src/messages/incoming/IncomingHeader.ts packages/communication/src/messages/outgoing/OutgoingHeader.ts` +Expected: only the line you just added. If `10047` already exists, use the next free integer and keep it identical to the Arcturus side (Task A1). + +- [ ] **Step 3: Create the parser** + +Create `packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts`: + +```ts +import { IMessageDataWrapper, IMessageParser } from '@nitrots/api'; + +export interface FurnidataDeltaEntry +{ + type: string; // "S" floor | "I" wall + id: number; + classname: string; + name: string; + description: string; +} + +export class FurnitureDataReloadParser implements IMessageParser +{ + private static readonly MAX_ENTRIES = 100000; + + private _mode: number; + private _entries: FurnidataDeltaEntry[]; + + public flush(): boolean + { + this._mode = 0; + this._entries = []; + return true; + } + + public parse(wrapper: IMessageDataWrapper): boolean + { + if(!wrapper) return false; + + this._mode = wrapper.readInt(); + this._entries = []; + + if(this._mode === 0) + { + let count = wrapper.readInt(); + if(count < 0) count = 0; + if(count > FurnitureDataReloadParser.MAX_ENTRIES) count = FurnitureDataReloadParser.MAX_ENTRIES; + + for(let i = 0; i < count; i++) + { + this._entries.push({ + type: wrapper.readString(), + id: wrapper.readInt(), + classname: wrapper.readString(), + name: wrapper.readString(), + description: wrapper.readString() + }); + } + } + + return true; + } + + public get mode(): number { return this._mode; } + public get entries(): FurnidataDeltaEntry[] { return this._entries; } +} +``` + +- [ ] **Step 4: Create the event** + +Create `packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts`: + +```ts +import { IMessageEvent } from '@nitrots/api'; +import { MessageEvent } from '@nitrots/events'; +import { FurnitureDataReloadParser } from '../../parser/furniture/FurnitureDataReloadParser'; + +export class FurnitureDataReloadEvent extends MessageEvent implements IMessageEvent +{ + constructor(callBack: Function) + { + super(callBack, FurnitureDataReloadParser); + } + + public getParser(): FurnitureDataReloadParser + { + return this.parser as FurnitureDataReloadParser; + } +} +``` + +- [ ] **Step 5: Export both from the barrels** + +In `packages/communication/src/messages/parser/furniture/index.ts` add: + +```ts +export * from './FurnitureDataReloadParser'; +``` + +In `packages/communication/src/messages/incoming/furniture/index.ts` add: + +```ts +export * from './FurnitureDataReloadEvent'; +``` + +(If either `furniture/index.ts` does not exist, create it with the single `export * from './FurnitureDataReloadParser';` / `...Event` line and add `export * from './furniture';` to the parent `incoming/index.ts` and `parser/index.ts`.) + +- [ ] **Step 6: Compile** + +Run: `yarn compile:fast` +Expected: no errors. + +- [ ] **Step 7: Commit** + +```bash +git add packages/communication/src/messages/incoming/IncomingHeader.ts packages/communication/src/messages/parser/furniture/ packages/communication/src/messages/incoming/furniture/ +git commit -m "feat(communication): FurnitureDataReload incoming event + parser (header 10047)" +``` + +--- + +### Task R2: Map the header → event in NitroMessages + +**Files:** +- Modify: `packages/communication/src/NitroMessages.ts:91` + +- [ ] **Step 1: Register the event class** + +In `packages/communication/src/NitroMessages.ts`, right after line 91 (`this._events.set(IncomingHeader.FURNI_DATA_UPDATED, FurniDataUpdatedEvent);`), add: + +```ts + this._events.set(IncomingHeader.FURNITURE_DATA_RELOAD, FurnitureDataReloadEvent); +``` + +Ensure `FurnitureDataReloadEvent` is imported at the top of the file (add it to the existing incoming-events import block, e.g. `import { ..., FurnitureDataReloadEvent } from './messages';` matching how `FurniDataUpdatedEvent` is imported there). + +- [ ] **Step 2: Compile** + +Run: `yarn compile:fast` +Expected: no errors (the symbol resolves through the barrels from Task R1 Step 5). + +- [ ] **Step 3: Commit** + +```bash +git add packages/communication/src/NitroMessages.ts +git commit -m "feat(communication): route FURNITURE_DATA_RELOAD to its event" +``` + +--- + +### Task R3: `applyFurnidataDelta` + reload-hint + register handler (TDD) + +**Files:** +- Modify: `packages/session/src/SessionDataManager.ts` +- Test: `packages/session/src/__tests__/SessionDataManager.furnidataDelta.test.ts` + +- [ ] **Step 1: Write the failing test** + +Create `packages/session/src/__tests__/SessionDataManager.furnidataDelta.test.ts`: + +```ts +import { describe, expect, it, vi, beforeEach } from 'vitest'; + +// Minimal localization + window doubles +const setValue = vi.fn(); +vi.mock('@nitrots/localization', () => ({ + GetLocalizationManager: () => ({ setValue }) +})); +vi.mock('@nitrots/events', async (orig) => { + const actual = await orig() as any; + return { ...actual, GetEventDispatcher: () => ({ dispatchEvent: vi.fn() }) }; +}); + +import { applyFurnidataDeltaTo } from '../furniture/applyFurnidataDelta'; + +describe('applyFurnidataDelta', () => { + beforeEach(() => { setValue.mockClear(); }); + + it('patches floor FurnitureData name/description and localization keys, dispatches window event', () => { + const floor: any = { _localizedName: 'Old', _description: 'Old desc' }; + const floorItems = new Map([[ 5, floor ]]); + const wallItems = new Map(); + const dispatched: string[] = []; + const win: any = { dispatchEvent: (e: any) => dispatched.push(e.type) }; + + applyFurnidataDeltaTo( + [ { type: 'S', id: 5, classname: 'chair', name: 'New', description: 'New desc' } ], + floorItems, wallItems, { setValue } as any, win + ); + + expect(floor._localizedName).toBe('New'); + expect(floor._description).toBe('New desc'); + expect(setValue).toHaveBeenCalledWith('roomItem.name.5', 'New'); + expect(setValue).toHaveBeenCalledWith('roomItem.desc.5', 'New desc'); + expect(dispatched).toContain('nitro-localization-updated'); + }); + + it('patches wall items by id', () => { + const wall: any = { _localizedName: 'W', _description: '' }; + const wallItems = new Map([[ 9, wall ]]); + applyFurnidataDeltaTo( + [ { type: 'I', id: 9, classname: 'poster', name: 'WallNew', description: 'd' } ], + new Map(), wallItems, { setValue } as any, { dispatchEvent: () => {} } as any + ); + expect(wall._localizedName).toBe('WallNew'); + expect(setValue).toHaveBeenCalledWith('wallItem.name.9', 'WallNew'); + }); +}); +``` + +- [ ] **Step 2: Run to verify it fails** + +Run: `yarn test SessionDataManager.furnidataDelta` +Expected: FAIL — `../furniture/applyFurnidataDelta` does not exist. + +- [ ] **Step 3: Extract the pure patch function** + +Create `packages/session/src/furniture/applyFurnidataDelta.ts`: + +```ts +import { ILocalizationManager } from '@nitrots/api'; +import { FurnidataDeltaEntry } from '@nitrots/communication'; + +/** + * Pure, testable furnidata-delta patcher. Mutates the FurnitureData objects in + * the given maps (by id) and the localization keys, then dispatches the + * `nitro-localization-updated` window event so subscribed React surfaces refresh. + */ +export function applyFurnidataDeltaTo( + entries: FurnidataDeltaEntry[], + floorItems: Map, + wallItems: Map, + localization: Pick, + win: { dispatchEvent: (event: Event) => void } +): void +{ + if(!entries || !entries.length) return; + + for(const e of entries) + { + if(e.type === 'I') + { + const wall = wallItems.get(e.id); + if(wall) { wall._localizedName = e.name; wall._description = e.description; } + localization.setValue('wallItem.name.' + e.id, e.name); + localization.setValue('wallItem.desc.' + e.id, e.description); + } + else + { + const floor = floorItems.get(e.id); + if(floor) { floor._localizedName = e.name; floor._description = e.description; } + localization.setValue('roomItem.name.' + e.id, e.name); + localization.setValue('roomItem.desc.' + e.id, e.description); + } + } + + if(win && typeof win.dispatchEvent === 'function') + { + win.dispatchEvent(new CustomEvent('nitro-localization-updated')); + } +} +``` + +If `FurnidataDeltaEntry` is not yet re-exported from `@nitrots/communication`, add `export * from './messages/parser/furniture/FurnitureDataReloadParser';` to `packages/communication/src/index.ts` (or the nearest public barrel) so the type is importable. + +- [ ] **Step 4: Run to verify it passes** + +Run: `yarn test SessionDataManager.furnidataDelta` +Expected: PASS — 2 tests. + +- [ ] **Step 5: Wire the methods + handler into SessionDataManager** + +In `packages/session/src/SessionDataManager.ts`, add the import near the other furniture imports: + +```ts +import { applyFurnidataDeltaTo } from './furniture/applyFurnidataDelta'; +import { FurnidataDeltaEntry, FurnitureDataReloadEvent } from '@nitrots/communication'; +``` + +Add these methods just after `applyLiveFurnitureNameUpdate` (after line 115): + +```ts + public applyFurnidataDelta(entries: FurnidataDeltaEntry[]): void + { + applyFurnidataDeltaTo(entries, this._floorItems as any, this._wallItems as any, GetLocalizationManager(), (typeof window !== 'undefined') ? window : { dispatchEvent: () => {} } as any); + } + + public async applyFurnidataReloadHint(): Promise + { + await this._furnitureData.init(); + if(typeof window !== 'undefined') window.dispatchEvent(new CustomEvent('nitro-localization-updated')); + } +``` + +In `init()`, add a registration to the `this._messageEvents.push(...)` list (after the `FurniDataUpdatedEvent` registration at line 208-212): + +```ts + GetCommunication().registerMessageEvent(new FurnitureDataReloadEvent((event: FurnitureDataReloadEvent) => + { + const parser = event.getParser(); + if(parser.mode === 1) { void this.applyFurnidataReloadHint(); } + else { this.applyFurnidataDelta(parser.entries); } + })) +``` + +(Add a comma after the previous entry as needed so the push args stay comma-separated.) + +- [ ] **Step 6: Compile + run the full session test suite** + +Run: `yarn compile:fast && yarn test packages/session` +Expected: no compile errors; all session tests pass including the new ones. + +- [ ] **Step 7: Commit** + +```bash +git add packages/session/src/furniture/applyFurnidataDelta.ts packages/session/src/SessionDataManager.ts packages/session/src/__tests__/SessionDataManager.furnidataDelta.test.ts packages/communication/src/index.ts +git commit -m "feat(session): apply FurnitureDataReload delta + reload-hint, separate from editor path" +``` + +--- + +## Part B — Emulator (Arcturus-Morningstar-Extended) + +All paths relative to `Arcturus-Morningstar-Extended/Emulator/`. + +### Task A1: Outgoing header + `FurnitureDataReloadComposer` + +**Files:** +- Modify: `src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java` +- Create: `src/main/java/com/eu/habbo/messages/outgoing/furniture/FurnitureDataReloadComposer.java` + +- [ ] **Step 1: Add the Outgoing header constant** + +In `src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java`, add (matching the file's existing `public static final int Name = id;` style): + +```java + public static final int FurnitureDataReloadComposer = 10047; +``` + +- [ ] **Step 2: Verify the id is free on the server side** + +Run: `grep -rn "= 10047" src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java` +Expected: only the line just added. If taken, pick the next free id and keep it equal to the renderer `IncomingHeader.FURNITURE_DATA_RELOAD` (Task R1). + +- [ ] **Step 3: Create the composer** + +Create `src/main/java/com/eu/habbo/messages/outgoing/furniture/FurnitureDataReloadComposer.java`: + +```java +package com.eu.habbo.messages.outgoing.furniture; + +import com.eu.habbo.habbohotel.items.FurnidataEntry; +import com.eu.habbo.habbohotel.items.FurnitureType; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.outgoing.MessageComposer; +import com.eu.habbo.messages.outgoing.Outgoing; + +import java.util.List; + +public class FurnitureDataReloadComposer extends MessageComposer { + + public static final int MODE_DELTA = 0; + public static final int MODE_RELOAD_HINT = 1; + + private final int mode; + private final List entries; + + public FurnitureDataReloadComposer(int mode, List entries) { + this.mode = mode; + this.entries = entries; + } + + @Override + protected ServerMessage composeInternal() { + this.response.init(Outgoing.FurnitureDataReloadComposer); + this.response.appendInt(this.mode); + + if (this.mode == MODE_DELTA) { + this.response.appendInt(this.entries.size()); + for (FurnidataEntry e : this.entries) { + this.response.appendString(e.type() == FurnitureType.FLOOR ? "S" : "I"); + this.response.appendInt(e.id()); + this.response.appendString(e.classname()); + this.response.appendString(e.name()); + this.response.appendString(e.description()); + } + } + + return this.response; + } +} +``` + +- [ ] **Step 4: Compile** + +Run: `mvn -q compile` +Expected: BUILD SUCCESS. + +- [ ] **Step 5: Commit** + +```bash +git add src/main/java/com/eu/habbo/messages/outgoing/Outgoing.java src/main/java/com/eu/habbo/messages/outgoing/furniture/FurnitureDataReloadComposer.java +git commit -m "feat(items): FurnitureDataReloadComposer (header 10047, delta + reload-hint)" +``` + +--- + +### Task A2: `FurnitureTextProvider.reindex` returns a sanitized delta (TDD) + +Piece 1 defined `reindex(List)` returning `void`. Change it to return the changed entries (sanitized), so the watcher can broadcast them. Existing call sites ignore the return value — no other change needed. + +**Files:** +- Modify: `src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java` +- Test: `src/test/java/com/eu/habbo/habbohotel/items/FurnitureTextProviderDeltaTest.java` + +- [ ] **Step 1: Write the failing test** + +Create `src/test/java/com/eu/habbo/habbohotel/items/FurnitureTextProviderDeltaTest.java`: + +```java +package com.eu.habbo.habbohotel.items; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class FurnitureTextProviderDeltaTest { + + @Test + void firstReindexReturnsAllAsDelta() { + FurnitureTextProvider p = new FurnitureTextProvider(true); + List delta = p.reindex(List.of( + new FurnidataEntry(1, "chair", FurnitureType.FLOOR, "Chair", "Sit"))); + assertEquals(1, delta.size()); + assertEquals("Chair", delta.get(0).name()); + } + + @Test + void unchangedReindexReturnsEmptyDelta() { + FurnitureTextProvider p = new FurnitureTextProvider(true); + List first = List.of(new FurnidataEntry(1, "chair", FurnitureType.FLOOR, "Chair", "Sit")); + p.reindex(first); + List delta = p.reindex(first); + assertTrue(delta.isEmpty(), "no change => empty delta"); + } + + @Test + void changedNameAppearsInDeltaWithSanitizedValue() { + FurnitureTextProvider p = new FurnitureTextProvider(true); + p.reindex(List.of(new FurnidataEntry(1, "chair", FurnitureType.FLOOR, "Chair", "Sit"))); + List delta = p.reindex(List.of( + new FurnidataEntry(1, "chair", FurnitureType.FLOOR, "New %x%", "Sit"))); + assertEquals(1, delta.size()); + assertFalse(delta.get(0).name().contains("%"), "delta carries the sanitized name"); + assertEquals(1, delta.get(0).id()); + assertEquals(FurnitureType.FLOOR, delta.get(0).type()); + } +} +``` + +- [ ] **Step 2: Run to verify it fails** + +Run: `mvn -q test -Dtest=FurnitureTextProviderDeltaTest` +Expected: FAIL — `reindex` returns `void` (compile error) / method shape mismatch. + +- [ ] **Step 3: Change `reindex` to compute and return the delta** + +In `FurnitureTextProvider.java`, replace the existing `reindex` method with: + +```java + /** + * Build a fresh sanitized index, swap it in atomically, and return the + * changed/added entries (sanitized) as the delta versus the previous index. + */ + public java.util.List reindex(java.util.List entries) { + Map next = new HashMap<>(Math.max(16, entries.size() * 2)); + for (FurnidataEntry e : entries) { + String key = baseKey(e.classname()); + if (key == null) continue; + next.put(key, new FurniText(e.id(), e.type(), sanitize(e.name()), sanitize(e.description()))); + } + + Map prev = this.index; + java.util.List delta = new java.util.ArrayList<>(); + for (Map.Entry en : next.entrySet()) { + FurniText cur = en.getValue(); + FurniText old = prev.get(en.getKey()); + if (old == null || !old.name().equals(cur.name()) || !old.description().equals(cur.description())) { + delta.add(new FurnidataEntry(cur.id(), en.getKey(), cur.type(), cur.name(), cur.description())); + } + } + + this.index = next; // atomic reference swap + return delta; + } +``` + +- [ ] **Step 4: Run both provider tests** + +Run: `mvn -q test -Dtest=FurnitureTextProviderTest,FurnitureTextProviderDeltaTest` +Expected: PASS — Piece-1 provider tests (6) still pass (they ignore the return value), delta tests (3) pass. + +- [ ] **Step 5: Commit** + +```bash +git add src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java src/test/java/com/eu/habbo/habbohotel/items/FurnitureTextProviderDeltaTest.java +git commit -m "feat(items): reindex returns sanitized furnidata delta" +``` + +--- + +### Task A3: File watcher — debounce, throttle, cap→hint, broadcast + +**Files:** +- Create: `src/main/java/com/eu/habbo/habbohotel/items/FurnidataWatcher.java` +- Modify: `src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java` (expose source path + start watcher) + +- [ ] **Step 1: Expose the resolved source and start the watcher from `init()`** + +In `FurnitureTextProvider.java`, store the resolved source and start the watcher at the end of `init()`. Replace the body of `init()` with: + +```java + private volatile Path source; + private FurnidataWatcher watcher; + + public void init() { + try { + this.source = resolveSource(); + if (this.source == null) { + LOGGER.warn("FurnitureTextProvider: no furnidata source resolved — names fall back to public_name"); + return; + } + reindex(new FurnidataReader(this.source, DEFAULT_MAX_BYTES).read()); + LOGGER.info("FurnitureTextProvider: indexed {} furnidata names from {}", this.index.size(), this.source); + + if (Boolean.parseBoolean(Emulator.getConfig().getValue("items.furnidata.watch.enabled", "true"))) { + this.watcher = new FurnidataWatcher(this, this.source, DEFAULT_MAX_BYTES); + this.watcher.start(); + } + } catch (Exception e) { + LOGGER.warn("FurnitureTextProvider.init failed — names fall back to public_name", e); + } + } + + public Path getSource() { + return this.source; + } +``` + +(Add `import java.nio.file.Path;` if not already present from Piece 1 Task 4.) + +- [ ] **Step 2: Create the watcher** + +Create `src/main/java/com/eu/habbo/habbohotel/items/FurnidataWatcher.java`: + +```java +package com.eu.habbo.habbohotel.items; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.messages.outgoing.furniture.FurnitureDataReloadComposer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.List; + +/** + * Watches the furnidata source on a single daemon thread. On change (debounced), + * re-indexes via the provider and broadcasts only the delta — or a compact + * reload-hint when the delta exceeds the cap. A minimum interval throttles bursts. + * Never throws out of the loop. + */ +public class FurnidataWatcher { + + private static final Logger LOGGER = LoggerFactory.getLogger(FurnidataWatcher.class); + + private final FurnitureTextProvider provider; + private final Path watchDir; + private final long maxBytes; + private final long debounceMs; + private final long minIntervalMs; + private final int deltaCap; + + private volatile boolean running = false; + private long lastBroadcast = 0L; + + public FurnidataWatcher(FurnitureTextProvider provider, Path source, long maxBytes) { + this.provider = provider; + // Watch the parent dir for a file, or the dir itself for a split layout. + this.watchDir = java.nio.file.Files.isDirectory(source) ? source : source.getParent(); + this.maxBytes = maxBytes; + this.debounceMs = Long.parseLong(Emulator.getConfig().getValue("items.furnidata.watch.debounce.ms", "750")); + this.minIntervalMs = Long.parseLong(Emulator.getConfig().getValue("items.furnidata.watch.min.interval.ms", "5000")); + this.deltaCap = Integer.parseInt(Emulator.getConfig().getValue("items.furnidata.delta.cap", "500")); + } + + public void start() { + if (this.running || this.watchDir == null) return; + this.running = true; + Thread t = new Thread(this::run, "FurnidataWatcher"); + t.setDaemon(true); + t.start(); + } + + public void stop() { + this.running = false; + } + + private void run() { + try (WatchService ws = FileSystems.getDefault().newWatchService()) { + this.watchDir.register(ws, StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE); + + while (this.running) { + WatchKey key = ws.take(); // blocks + key.pollEvents(); // drain + Thread.sleep(this.debounceMs); // debounce burst writes + key.pollEvents(); // drain anything that arrived during debounce + key.reset(); + + try { + onChange(); + } catch (Exception e) { + LOGGER.warn("FurnidataWatcher: onChange failed", e); + } + } + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + LOGGER.warn("FurnidataWatcher stopped", e); + } + } + + private void onChange() { + Path source = this.provider.getSource(); + if (source == null) return; + + List delta = this.provider.reindex(new FurnidataReader(source, this.maxBytes).read()); + if (delta.isEmpty()) return; + + long now = System.currentTimeMillis(); + if (now - this.lastBroadcast < this.minIntervalMs) { + LOGGER.info("FurnidataWatcher: {} changes throttled (min interval)", delta.size()); + return; // next change will pick up the new baseline + } + this.lastBroadcast = now; + + FurnitureDataReloadComposer composer = (delta.size() > this.deltaCap) + ? new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_RELOAD_HINT, List.of()) + : new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_DELTA, delta); + + broadcast(composer); + LOGGER.info("FurnidataWatcher: broadcast {} ({} entries)", + delta.size() > this.deltaCap ? "reload-hint" : "delta", delta.size()); + } + + private void broadcast(FurnitureDataReloadComposer composer) { + for (Habbo habbo : Emulator.getGameEnvironment().getHabboManager().getOnlineHabbos().values()) { + if (habbo.getClient() != null) { + habbo.getClient().sendResponse(composer); + } + } + } +} +``` + +`System.currentTimeMillis()` is fine here (production server runtime). Note: when throttled, the index is still updated but the delta is **not** sent, and it is not re-sent later — those names update on client reconnect (acceptable for infrequent admin edits). To never drop, raise `items.furnidata.delta.cap` is unrelated; instead lower `items.furnidata.watch.min.interval.ms`. + +- [ ] **Step 3: Compile** + +Run: `mvn -q compile` +Expected: BUILD SUCCESS. + +- [ ] **Step 4: Commit** + +```bash +git add src/main/java/com/eu/habbo/habbohotel/items/FurnidataWatcher.java src/main/java/com/eu/habbo/habbohotel/items/FurnitureTextProvider.java +git commit -m "feat(items): furnidata file watcher — debounce, throttle, delta cap to reload-hint, broadcast" +``` + +--- + +## Part C — Integration & acceptance + +### Task C1: Build both repos + manual live test + +**Files:** none (verification only) + +- [ ] **Step 1: Renderer build + tests** + +Run (in `Nitro_Render_V3/`): `yarn compile:fast && yarn test` +Expected: no compile errors; all tests pass including `SessionDataManager.furnidataDelta`. + +- [ ] **Step 2: Emulator build + tests** + +Run (in `Arcturus-Morningstar-Extended/Emulator/`): `mvn -q clean package` +Expected: BUILD SUCCESS; all `FurnitureTextProvider*Test` + `FurnidataReaderTest` pass. + +- [ ] **Step 3: Manual live acceptance (running hotel + client)** + +1. Start MariaDB, the emulator (with Piece 1 + Piece 2 jar), and the client; enter a room containing a furni whose furnidata `name` you will change. +2. Edit that furni's `name` in the furnidata file and save. +3. Within the debounce window (~1s), confirm WITHOUT refreshing the client: + - the furni infostand shows the new name (open the furni info), + - the catalog offer for it shows the new name, + - the inventory tile label shows the new name. +4. Check the emulator log: `FurnidataWatcher: broadcast delta (1 entries)`. +5. Replace the whole furnidata (mass change) → log shows `broadcast reload-hint`; the client re-loads furnidata and names update. +6. Set `items.furnidata.watch.enabled=false`, restart → editing the file no longer pushes; clients update only on reconnect (watcher disabled). + +- [ ] **Step 4: Final no-op commit (optional config docs)** + +```bash +git commit --allow-empty -m "docs(items): document items.furnidata.watch.* config keys" +``` + +--- + +## Notes for the implementer + +- **Header id must match** on both sides: `IncomingHeader.FURNITURE_DATA_RELOAD` (renderer) == `Outgoing.FurnitureDataReloadComposer` (Arcturus) == `10047`. Verify both Step-2 grep checks before wiring. +- **Do not reuse or modify** the editor path: `FurniDataUpdatedEvent`/`FurniDataUpdatedParser`/`applyLiveFurnitureNameUpdate` stay untouched; the new path is parallel (per the spec decision "keep separate"). +- **No Nitro-V3 client changes.** The three surfaces (`useCatalog.ts:919`, `useInventoryFurni.ts:137`, `useAvatarInfoWidget.ts:425`) already subscribe to `nitro-localization-updated`. If a regression test is wanted, add it under Nitro-V3 separately; it is not required for this plan. +- **Locale no-clobber (spec §7.1):** re-registering `roomItem.name.{id}` from the base furnidata will override an active per-locale text override for that id. If this hotel ships per-locale furni override files, follow up by re-applying overrides after the delta (out of scope here; single-furnidata hotels are unaffected). +- **Cache-busting** for the reload-hint: `applyFurnidataReloadHint` re-runs `FurnitureDataLoader.init()` against the configured `furnidata.url`; if the asset host serves a cached copy, add a `?v=` buster to the loader fetch (optional follow-up). From 17957e7f54f383ce6f4d7af6bf64981dabe2ab1f Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 21:50:34 +0200 Subject: [PATCH 3/6] feat(communication): FurnitureDataReload incoming event + parser (header 10047) --- packages/communication/src/index.ts | 2 + .../src/messages/incoming/IncomingHeader.ts | 1 + .../furniture/FurnitureDataReloadEvent.ts | 16 ++++++ .../src/messages/incoming/furniture/index.ts | 1 + .../src/messages/incoming/index.ts | 1 + .../furniture/FurnitureDataReloadParser.ts | 56 +++++++++++++++++++ .../src/messages/parser/furniture/index.ts | 1 + .../src/messages/parser/index.ts | 1 + 8 files changed, 79 insertions(+) create mode 100644 packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts create mode 100644 packages/communication/src/messages/incoming/furniture/index.ts create mode 100644 packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts create mode 100644 packages/communication/src/messages/parser/furniture/index.ts diff --git a/packages/communication/src/index.ts b/packages/communication/src/index.ts index 02a98fc..752a2cb 100644 --- a/packages/communication/src/index.ts +++ b/packages/communication/src/index.ts @@ -22,6 +22,7 @@ export * from './messages/incoming/crafting'; export * from './messages/incoming/desktop'; export * from './messages/incoming/friendlist'; export * from './messages/incoming/furnieditor'; +export * from './messages/incoming/furniture'; export * from './messages/incoming/game'; export * from './messages/incoming/game/directory'; export * from './messages/incoming/game/lobby'; @@ -181,6 +182,7 @@ export * from './messages/parser/crafting'; export * from './messages/parser/desktop'; export * from './messages/parser/friendlist'; export * from './messages/parser/furnieditor'; +export * from './messages/parser/furniture'; export * from './messages/parser/game'; export * from './messages/parser/game/directory'; export * from './messages/parser/game/lobby'; diff --git a/packages/communication/src/messages/incoming/IncomingHeader.ts b/packages/communication/src/messages/incoming/IncomingHeader.ts index f9eec93..eb000c7 100644 --- a/packages/communication/src/messages/incoming/IncomingHeader.ts +++ b/packages/communication/src/messages/incoming/IncomingHeader.ts @@ -493,6 +493,7 @@ export class IncomingHeader public static FURNI_EDITOR_DETAIL_RESULT = 10041; public static FURNI_EDITOR_INTERACTIONS_RESULT = 10043; public static FURNI_EDITOR_RESULT = 10044; + public static FURNITURE_DATA_RELOAD = 10047; // Catalog Admin public static CATALOG_ADMIN_RESULT = 10059; diff --git a/packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts b/packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts new file mode 100644 index 0000000..a99d7b3 --- /dev/null +++ b/packages/communication/src/messages/incoming/furniture/FurnitureDataReloadEvent.ts @@ -0,0 +1,16 @@ +import { IMessageEvent } from '@nitrots/api'; +import { MessageEvent } from '@nitrots/events'; +import { FurnitureDataReloadParser } from '../../parser/furniture/FurnitureDataReloadParser'; + +export class FurnitureDataReloadEvent extends MessageEvent implements IMessageEvent +{ + constructor(callBack: Function) + { + super(callBack, FurnitureDataReloadParser); + } + + public getParser(): FurnitureDataReloadParser + { + return this.parser as FurnitureDataReloadParser; + } +} diff --git a/packages/communication/src/messages/incoming/furniture/index.ts b/packages/communication/src/messages/incoming/furniture/index.ts new file mode 100644 index 0000000..bd1ea9e --- /dev/null +++ b/packages/communication/src/messages/incoming/furniture/index.ts @@ -0,0 +1 @@ +export * from './FurnitureDataReloadEvent'; diff --git a/packages/communication/src/messages/incoming/index.ts b/packages/communication/src/messages/incoming/index.ts index a01ad24..3e12de6 100644 --- a/packages/communication/src/messages/incoming/index.ts +++ b/packages/communication/src/messages/incoming/index.ts @@ -14,6 +14,7 @@ export * from './crafting'; export * from './desktop'; export * from './friendlist'; export * from './furnieditor'; +export * from './furniture'; export * from './game'; export * from './game/directory'; export * from './game/lobby'; diff --git a/packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts b/packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts new file mode 100644 index 0000000..fb5cb05 --- /dev/null +++ b/packages/communication/src/messages/parser/furniture/FurnitureDataReloadParser.ts @@ -0,0 +1,56 @@ +import { IMessageDataWrapper, IMessageParser } from '@nitrots/api'; + +export interface FurnidataDeltaEntry +{ + type: string; // "S" floor | "I" wall + id: number; + classname: string; + name: string; + description: string; +} + +export class FurnitureDataReloadParser implements IMessageParser +{ + private static readonly MAX_ENTRIES = 100000; + + private _mode: number; + private _entries: FurnidataDeltaEntry[]; + + public flush(): boolean + { + this._mode = 0; + this._entries = []; + return true; + } + + public parse(wrapper: IMessageDataWrapper): boolean + { + if(!wrapper) return false; + + this._mode = wrapper.readInt(); + this._entries = []; + + if(this._mode === 0) + { + let count = wrapper.readInt(); + if(count < 0) count = 0; + if(count > FurnitureDataReloadParser.MAX_ENTRIES) count = FurnitureDataReloadParser.MAX_ENTRIES; + + for(let i = 0; i < count; i++) + { + this._entries.push({ + type: wrapper.readString(), + id: wrapper.readInt(), + classname: wrapper.readString(), + name: wrapper.readString(), + description: wrapper.readString() + }); + } + } + + return true; + } + + public get mode(): number { return this._mode; } + public get entries(): FurnidataDeltaEntry[] { return this._entries; } +} diff --git a/packages/communication/src/messages/parser/furniture/index.ts b/packages/communication/src/messages/parser/furniture/index.ts new file mode 100644 index 0000000..3e010f8 --- /dev/null +++ b/packages/communication/src/messages/parser/furniture/index.ts @@ -0,0 +1 @@ +export * from './FurnitureDataReloadParser'; diff --git a/packages/communication/src/messages/parser/index.ts b/packages/communication/src/messages/parser/index.ts index ccf664c..6cdc929 100644 --- a/packages/communication/src/messages/parser/index.ts +++ b/packages/communication/src/messages/parser/index.ts @@ -13,6 +13,7 @@ export * from './crafting'; export * from './desktop'; export * from './friendlist'; export * from './furnieditor'; +export * from './furniture'; export * from './game'; export * from './game/directory'; export * from './game/lobby'; From dbd398ae80f4299755f6f9a771a30bbc2835674b Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 21:54:43 +0200 Subject: [PATCH 4/6] feat(communication): route FURNITURE_DATA_RELOAD to its event --- packages/communication/src/NitroMessages.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/communication/src/NitroMessages.ts b/packages/communication/src/NitroMessages.ts index 7c20de2..3b93ee5 100644 --- a/packages/communication/src/NitroMessages.ts +++ b/packages/communication/src/NitroMessages.ts @@ -1,5 +1,5 @@ import { IMessageConfiguration } from '@nitrots/api'; -import { AcceptFriendMessageComposer, AcceptFriendResultEvent, AcceptGameInviteMessageComposer, AcceptQuestMessageComposer, AccountSafetyLockStatusChangeMessageEvent, AchievementEvent, AchievementNotificationMessageEvent, AchievementResolutionCompletedMessageEvent, AchievementResolutionProgressMessageEvent, AchievementResolutionsMessageEvent, AchievementsEvent, AchievementsScoreEvent, ActivateQuestMessageComposer, ActivityPointNotificationMessageEvent, AddFavouriteRoomMessageComposer, AddJukeboxDiskComposer, AddSpamWallPostItMessageComposer, ApplySnapshotMessageComposer, ApplyTonerComposer, ApproveAllMembershipRequestsMessageComposer, ApproveNameMessageComposer, ApproveNameMessageEvent, AreaHideMessageEvent, AuthenticatedEvent, AuthenticationMessageComposer, AvailabilityStatusMessageEvent, AvailabilityTimeMessageEvent, AvailableCommandsEvent, AvatarEffectActivatedComposer, AvatarEffectActivatedEvent, AvatarEffectAddedEvent, AvatarEffectExpiredEvent, AvatarEffectSelectedComposer, AvatarEffectSelectedEvent, AvatarEffectsEvent, BadgePointLimitsEvent, BadgeReceivedEvent, BadgesEvent, BannedUsersFromRoomEvent, BonusRareInfoMessageEvent, BotAddedToInventoryEvent, BotCommandConfigurationEvent, BotErrorEvent, BotForceOpenContextMenuEvent, BotInventoryMessageEvent, BotPlaceComposer, BotReceivedMessageEvent, BotRemoveComposer, BotRemovedFromInventoryEvent, BotSkillListUpdateEvent, BotSkillSaveComposer, BreedPetsMessageComposer, BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, BundleDiscountRulesetMessageEvent, BuyMarketplaceOfferMessageComposer, BuyMarketplaceTokensMessageComposer, CallForHelpDisabledNotifyMessageEvent, CallForHelpFromForumMessageMessageComposer, CallForHelpFromForumThreadMessageComposer, CallForHelpFromIMMessageComposer, CallForHelpFromPhotoMessageComposer, CallForHelpFromSelfieMessageComposer, CallForHelpMessageComposer, CallForHelpPendingCallsDeletedMessageEvent, CallForHelpPendingCallsMessageEvent, CallForHelpReplyMessageEvent, CallForHelpResultMessageEvent, CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraSnapshotMessageEvent, CameraStorageUrlMessageEvent, CampaignCalendarDataMessageEvent, CampaignCalendarDoorOpenedMessageEvent, CancelEventMessageComposer, CancelMarketplaceOfferMessageComposer, CancelMysteryBoxWaitMessageEvent, CancelPetBreedingComposer, CancelQuestMessageComposer, CanCreateRoomEvent, CanCreateRoomEventEvent, CanCreateRoomMessageComposer, CatalogAdminCreateOfferComposer, CatalogAdminCreatePageComposer, CatalogAdminDeleteOfferComposer, CatalogAdminDeletePageComposer, CatalogAdminMoveOfferComposer, CatalogAdminMovePageComposer, CatalogAdminPublishComposer, CatalogAdminResultEvent, CatalogAdminSaveOfferComposer, CatalogAdminSavePageComposer, CatalogGroupsComposer, CatalogPageExpirationEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPageWithEarliestExpiryMessageEvent, CatalogPublishedMessageEvent, CategoriesWithVisitorCountEvent, CfhChatlogEvent, CfhSanctionMessageEvent, CfhTopicsInitEvent, ChangeEmailComposer, ChangeEmailResultEvent, ChangeQueueMessageComposer, ChangeUserNameMessageComposer, ChangeUserNameResultMessageEvent, ChatReviewGuideDecidesOnOfferMessageComposer, ChatReviewGuideDetachedMessageComposer, ChatReviewGuideVoteMessageComposer, ChatReviewSessionCreateMessageComposer, ChatReviewSessionDetachedMessageEvent, ChatReviewSessionOfferedToGuideMessageEvent, ChatReviewSessionResultsMessageEvent, ChatReviewSessionStartedMessageEvent, ChatReviewSessionVotingStatusMessageEvent, CheckUserNameMessageComposer, CheckUserNameResultMessageEvent, ClickFurniMessageComposer, ClientHelloMessageComposer, ClientPingEvent, CloseIssueDefaultActionMessageComposer, CloseIssuesMessageComposer, ClubGiftInfoEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, CommunityGoalEarnedPrizesMessageEvent, CommunityGoalHallOfFameMessageEvent, CommunityGoalProgressMessageEvent, CommunityGoalVoteMessageComposer, CommunityGoalVoteMessageEvent, CompetitionEntrySubmitResultEvent, CompetitionRoomsDataMessageEvent, CompetitionRoomsSearchMessageComposer, CompetitionStatusMessageEvent, CompetitionVotingInfoMessageEvent, CompleteDiffieHandshakeEvent, CompleteDiffieHandshakeMessageComposer, CompostPlantMessageComposer, ConcurrentUsersGoalProgressMessageEvent, ConfirmPetBreedingComposer, ConnectionErrorEvent, ControlYoutubeDisplayPlaybackMessageComposer, ConvertedRoomIdEvent, ConvertGlobalRoomIdMessageComposer, CraftableProductsEvent, CraftComposer, CraftingRecipeEvent, CraftingRecipesAvailableEvent, CraftingResultEvent, CraftSecretComposer, CreateFlatMessageComposer, CurrentTimingCodeMessageEvent, CustomUserNotificationMessageEvent, DeclineFriendMessageComposer, DefaultSanctionMessageComposer, DeleteBadgeMessageComposer, DeleteFavouriteRoomMessageComposer, DeleteItemMessageComposer, DeletePendingCallsForHelpMessageComposer, DeletePetMessageComposer, DesktopViewComposer, DesktopViewEvent, DiceValueMessageEvent, DirectSMSClubBuyAvailableMessageEvent, DisconnectMessageComposer, DisconnectReasonEvent, DoorbellMessageEvent, EditEventMessageComposer, ElementPointerMessageEvent, EmailStatusResultEvent, EpicPopupMessageEvent, ExtendedProfileChangedMessageEvent, ExtendRentOrBuyoutFurniMessageComposer, ExtendRentOrBuyoutStripItemMessageComposer, FavoriteMembershipUpdateMessageEvent, FavouriteChangedEvent, FavouritesEvent, FigureSetIdsMessageEvent, FigureUpdateEvent, FindFriendsProcessResultEvent, FindNewFriendsMessageComposer, FireworkChargeDataEvent, FlatAccessDeniedMessageEvent, FlatControllerAddedEvent, FlatControllerRemovedEvent, FlatControllersEvent, FlatCreatedEvent, FloodControlEvent, FloorHeightMapEvent, FollowFriendFailedEvent, FollowFriendMessageComposer, ForumDataMessageEvent, ForumsListMessageEvent, ForwardToACompetitionRoomMessageComposer, ForwardToARandomPromotedRoomMessageComposer, ForwardToASubmittableRoomMessageComposer, ForwardToRandomCompetitionRoomMessageComposer, ForwardToSomeRoomMessageComposer, FriendFurniConfirmLockMessageComposer, FriendListFragmentEvent, FriendListUpdateComposer, FriendListUpdateEvent, FriendNotificationEvent, FriendRequestQuestCompleteMessageComposer, FriendRequestsEvent, FurniRentOrBuyoutOfferMessageEvent, FurnitureAliasesComposer, FurnitureAliasesEvent, FurnitureColorWheelComposer, FurnitureDataEvent, FurnitureDiceActivateComposer, FurnitureDiceDeactivateComposer, FurnitureExchangeComposer, FurnitureFloorAddEvent, FurnitureFloorEvent, FurnitureFloorRemoveEvent, FurnitureFloorUpdateComposer, FurnitureFloorUpdateEvent, FurnitureGroupInfoComposer, FurnitureListAddOrUpdateEvent, FurnitureListComposer, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListRemovedEvent, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, FurnitureOneWayDoorComposer, FurniturePickupAllComposer, FurniturePickupComposer, FurniturePlaceComposer, FurniturePlacePaintComposer, FurniturePostItPlaceComposer, FurniturePostItPlacedEvent, FurnitureRandomStateComposer, FurnitureStackHeightComposer, FurnitureStackHeightEvent, FurnitureWallAddEvent, FurnitureWallEvent, FurnitureWallMultiStateComposer, FurnitureWallRemoveEvent, FurnitureWallUpdateComposer, FurnitureWallUpdateEvent, FurniEditorBySpriteComposer, FurniEditorDeleteComposer, FurniEditorDetailComposer, FurniEditorDetailResultEvent, FurniEditorInteractionsComposer, FurniEditorInteractionsResultEvent, FurniEditorResultEvent, FurniEditorSearchComposer, FurniEditorSearchResultEvent, FurniEditorUpdateComposer, Game2AccountGameStatusMessageEvent, Game2CheckGameDirectoryStatusMessageComposer, Game2ExitGameMessageComposer, Game2GameChatMessageComposer, Game2GameDirectoryStatusMessageEvent, Game2GetAccountGameStatusMessageComposer, Game2GetWeeklyFriendsLeaderboardComposer, Game2GetWeeklyLeaderboardComposer, Game2InArenaQueueMessageEvent, Game2JoiningGameFailedMessageEvent, Game2LoadStageReadyMessageComposer, Game2PlayAgainMessageComposer, Game2RequestFullStatusUpdateMessageComposer, Game2StartingGameFailedMessageEvent, Game2StopCounterMessageEvent, Game2UserLeftGameMessageEvent, Game2WeeklyFriendsLeaderboardEvent, Game2WeeklyLeaderboardEvent, GameAchievementsMessageEvent, GameInviteMessageEvent, GameListMessageEvent, GameStatusMessageEvent, GameUnloadedMessageComposer, GenericErrorEvent, GetBadgePointLimitsComposer, GetBonusRareInfoMessageComposer, GetBotInventoryComposer, GetBundleDiscountRulesetComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetCatalogPageExpirationComposer, GetCatalogPageWithEarliestExpiryComposer, GetCategoriesWithUserCountMessageComposer, GetCfhChatlogMessageComposer, GetCfhStatusMessageComposer, GetClubGiftInfo, GetClubOffersMessageComposer, GetCommunityGoalEarnedPrizesMessageComposer, GetCommunityGoalHallOfFameMessageComposer, GetCommunityGoalProgressMessageComposer, GetConcurrentUsersGoalProgressMessageComposer, GetConcurrentUsersRewardMessageComposer, GetCraftableProductsComposer, GetCraftingRecipeComposer, GetCraftingRecipesAvailableComposer, GetCurrentTimingCodeMessageComposer, GetCustomRoomFilterMessageComposer, GetDailyQuestMessageComposer, GetDirectClubBuyAvailableComposer, GetEmailStatusComposer, GetExtendedProfileByNameMessageComposer, GetFaqCategoryMessageComposer, GetFaqTextMessageComposer, GetForumsListMessageComposer, GetForumStatsMessageComposer, GetFriendRequestsComposer, GetGameAchievementsMessageComposer, GetGameListMessageComposer, GetGameStatusMessageComposer, GetGiftMessageComposer, GetGiftWrappingConfigurationComposer, GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetGuideReportingStatusMessageComposer, GetHabboBasicMembershipExtendOfferComposer, GetHabboClubExtendOfferMessageComposer, GetHabboGroupBadgesMessageComposer, GetIgnoredUsersComposer, GetInterstitialMessageComposer, GetIsBadgeRequestFulfilledComposer, GetIsOfferGiftableComposer, GetIsUserPartOfCompetitionMessageComposer, GetItemDataComposer, GetJukeboxPlayListMessageComposer, GetLimitedOfferAppearingNextComposer, GetMarketplaceCanMakeOfferComposer, GetMarketplaceConfigurationMessageComposer, GetMarketplaceItemStatsComposer, GetMarketplaceOffersMessageComposer, GetMarketplaceOwnOffersMessageComposer, GetMessagesMessageComposer, GetModeratorRoomInfoMessageComposer, GetModeratorUserInfoMessageComposer, GetNextTargetedOfferComposer, GetNowPlayingMessageComposer, GetOccupiedTilesMessageComposer, GetOfficialRoomsMessageComposer, GetOfficialSongIdMessageComposer, GetPendingCallsForHelpMessageComposer, GetPetCommandsComposer, GetPopularRoomTagsMessageComposer, GetProductOfferComposer, GetPromoArticlesComposer, GetQuestsMessageComposer, GetQuizQuestionsComposer, GetRecyclerStatusMessageComposer, GetRentOrBuyoutOfferMessageComposer, GetResolutionAchievementsMessageComposer, GetRoomAdPurchaseInfoComposer, GetRoomChatlogMessageComposer, GetRoomEntryDataMessageComposer, GetRoomEntryTileMessageComposer, GetRoomVisitsMessageComposer, GetSeasonalCalendarDailyOfferComposer, GetSeasonalQuestsOnlyMessageComposer, GetSecondsUntilMessageComposer, GetSellablePetPalettesComposer, GetSongInfoMessageComposer, GetSoundMachinePlayListMessageComposer, GetSoundSettingsComposer, GetTalentTrackLevelMessageComposer, GetTargetedOfferComposer, GetThreadMessageComposer, GetThreadsMessageComposer, GetUnreadForumsCountMessageComposer, GetUserChatlogMessageComposer, GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer, GetUserGameAchievementsMessageComposer, GetUserSongDisksMessageComposer, GetUserTagsComposer, GetWardrobeMessageComposer, GetWeeklyGameRewardComposer, GetWeeklyGameRewardWinnersComposer, GetYoutubeDisplayStatusMessageComposer, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, GotMysteryBoxPrizeMessageEvent, GoToFlatMessageComposer, GroupAdminGiveComposer, GroupAdminTakeComposer, GroupBadgePartsComposer, GroupBadgePartsEvent, GroupBuyComposer, GroupBuyDataComposer, GroupBuyDataEvent, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupDeleteComposer, GroupDetailsChangedMessageEvent, GroupFavoriteComposer, GroupFurniContextMenuInfoMessageEvent, GroupInformationComposer, GroupInformationEvent, GroupJoinComposer, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembershipRequestedMessageEvent, GroupPurchasedEvent, GroupRemoveMemberComposer, GroupSaveBadgeComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer, GroupSettingsComposer, GroupSettingsEvent, GroupUnfavoriteComposer, GuestRoomSearchResultEvent, GuideOnDutyStatusMessageEvent, GuideReportingStatusMessageEvent, GuideSessionAttachedMessageEvent, GuideSessionCreateMessageComposer, GuideSessionDetachedMessageEvent, GuideSessionEndedMessageEvent, GuideSessionErrorMessageEvent, GuideSessionFeedbackMessageComposer, GuideSessionGetRequesterRoomMessageComposer, GuideSessionGuideDecidesMessageComposer, GuideSessionInvitedToGuideRoomMessageEvent, GuideSessionInviteRequesterMessageComposer, GuideSessionIsTypingMessageComposer, GuideSessionMessageMessageComposer, GuideSessionMessageMessageEvent, GuideSessionOnDutyUpdateMessageComposer, GuideSessionPartnerIsTypingMessageEvent, GuideSessionReportMessageComposer, GuideSessionRequesterCancelsMessageComposer, GuideSessionRequesterRoomMessageEvent, GuideSessionResolvedMessageComposer, GuideSessionStartedMessageEvent, GuideTicketCreationResultMessageEvent, GuideTicketResolutionMessageEvent, GuildBaseSearchMessageComposer, GuildEditFailedMessageEvent, GuildForumThreadsEvent, GuildMemberMgmtFailedMessageEvent, GuildMembershipsMessageEvent, HabboBroadcastMessageEvent, HabboClubExtendOfferMessageEvent, HabboClubOffersMessageEvent, HabboGroupBadgesMessageEvent, HabboGroupDeactivatedMessageEvent, HabboGroupJoinFailedMessageEvent, HabboSearchComposer, HabboSearchResultEvent, HarvestPetMessageComposer, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelMergeNameChangeEvent, HotelWillCloseInMinutesEvent, IdentityAccountsEvent, IgnoredUsersEvent, IgnoreResultEvent, IgnoreUserComposer, IgnoreUserIdComposer, InClientLinkEvent, IncomingHeader, InfoFeedEnableMessageEvent, InfoRetrieveMessageComposer, InitCameraMessageEvent, InitDiffieHandshakeEvent, InitDiffieHandshakeMessageComposer, InstantMessageErrorEvent, InterstitialMessageEvent, InterstitialShownMessageComposer, IsBadgeRequestFulfilledEvent, IsOfferGiftableMessageEvent, IssueCloseNotificationMessageEvent, IssueDeletedMessageEvent, IssueInfoMessageEvent, IssuePickFailedMessageEvent, IsUserPartOfCompetitionMessageEvent, ItemDataUpdateMessageEvent, JoinedQueueMessageEvent, JoiningQueueFailedMessageEvent, JoinQueueMessageComposer, JukeboxPlayListFullMessageEvent, JukeboxSongDisksMessageEvent, LagWarningReportMessageComposer, LeaveQueueMessageComposer, LeftQueueMessageEvent, LimitedEditionSoldOutEvent, LimitedOfferAppearingNextMessageEvent, LoadGameMessageEvent, LoadGameUrlEvent, LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, MaintenanceStatusMessageEvent, MakeOfferMessageComposer, MarkCatalogNewAdditionsPageOpenedComposer, MarketplaceBuyOfferResultEvent, MarketplaceCancelOfferResultEvent, MarketplaceCanMakeOfferResult, MarketplaceConfigurationEvent, MarketplaceItemStatsEvent, MarketplaceMakeOfferResult, MarketPlaceOffersEvent, MarketplaceOwnOffersEvent, MessageErrorEvent, MessengerInitComposer, MessengerInitEvent, MiniMailNewMessageEvent, MiniMailUnreadCountEvent, ModAlertMessageComposer, ModBanMessageComposer, ModerateMessageMessageComposer, ModerateRoomMessageComposer, ModerateThreadMessageComposer, ModeratorActionMessageComposer, ModeratorActionResultMessageEvent, ModeratorCautionEvent, ModeratorInitMessageEvent, ModeratorMessageEvent, ModeratorRoomInfoEvent, ModeratorToolPreferencesEvent, ModeratorUserInfoEvent, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModToolPreferencesComposer, ModToolSanctionComposer, ModTradingLockMessageComposer, MoodlightSettingsComposer, MoodlightSettingsSaveComposer, MoodlightTogggleStateComposer, MOTDNotificationEvent, MuteAllInRoomEvent, MyFavouriteRoomsSearchMessageComposer, MyFrequentRoomHistorySearchMessageComposer, MyFriendsRoomsSearchMessageComposer, MyGuildBasesSearchMessageComposer, MyRecommendedRoomsMessageComposer, MyRoomHistorySearchMessageComposer, MyRoomRightsSearchMessageComposer, MyRoomsSearchMessageComposer, MysteryBoxKeysEvent, MysteryBoxWaitingCanceledMessageComposer, NavigatorCategoryListModeComposer, NavigatorCollapsedEvent, NavigatorDeleteSavedSearchComposer, NavigatorHomeRoomEvent, NavigatorInitComposer, NavigatorLiftedEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSearchCloseComposer, NavigatorSearchComposer, NavigatorSearchesEvent, NavigatorSearchEvent, NavigatorSearchOpenComposer, NavigatorSearchSaveComposer, NavigatorSettingsEvent, NavigatorSettingsSaveComposer, NewConsoleMessageEvent, NewFriendRequestEvent, NewUserExperienceGetGiftsComposer, NewUserExperienceGiftOfferMessageEvent, NewUserExperienceNotCompleteEvent, NewUserExperienceScriptProceedComposer, NoobnessLevelMessageEvent, NoOwnedRoomsAlertMessageEvent, NoSuchFlatEvent, NotEnoughBalanceMessageEvent, NotificationDialogMessageEvent, NowPlayingMessageEvent, ObjectsDataUpdateEvent, ObjectsRollingEvent, OfferRewardDeliveredMessageEvent, OfficialSongIdMessageEvent, OneWayDoorStatusMessageEvent, OpenCampaignCalendarDoorAsStaffComposer, OpenCampaignCalendarDoorComposer, OpenMessageComposer, OpenMysteryTrophyMessageComposer, OpenPetPackageMessageComposer, OpenPetPackageRequestedMessageEvent, OpenPetPackageResultMessageEvent, OpenPresentComposer, OpenQuestTrackerMessageComposer, OpenWelcomeGiftComposer, OutgoingHeader, PeerUsersClassificationMessageComposer, PerformanceLogMessageComposer, PerkAllowancesMessageEvent, PetAddedToInventoryEvent, PetBreedingResultEvent, PetExperienceEvent, PetFigureUpdateEvent, PetInfoEvent, PetInventoryEvent, PetLevelNotificationEvent, PetLevelUpdateMessageEvent, PetMountComposer, PetMoveComposer, PetPlaceComposer, PetPlacingErrorEvent, PetReceivedMessageEvent, PetRemoveComposer, PetRemovedFromInventory, PetRespectComposer, PetRespectNoficationEvent, PetScratchFailedMessageEvent, PetSelectedMessageComposer, PetStatusUpdateEvent, PetSupplementComposer, PetSupplementedNotificationEvent, PetTrainingPanelMessageEvent, PhoneCollectionStateMessageEvent, PhotoCompetitionMessageComposer, PickIssuesMessageComposer, PlayListMessageEvent, PlayListSongAddedMessageEvent, PollAnswerComposer, PollContentsEvent, PollErrorEvent, PollOfferEvent, PollRejectComposer, PollStartComposer, PongMessageComposer, PopularRoomsSearchMessageComposer, PopularRoomTagsResultEvent, PostMessageMessageComposer, PostMessageMessageEvent, PostQuizAnswersComposer, PostThreadMessageEvent, PresentOpenedMessageEvent, ProductOfferEvent, PromoArticlesMessageEvent, PublishPhotoMessageComposer, PurchaseBasicMembershipExtensionComposer, PurchaseErrorMessageEvent, PurchaseFromCatalogAsGiftComposer, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, PurchasePhotoMessageComposer, PurchaseRoomAdMessageComposer, PurchaseTargetedOfferComposer, PurchaseVipMembershipExtensionComposer, QuestCancelledMessageEvent, QuestCompletedMessageEvent, QuestDailyMessageEvent, QuestionAnsweredEvent, QuestionEvent, QuestionFinishedEvent, QuestMessageEvent, QuestsMessageEvent, QuizDataMessageEvent, QuizResultsMessageEvent, RateFlatMessageComposer, RecycleItemsMessageComposer, RecyclerFinishedMessageEvent, RecyclerStatusMessageEvent, RedeemCommunityGoalPrizeMessageComposer, RedeemItemClothingComposer, RedeemMarketplaceOfferCreditsMessageComposer, RedeemVoucherMessageComposer, RejectQuestMessageComposer, RelationshipStatusInfoEvent, ReleaseIssuesMessageComposer, RemainingMuteEvent, RemoveAllRightsMessageComposer, RemoveFriendComposer, RemoveJukeboxDiskComposer, RemoveOwnRoomRightsRoomMessageComposer, RemovePetSaddleComposer, RemoveWallItemComposer, RenderRoomMessageComposer, RenderRoomThumbnailMessageComposer, RentableSpaceCancelRentMessageComposer, RentableSpaceRentFailedMessageEvent, RentableSpaceRentMessageComposer, RentableSpaceRentOkMessageEvent, RentableSpaceStatusMessageComposer, RentableSpaceStatusMessageEvent, RequestABadgeComposer, RequestAchievementsMessageComposer, RequestBadgesComposer, RequestBotCommandConfigurationComposer, RequestCameraConfigurationComposer, RequestFriendComposer, RequestFurniInventoryWhenNotInRoomComposer, RequestPetInfoComposer, RequestPetsComposer, RequestSpamWallPostItMessageEvent, ResetPhoneNumberStateMessageComposer, ResetResolutionAchievementMessageComposer, RespectReceivedEvent, RestoreClientMessageEvent, RoomAdErrorEvent, RoomAdEventTabAdClickedComposer, RoomAdEventTabViewedComposer, RoomAdPurchaseInfoEvent, RoomAdPurchaseInitiatedComposer, RoomAdSearchMessageComposer, RoomAmbassadorAlertComposer, RoomBannedUsersComposer, RoomBanUserComposer, RoomChatlogEvent, RoomChatSettingsEvent, RoomCompetitionInitMessageComposer, RoomDeleteComposer, RoomDimmerPresetsEvent, RoomDoorbellAcceptedEvent, RoomDoorbellAccessComposer, RoomEnterComposer, RoomEnterErrorEvent, RoomEnterEvent, RoomEntryInfoMessageEvent, RoomEntryTileMessageEvent, RoomEventCancelEvent, RoomEventEvent, RoomFilterSettingsMessageEvent, RoomForwardEvent, RoomGiveRightsComposer, RoomHeightMapEvent, RoomHeightMapUpdateEvent, RoomInviteErrorEvent, RoomInviteEvent, RoomKickUserComposer, RoomMessageNotificationMessageEvent, RoomMuteComposer, RoomMuteUserComposer, RoomNetworkOpenConnectionMessageComposer, RoomOccupiedTilesMessageEvent, RoomPaintEvent, RoomPollResultEvent, RoomReadyMessageEvent, RoomRightsClearEvent, RoomRightsEvent, RoomRightsOwnerEvent, RoomScoreEvent, RoomSettingsComposer, RoomSettingsDataEvent, RoomSettingsErrorEvent, RoomSettingsSavedEvent, RoomSettingsSaveErrorEvent, RoomSettingsUpdatedEvent, RoomsWhereMyFriendsAreSearchMessageComposer, RoomsWithHighestScoreSearchMessageComposer, RoomTakeRightsComposer, RoomTextSearchMessageComposer, RoomThumbnailUpdateResultEvent, RoomUnbanUserComposer, RoomUnitActionComposer, RoomUnitBackgroundComposer, RoomUnitChatComposer, RoomUnitChatEvent, RoomUnitChatShoutComposer, RoomUnitChatShoutEvent, RoomUnitChatStyleComposer, RoomUnitChatWhisperComposer, RoomUnitChatWhisperEvent, RoomUnitDanceComposer, RoomUnitDanceEvent, RoomUnitDropHandItemComposer, RoomUnitEffectEvent, RoomUnitEvent, RoomUnitExpressionEvent, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUnitHandItemEvent, RoomUnitHandItemReceivedEvent, RoomUnitIdleEvent, RoomUnitInfoEvent, RoomUnitLookComposer, RoomUnitNumberEvent, RoomUnitPostureComposer, RoomUnitRemoveEvent, RoomUnitSignComposer, RoomUnitStatusEvent, RoomUnitTypingEvent, RoomUnitTypingStartComposer, RoomUnitTypingStopComposer, RoomUnitWalkComposer, RoomUsersClassificationMessageComposer, RoomUsersWithRightsComposer, RoomVisitsEvent, RoomVisualizationSettingsEvent, SanctionStatusEvent, SaveRoomSettingsComposer, SaveWardrobeOutfitMessageComposer, ScrGetKickbackInfoMessageComposer, ScrSendKickbackInfoMessageEvent, SearchFaqsMessageComposer, SeasonalCalendarDailyOfferMessageEvent, SeasonalQuestsMessageEvent, SecondsUntilMessageEvent, SelectClubGiftComposer, SellablePetPalettesMessageEvent, SendMessageComposer, SendRoomInviteComposer, SetActivatedBadgesComposer, SetClothingChangeDataMessageComposer, SetItemDataMessageComposer, SetObjectDataMessageComposer, SetPhoneNumberVerificationStatusMessageComposer, SetRelationshipStatusComposer, SetRoomSessionTagsMessageComposer, SetTargetedOfferStateComposer, SetYoutubeDisplayPlaylistMessageComposer, ShopTargetedOfferViewedComposer, ShowEnforceRoomCategoryDialogEvent, ShowMysteryBoxWaitMessageEvent, SimpleAlertMessageEvent, SSOTicketMessageComposer, StartCampaignMessageComposer, StartRoomPollEvent, SubmitRoomToCompetitionMessageComposer, TalentLevelUpEvent, TalentTrackComposer, TalentTrackLevelMessageEvent, TalentTrackMessageEvent, TargetedOfferEvent, TargetedOfferNotFoundEvent, ThreadMessagesMessageEvent, ThumbnailStatusMessageEvent, TogglePetBreedingComposer, TogglePetRidingComposer, ToggleStaffPickMessageComposer, TradingAcceptComposer, TradingAcceptEvent, TradingCancelComposer, TradingCloseComposer, TradingCloseEvent, TradingCompletedEvent, TradingConfirmationComposer, TradingConfirmationEvent, TradingListAddItemComposer, TradingListAddItemsComposer, TradingListItemEvent, TradingListItemRemoveComposer, TradingNoSuchItemEvent, TradingNotOpenEvent, TradingOpenComposer, TradingOpenEvent, TradingOpenFailedEvent, TradingOtherNotAllowedEvent, TradingUnacceptComposer, TradingYouAreNotAllowedEvent, TraxSongInfoMessageEvent, TryPhoneNumberMessageComposer, TryPhoneNumberResultMessageEvent, TryVerificationCodeResultMessageEvent, UnblockGroupMemberMessageComposer, UnignoreUserComposer, UniqueIDMessageComposer, UnloadGameMessageEvent, UnreadForumsCountMessageEvent, UnseenItemsEvent, UnseenResetCategoryComposer, UnseenResetItemsComposer, UpdateActionMessageComposer, UpdateConditionMessageComposer, UpdateFloorPropertiesMessageComposer, UpdateForumReadMarkerMessageComposer, UpdateForumSettingsMessageComposer, UpdateFurniturePositionComposer, UpdateHomeRoomMessageComposer, UpdateMessageMessageEvent, UpdateRoomCategoryAndTradeSettingsComposer, UpdateRoomFilterMessageComposer, UpdateRoomThumbnailMessageComposer, UpdateThreadMessageComposer, UpdateThreadMessageEvent, UpdateTriggerMessageComposer, UsePetProductComposer, UserBannedMessageEvent, UserChatlogEvent, UserClassificationMessageEvent, UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserEventCatsEvent, UserFigureComposer, UserFlatCatsEvent, UserGameAchievementsMessageEvent, UserInfoEvent, UserMottoComposer, UserNameChangeMessageEvent, UserPermissionsEvent, UserProfileComposer, UserProfileEvent, UserRelationshipsComposer, UserRespectComposer, UserSettingsCameraFollowComposer, UserSettingsEvent, UserSettingsOldChatComposer, UserSettingsRoomInvitesComposer, UserSettingsSoundComposer, UserSongDisksInventoryMessageEvent, UserSubscriptionComposer, UserSubscriptionEvent, UserTagsMessageEvent, UserUnbannedFromRoomEvent, UserWardrobePageEvent, VerifyCodeMessageComposer, VersionCheckMessageComposer, VisitUserComposer, VoteForRoomMessageComposer, VotePollCounterMessageComposer, VoucherRedeemErrorMessageEvent, VoucherRedeemOkMessageEvent, WardrobeMessageEvent, WeeklyCompetitiveFriendsLeaderboardEvent, WeeklyCompetitiveLeaderboardEvent, WeeklyGameRewardEvent, WeeklyGameRewardWinnersEvent, WelcomeGiftChangeEmailComposer, WelcomeGiftChangeEmailResultEvent, WelcomeGiftStatusEvent, WiredFurniActionEvent, WiredFurniConditionEvent, WiredFurniTriggerEvent, WiredOpenEvent, WiredRewardResultMessageEvent, WiredSaveSuccessEvent, WiredValidationErrorEvent, YouArePlayingGameEvent, YouAreSpectatorMessageEvent, YoutubeControlVideoMessageEvent, YoutubeDisplayPlaylistsEvent, YoutubeDisplayVideoMessageEvent, ClickUserMessageComposer } from './messages'; +import { AcceptFriendMessageComposer, AcceptFriendResultEvent, AcceptGameInviteMessageComposer, AcceptQuestMessageComposer, AccountSafetyLockStatusChangeMessageEvent, AchievementEvent, AchievementNotificationMessageEvent, AchievementResolutionCompletedMessageEvent, AchievementResolutionProgressMessageEvent, AchievementResolutionsMessageEvent, AchievementsEvent, AchievementsScoreEvent, ActivateQuestMessageComposer, ActivityPointNotificationMessageEvent, AddFavouriteRoomMessageComposer, AddJukeboxDiskComposer, AddSpamWallPostItMessageComposer, ApplySnapshotMessageComposer, ApplyTonerComposer, ApproveAllMembershipRequestsMessageComposer, ApproveNameMessageComposer, ApproveNameMessageEvent, AreaHideMessageEvent, AuthenticatedEvent, AuthenticationMessageComposer, AvailabilityStatusMessageEvent, AvailabilityTimeMessageEvent, AvailableCommandsEvent, AvatarEffectActivatedComposer, AvatarEffectActivatedEvent, AvatarEffectAddedEvent, AvatarEffectExpiredEvent, AvatarEffectSelectedComposer, AvatarEffectSelectedEvent, AvatarEffectsEvent, BadgePointLimitsEvent, BadgeReceivedEvent, BadgesEvent, BannedUsersFromRoomEvent, BonusRareInfoMessageEvent, BotAddedToInventoryEvent, BotCommandConfigurationEvent, BotErrorEvent, BotForceOpenContextMenuEvent, BotInventoryMessageEvent, BotPlaceComposer, BotReceivedMessageEvent, BotRemoveComposer, BotRemovedFromInventoryEvent, BotSkillListUpdateEvent, BotSkillSaveComposer, BreedPetsMessageComposer, BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, BundleDiscountRulesetMessageEvent, BuyMarketplaceOfferMessageComposer, BuyMarketplaceTokensMessageComposer, CallForHelpDisabledNotifyMessageEvent, CallForHelpFromForumMessageMessageComposer, CallForHelpFromForumThreadMessageComposer, CallForHelpFromIMMessageComposer, CallForHelpFromPhotoMessageComposer, CallForHelpFromSelfieMessageComposer, CallForHelpMessageComposer, CallForHelpPendingCallsDeletedMessageEvent, CallForHelpPendingCallsMessageEvent, CallForHelpReplyMessageEvent, CallForHelpResultMessageEvent, CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraSnapshotMessageEvent, CameraStorageUrlMessageEvent, CampaignCalendarDataMessageEvent, CampaignCalendarDoorOpenedMessageEvent, CancelEventMessageComposer, CancelMarketplaceOfferMessageComposer, CancelMysteryBoxWaitMessageEvent, CancelPetBreedingComposer, CancelQuestMessageComposer, CanCreateRoomEvent, CanCreateRoomEventEvent, CanCreateRoomMessageComposer, CatalogAdminCreateOfferComposer, CatalogAdminCreatePageComposer, CatalogAdminDeleteOfferComposer, CatalogAdminDeletePageComposer, CatalogAdminMoveOfferComposer, CatalogAdminMovePageComposer, CatalogAdminPublishComposer, CatalogAdminResultEvent, CatalogAdminSaveOfferComposer, CatalogAdminSavePageComposer, CatalogGroupsComposer, CatalogPageExpirationEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPageWithEarliestExpiryMessageEvent, CatalogPublishedMessageEvent, CategoriesWithVisitorCountEvent, CfhChatlogEvent, CfhSanctionMessageEvent, CfhTopicsInitEvent, ChangeEmailComposer, ChangeEmailResultEvent, ChangeQueueMessageComposer, ChangeUserNameMessageComposer, ChangeUserNameResultMessageEvent, ChatReviewGuideDecidesOnOfferMessageComposer, ChatReviewGuideDetachedMessageComposer, ChatReviewGuideVoteMessageComposer, ChatReviewSessionCreateMessageComposer, ChatReviewSessionDetachedMessageEvent, ChatReviewSessionOfferedToGuideMessageEvent, ChatReviewSessionResultsMessageEvent, ChatReviewSessionStartedMessageEvent, ChatReviewSessionVotingStatusMessageEvent, CheckUserNameMessageComposer, CheckUserNameResultMessageEvent, ClickFurniMessageComposer, ClientHelloMessageComposer, ClientPingEvent, CloseIssueDefaultActionMessageComposer, CloseIssuesMessageComposer, ClubGiftInfoEvent, ClubGiftNotificationEvent, ClubGiftSelectedEvent, CommunityGoalEarnedPrizesMessageEvent, CommunityGoalHallOfFameMessageEvent, CommunityGoalProgressMessageEvent, CommunityGoalVoteMessageComposer, CommunityGoalVoteMessageEvent, CompetitionEntrySubmitResultEvent, CompetitionRoomsDataMessageEvent, CompetitionRoomsSearchMessageComposer, CompetitionStatusMessageEvent, CompetitionVotingInfoMessageEvent, CompleteDiffieHandshakeEvent, CompleteDiffieHandshakeMessageComposer, CompostPlantMessageComposer, ConcurrentUsersGoalProgressMessageEvent, ConfirmPetBreedingComposer, ConnectionErrorEvent, ControlYoutubeDisplayPlaybackMessageComposer, ConvertedRoomIdEvent, ConvertGlobalRoomIdMessageComposer, CraftableProductsEvent, CraftComposer, CraftingRecipeEvent, CraftingRecipesAvailableEvent, CraftingResultEvent, CraftSecretComposer, CreateFlatMessageComposer, CurrentTimingCodeMessageEvent, CustomUserNotificationMessageEvent, DeclineFriendMessageComposer, DefaultSanctionMessageComposer, DeleteBadgeMessageComposer, DeleteFavouriteRoomMessageComposer, DeleteItemMessageComposer, DeletePendingCallsForHelpMessageComposer, DeletePetMessageComposer, DesktopViewComposer, DesktopViewEvent, DiceValueMessageEvent, DirectSMSClubBuyAvailableMessageEvent, DisconnectMessageComposer, DisconnectReasonEvent, DoorbellMessageEvent, EditEventMessageComposer, ElementPointerMessageEvent, EmailStatusResultEvent, EpicPopupMessageEvent, ExtendedProfileChangedMessageEvent, ExtendRentOrBuyoutFurniMessageComposer, ExtendRentOrBuyoutStripItemMessageComposer, FavoriteMembershipUpdateMessageEvent, FavouriteChangedEvent, FavouritesEvent, FigureSetIdsMessageEvent, FigureUpdateEvent, FindFriendsProcessResultEvent, FindNewFriendsMessageComposer, FireworkChargeDataEvent, FlatAccessDeniedMessageEvent, FlatControllerAddedEvent, FlatControllerRemovedEvent, FlatControllersEvent, FlatCreatedEvent, FloodControlEvent, FloorHeightMapEvent, FollowFriendFailedEvent, FollowFriendMessageComposer, ForumDataMessageEvent, ForumsListMessageEvent, ForwardToACompetitionRoomMessageComposer, ForwardToARandomPromotedRoomMessageComposer, ForwardToASubmittableRoomMessageComposer, ForwardToRandomCompetitionRoomMessageComposer, ForwardToSomeRoomMessageComposer, FriendFurniConfirmLockMessageComposer, FriendListFragmentEvent, FriendListUpdateComposer, FriendListUpdateEvent, FriendNotificationEvent, FriendRequestQuestCompleteMessageComposer, FriendRequestsEvent, FurniRentOrBuyoutOfferMessageEvent, FurnitureAliasesComposer, FurnitureAliasesEvent, FurnitureColorWheelComposer, FurnitureDataEvent, FurnitureDataReloadEvent, FurnitureDiceActivateComposer, FurnitureDiceDeactivateComposer, FurnitureExchangeComposer, FurnitureFloorAddEvent, FurnitureFloorEvent, FurnitureFloorRemoveEvent, FurnitureFloorUpdateComposer, FurnitureFloorUpdateEvent, FurnitureGroupInfoComposer, FurnitureListAddOrUpdateEvent, FurnitureListComposer, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListRemovedEvent, FurnitureMannequinSaveLookComposer, FurnitureMannequinSaveNameComposer, FurnitureMultiStateComposer, FurnitureOneWayDoorComposer, FurniturePickupAllComposer, FurniturePickupComposer, FurniturePlaceComposer, FurniturePlacePaintComposer, FurniturePostItPlaceComposer, FurniturePostItPlacedEvent, FurnitureRandomStateComposer, FurnitureStackHeightComposer, FurnitureStackHeightEvent, FurnitureWallAddEvent, FurnitureWallEvent, FurnitureWallMultiStateComposer, FurnitureWallRemoveEvent, FurnitureWallUpdateComposer, FurnitureWallUpdateEvent, FurniEditorBySpriteComposer, FurniEditorDeleteComposer, FurniEditorDetailComposer, FurniEditorDetailResultEvent, FurniEditorInteractionsComposer, FurniEditorInteractionsResultEvent, FurniEditorResultEvent, FurniEditorSearchComposer, FurniEditorSearchResultEvent, FurniEditorUpdateComposer, Game2AccountGameStatusMessageEvent, Game2CheckGameDirectoryStatusMessageComposer, Game2ExitGameMessageComposer, Game2GameChatMessageComposer, Game2GameDirectoryStatusMessageEvent, Game2GetAccountGameStatusMessageComposer, Game2GetWeeklyFriendsLeaderboardComposer, Game2GetWeeklyLeaderboardComposer, Game2InArenaQueueMessageEvent, Game2JoiningGameFailedMessageEvent, Game2LoadStageReadyMessageComposer, Game2PlayAgainMessageComposer, Game2RequestFullStatusUpdateMessageComposer, Game2StartingGameFailedMessageEvent, Game2StopCounterMessageEvent, Game2UserLeftGameMessageEvent, Game2WeeklyFriendsLeaderboardEvent, Game2WeeklyLeaderboardEvent, GameAchievementsMessageEvent, GameInviteMessageEvent, GameListMessageEvent, GameStatusMessageEvent, GameUnloadedMessageComposer, GenericErrorEvent, GetBadgePointLimitsComposer, GetBonusRareInfoMessageComposer, GetBotInventoryComposer, GetBundleDiscountRulesetComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetCatalogPageExpirationComposer, GetCatalogPageWithEarliestExpiryComposer, GetCategoriesWithUserCountMessageComposer, GetCfhChatlogMessageComposer, GetCfhStatusMessageComposer, GetClubGiftInfo, GetClubOffersMessageComposer, GetCommunityGoalEarnedPrizesMessageComposer, GetCommunityGoalHallOfFameMessageComposer, GetCommunityGoalProgressMessageComposer, GetConcurrentUsersGoalProgressMessageComposer, GetConcurrentUsersRewardMessageComposer, GetCraftableProductsComposer, GetCraftingRecipeComposer, GetCraftingRecipesAvailableComposer, GetCurrentTimingCodeMessageComposer, GetCustomRoomFilterMessageComposer, GetDailyQuestMessageComposer, GetDirectClubBuyAvailableComposer, GetEmailStatusComposer, GetExtendedProfileByNameMessageComposer, GetFaqCategoryMessageComposer, GetFaqTextMessageComposer, GetForumsListMessageComposer, GetForumStatsMessageComposer, GetFriendRequestsComposer, GetGameAchievementsMessageComposer, GetGameListMessageComposer, GetGameStatusMessageComposer, GetGiftMessageComposer, GetGiftWrappingConfigurationComposer, GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetGuideReportingStatusMessageComposer, GetHabboBasicMembershipExtendOfferComposer, GetHabboClubExtendOfferMessageComposer, GetHabboGroupBadgesMessageComposer, GetIgnoredUsersComposer, GetInterstitialMessageComposer, GetIsBadgeRequestFulfilledComposer, GetIsOfferGiftableComposer, GetIsUserPartOfCompetitionMessageComposer, GetItemDataComposer, GetJukeboxPlayListMessageComposer, GetLimitedOfferAppearingNextComposer, GetMarketplaceCanMakeOfferComposer, GetMarketplaceConfigurationMessageComposer, GetMarketplaceItemStatsComposer, GetMarketplaceOffersMessageComposer, GetMarketplaceOwnOffersMessageComposer, GetMessagesMessageComposer, GetModeratorRoomInfoMessageComposer, GetModeratorUserInfoMessageComposer, GetNextTargetedOfferComposer, GetNowPlayingMessageComposer, GetOccupiedTilesMessageComposer, GetOfficialRoomsMessageComposer, GetOfficialSongIdMessageComposer, GetPendingCallsForHelpMessageComposer, GetPetCommandsComposer, GetPopularRoomTagsMessageComposer, GetProductOfferComposer, GetPromoArticlesComposer, GetQuestsMessageComposer, GetQuizQuestionsComposer, GetRecyclerStatusMessageComposer, GetRentOrBuyoutOfferMessageComposer, GetResolutionAchievementsMessageComposer, GetRoomAdPurchaseInfoComposer, GetRoomChatlogMessageComposer, GetRoomEntryDataMessageComposer, GetRoomEntryTileMessageComposer, GetRoomVisitsMessageComposer, GetSeasonalCalendarDailyOfferComposer, GetSeasonalQuestsOnlyMessageComposer, GetSecondsUntilMessageComposer, GetSellablePetPalettesComposer, GetSongInfoMessageComposer, GetSoundMachinePlayListMessageComposer, GetSoundSettingsComposer, GetTalentTrackLevelMessageComposer, GetTargetedOfferComposer, GetThreadMessageComposer, GetThreadsMessageComposer, GetUnreadForumsCountMessageComposer, GetUserChatlogMessageComposer, GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer, GetUserGameAchievementsMessageComposer, GetUserSongDisksMessageComposer, GetUserTagsComposer, GetWardrobeMessageComposer, GetWeeklyGameRewardComposer, GetWeeklyGameRewardWinnersComposer, GetYoutubeDisplayStatusMessageComposer, GiftReceiverNotFoundEvent, GiftWrappingConfigurationEvent, GotMysteryBoxPrizeMessageEvent, GoToFlatMessageComposer, GroupAdminGiveComposer, GroupAdminTakeComposer, GroupBadgePartsComposer, GroupBadgePartsEvent, GroupBuyComposer, GroupBuyDataComposer, GroupBuyDataEvent, GroupConfirmMemberRemoveEvent, GroupConfirmRemoveMemberComposer, GroupDeleteComposer, GroupDetailsChangedMessageEvent, GroupFavoriteComposer, GroupFurniContextMenuInfoMessageEvent, GroupInformationComposer, GroupInformationEvent, GroupJoinComposer, GroupMembersComposer, GroupMembersEvent, GroupMembershipAcceptComposer, GroupMembershipDeclineComposer, GroupMembershipRequestedMessageEvent, GroupPurchasedEvent, GroupRemoveMemberComposer, GroupSaveBadgeComposer, GroupSaveColorsComposer, GroupSaveInformationComposer, GroupSavePreferencesComposer, GroupSettingsComposer, GroupSettingsEvent, GroupUnfavoriteComposer, GuestRoomSearchResultEvent, GuideOnDutyStatusMessageEvent, GuideReportingStatusMessageEvent, GuideSessionAttachedMessageEvent, GuideSessionCreateMessageComposer, GuideSessionDetachedMessageEvent, GuideSessionEndedMessageEvent, GuideSessionErrorMessageEvent, GuideSessionFeedbackMessageComposer, GuideSessionGetRequesterRoomMessageComposer, GuideSessionGuideDecidesMessageComposer, GuideSessionInvitedToGuideRoomMessageEvent, GuideSessionInviteRequesterMessageComposer, GuideSessionIsTypingMessageComposer, GuideSessionMessageMessageComposer, GuideSessionMessageMessageEvent, GuideSessionOnDutyUpdateMessageComposer, GuideSessionPartnerIsTypingMessageEvent, GuideSessionReportMessageComposer, GuideSessionRequesterCancelsMessageComposer, GuideSessionRequesterRoomMessageEvent, GuideSessionResolvedMessageComposer, GuideSessionStartedMessageEvent, GuideTicketCreationResultMessageEvent, GuideTicketResolutionMessageEvent, GuildBaseSearchMessageComposer, GuildEditFailedMessageEvent, GuildForumThreadsEvent, GuildMemberMgmtFailedMessageEvent, GuildMembershipsMessageEvent, HabboBroadcastMessageEvent, HabboClubExtendOfferMessageEvent, HabboClubOffersMessageEvent, HabboGroupBadgesMessageEvent, HabboGroupDeactivatedMessageEvent, HabboGroupJoinFailedMessageEvent, HabboSearchComposer, HabboSearchResultEvent, HarvestPetMessageComposer, HotelClosedAndOpensEvent, HotelClosesAndWillOpenAtEvent, HotelMergeNameChangeEvent, HotelWillCloseInMinutesEvent, IdentityAccountsEvent, IgnoredUsersEvent, IgnoreResultEvent, IgnoreUserComposer, IgnoreUserIdComposer, InClientLinkEvent, IncomingHeader, InfoFeedEnableMessageEvent, InfoRetrieveMessageComposer, InitCameraMessageEvent, InitDiffieHandshakeEvent, InitDiffieHandshakeMessageComposer, InstantMessageErrorEvent, InterstitialMessageEvent, InterstitialShownMessageComposer, IsBadgeRequestFulfilledEvent, IsOfferGiftableMessageEvent, IssueCloseNotificationMessageEvent, IssueDeletedMessageEvent, IssueInfoMessageEvent, IssuePickFailedMessageEvent, IsUserPartOfCompetitionMessageEvent, ItemDataUpdateMessageEvent, JoinedQueueMessageEvent, JoiningQueueFailedMessageEvent, JoinQueueMessageComposer, JukeboxPlayListFullMessageEvent, JukeboxSongDisksMessageEvent, LagWarningReportMessageComposer, LeaveQueueMessageComposer, LeftQueueMessageEvent, LimitedEditionSoldOutEvent, LimitedOfferAppearingNextMessageEvent, LoadGameMessageEvent, LoadGameUrlEvent, LoveLockFurniFinishedEvent, LoveLockFurniFriendConfirmedEvent, LoveLockFurniStartEvent, MaintenanceStatusMessageEvent, MakeOfferMessageComposer, MarkCatalogNewAdditionsPageOpenedComposer, MarketplaceBuyOfferResultEvent, MarketplaceCancelOfferResultEvent, MarketplaceCanMakeOfferResult, MarketplaceConfigurationEvent, MarketplaceItemStatsEvent, MarketplaceMakeOfferResult, MarketPlaceOffersEvent, MarketplaceOwnOffersEvent, MessageErrorEvent, MessengerInitComposer, MessengerInitEvent, MiniMailNewMessageEvent, MiniMailUnreadCountEvent, ModAlertMessageComposer, ModBanMessageComposer, ModerateMessageMessageComposer, ModerateRoomMessageComposer, ModerateThreadMessageComposer, ModeratorActionMessageComposer, ModeratorActionResultMessageEvent, ModeratorCautionEvent, ModeratorInitMessageEvent, ModeratorMessageEvent, ModeratorRoomInfoEvent, ModeratorToolPreferencesEvent, ModeratorUserInfoEvent, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModToolPreferencesComposer, ModToolSanctionComposer, ModTradingLockMessageComposer, MoodlightSettingsComposer, MoodlightSettingsSaveComposer, MoodlightTogggleStateComposer, MOTDNotificationEvent, MuteAllInRoomEvent, MyFavouriteRoomsSearchMessageComposer, MyFrequentRoomHistorySearchMessageComposer, MyFriendsRoomsSearchMessageComposer, MyGuildBasesSearchMessageComposer, MyRecommendedRoomsMessageComposer, MyRoomHistorySearchMessageComposer, MyRoomRightsSearchMessageComposer, MyRoomsSearchMessageComposer, MysteryBoxKeysEvent, MysteryBoxWaitingCanceledMessageComposer, NavigatorCategoryListModeComposer, NavigatorCollapsedEvent, NavigatorDeleteSavedSearchComposer, NavigatorHomeRoomEvent, NavigatorInitComposer, NavigatorLiftedEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSearchCloseComposer, NavigatorSearchComposer, NavigatorSearchesEvent, NavigatorSearchEvent, NavigatorSearchOpenComposer, NavigatorSearchSaveComposer, NavigatorSettingsEvent, NavigatorSettingsSaveComposer, NewConsoleMessageEvent, NewFriendRequestEvent, NewUserExperienceGetGiftsComposer, NewUserExperienceGiftOfferMessageEvent, NewUserExperienceNotCompleteEvent, NewUserExperienceScriptProceedComposer, NoobnessLevelMessageEvent, NoOwnedRoomsAlertMessageEvent, NoSuchFlatEvent, NotEnoughBalanceMessageEvent, NotificationDialogMessageEvent, NowPlayingMessageEvent, ObjectsDataUpdateEvent, ObjectsRollingEvent, OfferRewardDeliveredMessageEvent, OfficialSongIdMessageEvent, OneWayDoorStatusMessageEvent, OpenCampaignCalendarDoorAsStaffComposer, OpenCampaignCalendarDoorComposer, OpenMessageComposer, OpenMysteryTrophyMessageComposer, OpenPetPackageMessageComposer, OpenPetPackageRequestedMessageEvent, OpenPetPackageResultMessageEvent, OpenPresentComposer, OpenQuestTrackerMessageComposer, OpenWelcomeGiftComposer, OutgoingHeader, PeerUsersClassificationMessageComposer, PerformanceLogMessageComposer, PerkAllowancesMessageEvent, PetAddedToInventoryEvent, PetBreedingResultEvent, PetExperienceEvent, PetFigureUpdateEvent, PetInfoEvent, PetInventoryEvent, PetLevelNotificationEvent, PetLevelUpdateMessageEvent, PetMountComposer, PetMoveComposer, PetPlaceComposer, PetPlacingErrorEvent, PetReceivedMessageEvent, PetRemoveComposer, PetRemovedFromInventory, PetRespectComposer, PetRespectNoficationEvent, PetScratchFailedMessageEvent, PetSelectedMessageComposer, PetStatusUpdateEvent, PetSupplementComposer, PetSupplementedNotificationEvent, PetTrainingPanelMessageEvent, PhoneCollectionStateMessageEvent, PhotoCompetitionMessageComposer, PickIssuesMessageComposer, PlayListMessageEvent, PlayListSongAddedMessageEvent, PollAnswerComposer, PollContentsEvent, PollErrorEvent, PollOfferEvent, PollRejectComposer, PollStartComposer, PongMessageComposer, PopularRoomsSearchMessageComposer, PopularRoomTagsResultEvent, PostMessageMessageComposer, PostMessageMessageEvent, PostQuizAnswersComposer, PostThreadMessageEvent, PresentOpenedMessageEvent, ProductOfferEvent, PromoArticlesMessageEvent, PublishPhotoMessageComposer, PurchaseBasicMembershipExtensionComposer, PurchaseErrorMessageEvent, PurchaseFromCatalogAsGiftComposer, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, PurchasePhotoMessageComposer, PurchaseRoomAdMessageComposer, PurchaseTargetedOfferComposer, PurchaseVipMembershipExtensionComposer, QuestCancelledMessageEvent, QuestCompletedMessageEvent, QuestDailyMessageEvent, QuestionAnsweredEvent, QuestionEvent, QuestionFinishedEvent, QuestMessageEvent, QuestsMessageEvent, QuizDataMessageEvent, QuizResultsMessageEvent, RateFlatMessageComposer, RecycleItemsMessageComposer, RecyclerFinishedMessageEvent, RecyclerStatusMessageEvent, RedeemCommunityGoalPrizeMessageComposer, RedeemItemClothingComposer, RedeemMarketplaceOfferCreditsMessageComposer, RedeemVoucherMessageComposer, RejectQuestMessageComposer, RelationshipStatusInfoEvent, ReleaseIssuesMessageComposer, RemainingMuteEvent, RemoveAllRightsMessageComposer, RemoveFriendComposer, RemoveJukeboxDiskComposer, RemoveOwnRoomRightsRoomMessageComposer, RemovePetSaddleComposer, RemoveWallItemComposer, RenderRoomMessageComposer, RenderRoomThumbnailMessageComposer, RentableSpaceCancelRentMessageComposer, RentableSpaceRentFailedMessageEvent, RentableSpaceRentMessageComposer, RentableSpaceRentOkMessageEvent, RentableSpaceStatusMessageComposer, RentableSpaceStatusMessageEvent, RequestABadgeComposer, RequestAchievementsMessageComposer, RequestBadgesComposer, RequestBotCommandConfigurationComposer, RequestCameraConfigurationComposer, RequestFriendComposer, RequestFurniInventoryWhenNotInRoomComposer, RequestPetInfoComposer, RequestPetsComposer, RequestSpamWallPostItMessageEvent, ResetPhoneNumberStateMessageComposer, ResetResolutionAchievementMessageComposer, RespectReceivedEvent, RestoreClientMessageEvent, RoomAdErrorEvent, RoomAdEventTabAdClickedComposer, RoomAdEventTabViewedComposer, RoomAdPurchaseInfoEvent, RoomAdPurchaseInitiatedComposer, RoomAdSearchMessageComposer, RoomAmbassadorAlertComposer, RoomBannedUsersComposer, RoomBanUserComposer, RoomChatlogEvent, RoomChatSettingsEvent, RoomCompetitionInitMessageComposer, RoomDeleteComposer, RoomDimmerPresetsEvent, RoomDoorbellAcceptedEvent, RoomDoorbellAccessComposer, RoomEnterComposer, RoomEnterErrorEvent, RoomEnterEvent, RoomEntryInfoMessageEvent, RoomEntryTileMessageEvent, RoomEventCancelEvent, RoomEventEvent, RoomFilterSettingsMessageEvent, RoomForwardEvent, RoomGiveRightsComposer, RoomHeightMapEvent, RoomHeightMapUpdateEvent, RoomInviteErrorEvent, RoomInviteEvent, RoomKickUserComposer, RoomMessageNotificationMessageEvent, RoomMuteComposer, RoomMuteUserComposer, RoomNetworkOpenConnectionMessageComposer, RoomOccupiedTilesMessageEvent, RoomPaintEvent, RoomPollResultEvent, RoomReadyMessageEvent, RoomRightsClearEvent, RoomRightsEvent, RoomRightsOwnerEvent, RoomScoreEvent, RoomSettingsComposer, RoomSettingsDataEvent, RoomSettingsErrorEvent, RoomSettingsSavedEvent, RoomSettingsSaveErrorEvent, RoomSettingsUpdatedEvent, RoomsWhereMyFriendsAreSearchMessageComposer, RoomsWithHighestScoreSearchMessageComposer, RoomTakeRightsComposer, RoomTextSearchMessageComposer, RoomThumbnailUpdateResultEvent, RoomUnbanUserComposer, RoomUnitActionComposer, RoomUnitBackgroundComposer, RoomUnitChatComposer, RoomUnitChatEvent, RoomUnitChatShoutComposer, RoomUnitChatShoutEvent, RoomUnitChatStyleComposer, RoomUnitChatWhisperComposer, RoomUnitChatWhisperEvent, RoomUnitDanceComposer, RoomUnitDanceEvent, RoomUnitDropHandItemComposer, RoomUnitEffectEvent, RoomUnitEvent, RoomUnitExpressionEvent, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUnitHandItemEvent, RoomUnitHandItemReceivedEvent, RoomUnitIdleEvent, RoomUnitInfoEvent, RoomUnitLookComposer, RoomUnitNumberEvent, RoomUnitPostureComposer, RoomUnitRemoveEvent, RoomUnitSignComposer, RoomUnitStatusEvent, RoomUnitTypingEvent, RoomUnitTypingStartComposer, RoomUnitTypingStopComposer, RoomUnitWalkComposer, RoomUsersClassificationMessageComposer, RoomUsersWithRightsComposer, RoomVisitsEvent, RoomVisualizationSettingsEvent, SanctionStatusEvent, SaveRoomSettingsComposer, SaveWardrobeOutfitMessageComposer, ScrGetKickbackInfoMessageComposer, ScrSendKickbackInfoMessageEvent, SearchFaqsMessageComposer, SeasonalCalendarDailyOfferMessageEvent, SeasonalQuestsMessageEvent, SecondsUntilMessageEvent, SelectClubGiftComposer, SellablePetPalettesMessageEvent, SendMessageComposer, SendRoomInviteComposer, SetActivatedBadgesComposer, SetClothingChangeDataMessageComposer, SetItemDataMessageComposer, SetObjectDataMessageComposer, SetPhoneNumberVerificationStatusMessageComposer, SetRelationshipStatusComposer, SetRoomSessionTagsMessageComposer, SetTargetedOfferStateComposer, SetYoutubeDisplayPlaylistMessageComposer, ShopTargetedOfferViewedComposer, ShowEnforceRoomCategoryDialogEvent, ShowMysteryBoxWaitMessageEvent, SimpleAlertMessageEvent, SSOTicketMessageComposer, StartCampaignMessageComposer, StartRoomPollEvent, SubmitRoomToCompetitionMessageComposer, TalentLevelUpEvent, TalentTrackComposer, TalentTrackLevelMessageEvent, TalentTrackMessageEvent, TargetedOfferEvent, TargetedOfferNotFoundEvent, ThreadMessagesMessageEvent, ThumbnailStatusMessageEvent, TogglePetBreedingComposer, TogglePetRidingComposer, ToggleStaffPickMessageComposer, TradingAcceptComposer, TradingAcceptEvent, TradingCancelComposer, TradingCloseComposer, TradingCloseEvent, TradingCompletedEvent, TradingConfirmationComposer, TradingConfirmationEvent, TradingListAddItemComposer, TradingListAddItemsComposer, TradingListItemEvent, TradingListItemRemoveComposer, TradingNoSuchItemEvent, TradingNotOpenEvent, TradingOpenComposer, TradingOpenEvent, TradingOpenFailedEvent, TradingOtherNotAllowedEvent, TradingUnacceptComposer, TradingYouAreNotAllowedEvent, TraxSongInfoMessageEvent, TryPhoneNumberMessageComposer, TryPhoneNumberResultMessageEvent, TryVerificationCodeResultMessageEvent, UnblockGroupMemberMessageComposer, UnignoreUserComposer, UniqueIDMessageComposer, UnloadGameMessageEvent, UnreadForumsCountMessageEvent, UnseenItemsEvent, UnseenResetCategoryComposer, UnseenResetItemsComposer, UpdateActionMessageComposer, UpdateConditionMessageComposer, UpdateFloorPropertiesMessageComposer, UpdateForumReadMarkerMessageComposer, UpdateForumSettingsMessageComposer, UpdateFurniturePositionComposer, UpdateHomeRoomMessageComposer, UpdateMessageMessageEvent, UpdateRoomCategoryAndTradeSettingsComposer, UpdateRoomFilterMessageComposer, UpdateRoomThumbnailMessageComposer, UpdateThreadMessageComposer, UpdateThreadMessageEvent, UpdateTriggerMessageComposer, UsePetProductComposer, UserBannedMessageEvent, UserChatlogEvent, UserClassificationMessageEvent, UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserCurrentBadgesComposer, UserCurrentBadgesEvent, UserEventCatsEvent, UserFigureComposer, UserFlatCatsEvent, UserGameAchievementsMessageEvent, UserInfoEvent, UserMottoComposer, UserNameChangeMessageEvent, UserPermissionsEvent, UserProfileComposer, UserProfileEvent, UserRelationshipsComposer, UserRespectComposer, UserSettingsCameraFollowComposer, UserSettingsEvent, UserSettingsOldChatComposer, UserSettingsRoomInvitesComposer, UserSettingsSoundComposer, UserSongDisksInventoryMessageEvent, UserSubscriptionComposer, UserSubscriptionEvent, UserTagsMessageEvent, UserUnbannedFromRoomEvent, UserWardrobePageEvent, VerifyCodeMessageComposer, VersionCheckMessageComposer, VisitUserComposer, VoteForRoomMessageComposer, VotePollCounterMessageComposer, VoucherRedeemErrorMessageEvent, VoucherRedeemOkMessageEvent, WardrobeMessageEvent, WeeklyCompetitiveFriendsLeaderboardEvent, WeeklyCompetitiveLeaderboardEvent, WeeklyGameRewardEvent, WeeklyGameRewardWinnersEvent, WelcomeGiftChangeEmailComposer, WelcomeGiftChangeEmailResultEvent, WelcomeGiftStatusEvent, WiredFurniActionEvent, WiredFurniConditionEvent, WiredFurniTriggerEvent, WiredOpenEvent, WiredRewardResultMessageEvent, WiredSaveSuccessEvent, WiredValidationErrorEvent, YouArePlayingGameEvent, YouAreSpectatorMessageEvent, YoutubeControlVideoMessageEvent, YoutubeDisplayPlaylistsEvent, YoutubeDisplayVideoMessageEvent, ClickUserMessageComposer } from './messages'; import { CustomPrefixPurchaseFailedEvent, PurchaseCatalogPrefixComposer, PurchaseNickIconComposer, PurchasePrefixComposer, RequestNickIconsComposer, SetActiveNickIconComposer, SetActivePrefixComposer, SetDisplayOrderComposer, UserNickIconsEvent } from './messages'; import { WiredMonitorDataEvent, WiredMonitorRequestComposer, WiredRoomSettingsDataEvent, WiredRoomSettingsRequestComposer, WiredRoomSettingsSaveComposer, WiredUserInspectMoveComposer, WiredUserVariableManageComposer, WiredUserVariableUpdateComposer, WiredUserVariablesDataEvent, WiredUserVariablesRequestComposer } from './messages'; import { WiredMovementsEvent } from './messages'; @@ -432,6 +432,7 @@ export class NitroMessages implements IMessageConfiguration this._events.set(IncomingHeader.ITEM_WALL_UPDATE, FurnitureWallUpdateEvent); this._events.set(IncomingHeader.FURNITURE_ALIASES, FurnitureAliasesEvent); this._events.set(IncomingHeader.FURNITURE_DATA, FurnitureDataEvent); + this._events.set(IncomingHeader.FURNITURE_DATA_RELOAD, FurnitureDataReloadEvent); this._events.set(IncomingHeader.FURNITURE_ITEMDATA, ItemDataUpdateMessageEvent); this._events.set(IncomingHeader.ITEM_STACK_HELPER, FurnitureStackHeightEvent); this._events.set(IncomingHeader.FURNITURE_STATE, OneWayDoorStatusMessageEvent); From 1d6c102fd663757a5a91425fd73d119f566f4a5d Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 22:01:08 +0200 Subject: [PATCH 5/6] feat(session): apply FurnitureDataReload delta + reload-hint (separate path) --- packages/session/src/SessionDataManager.ts | 23 +++++++++- .../src/__tests__/applyFurnidataDelta.test.ts | 36 ++++++++++++++++ .../src/furniture/applyFurnidataDelta.ts | 43 +++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 packages/session/src/__tests__/applyFurnidataDelta.test.ts create mode 100644 packages/session/src/furniture/applyFurnidataDelta.ts diff --git a/packages/session/src/SessionDataManager.ts b/packages/session/src/SessionDataManager.ts index f78c7bb..687f3ea 100644 --- a/packages/session/src/SessionDataManager.ts +++ b/packages/session/src/SessionDataManager.ts @@ -1,5 +1,7 @@ import { IFurnitureData, IGroupInformationManager, IMessageComposer, IMessageEvent, IProductData, ISessionDataManager, IUserDataSnapshot, NoobnessLevelEnum, SecurityLevel } from '@nitrots/api'; -import { AccountSafetyLockStatusChangeMessageEvent, AccountSafetyLockStatusChangeParser, AvailabilityStatusMessageEvent, ChangeUserNameResultMessageEvent, EmailStatusResultEvent, FigureUpdateEvent, GetCommunication, GetUserTagsComposer, InClientLinkEvent, MysteryBoxKeysEvent, NoobnessLevelMessageEvent, PetRespectComposer, PetScratchFailedMessageEvent, RoomReadyMessageEvent, RoomUnitChatComposer, UserInfoEvent, UserNameChangeMessageEvent, UserPermissionsEvent, UserRespectComposer, UserTagsMessageEvent } from '@nitrots/communication'; +import { AccountSafetyLockStatusChangeMessageEvent, AccountSafetyLockStatusChangeParser, AvailabilityStatusMessageEvent, ChangeUserNameResultMessageEvent, EmailStatusResultEvent, FigureUpdateEvent, FurnitureDataReloadEvent, GetCommunication, GetUserTagsComposer, InClientLinkEvent, MysteryBoxKeysEvent, NoobnessLevelMessageEvent, PetRespectComposer, PetScratchFailedMessageEvent, RoomReadyMessageEvent, RoomUnitChatComposer, UserInfoEvent, UserNameChangeMessageEvent, UserPermissionsEvent, UserRespectComposer, UserTagsMessageEvent } from '@nitrots/communication'; +import type { FurnidataDeltaEntry } from '@nitrots/communication'; +import { applyFurnidataDeltaTo } from './furniture/applyFurnidataDelta'; import { GetConfiguration } from '@nitrots/configuration'; import { GetLocalizationManager } from '@nitrots/localization'; import { GetEventDispatcher, MysteryBoxKeysUpdateEvent, NitroEvent, NitroEventType, NitroSettingsEvent, SessionDataPreferencesEvent, UserNameUpdateEvent } from '@nitrots/events'; @@ -171,7 +173,13 @@ export class SessionDataManager implements ISessionDataManager GetCommunication().registerMessageEvent(new MysteryBoxKeysEvent(this.onMysteryBoxKeysEvent.bind(this))), GetCommunication().registerMessageEvent(new NoobnessLevelMessageEvent(this.onNoobnessLevelMessageEvent.bind(this))), GetCommunication().registerMessageEvent(new AccountSafetyLockStatusChangeMessageEvent(this.onAccountSafetyLockStatusChangeMessageEvent.bind(this))), - GetCommunication().registerMessageEvent(new EmailStatusResultEvent(this.onEmailStatus.bind(this))) + GetCommunication().registerMessageEvent(new EmailStatusResultEvent(this.onEmailStatus.bind(this))), + GetCommunication().registerMessageEvent(new FurnitureDataReloadEvent((event: FurnitureDataReloadEvent) => + { + const parser = event.getParser(); + if(parser.mode === 1) { void this.applyFurnidataReloadHint(); } + else { this.applyFurnidataDelta(parser.entries); } + })) ); // Store event dispatcher callback for cleanup @@ -564,6 +572,17 @@ export class SessionDataManager implements ISessionDataManager } } + public applyFurnidataDelta(entries: FurnidataDeltaEntry[]): void + { + applyFurnidataDeltaTo(entries, this._floorItems as any, this._wallItems as any, GetLocalizationManager(), (typeof window !== 'undefined') ? window : { dispatchEvent: () => {} } as any); + } + + public async applyFurnidataReloadHint(): Promise + { + await this._furnitureData.init(); + if(typeof window !== 'undefined') window.dispatchEvent(new CustomEvent('nitro-localization-updated')); + } + public getBadgeUrl(name: string): string { return this._badgeImageManager.getBadgeUrl(name); diff --git a/packages/session/src/__tests__/applyFurnidataDelta.test.ts b/packages/session/src/__tests__/applyFurnidataDelta.test.ts new file mode 100644 index 0000000..25f4ada --- /dev/null +++ b/packages/session/src/__tests__/applyFurnidataDelta.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import { applyFurnidataDeltaTo } from '../furniture/applyFurnidataDelta'; + +describe('applyFurnidataDeltaTo', () => { + const setValue = vi.fn(); + beforeEach(() => setValue.mockClear()); + + it('patches floor FurnitureData name/desc + localization keys, dispatches window event', () => { + const floor: any = { _localizedName: 'Old', _description: 'Old desc' }; + const floorItems = new Map([[ 5, floor ]]); + const dispatched: string[] = []; + const win: any = { dispatchEvent: (e: any) => dispatched.push(e.type) }; + + applyFurnidataDeltaTo( + [ { type: 'S', id: 5, classname: 'chair', name: 'New', description: 'New desc' } ], + floorItems, new Map(), { setValue }, win + ); + + expect(floor._localizedName).toBe('New'); + expect(floor._description).toBe('New desc'); + expect(setValue).toHaveBeenCalledWith('roomItem.name.5', 'New'); + expect(setValue).toHaveBeenCalledWith('roomItem.desc.5', 'New desc'); + expect(dispatched).toContain('nitro-localization-updated'); + }); + + it('patches wall items by id', () => { + const wall: any = { _localizedName: 'W', _description: '' }; + const wallItems = new Map([[ 9, wall ]]); + applyFurnidataDeltaTo( + [ { type: 'I', id: 9, classname: 'poster', name: 'WallNew', description: 'd' } ], + new Map(), wallItems, { setValue }, { dispatchEvent: () => {} } + ); + expect(wall._localizedName).toBe('WallNew'); + expect(setValue).toHaveBeenCalledWith('wallItem.name.9', 'WallNew'); + }); +}); diff --git a/packages/session/src/furniture/applyFurnidataDelta.ts b/packages/session/src/furniture/applyFurnidataDelta.ts new file mode 100644 index 0000000..86b2c07 --- /dev/null +++ b/packages/session/src/furniture/applyFurnidataDelta.ts @@ -0,0 +1,43 @@ +import type { FurnidataDeltaEntry } from '@nitrots/communication'; + +/** + * Pure, testable furnidata-delta patcher. Mutates the FurnitureData objects in + * the given maps (by id) and the localization keys, then dispatches the + * `nitro-localization-updated` window event so subscribed React surfaces refresh. + */ +export function applyFurnidataDeltaTo( + entries: FurnidataDeltaEntry[], + floorItems: Map, + wallItems: Map, + localization: { setValue: (key: string, value: string) => void }, + win: { dispatchEvent: (event: any) => void } +): void +{ + if(!entries || !entries.length) return; + + for(const e of entries) + { + if(e.type === 'I') + { + const wall = wallItems.get(e.id); + if(wall) { wall._localizedName = e.name; wall._description = e.description; } + localization.setValue('wallItem.name.' + e.id, e.name); + localization.setValue('wallItem.desc.' + e.id, e.description); + } + else + { + const floor = floorItems.get(e.id); + if(floor) { floor._localizedName = e.name; floor._description = e.description; } + localization.setValue('roomItem.name.' + e.id, e.name); + localization.setValue('roomItem.desc.' + e.id, e.description); + } + } + + if(win && typeof win.dispatchEvent === 'function') + { + const evt = (typeof CustomEvent !== 'undefined') + ? new CustomEvent('nitro-localization-updated') + : { type: 'nitro-localization-updated' } as any; + win.dispatchEvent(evt); + } +} From 437bba5a88d4478edfa3a1fb2f2e2f3d61892f40 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 22:39:41 +0200 Subject: [PATCH 6/6] docs: document FurnitureDataReload live furnidata pipeline --- CLAUDE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f7fe301..c1ded40 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -277,3 +277,24 @@ for the React-side bridge code. - `../Nitro-V3` — React 19 client (consumes this lib via link) - `../Arcturus-Morningstar-Extended` — Java emulator (server side) - `../NitroV3-Housekeeping` — Next.js + Prisma admin CMS + +## Live furnidata updates: `FurnitureDataReload` (incoming header 10047) + +Server-pushed furni name/description changes (pairs with Arcturus' +`FurnitureDataReloadComposer`). `SessionDataManager.applyFurnidataDelta` (pure +`applyFurnidataDeltaTo` in `packages/session/src/furniture/`) patches +`_floorItems`/`_wallItems` by id + the `roomItem/wallItem.name/desc.{id}` +localization keys, then dispatches the window event `nitro-localization-updated` +so the client's already-subscribed surfaces refresh. `mode` 0 = delta, 1 = +reload-hint (re-runs `FurnitureDataLoader.init()`). Kept SEPARATE from the +furni-editor's `applyLiveFurnitureNameUpdate`. + +**Adding an incoming packet:** id in `IncomingHeader.ts` -> map in +`NitroMessages.ts` (`this._events.set(IncomingHeader.X, XEvent)`) -> Event + +Parser under `messages/incoming/` + `messages/parser/` -> wire the +barrel chain (`/index.ts` -> parent `index.ts` -> package `src/index.ts`). + +**Gotchas:** +- A branch based on `origin/Dev` may NOT contain the furni-editor slice + (`FurniDataUpdatedEvent` / `applyLiveFurnitureNameUpdate`) — verify, don't assume. +- Building the renderer in a fresh git worktree needs its own `yarn install`.