Investigation only -- nothing below is implemented

Research notes

Investigation notes: findings from the real game data and a recommended approach for a future implementation session to pick up. Each section starts with a Status line saying how much of it has been built (several sections have since been implemented, partly or fully, and now live on their own Source pages instead); a section without one is still just research.

1. Collapsing direction/orientation variants that share one localization name

The question: do blocks with north/south/east/west (or similar orientation) variants that all resolve to the same display name get surfaced as separate rows on /items/ unnecessarily, and could they be summarized into one row?

Findings:

Recommended approach (not implemented):

  1. At ingest time, for each concrete ItemIndexEntry, also compute a "display group key" = (domain, baseCode, displayName) — i.e. group by resolved display name within the same base block, not by full concrete code.
  2. On /items/ specifically (not on a block's own detail page — the detail page should still show its own real concrete code), collapse rows sharing a display-group key into one row, picking a representative concrete code to link to (e.g. the alphabetically-first, or whichever orientation variant isn't in a skipVariants list) and showing something like "× 8 orientations" as a secondary annotation.
  3. This must NOT merge rows whose variant differs in something that genuinely changes the item (metal, wood species, rock type, etc.) even if by coincidence two unrelated codes happened to share a display string — grouping strictly by (baseCode, displayName) together, not display name alone, avoids that.
  4. Watch for the already-documented resolveByType-must-pick-most-specific-match gotcha (variantExpander.ts) if this grouping logic ends up needing its own glob resolution.

3. Integrating jgwoolley/vintage-story-schematic-preview (upload + in-browser viewing)

Status: upload + bundled samples implemented (/tools/schematic/, as an adapter on the shared voxelViewer.ts), and browsing this wiki's own worldgen/schematics/** is implemented too: every schematic is gzip-compressed at ingest and fetched on demand (?schematic=<domain>/<relPath>); see apps/vs-wiki/site/_includes/sourcedocs/schematics.njk (/sources/schematics/). Each schematic page also embeds a read-only preview (rotate/pan/zoom only), the full viewer has Download buttons, and meta-filler starts hidden. Not done: rendering chiseled blocks as their real shape (topic 6). The findings below are the pre-implementation notes.

The question: what would it take to pull in upload + browser-side viewing from the external schematic-preview project, beyond the decode logic already adapted earlier this project?

Findings:

