Design: express terrain + static definitions as a data file (tiledata-style), not code tables #143

Closed
opened 2026-07-22 14:28:01 +02:00 by marco · 1 comment
Owner

Design follow-up (raised during #89). Today a biome/static kind is defined across three code places that must stay in sync: the TerrainType enum, TerrainInfo (server flags: walkable, blocksSight), and BiomeVisuals (client: land-art key + placeholder tint) — plus StaticCatalog for statics. Adding a biome = edits in 3 files.

Idea

Express terrain + static definitions as data, not code (UO tiledata.mul-style): one file (e.g. content/tiledata.json or a diffable text table), one row per kind, the single source of truth for flags + art-key + authoring char + placeholder colour. Loaded by both server (flags) and client (art). Could drop the TerrainType enum in favour of string ids (like statics already use), and merge StaticCatalog into the same file so terrain + statics share one definition table.

Considerations

  • Type-safety trade-off: the enum + Table[terrain] gives compile/test-time completeness; a file is validated at load — add load-time validation (every id known, no dupes) to compensate.
  • Both server (Shared) and client read it — keep parsing in Shared, no MonoGame dep.
  • Worth it when adding biomes/statics is frequent or content-author-driven; at ~11 terrains a code table is still proportionate, so this is not urgent.
  • Coordinate with @andrea — this refactors the #142 biome foundation (TerrainInfo). Needs its own critical-design-review.

Out of scope

Not part of #89 (the object/static layer) — filed so it isn't lost.

Design follow-up (raised during #89). Today a biome/static kind is defined across **three code places that must stay in sync**: the `TerrainType` enum, `TerrainInfo` (server flags: walkable, blocksSight), and `BiomeVisuals` (client: land-art key + placeholder tint) — plus `StaticCatalog` for statics. Adding a biome = edits in 3 files. ## Idea Express terrain + static definitions as **data, not code** (UO `tiledata.mul`-style): one file (e.g. `content/tiledata.json` or a diffable text table), one row per kind, the **single source of truth** for flags + art-key + authoring char + placeholder colour. Loaded by both server (flags) and client (art). Could drop the `TerrainType` enum in favour of string ids (like statics already use), and merge `StaticCatalog` into the same file so terrain + statics share one definition table. ## Considerations - Type-safety trade-off: the enum + `Table[terrain]` gives compile/test-time completeness; a file is validated at load — add load-time validation (every id known, no dupes) to compensate. - Both server (Shared) and client read it — keep parsing in Shared, no MonoGame dep. - Worth it when adding biomes/statics is frequent or content-author-driven; at ~11 terrains a code table is still proportionate, so this is not urgent. - Coordinate with @andrea — this refactors the #142 biome foundation (`TerrainInfo`). Needs its own critical-design-review. ## Out of scope Not part of #89 (the object/static layer) — filed so it isn't lost.
Author
Owner

Critical design review — agreed, promoted ahead of #113 (2026-07-22)

Decision (owner): do the full tiledata refactor now, as the foundation the map editor (#113) builds on. Verdict: go.

Design (concrete)

  • content/tiledata.txt — committed, diffable text table (not JSON; consistent with world.map/world.statics), copied to server+client output. Typed rows:
    • terrain <id> <char> <walkable> <blocksSight> <landArt> <tintRGBAhex>
    • static <id> <blocks>
  • Shared: a TileData loader → TerrainDef/StaticDef records, load-time validated (unique ids, unique chars, required fields → FormatException). Replaces TerrainInfo + StaticCatalog + TileMap.Legend + the data half of BiomeVisuals. Tint parsed as uint RGBA (no MonoGame in Shared).
  • TerrainType enum dropped → terrain is a string id everywhere (TileMap._terrainstring[,]). #→tree and S→spawn stay grid conventions in TileMap.
  • TileMap.Parse/Load take the loaded TileData; it is loaded at server startup (single-threaded, before the sim host) and at client LoadContent.
  • Client: BiomeVisuals deleted (art-key + tint now from TileData; client converts uintColor); GroundTileSelector/GridRenderer/ClientContext keyed by string.

Why now / coordination

TerrainType is not a wire type (absent from Shared/Protocol) → no ProtocolVersion bump. Ripple ≈ 9 files + 3 test files, mechanical. This refactors the #142 terrain foundation (@andrea): collision risk is low right now — no open PRs, no branch implementing #137/#138/#140 yet; the biome foundation is already on main. @andrea heads-up: #137/#138/#140 will rebase onto this — terrain becomes string ids + a tiledata.txt, TerrainInfo/BiomeVisuals are absorbed into TileData.

Invariants Check

Full walk of ## Design checklist recorded in the PR (all N/A except: Scope ✓, Server-authoritative ✓, Single-threaded-sim ✓, Client-engine-independence ✓, Extend-by-data ✓✓ [the goal], Multi-platform ✓, Assets ✓, ModernUO ✓ [converge on tiledata.mul, diverge to diffable text], Docs&DoD ✓). No HARD GATE tripped. Protocol N/A (verified TerrainType is not wire).

DoD (delta)

  • Adding a terrain or static kind = one row in content/tiledata.txt + its art, zero code change (demonstrable).
  • Server + client + (later) editor all read flags/art from the one TileData; a bad/duplicate/unknown row fails at load with a clear error, not silently.
  • TerrainType enum removed; no behavioural regression (walkability, LOS, biome rendering, marsh scatter unchanged).
  • Base DoD (tests green, whole solution builds, 0 warnings, multi-platform).
## Critical design review — agreed, promoted ahead of #113 (2026-07-22) **Decision (owner):** do the **full** tiledata refactor now, as the foundation the map editor (#113) builds on. **Verdict: go.** ### Design (concrete) - **`content/tiledata.txt`** — committed, diffable **text table** (not JSON; consistent with `world.map`/`world.statics`), copied to server+client output. Typed rows: - `terrain <id> <char> <walkable> <blocksSight> <landArt> <tintRGBAhex>` - `static <id> <blocks>` - **`Shared`:** a `TileData` loader → `TerrainDef`/`StaticDef` records, load-time validated (unique ids, unique chars, required fields → `FormatException`). **Replaces** `TerrainInfo` + `StaticCatalog` + `TileMap.Legend` + the data half of `BiomeVisuals`. Tint parsed as `uint` RGBA (no MonoGame in Shared). - **`TerrainType` enum dropped** → terrain is a `string` id everywhere (`TileMap._terrain` → `string[,]`). `#`→tree and `S`→spawn stay grid conventions in `TileMap`. - **`TileMap.Parse/Load` take the loaded `TileData`**; it is loaded at server startup (single-threaded, before the sim host) and at client `LoadContent`. - **Client:** `BiomeVisuals` **deleted** (art-key + tint now from `TileData`; client converts `uint`→`Color`); `GroundTileSelector`/`GridRenderer`/`ClientContext` keyed by `string`. ### Why now / coordination `TerrainType` is **not** a wire type (absent from `Shared/Protocol`) → **no `ProtocolVersion` bump**. Ripple ≈ 9 files + 3 test files, mechanical. This refactors the #142 terrain foundation (@andrea): collision risk is **low right now** — no open PRs, no branch implementing #137/#138/#140 yet; the biome foundation is already on `main`. **@andrea heads-up:** #137/#138/#140 will rebase onto this — terrain becomes string ids + a `tiledata.txt`, `TerrainInfo`/`BiomeVisuals` are absorbed into `TileData`. ### Invariants Check Full walk of `## Design checklist` recorded in the PR (all N/A except: Scope ✓, Server-authoritative ✓, Single-threaded-sim ✓, Client-engine-independence ✓, **Extend-by-data ✓✓** [the goal], Multi-platform ✓, Assets ✓, ModernUO ✓ [converge on tiledata.mul, diverge to diffable text], Docs&DoD ✓). No HARD GATE tripped. Protocol N/A (verified TerrainType is not wire). ### DoD (delta) - [ ] Adding a terrain **or** static kind = one row in `content/tiledata.txt` + its art, **zero code change** (demonstrable). - [ ] Server + client + (later) editor all read flags/art from the one `TileData`; a bad/duplicate/unknown row fails at load with a clear error, not silently. - [ ] `TerrainType` enum removed; no behavioural regression (walkability, LOS, biome rendering, marsh scatter unchanged). - [ ] Base DoD (tests green, whole solution builds, 0 warnings, multi-platform).
marco closed this issue 2026-07-22 15:44:11 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#143
No description provided.