Save data format: versioned registry, uniform item tree #26

Closed
opened 2026-10-19 22:11:07 +00:00 by muse-lantern · 1 comment
Member

Principles

  • Data-driven internally, Minecraft-style transparency. Everything is data, human-readable, inspectable. People should be able to look at stuff.
  • Uniform item tree. Containers ARE items — no special-case types in the save structure. A container is an item with children plus acceptance rules.
  • Base definition vs instance state. The registry holds static truth; the save holds a registry reference + full snapshot of per-instance state + identity.
  • Full snapshots, not deltas. Simpler, fewer edge cases.
  • Save-format versioning. Enables conversion/updates. A mod loader can check save item versions against what's installed.

Item instance structure

item_instance = {
  def = "deadair.item.weapons.1911",  -- registry reference, required
  uuid = "550e8400-...",              -- per-instance identity
  version = 3,                        -- registry version this snapshot was written against
  stats = {
    -- gameplay-modified values only, NOT base values
    condition = 0.72,
    affected_pct = 0.15,  -- grows, never shrinks (#8)
    affliction = 0.08,    -- never decreases (#8)
  },
  location = {
    -- tagged union: world position OR container slot
    -- world:     { kind = "world", pos = {...}, rot = {...} }
    -- container: { kind = "container", parent_uuid = "...", slot = "..." }
  },
  children = { ... }  -- nested items, same structure, recursive
}

Parent-child chains

Example — 1911 on the player's hip:

World > Player > Holster > 1911 > Magazine > Bullets
                  (container)   (container: chamber/magwell)

Every link in the chain is an item. The holster is an item (snapper type). The magazine is an item (ammo holder type). Bullets are generic items — no real stat modifiers for now.

Container types

Containers are items with acceptance rules:

  • Volumes (backpacks, persistent containers) — physics-locked storage, see #24 inventory milestone
  • Snappers (holsters) — dedicated slots for specific objects
  • Ammo holders (magazines, chambers) — check item type tags for ammo type. A 9mm mag only accepts items tagged ammo.9mm.

Ammo load order

Bullets are individual items in the holder's children list, in order. First in list = first fired. This means you can have an Altered bullet loaded in your gun and not realize it — it fires when its turn in the load order comes up. Gameplay emerges from the data model.

Dangling references (mod removed)

If a def is no longer in the registry, the node's data persists but nothing affects it — inert, not deleted, not placeholder-swapped. If the mod comes back, the item reactivates with its data intact.

Open questions

  • Exact serialization (JSON? human-readable text format?)
  • Migration path details for version bumps
  • Type tag taxonomy (ammo.9mm etc.) — needs its own pass
## Principles - **Data-driven internally, Minecraft-style transparency.** Everything is data, human-readable, inspectable. People should be able to look at stuff. - **Uniform item tree.** Containers ARE items — no special-case types in the save structure. A container is an item with children plus acceptance rules. - **Base definition vs instance state.** The registry holds static truth; the save holds a registry reference + full snapshot of per-instance state + identity. - **Full snapshots, not deltas.** Simpler, fewer edge cases. - **Save-format versioning.** Enables conversion/updates. A mod loader can check save item versions against what's installed. ## Item instance structure ```lua item_instance = { def = "deadair.item.weapons.1911", -- registry reference, required uuid = "550e8400-...", -- per-instance identity version = 3, -- registry version this snapshot was written against stats = { -- gameplay-modified values only, NOT base values condition = 0.72, affected_pct = 0.15, -- grows, never shrinks (#8) affliction = 0.08, -- never decreases (#8) }, location = { -- tagged union: world position OR container slot -- world: { kind = "world", pos = {...}, rot = {...} } -- container: { kind = "container", parent_uuid = "...", slot = "..." } }, children = { ... } -- nested items, same structure, recursive } ``` ## Parent-child chains Example — 1911 on the player's hip: ``` World > Player > Holster > 1911 > Magazine > Bullets (container) (container: chamber/magwell) ``` Every link in the chain is an item. The holster is an item (snapper type). The magazine is an item (ammo holder type). Bullets are generic items — no real stat modifiers for now. ## Container types Containers are items with acceptance rules: - **Volumes** (backpacks, persistent containers) — physics-locked storage, see #24 inventory milestone - **Snappers** (holsters) — dedicated slots for specific objects - **Ammo holders** (magazines, chambers) — check **item type tags** for ammo type. A 9mm mag only accepts items tagged `ammo.9mm`. ## Ammo load order Bullets are individual items in the holder's children list, **in order**. First in list = first fired. This means you can have an **Altered bullet** loaded in your gun and not realize it — it fires when its turn in the load order comes up. Gameplay emerges from the data model. ## Dangling references (mod removed) If a `def` is no longer in the registry, the node's data **persists but nothing affects it** — inert, not deleted, not placeholder-swapped. If the mod comes back, the item reactivates with its data intact. ## Open questions - Exact serialization (JSON? human-readable text format?) - Migration path details for version bumps - Type tag taxonomy (`ammo.9mm` etc.) — needs its own pass
Author
Member

Duplicate of #28 — created by retry loop during flaky connection. Closing.

Duplicate of #28 — created by retry loop during flaky connection. Closing.
Sign in to join this conversation.
No labels
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
dead-air/game-planning#26
No description provided.