Recommended approach (not implemented):

  1. Ship the upload-only case first — it's genuinely standalone (no server/build changes), reuses the existing decode module as-is, and delivers real value (anyone can inspect any schematic file, not just this wiki's own curated set).
  2. Scope the voxel-grid parsing (Blocks/BlockIds/BlockCodes → a 3D array of concrete block codes) as new, dedicated client-side JS — a sibling to schematicDecode.ts, not an extension of it, since it's a different part of the schematic format (bulk voxel grid vs. per-instance block-entity attributes).
  3. Pin whichever 3D rendering approach is chosen to a specific version per this project's existing CSP/library-pinning convention (the artifact tooling's own rule of thumb applies generally here too: pin an exact version, don't float a tag) if a library like Three.js is added — this would be the first binary/3D dependency in the project, worth flagging explicitly in README.md when it lands.
  4. "Browse this wiki's own schematics in the viewer" should be treated as a distinct follow-up phase, gated on deciding how (and how much of) worldgen/schematics/** gets copied into the static build output — don't conflate it with the upload feature's scope.

4. Resolution of "clutter" objects

Status: partly implemented. Clutter types are now decoded per structure from schematic block entities and shown on /clutter/ (models/clutterInstances.ts). Not done: the lore bookshelf's lore content, and clutter placed by stackrandomizer-clutter loot mechanisms (not visible in schematic data). The notes below predate this.

The question: what are "clutter" blocks/items, and how/whether they should be resolved into the existing catalogs (items, decor, etc.)?

Findings:

Recommended approach (not implemented):

  1. Re-run the same "grep the real lang file for this family's actual key shape" check (that caught the metalblock-corroded-riveted-* bug) across all 8 clutter files specifically, rather than assuming the generic pipeline already handles every clutter variant correctly — this family wasn't exhaustively checked, only spot-checked.
  2. Decide, block-by-block, whether a given clutter block is "just a decoration with no special behavior" (leave it as a generic item/block, relying on the generalized appearsInStructures) versus "has real entityBehaviors worth extracting" (BlockClutterBookshelfWithLore is the clear example — it's conceptually identical to the "book drops" ask from this same session's task list, i.e. a recipe-less, content-bearing block found in structures) — the latter category is a much better candidate for a small dedicated model (mirroring models/decor.ts's pattern) than for being force-fit through the generic item index.
  3. Specifically for BlockClutterBookshelfWithLore: investigate whether its lore content is runtime-randomized (picked at interaction/loot time, analogous to how tapestries reroll via a type block-entity attribute) or fixed per schematic instance — if it's the former, the same tapestryInstances.ts-style per-instance block-entity decode approach would apply directly; if fixed, a simpler static extraction suffices.

5. Tool-only builds: regenerating just the non-ingest pages

Status: not researched yet -- notes on what the investigation will need.

The question: today npm run build = ingest (minutes of asset parsing) + Eleventy over everything. The tool pages (/tools/schematic/, /tools/waypoints/, /tools/calendar/, /nf3t/) and shared assets are pure client-side and don't need ingest data. Could a change to only those be built and deployed without re-running ingest -- and could the build be split so those are "the only thing that generates"?

What I expect to need to find out:

6. Chiseled block viewer (rendering chiseled blocks as they really look)

Status: researched, not implemented. Everything needed to draw the shape is verified to decode; drawing real appearance (textures/colors) is a separate, bigger gap. A matching "chiseled block viewer" stub may still be on /tools/.

The idea: today a chiseled block (chiseledblock, a "microblock") is drawn in the schematic viewer as one full unit cube colored by the generic block code. Draw its real sub-block shape instead.

Findings (verified against vssurvivalmod/Systems/Microblock/BEMicroBlock.cs and real decoded blobs):

What implementing the shape would take:

  1. When a schematic loads, decode the chiseledblock entities (decodeBlockEntity) and replace each one's single unit-cube voxel with one box per cuboid, colored per material block instead of by chiseledblock.
  2. Teach voxelViewer.ts sized boxes. It draws only unit cubes today (one InstancedMesh per opacity with per-instance color); a per-instance scale/offset on the same mesh is a small change and keeps the cost proportional to box count. Voxelizing at 16x instead (up to 4096 cubes per block) is too heavy.
  3. Decide how hiding, the legend counts and pick treat the new boxes, and keep the schematic-page preview and the full viewer consistent (they share schematicVoxels.ts).
  4. Add a test using a chiseled blob decoded from a bundled sample (e.g. story/devastation/small/small-13.json has two).

What "actually look" needs beyond shape (not solved):

Open questions:

7. vs-mod-updater (separate repo, not merged)

The user's Python vs-mod-updater lives in its own repo, https://github.com/jgwoolley/vs-mod-updater. A local copy was briefly placed in this checkout but is being removed; the current plan is to keep the two projects separate rather than merge the updater into this one (at least for now), so nothing here depends on it. apps/vs-wiki/scripts/download.ts already implements the overlapping part in TypeScript: resolving a mod via mods.vintagestory.at/api/mod/<id>, picking a release, downloading and skipping when present. What the updater has beyond that (per its README): checking installed mods for newer releases, recursively installing declared dependencies, and never leaving duplicate versions. If this is revisited, the question is whether to port those behaviors to TypeScript (keeping the pipeline pure Node, per CLAUDE.md) or leave the Python tool as a companion. No earlier note in this file referred to the updater.

Vintage Story temperature from seed

Status: the easy part is implemented (/tools/climate/, apps/vs-wiki/site/assets/climate.js). What is and isn't ported, and how it was checked, now lives on the site's own source page (apps/vs-wiki/site/_includes/sourcedocs/climate.njk, shown at /sources/climate/), not here. Still open and worth researching: porting the simplex wobble (MapLayerPerlinWobble), and finding the text-seed to int conversion (closed VintagestoryLib.dll; candidates GameMath.DotNetStringHash and StringUtil.GetNonRandomizedHashCode).

8. Splitting the pipeline into packages, and a VS Code schematic preview

Status: implemented. The repo is an npm-workspaces monorepo: libs/vs-wiki-core (asset loading, JSON patching, variant expansion, lang resolution), libs/vs-schematic (schematic decode, voxel conversion, the Three.js voxel viewer; TypeScript, compiled to dist/ for browsers) and apps/vs-wiki (the ingest, Eleventy site, scripts and tests). apps/vs-vscode-extension is a custom read-only editor that previews .json / .json.gz schematics, bundled with esbuild around the same vs-schematic code. See README.md ("Repository layout") for how they fit together.

Decisions and what was left out, for whoever revisits this: