simoleo89 1bad1b20ca fix(parser): restore per-user borderId read in RoomUnitParser
The earlier "drop unsafe borderId read" fix (05ea0db) was based on the
assumption that Arcturus did not append a per-user borderId at the end
of each user record in RoomUsersComposer. That was true at the time —
but the Infostand Borders cherry-pick on the Arcturus side
(8f8f568, "feat: Infostand Borders") then added
`appendInt(getInfostandBorder())` at the end of EVERY user record
(single habbo, habbos collection, single bot=0, bots collection=0).

With the cherry-pick applied and the parser still skipping the read,
each user record left 4 unconsumed bytes on the wire. The NEXT
iteration's `id = wrapper.readInt()` then picked up the previous
user's borderId, the rest of the loop interpreted shifted bytes as
strings/ints, and the entire roster cascaded into corruption —
visible to the user as "I cannot see the other users in the room".

The bytesAvailable guard around this read is intentionally NOT
re-added: `bytesAvailable` is a boolean meaning "any bytes left in
the whole packet?", not "any bytes left for THIS user". With Arcturus
guaranteed to ship a borderId for every record (constant 0 for bots),
the read must be unconditional to stay wire-aligned.
2026-05-19 21:48:02 +02:00
2024-04-03 09:27:56 +02:00
2024-04-03 09:27:56 +02:00
2024-07-04 15:03:26 +02:00
2024-07-04 15:03:26 +02:00
2024-04-03 09:27:56 +02:00
2024-04-03 09:27:56 +02:00
2024-04-03 09:27:56 +02:00
🆙 Updates
2026-01-31 13:21:59 +01:00

Nitro Renderer

nitro-renderer is a Javascript library for rendering Nitro in the browser using PixiJS

Installation

npm

npm install @nitrots/nitro-renderer

yarn

yarn add @nitrots/nitro-renderer

JSON / JSON5 configuration parser

Every configuration file and gamedata file loaded by the renderer (figuredata, furnidata, productdata, effectmap, avatar actions, etc.) goes through @nitrots/utilsJsonParser.ts. The parser supports three modes, selected at the host build time through the compile-time constant __NITRO_JSON_MODE__:

Mode Behaviour
legacy Strict JSON.parse only. Comments / trailing commas raise a clear error.
json5 JSON5.parse only. Accepts comments, trailing commas, single quotes.
auto Try strict JSON first, fall back to JSON5. Default when the flag is unset.

URL hints are still honoured: files ending in .json5 (or served with a application/json5 content-type) always go through JSON5, regardless of mode.

Wiring the flag into a host

The renderer does not ship its own build for the flag — the host application (typically Nitro V3) defines it via its bundler. Example with Vite:

// vite.config.mjs in the host
export default defineConfig({
    define: {
        __NITRO_JSON_MODE__: JSON.stringify('json5')   // or 'legacy' / 'auto'
    }
});

If the constant is not defined the parser falls back to auto, which preserves the original behaviour of older releases — so existing hosts keep working without any change.

Using the parser directly

import { parseConfigJson, fetchConfigJson } from '@nitrots/utils';

const data  = parseConfigJson<MyConfig>(rawText, '/configuration/ui-config.json');
const data2 = await fetchConfigJson<MyConfig>('/configuration/ui-config.json5');

Errors carry the source URL and, in legacy mode, a hint about switching to JSON5 — making misconfigurations easy to diagnose in production logs.

Split-aware gamedata loader

@nitrots/utils also exports loadGamedata, the loader that backs every gamedata consumer in the renderer (FurnitureDataLoader, ProductDataLoader, EffectAssetDownloadManager, AvatarRenderManager, LocalizationManager). It accepts either a single-file URL (legacy) or a directory URL (split mode) — detected automatically by the trailing slash.

Directory layout

<gamedata-dir>/
  manifest.json5            # OPTIONAL — { "tiers": ["core", "custom", "seasonal"] }
  core/
    manifest.json5          # REQUIRED — { "files": ["a.json5", "b.json5", ...] }
    a.json5
    b.json5
  custom/                   # OPTIONAL tier
    manifest.json5
    overrides.json5
  seasonal/                 # OPTIONAL tier
    manifest.json5
    xmas.json5

If the directory manifest.json5 is absent, the loader falls back to the default tier order core → custom → seasonal. Each tier is skipped silently if its manifest.json5 is missing.

Merge semantics

mergeGamedata(a, b) (also exported) implements the rules below; tiers and files within a tier are merged in order, with later layers overriding earlier ones:

Combination Result
Two plain objects recursive merge, key by key
Two arrays of objects sharing an id key merged by id (later overrides earlier)
Two arrays without an id key concatenated
Anything else later value wins

Recognised id keys (in priority order): id, classname, name. Pass mergeArrayIdKeys in the options object to extend or override them.

Programmatic usage

import { loadGamedata, mergeGamedata } from '@nitrots/utils';

// host code never needs to care whether the URL is split or not
const furnidata = await loadGamedata('https://example.com/gamedata/furnidata/');

// merge ad-hoc if you load tiers manually
const merged = mergeGamedata(coreData, customData);

A CLI splitter for legacy single-file gamedata lives in the Nitro V3 client repo at scripts/split-gamedata.mjs — see the Nitro V3 README for usage.

S
Description
No description provided
Readme GPL-3.0 4.9 MiB
Languages
TypeScript 99.9%
JavaScript 0.1%