synvael/docs/adr/0005-namespaced-content-ids.md

1.9 KiB

0005. Namespaced content IDs

  • Status: Accepted
  • Date: 2026-06-27

Context

All registered content (blocks, items, recipes, biomes, entities, and so on) needs a stable identifier that is unambiguous across the engine, data packs, Lua mods, recipe references, and save files. First-party and third-party content must coexist without collision, and identifiers must survive being written to disk and read back.

Decision

Content is identified by a namespaced string of the form "namespace:id".

  • The namespace core: is reserved for first-party content ("core:stone", "core:iron_sword"). Mods choose their own short namespace ("mymod:weird_dirt").
  • The strict form is mandatory. A bare id with no : is an error at registration / parse time, never silently coerced to core:. The rule is identical everywhere: engine scripts, data packs, Lua mods, recipe references, save files.
  • Charset: namespace and id are each [a-z0-9_-]+ with exactly one : between them. Lowercase ASCII only, no uppercase, Unicode, spaces, dots, or slashes.
  • At registration time each id string is interned into a small integer handle (e.g. BlockId(u32)). Hot paths compare handles; the original string is kept for display, save/load, and the Lua API.

The reserved namespace is deliberately core: rather than the project name, so it remains stable independent of branding.

Consequences

  • Identifiers are greppable, filesystem-safe, and unambiguous in logs and save files.
  • First-party and mod content cannot collide, and the absence of a silent default means mistakes surface immediately rather than corrupting data.
  • A small runtime cost is paid at registration to intern strings, in exchange for handle comparison on hot paths.
  • The strict form is a permanent contract: relaxing it later (e.g. defaulting bare ids) would change the meaning of existing save files and is therefore effectively irreversible.