Introduces loadGamedata(url, options?) and mergeGamedata(a, b) in
@nitrots/utils. The loader transparently accepts:
- a single-file URL (legacy) -> parsed as before
- a directory URL ending with '/' -> tier-merged from core/custom/seasonal,
each tier driven by its own manifest.json5
Merge rules:
- arrays of objects sharing an id key (id, classname, name): merged by id,
later layers overriding earlier ones
- arrays without an id key: concatenated
- plain objects: recursive merge per key
- anything else: later value wins
All gamedata consumers (FurnitureDataLoader, ProductDataLoader,
EffectAssetDownloadManager, AvatarRenderManager actions+figuredata,
LocalizationManager) are migrated to loadGamedata. Behaviour is unchanged
for single-file URLs, so existing deployments need no config changes;
opt-in to split mode by appending '/' to the URL once the layout is in
place.
README updated with the directory layout, merge table and programmatic
usage example. The companion CLI splitter that produces the core/ tier
from legacy files lives in the Nitro V3 client repo.
Cover every public method on the binary pair, plus the typical packet
shape (header + mixed payload) the composer/parser pipeline emits:
- byte / short / int round-trips, including signed-edge values
(int8 -1 from 0xFF, int16 / int32 boundaries)
- big-endian wire-order assertions on writeShort / writeInt (matches
Arcturus's DataInputStream)
- string round-trip with length prefix + bare (includeLength=false)
+ UTF-8 multibyte byte count + empty-string edge
- writeBytes for both number[] and ArrayBuffer payloads
- readBytes slice returns an independent reader whose position is
decoupled from the outer reader
- remaining() decrements correctly across mixed-size reads
- readFloat / readDouble decode IEEE-754 big-endian values
(the writer has no float/double counterparts — buffer is built via
DataView for these cases)
- writer position getter + explicit setter (caller-managed reposition)
- two independent writers concatenate cleanly into a single reader
Suite: 127/127 (was 104/104). typecheck clean.
Adds three explicit parsing strategies selectable at host build time via
the compile-time constant __NITRO_JSON_MODE__:
- legacy: strict JSON.parse only; clear error suggesting JSON5 mode
- json5 : JSON5.parse only
- auto : try JSON, fall back to JSON5 (existing behaviour and default
when the flag is undefined, so older hosts keep working)
URL/MIME hints for .json5 sources are still respected. README updated
with the modes table and a Vite wiring example.
Two tsgo nits that propagate to the client when the renderer is linked
in:
- packages/utils/src/NitroConfig.ts declared
'NitroConfig?: { [index: string]: any }' on Window, but Nitro-V3
declares 'NitroConfig?: Record<string, unknown>' in its
react-app-env.d.ts. The two declaration-merging fragments must
match — switching the renderer side to Record<string, unknown>
unifies them.
- packages/assets/src/AssetManager.ts: 'import.meta.glob(...)' is
augmented as Record<string, string> in the client's typedef, so
'mod.default ?? mod' (defensive handling of either string or
{ default: string }) failed because mod is typed string. Cast
inline: '((mod as { default?: string }).default ?? mod)'.
Renderer tsgo error count: 3 -> 0.
Four sites where Pixi v8's stricter typing tripped tsgo:
- AvatarImage: container.filters is typed as 'readonly Filter[] | null'
in v8 (no longer a single-Filter union). The old fallback branch
'else container.filters = [container.filters, …]' tried to treat a
readonly array as a single Filter; collapsed to the array-spread
path which now covers both undefined and non-empty cases. Added
Filter to the pixi.js import.
- FurnitureBadgeDisplayVisualization.updateSprite() had a 4-arg
override (sprite, asset, scale, layerId) of the parent's 2-arg
signature (scale, layerId). The sprite/asset were never used from
the parameters — the body only mutated 'sprite'. Refactored to
fetch the sprite via this.getSprite(layerId) inside the override
body so the signature matches the base.
- ExtendedSprite: 'renderer.gl' / 'glRenderTarget.resolveTargetFramebuffer'
exist only on WebGLRenderer / GlRenderTarget (not the WebGPU
variants). The runtime check 'renderer.type === RendererType.WEBGL'
guarantees this; cast at the boundary to satisfy the typechecker.
- TextureUtils.generateImage: Pixi v8's Extractor.image() returns the
union ImageLike (HTMLCanvasElement | HTMLImageElement); the public
signature promises HTMLImageElement. Cast at return — the Pixi
default backend returns HTMLImageElement here.
TypeScript 5.7 split ArrayBuffer / SharedArrayBuffer at the type level
(ArrayBuffer now exposes resizable/transfer/detached etc; SharedArrayBuffer
doesn't), and parametrized the typed-array constructors so plain
Uint8Array became Uint8Array<ArrayBufferLike>.
The renderer never uses SharedArrayBuffer, so this is type-level only —
narrowing back to ArrayBuffer at the boundaries:
- BinaryReader.readBytes() / .toArrayBuffer() return the underlying
DataView buffer; cast to ArrayBuffer.
- BinaryWriter.getBuffer() same shape.
- WsSessionCrypto.randomNonce() now returns Uint8Array<ArrayBuffer>
(it's always backed by a plain ArrayBuffer); aesGcmEncrypt/Decrypt
nonce parameter retyped accordingly so SubtleCrypto.encrypt accepts
it as BufferSource.
- ArrayBufferToBase64 now accepts Uint8Array | ArrayBufferLike directly
(pako/inflate hands back Uint8Array<ArrayBuffer> which the old
ArrayBuffer-only signature rejected).
Each workspace package was still pinning `typescript: ~5.5.x` or
`~5.8.2` in its own devDependencies even though the root bumped to 6.0.3
in 60b1143. The pins were dead (yarn 1 hoists from root) but they're
misleading when reading a single package.json. Bring them all to
`^6.0.3` to match the root.
Other:
- @thumbmarkjs/thumbmarkjs 1.8.1 → 1.9.0 (root + communication package)
- yarn.lock regenerated from a clean install (vitest 4 hoisting was
flaking on the patch vite bump; reverted vite to ^8.0.10)
Adds CLAUDE.md at the repo root: short project context for future
sessions — stack, the 12-workspace layout, the React-friendly v2.1.0
additions (`subscribe()`, `subscribeMessage()`, snapshot getters), build
scripts, and known gotchas (`SessionDataManager.getUserData` does NOT
exist; sendChat* expects 3 args; dispatchEvent is sync).
- keep selected items fully opaque
- blend only the wired overlay tint instead of lowering item alpha
- improve readability for green primary and blue secondary wired highlights
- keep selected items fully opaque
- blend only the wired overlay tint instead of lowering item alpha
- improve readability for green primary and blue secondary wired highlights