diff --git a/.github/workflows/sync-fork.yml b/.github/workflows/sync-fork.yml
new file mode 100644
index 0000000..7701682
--- /dev/null
+++ b/.github/workflows/sync-fork.yml
@@ -0,0 +1,43 @@
+name: Safe Sync - Nitro_Render_V3
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+
+jobs:
+ sync-safe:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+
+ steps:
+ - name: Checkout Fork
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Configure Git Credentials
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+ - name: Fetch and Merge Upstream
+ run: |
+ git remote add upstream https://github.com/duckietm/Nitro_Render_V3.git
+ git fetch upstream
+
+ for branch in $(git branch -r | grep 'upstream/' | grep -v 'HEAD'); do
+ local_branch=${branch#upstream/}
+ echo "Elaborazione branch: $local_branch"
+
+ git checkout $local_branch || git checkout -b $local_branch upstream/$local_branch
+
+ if git merge upstream/$local_branch --no-edit; then
+ echo "Merge completato con successo per $local_branch. Invio gli aggiornamenti..."
+ git push origin $local_branch
+ else
+ echo "Rilevato conflitto di merge su $local_branch! Il tuo lavoro è al sicuro. Salto il push per questo branch."
+ git merge --abort
+ fi
+ done
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`.
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).
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).
diff --git a/packages/communication/src/NitroMessages.ts b/packages/communication/src/NitroMessages.ts
index 7af05d4..22d6719 100644
--- a/packages/communication/src/NitroMessages.ts
+++ b/packages/communication/src/NitroMessages.ts
@@ -1,6 +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, CatalogAdminLoadOfferComposer, CatalogAdminLoadPageComposer, CatalogAdminMoveOfferComposer, CatalogAdminOfferDetailsEvent, CatalogAdminPageDetailsEvent, 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, FurnitureDataReloadEvent, FurniEditorUpdateFurnidataComposer, FurniEditorRevertFurnidataComposer, FurniEditorImportTextComposer, FurniEditorImportTextResultEvent } from './messages';
-import { CustomPrefixPurchaseFailedEvent, PurchaseCatalogPrefixComposer, PurchaseNickIconComposer, PurchasePrefixComposer, RequestNickIconsComposer, SetActiveNickIconComposer, SetActivePrefixComposer, SetDisplayOrderComposer, UserNickIconsEvent } from './messages';
+import { FurnitureDataReloadEvent, 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, CatalogAdminLoadOfferComposer, CatalogAdminLoadPageComposer, CatalogAdminMoveOfferComposer, CatalogAdminOfferDetailsEvent, CatalogAdminPageDetailsEvent, 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, FurniEditorUpdateFurnidataComposer, FurniEditorRevertFurnidataComposer, FurniEditorImportTextComposer, FurniEditorImportTextResultEvent, 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, CustomPrefixPurchaseFailedEvent, PurchaseCatalogPrefixComposer, PurchaseNickIconComposer, PurchasePrefixComposer, RequestNickIconsComposer, SetActiveNickIconComposer, SetActivePrefixComposer, SetDisplayOrderComposer, UserNickIconsEvent, AddFriendCategoryComposer, ConsoleReadReceiptEvent, ConsoleTypingComposer, FriendIsTypingEvent, MarkConsoleReadComposer, MoveFriendToCategoryComposer, RemoveFriendCategoryComposer, RenameFriendCategoryComposer } from './messages';
import { WiredMonitorDataEvent, WiredMonitorRequestComposer, WiredRoomSettingsDataEvent, WiredRoomSettingsRequestComposer, WiredRoomSettingsSaveComposer, WiredUserInspectMoveComposer, WiredUserVariableManageComposer, WiredUserVariableUpdateComposer, WiredUserVariablesDataEvent, WiredUserVariablesRequestComposer } from './messages';
import { WiredMovementsEvent } from './messages';
import { ConfInvisStateMessageEvent } from './messages';
@@ -90,7 +89,6 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.FURNI_EDITOR_DETAIL_RESULT, FurniEditorDetailResultEvent);
this._events.set(IncomingHeader.FURNI_EDITOR_INTERACTIONS_RESULT, FurniEditorInteractionsResultEvent);
this._events.set(IncomingHeader.FURNI_EDITOR_RESULT, FurniEditorResultEvent);
- this._events.set(IncomingHeader.FURNITURE_DATA_RELOAD, FurnitureDataReloadEvent);
this._events.set(IncomingHeader.FURNI_EDITOR_IMPORT_TEXT_RESULT, FurniEditorImportTextResultEvent);
this._events.set(IncomingHeader.CLUB_GIFT_INFO, ClubGiftInfoEvent);
@@ -154,6 +152,8 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.MESSENGER_MINIMAIL_NEW, MiniMailNewMessageEvent);
this._events.set(IncomingHeader.MESSENGER_MINIMAIL_COUNT, MiniMailUnreadCountEvent);
this._events.set(IncomingHeader.MESSENGER_CHAT, NewConsoleMessageEvent);
+ this._events.set(IncomingHeader.CONSOLE_READ_RECEIPT, ConsoleReadReceiptEvent);
+ this._events.set(IncomingHeader.FRIEND_TYPING, FriendIsTypingEvent);
this._events.set(IncomingHeader.MESSENGER_REQUEST, NewFriendRequestEvent);
this._events.set(IncomingHeader.MESSENGER_INVITE_ERROR, RoomInviteErrorEvent);
this._events.set(IncomingHeader.MESSENGER_INVITE, RoomInviteEvent);
@@ -436,6 +436,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);
@@ -764,8 +765,14 @@ export class NitroMessages implements IMessageConfiguration
this._composers.set(OutgoingHeader.REMOVE_FRIEND, RemoveFriendComposer);
this._composers.set(OutgoingHeader.REQUEST_FRIEND, RequestFriendComposer);
this._composers.set(OutgoingHeader.MESSENGER_CHAT, SendMessageComposer);
+ this._composers.set(OutgoingHeader.MARK_CONSOLE_READ, MarkConsoleReadComposer);
+ this._composers.set(OutgoingHeader.CONSOLE_TYPING, ConsoleTypingComposer);
this._composers.set(OutgoingHeader.SEND_ROOM_INVITE, SendRoomInviteComposer);
this._composers.set(OutgoingHeader.SET_RELATIONSHIP_STATUS, SetRelationshipStatusComposer);
+ this._composers.set(OutgoingHeader.ADD_FRIEND_CATEGORY, AddFriendCategoryComposer);
+ this._composers.set(OutgoingHeader.RENAME_FRIEND_CATEGORY, RenameFriendCategoryComposer);
+ this._composers.set(OutgoingHeader.REMOVE_FRIEND_CATEGORY, RemoveFriendCategoryComposer);
+ this._composers.set(OutgoingHeader.MOVE_FRIEND_TO_CATEGORY, MoveFriendToCategoryComposer);
this._composers.set(OutgoingHeader.VISIT_USER, VisitUserComposer);
// GAME
diff --git a/packages/communication/src/messages/incoming/IncomingHeader.ts b/packages/communication/src/messages/incoming/IncomingHeader.ts
index 2e7d2f4..b3fb3ae 100644
--- a/packages/communication/src/messages/incoming/IncomingHeader.ts
+++ b/packages/communication/src/messages/incoming/IncomingHeader.ts
@@ -117,6 +117,8 @@ export class IncomingHeader
public static MESSENGER_REQUESTS = 280;
public static MESSENGER_SEARCH = 973;
public static MESSENGER_UPDATE = 2800;
+ public static CONSOLE_READ_RECEIPT = 4086;
+ public static FRIEND_TYPING = 4088;
public static MODERATION_REPORT_DISABLED = 1651;
public static MODERATION_TOOL = 2696;
public static MODERATION_USER_INFO = 2866;
diff --git a/packages/communication/src/messages/incoming/friendlist/ConsoleReadReceiptEvent.ts b/packages/communication/src/messages/incoming/friendlist/ConsoleReadReceiptEvent.ts
new file mode 100644
index 0000000..83c7368
--- /dev/null
+++ b/packages/communication/src/messages/incoming/friendlist/ConsoleReadReceiptEvent.ts
@@ -0,0 +1,16 @@
+import { IMessageEvent } from '@nitrots/api';
+import { MessageEvent } from '@nitrots/events';
+import { ConsoleReadReceiptParser } from '../../parser';
+
+export class ConsoleReadReceiptEvent extends MessageEvent implements IMessageEvent
+{
+ constructor(callBack: Function)
+ {
+ super(callBack, ConsoleReadReceiptParser);
+ }
+
+ public getParser(): ConsoleReadReceiptParser
+ {
+ return this.parser as ConsoleReadReceiptParser;
+ }
+}
diff --git a/packages/communication/src/messages/incoming/friendlist/FriendIsTypingEvent.ts b/packages/communication/src/messages/incoming/friendlist/FriendIsTypingEvent.ts
new file mode 100644
index 0000000..8a18ac8
--- /dev/null
+++ b/packages/communication/src/messages/incoming/friendlist/FriendIsTypingEvent.ts
@@ -0,0 +1,16 @@
+import { IMessageEvent } from '@nitrots/api';
+import { MessageEvent } from '@nitrots/events';
+import { FriendIsTypingParser } from '../../parser';
+
+export class FriendIsTypingEvent extends MessageEvent implements IMessageEvent
+{
+ constructor(callBack: Function)
+ {
+ super(callBack, FriendIsTypingParser);
+ }
+
+ public getParser(): FriendIsTypingParser
+ {
+ return this.parser as FriendIsTypingParser;
+ }
+}
diff --git a/packages/communication/src/messages/incoming/friendlist/index.ts b/packages/communication/src/messages/incoming/friendlist/index.ts
index 47c991c..14fe2dd 100644
--- a/packages/communication/src/messages/incoming/friendlist/index.ts
+++ b/packages/communication/src/messages/incoming/friendlist/index.ts
@@ -1,4 +1,6 @@
export * from './AcceptFriendResultEvent';
+export * from './ConsoleReadReceiptEvent';
+export * from './FriendIsTypingEvent';
export * from './FindFriendsProcessResultEvent';
export * from './FollowFriendFailedEvent';
export * from './FriendListFragmentEvent';
diff --git a/packages/communication/src/messages/outgoing/OutgoingHeader.ts b/packages/communication/src/messages/outgoing/OutgoingHeader.ts
index bf441b2..2537e23 100644
--- a/packages/communication/src/messages/outgoing/OutgoingHeader.ts
+++ b/packages/communication/src/messages/outgoing/OutgoingHeader.ts
@@ -130,6 +130,12 @@ export class OutgoingHeader
public static SEND_ROOM_INVITE = 1276;
public static HABBO_SEARCH = 1210;
public static FRIEND_LIST_UPDATE = 1419;
+ public static ADD_FRIEND_CATEGORY = 4081;
+ public static RENAME_FRIEND_CATEGORY = 4082;
+ public static REMOVE_FRIEND_CATEGORY = 4083;
+ public static MOVE_FRIEND_TO_CATEGORY = 4084;
+ public static MARK_CONSOLE_READ = 4085;
+ public static CONSOLE_TYPING = 4087;
public static MOD_TOOL_USER_INFO = 3295;
public static GET_USER_FLAT_CATS = 3027;
public static NAVIGATOR_INIT = 2110;
diff --git a/packages/communication/src/messages/outgoing/friendlist/AddFriendCategoryComposer.ts b/packages/communication/src/messages/outgoing/friendlist/AddFriendCategoryComposer.ts
new file mode 100644
index 0000000..23f6cd7
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/AddFriendCategoryComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class AddFriendCategoryComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(name: string)
+ {
+ this._data = [ name ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/ConsoleTypingComposer.ts b/packages/communication/src/messages/outgoing/friendlist/ConsoleTypingComposer.ts
new file mode 100644
index 0000000..11a360c
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/ConsoleTypingComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class ConsoleTypingComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(peerId: number, isTyping: boolean)
+ {
+ this._data = [ peerId, isTyping ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/MarkConsoleReadComposer.ts b/packages/communication/src/messages/outgoing/friendlist/MarkConsoleReadComposer.ts
new file mode 100644
index 0000000..d87ee15
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/MarkConsoleReadComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class MarkConsoleReadComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(peerId: number)
+ {
+ this._data = [ peerId ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/MoveFriendToCategoryComposer.ts b/packages/communication/src/messages/outgoing/friendlist/MoveFriendToCategoryComposer.ts
new file mode 100644
index 0000000..5022a84
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/MoveFriendToCategoryComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class MoveFriendToCategoryComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(friendId: number, categoryId: number)
+ {
+ this._data = [ friendId, categoryId ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/RemoveFriendCategoryComposer.ts b/packages/communication/src/messages/outgoing/friendlist/RemoveFriendCategoryComposer.ts
new file mode 100644
index 0000000..237fea2
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/RemoveFriendCategoryComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class RemoveFriendCategoryComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(categoryId: number)
+ {
+ this._data = [ categoryId ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/RenameFriendCategoryComposer.ts b/packages/communication/src/messages/outgoing/friendlist/RenameFriendCategoryComposer.ts
new file mode 100644
index 0000000..cd75bc9
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/RenameFriendCategoryComposer.ts
@@ -0,0 +1,21 @@
+import { IMessageComposer } from '@nitrots/api';
+
+export class RenameFriendCategoryComposer implements IMessageComposer>
+{
+ private _data: ConstructorParameters;
+
+ constructor(categoryId: number, name: string)
+ {
+ this._data = [ categoryId, name ];
+ }
+
+ public getMessageArray()
+ {
+ return this._data;
+ }
+
+ public dispose(): void
+ {
+ return;
+ }
+}
diff --git a/packages/communication/src/messages/outgoing/friendlist/__tests__/FriendCategoryComposers.test.ts b/packages/communication/src/messages/outgoing/friendlist/__tests__/FriendCategoryComposers.test.ts
new file mode 100644
index 0000000..7cb4afd
--- /dev/null
+++ b/packages/communication/src/messages/outgoing/friendlist/__tests__/FriendCategoryComposers.test.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from 'vitest';
+import { AddFriendCategoryComposer } from '../AddFriendCategoryComposer';
+import { RenameFriendCategoryComposer } from '../RenameFriendCategoryComposer';
+import { RemoveFriendCategoryComposer } from '../RemoveFriendCategoryComposer';
+import { MoveFriendToCategoryComposer } from '../MoveFriendToCategoryComposer';
+
+describe('friend category composers', () =>
+{
+ it('AddFriendCategoryComposer carries the name', () =>
+ {
+ expect(new AddFriendCategoryComposer('Best friends').getMessageArray()).toEqual([ 'Best friends' ]);
+ });
+
+ it('RenameFriendCategoryComposer carries id + name', () =>
+ {
+ expect(new RenameFriendCategoryComposer(5, 'Staff').getMessageArray()).toEqual([ 5, 'Staff' ]);
+ });
+
+ it('RemoveFriendCategoryComposer carries the id', () =>
+ {
+ expect(new RemoveFriendCategoryComposer(7).getMessageArray()).toEqual([ 7 ]);
+ });
+
+ it('MoveFriendToCategoryComposer carries friendId + categoryId', () =>
+ {
+ expect(new MoveFriendToCategoryComposer(42, 3).getMessageArray()).toEqual([ 42, 3 ]);
+ });
+});
diff --git a/packages/communication/src/messages/outgoing/friendlist/index.ts b/packages/communication/src/messages/outgoing/friendlist/index.ts
index 501c3c9..148a5c9 100644
--- a/packages/communication/src/messages/outgoing/friendlist/index.ts
+++ b/packages/communication/src/messages/outgoing/friendlist/index.ts
@@ -1,4 +1,7 @@
export * from './AcceptFriendMessageComposer';
+export * from './MarkConsoleReadComposer';
+export * from './ConsoleTypingComposer';
+export * from './AddFriendCategoryComposer';
export * from './DeclineFriendMessageComposer';
export * from './FindNewFriendsMessageComposer';
export * from './FollowFriendMessageComposer';
@@ -6,7 +9,10 @@ export * from './FriendListUpdateComposer';
export * from './GetFriendRequestsComposer';
export * from './HabboSearchComposer';
export * from './MessengerInitComposer';
+export * from './MoveFriendToCategoryComposer';
+export * from './RemoveFriendCategoryComposer';
export * from './RemoveFriendComposer';
+export * from './RenameFriendCategoryComposer';
export * from './RequestFriendComposer';
export * from './SendMessageComposer';
export * from './SendRoomInviteComposer';
diff --git a/packages/communication/src/messages/parser/friendlist/ConsoleReadReceiptParser.ts b/packages/communication/src/messages/parser/friendlist/ConsoleReadReceiptParser.ts
new file mode 100644
index 0000000..ff56b6b
--- /dev/null
+++ b/packages/communication/src/messages/parser/friendlist/ConsoleReadReceiptParser.ts
@@ -0,0 +1,26 @@
+import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
+
+export class ConsoleReadReceiptParser implements IMessageParser
+{
+ private _readerId: number;
+
+ public flush(): boolean
+ {
+ this._readerId = 0;
+ return true;
+ }
+
+ public parse(wrapper: IMessageDataWrapper): boolean
+ {
+ if(!wrapper) return false;
+
+ this._readerId = wrapper.readInt();
+
+ return true;
+ }
+
+ public get readerId(): number
+ {
+ return this._readerId;
+ }
+}
diff --git a/packages/communication/src/messages/parser/friendlist/FriendIsTypingParser.ts b/packages/communication/src/messages/parser/friendlist/FriendIsTypingParser.ts
new file mode 100644
index 0000000..d383b48
--- /dev/null
+++ b/packages/communication/src/messages/parser/friendlist/FriendIsTypingParser.ts
@@ -0,0 +1,34 @@
+import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
+
+export class FriendIsTypingParser implements IMessageParser
+{
+ private _senderId: number;
+ private _isTyping: boolean;
+
+ public flush(): boolean
+ {
+ this._senderId = 0;
+ this._isTyping = false;
+ return true;
+ }
+
+ public parse(wrapper: IMessageDataWrapper): boolean
+ {
+ if(!wrapper) return false;
+
+ this._senderId = wrapper.readInt();
+ this._isTyping = wrapper.readBoolean();
+
+ return true;
+ }
+
+ public get senderId(): number
+ {
+ return this._senderId;
+ }
+
+ public get isTyping(): boolean
+ {
+ return this._isTyping;
+ }
+}
diff --git a/packages/communication/src/messages/parser/friendlist/__tests__/ConsoleReadReceiptParser.test.ts b/packages/communication/src/messages/parser/friendlist/__tests__/ConsoleReadReceiptParser.test.ts
new file mode 100644
index 0000000..d83213d
--- /dev/null
+++ b/packages/communication/src/messages/parser/friendlist/__tests__/ConsoleReadReceiptParser.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, it } from 'vitest';
+import { BinaryReader, BinaryWriter } from '@nitrots/utils';
+import { ConsoleReadReceiptParser } from '../ConsoleReadReceiptParser';
+
+class TestWrapper
+{
+ constructor(private reader: BinaryReader) {}
+ readByte() { return this.reader.readByte(); }
+ readShort() { return this.reader.readShort(); }
+ readInt() { return this.reader.readInt(); }
+ readString() { const len = this.reader.readShort(); return this.reader.readBytes(len).toString(); }
+ header = 0;
+ get bytesAvailable() { return this.reader.remaining() > 0; }
+}
+
+describe('ConsoleReadReceiptParser', () =>
+{
+ it('parses the reader id', () =>
+ {
+ const w = new BinaryWriter();
+ w.writeInt(42);
+ const parser = new ConsoleReadReceiptParser();
+ parser.flush();
+ parser.parse(new TestWrapper(new BinaryReader(w.getBuffer())) as any);
+ expect(parser.readerId).toBe(42);
+ });
+});
diff --git a/packages/communication/src/messages/parser/friendlist/__tests__/FriendIsTypingParser.test.ts b/packages/communication/src/messages/parser/friendlist/__tests__/FriendIsTypingParser.test.ts
new file mode 100644
index 0000000..9d8c7bd
--- /dev/null
+++ b/packages/communication/src/messages/parser/friendlist/__tests__/FriendIsTypingParser.test.ts
@@ -0,0 +1,39 @@
+import { describe, expect, it } from 'vitest';
+import { BinaryReader, BinaryWriter } from '@nitrots/utils';
+import { FriendIsTypingParser } from '../FriendIsTypingParser';
+
+class TestWrapper
+{
+ constructor(private reader: BinaryReader) {}
+ readByte() { return this.reader.readByte(); }
+ readBoolean() { return this.reader.readByte() === 1; }
+ readShort() { return this.reader.readShort(); }
+ readInt() { return this.reader.readInt(); }
+ readString() { const len = this.reader.readShort(); return this.reader.readBytes(len).toString(); }
+ header = 0;
+ get bytesAvailable() { return this.reader.remaining() > 0; }
+}
+
+describe('FriendIsTypingParser', () =>
+{
+ it('parses senderId + isTyping=true', () =>
+ {
+ const w = new BinaryWriter();
+ w.writeInt(42); w.writeByte(1);
+ const parser = new FriendIsTypingParser();
+ parser.flush();
+ parser.parse(new TestWrapper(new BinaryReader(w.getBuffer())) as any);
+ expect(parser.senderId).toBe(42);
+ expect(parser.isTyping).toBe(true);
+ });
+
+ it('parses isTyping=false', () =>
+ {
+ const w = new BinaryWriter();
+ w.writeInt(42); w.writeByte(0);
+ const parser = new FriendIsTypingParser();
+ parser.flush();
+ parser.parse(new TestWrapper(new BinaryReader(w.getBuffer())) as any);
+ expect(parser.isTyping).toBe(false);
+ });
+});
diff --git a/packages/communication/src/messages/parser/friendlist/index.ts b/packages/communication/src/messages/parser/friendlist/index.ts
index 6542588..8885b7a 100644
--- a/packages/communication/src/messages/parser/friendlist/index.ts
+++ b/packages/communication/src/messages/parser/friendlist/index.ts
@@ -1,4 +1,6 @@
export * from './AcceptFriendFailureData';
+export * from './ConsoleReadReceiptParser';
+export * from './FriendIsTypingParser';
export * from './AcceptFriendResultParser';
export * from './FindFriendsProcessResultParser';
export * from './FollowFriendFailedParser';
diff --git a/packages/communication/src/messages/parser/furnieditor/FurniEditorDetailResultMessageParser.ts b/packages/communication/src/messages/parser/furnieditor/FurniEditorDetailResultMessageParser.ts
index 60d68a0..c6a71a8 100644
--- a/packages/communication/src/messages/parser/furnieditor/FurniEditorDetailResultMessageParser.ts
+++ b/packages/communication/src/messages/parser/furnieditor/FurniEditorDetailResultMessageParser.ts
@@ -7,12 +7,14 @@ export class FurniEditorDetailResultMessageParser implements IMessageParser
private _item: FurniDetailData;
private _catalogItems: CatalogRefData[];
private _furniDataJson: string;
+ private _furniDataDiagnosticJson: string;
public flush(): boolean
{
this._item = null;
this._catalogItems = [];
this._furniDataJson = '';
+ this._furniDataDiagnosticJson = '';
return true;
}
@@ -36,6 +38,7 @@ export class FurniEditorDetailResultMessageParser implements IMessageParser
}
this._furniDataJson = wrapper.readString();
+ this._furniDataDiagnosticJson = wrapper.bytesAvailable ? wrapper.readString() : '{}';
return true;
}
@@ -54,4 +57,9 @@ export class FurniEditorDetailResultMessageParser implements IMessageParser
{
return this._furniDataJson;
}
+
+ public get furniDataDiagnosticJson(): string
+ {
+ return this._furniDataDiagnosticJson;
+ }
}