synvael/docs/adr/0005-namespaced-content-ids.md
Serkyo cae434d227
Some checks are pending
CI / Rust Check & Lint (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Lua Lint & Format (push) Waiting to run
CI / LFS Pointer Guard (push) Waiting to run
docs(workspace): rewrite the subsystem notes and ADRs
2026-08-06 22:52:57 +02:00

2.4 KiB

0005. Namespaced content IDs

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

Context

Every single piece of registered content (blocks, items, recipes, biomes, entities, etc.) needs a perfectly stable identifier. This identifier has to be completely unambiguous across the engine, data packs, Lua mods, recipe references, and save files. First-party content and third-party content must be able to coexist without ever colliding, and these identifiers must be able to survive being written to disk and read back later.

Decision

We identify all content using a namespaced string in the exact format "namespace:id".

  • We strictly reserve the core: namespace for first-party content (e.g., "core:stone", "core:iron_sword"). Modders must choose their own short namespace (e.g., "mymod:weird_dirt").
  • This format is absolutely mandatory. A bare ID with no : is an immediate error at registration or parse time. We will never silently coerce it to core:. This exact rule applies everywhere: engine scripts, data packs, Lua mods, recipe references, and save files.
  • Charset rules: Both the namespace and the ID must match [a-z0-9_-]+ and have exactly one : sitting between them. We only allow lowercase ASCII. No uppercase letters, no Unicode, no spaces, no dots, and no slashes.
  • At registration time, we intern each ID string into a small integer handle (for example, BlockId(u32)). All hot paths compare these integer handles for speed. We only keep the original string around for display purposes, save/load routines, and the Lua API.

Notice that the reserved namespace is deliberately called core: rather than naming it after the project itself. This ensures the namespace stays completely stable regardless of any future rebranding.

Consequences

  • Identifiers are highly greppable, inherently filesystem-safe, and totally unambiguous in both logs and save files.
  • First-party and mod content simply cannot collide. By refusing to provide a silent default namespace, we ensure mistakes blow up immediately rather than silently corrupting data.
  • We pay a tiny runtime cost at registration to intern the strings, but in exchange, we get lightning-fast handle comparisons on all hot paths.
  • This strict formatting is a permanent contract. If we relaxed it later (for example, by defaulting bare IDs), we would fundamentally change the meaning of existing save files, making such a change effectively irreversible.