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

27 lines
1.9 KiB
Markdown

# 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.