Skip to content

Item Vault v1.1

Save Schema

The JSON save envelope: schema version, inventory shape, containers, stacks, and instance resources.

Status
Current
Version
v1.1
Source updated
2026-09-12
Generated on
2026-09-20

Version: 1

All ItemVault persistence uses JSON. The top-level envelope carries a version field so future shape changes can add upgrade steps.

Envelope

{
  "version":1,
  "inventory": {... }
}
Field Type Notes
version int Schema version. Increment on any breaking change.
inventory object The inventory shape (see below).

Inventory

{
  "id":"player_inventory",
  "name":"Inventory",
  "max_containers":12,
  "containers": [... ]
}
Field Type Notes
id string Stable identifier.
name string Display name.
max_containers int Optional operational container cap; omitted when the default is used.
containers array Array of container objects.

Container

Two modes — free-list and slot-based. The mode field determines which shape is used.

Free-list mode

{
  "type":"backpack",
  "mode":"free",
  "stacks": [... ]
}

Slot-based mode

{
  "type":"equipment",
  "mode":"slot",
  "type_config": {
    "slot_based":true,
    "slot_count":2,
    "max_stacks":20,
    "max_weight":0,
    "slot_templates": [
      {"allowed_category":"weapon","stackable_only":false,"max_override":1 },
      {}
    ]
  },
  "slots": [... ]
}

Each element in slots is a slot object. Empty slots serialize as {}; occupied slots serialize as { "stack": { ... } }.

"slots": [
  {},
  {"stack": {"id":"iron_ore","qty":5 } }
]
Field Type Notes
type string The ContainerType.id this was created from.
mode string "free" or "slot".
type_config object Optional operational capacity and slot-constraint configuration. Older saves may omit it.
stacks array Free-list mode: array of stack objects.
slots array Slot-mode: array of slot objects ({} or { "stack": { ... } }).

type_config.slot_templates is parallel to the concrete slot indices. Each template may contain allowed_category, stackable_only, and max_override; omitted values use their defaults. The saved configuration is restored before stacks so subsequent additions enforce the same constraints.

Stack

{
  "id":"iron_ore",
  "qty":5
}

Optional fields are omitted when absent (not written as null).

Field Type Required Notes
id string yes ItemDefinition.id — the stable key.
qty int yes Stack quantity.
iid string no Retained item-instance identity.
sid string no Concrete stack-boundary identity.
max int no Max-stack override (0 or absent = use default).
wt float no Weight override (0 or absent = use default).
resources object no Numeric retained resources by stable resource id.

Missing-id policy (locked)

When loading, if a stack's id does not resolve to an ItemDefinition via ItemDatabase:

  1. Log a push_warning with the unresolved id and the container it came from.
  2. Drop that stack — skip it, do not insert a placeholder.
  3. Continue loading the rest of the save. Do not abort.

This means old saves survive the removal of items gracefully — the missing stacks vanish with a warning rather than corrupting the load.

Version compatibility policy

InventorySave.from_dict() enforces strict version checking:

  • Current version saves load normally.
  • Missing version field is rejected with push_error and returns null.
  • Older versions are rejected unless a migration callable was registered via InventorySave.register_migration(old_version, callable). The callable receives the old dict and must return a dict with "version" set to the current SCHEMA_VERSION.
  • Newer versions are always rejected with push_error and return null. We cannot know future schema shape; refusing prevents silent data loss.

This means old saves are not silently loaded through the current schema without an explicit migration step. Callers should check the return value for null and surface an error to the user.

Migration state for SCHEMA_VERSION = 1

Version 1 is the initial schema. No migrations are registered in InventorySave; any save missing version or with version < 1 is rejected. When SCHEMA_VERSION is incremented, the first migration should be registered for 1 and return a dict with the new version.

Version-bump rule

Any change to the JSON shape (renamed keys, restructured nesting, removed fields) must increment version and add an upgrade step that converts the old shape to the new one. Adding optional fields with sensible defaults does not require a version bump.