Modulare Asset-Kits & Out-of-Tree-Asset-Bibliotheks-Architektur
Kontext & Problem
Während das Small-World-Ökosystem um reichhaltigere Diorama-Szenen, Charaktere, Animationen und 3D-Requisiten wächst (etwa die per Tripo3D generierten Industrie-Schott-Wandlampen, Holzkisten, Wellblech-Ölfässer, polygonalen Schutthaufen und humanoiden Biped-Rigs), wächst das Volumen binärer Assets rasant.
Große Binär-Assets (.glb-Modelle, 2K/4K-PBR-Texturkarten, Skelett-Bewegungsclips, Audio) direkt in das Git-Repository des Kern-Engines einzuchecken, verursacht grundlegende Probleme:
- Repository-Aufblähung: Git verfolgt binäre Delta-Historien ineffizient. Die Größe des
.git-Repositorys wächst monoton und verlangsamtgit clone, CI-Builds undnpm installfür Entwickler, die nur die TypeScript-Rendering-/Mathe-Laufzeit benötigen. - Kopplung von Engine & Inhalt: Beispiel-Assets und app-spezifische Requisiten verheddern sich mit den Kernmodulen der Engine.
- Auffindbarkeit & Wiederverwendbarkeit: Assets, die für eine App erstellt wurden (z. B.
and-now), sind schwer zu durchsuchen, zu betrachten und in anderen Apps oder Werkzeugen (wieMaterialStudiooderMaker) wiederzuverwenden.
Entscheidung
Wir etablieren einen 3-phasigen Architekturstandard für modulare Asset-Kits und Out-of-Tree-Asset-Distribution:
1. Phase 1: Semantische Kit-Struktur & Metadaten-Standard
Namensfrage geklärt (2026-09-17): Der Dateiname für das Kit-Manifest ist kit.json -- verbindlich, keine Abweichung mehr zulässig. Erwogen und verworfen:
package.json-- kollidiert mit npm; selbst wenn der aktuelle Workspace-Glob (packages/*,apps/*)public/assets/kits/*nicht erfasst, durchsuchen IDEs und andere Tools den Baum nach jeder Datei mit diesem Namen, nicht nur nach Workspace-Mitgliedern.manifest.json-- keine harte Kollision (kein PWA-Manifest, kein Vite-Build-Manifest im Projekt referenziert), aber der Name ist im Web-Kontext stark mit PWA-/Browser-Extension- Manifesten assoziiert -- unnötige Verwechslungsgefahr ohne echten Zugewinn.sw.json-- kollidiert real mit dem projekteigenen glTF-Vendor-NamensraumSW_*(SW_prefab_instance,SW_stage_zone, siehe ADR 0016/0017); neben einermodel.glbläge einsw.jsonnahe, es für einen Export derSW_*-Extension-Daten dieses Modells zu halten.
**Verbindliches Schema (validiert durch AssetKitValidation.test.ts, das seit 2026-09-17 jedes Verzeichnis unter public/assets/kits/ mit einer kit.json findet und prüft -- nicht mehr nur industrial/ wie zuvor):
- Ort:
public/assets/kits/<kit-id>/(z. B.industrial/,bunker/,flakturm/). kit.json(ein pro Kit, Feldreihenfolge wie unten):jsonc{ "id": "flakturm", // == Verzeichnisname, keine Ausnahme "name": "Vienna Flakturm Architectural & Bunker Kit", // Anzeigename, kein separates displayName "version": "1.0.0", // SemVer "description": "...", "items": [ // 3D-Requisiten mit model.glb; leeres Array zulässig { "id": "flakturm/bunker_blast_door", // IMMER "<kit-id>/<slug>", auch in meta.json gespiegelt "name": "Flakturm Heavy Double Blast Door", "category": "doors", "path": "props/bunker_blast_door/model.glb", // relativ zum Kit-Root, nicht zum Item "preview": "props/bunker_blast_door/preview.jpg", // .jpg ratifiziert, nicht .webp "meta": "props/bunker_blast_door/meta.json" // Pflichtfeld -- jedes Item verweist auf sein meta.json } ], "textures": [ // optional: reine PBR-Materialien ohne eigenes meta.json { "id": "flakturm/concrete_board", "name": "...", "category": "walls", "maps": ["albedo.png", "normal.png", "roughness.png", "ao.png"] } ], "decals": [ // optional: einzelne PNG-Sticker ohne eigenes meta.json { "id": "flakturm/sign_koje42", "name": "...", "file": "sign_koje42.png" } ], "author": "Small World Studio", "license": "CC0-1.0" }meta.json(eines proitems-Eintrag, neben dessenmodel.glb):id(== dasitems[].idaus derkit.json),name,category,kit(== diekit.json-id),version,description,triangles(> 0, $\le 25.000$),materials,textures(Namen der verwendeten Maps),dimensions({width, height, depth}oder{radius, height}),recommendedScale, optionalsockets(Anbaupunkte mitname,position, optionalrecommendedLight), optionalhazardClass(ADR/GHS-Gefahrgutklasse),author,license.- Metrischer Einheiten-Standard: Alle Kits halten sich strikt an $1{,}0\text{ Einheit} = 1{,}0\text{ Meter}$.
textures/decals-Einträge brauchen kein eigenesmeta.json-- sie haben keine Dreieckszahl, keine Bounding-Box, keine Sockets; ihreidfolgt trotzdem derselben<kit-id>/<slug>-Konvention.
Status: umgesetzt und seit 2026-09-17 für alle drei existierenden Kits (bunker, flakturm, industrial) einheitlich; zuvor nutzten bunker/flakturm noch ein abweichendes, unvalidiertes Schema (props statt items, model statt path, kein meta-Verweis, kein Top-Level-id) -- eine stille Drift, die der damals nur auf industrial hartverdrahtete Validierungstest nicht erkennen konnte.
Formales JSON Schema (seit 2026-09-17): Das obige Schema ist zusätzlich als echtes JSON Schema (Draft 2020-12) niedergeschrieben -- public/schemas/kit.schema.json für kit.json, public/schemas/prop-meta.schema.json für meta.json -- unter public/schemas/, nicht neben den Kits selbst, weil beide Schemas eine "$id" von der Form https://small-world.dev/schemas/<name>.schema.json tragen; sobald public/ unter dieser Domain ausgeliefert wird, referenziert die $id exakt den Pfad, unter dem die Datei tatsächlich liegt, statt eine URL zu behaupten, die nirgendwo real bedient wird. Jede echte kit.json/meta.json trägt ein "$schema"-Feld (relativer Pfad zu public/schemas/), das auf die passende Datei zeigt -- IDEs mit JSON-Schema-Unterstützung (VSCode, WebStorm/PhpStorm) validieren und autovervollständigen damit schon beim Tippen, nicht erst beim Testlauf. AssetKitValidation.test.ts kompiliert beide Schemas per Ajv und validiert jede reale Datei dagegen. Ein JSON Schema prüft nur die Form eines einzelnen Dokuments (Pflichtfelder, Typen, Patterns); es sieht nicht über Dateigrenzen hinweg. Cross-File-Prüfungen -- id gleich Verzeichnisname, item.meta verweist auf eine wirklich existierende Datei, meta.kit gleich der eigenen kit.json-id -- bleiben deshalb als eigene Assertions im selben Test bestehen, nicht als Schema-Keywords erzwungen.
Bewusst nicht eingeführt: ein eigenes meta.json für textures/decals-Einträge. Deren gesamte Metadaten (id, name, category/maps/file) stehen schon vollständig inline in kit.json -- ein Sidecar-File hätte nur id/name dupliziert, ohne echten Informationsgewinn (keine Dreiecke, keine Bounding-Box, keine Sockets, die ein solches File rechtfertigen würden).
2. Phase 2: Out-of-Tree-Repository & CDN-Distribution
- Dediziertes Asset-Repository (
small-world-assets): Binäre Produktions-Assets, rohe DCC-Dateien und Kit-Bündel werden in einem dedizierten Repository oder Git-LFS-Speicher untergebracht. - CDN-Distribution: Assets werden auf ein schnelles CDN veröffentlicht (über GitHub Releases, jsDelivr oder Cloudflare), sodass Szenen Standard-Assets über Remote-URIs referenzieren können, ohne den lokalen Datenträger aufzublähen.
Status: nicht umgesetzt. Kit-Binärdateien (z. B. model.glb mit 1,2 MB) liegen aktuell direkt im Haupt-Repository, ohne Git-LFS-Filter und ohne dediziertes small-world-assets-Repo -- exakt das Anti-Pattern aus dem Kontext-Abschnitt oben, das diese Phase verhindern sollte.
3. Phase 3: Laufzeit-Katalog-Ingest & CLI-Tooling
GltfLoader-Katalog-Auflösung:GltfLoaderum Katalog-Alias-Auflösung erweitern:typescript// Löst zum lokalen Cache oder CDN-Fallback auf: const lamp = await GltfLoader.loadFromCatalog("industrial/wall_lamp");- CLI-Download-Hilfsprogramm: Entwickler können Kit-Bündel optional bei Bedarf in ihren lokalen Arbeitsbereich herunterladen:bash
npx small-world add kit industrial npx small-world add prop industrial/wall_lamp
Status: nicht umgesetzt. Weder GltfLoader.loadFromCatalog() noch die small-world-CLI existieren im Code -- setzt ohnehin Phase 2 (ein echtes Remote-Katalog-Ziel) voraus.
Konsequenzen
- Leichtgewichtiger Kern-Engine: Das Kern-Repository von
small-worldbleibt schlank, schnell zu klonen und frei von schwerem binärem Ballast. - Modularität: Ersteller können in sich geschlossene, thematische Asset-Pakete veröffentlichen und konsumieren (z. B. Graphic Noir Sewer Kit, Industrial Bunker Kit, Character Starter Kit).
- Tooling-Kompatibilität:
Maker(Nachfolger vonGadgetInspector, siehe ADR 0010),MaterialStudiound künftige In-Game-Szeneneditoren können Asset-Auswahl-Paletten dynamisch direkt aus Kit-Manifesten befüllen.