Registries, identifiers, and tags
Registries are the identity boundary for shared game definitions. A registry maps a namespaced Identifier to an object, keeps reverse key and insertion-order numeric lookups, and exposes the values used by runtime systems. This separates stable save/data identity from direct object references and fast local numeric representation.
Registration lifecycle
Section titled “Registration lifecycle”Registry<T>.Register rejects duplicate keys, duplicate object instances, and writes after the registry is frozen. Each successful registration receives an insertion-order numeric ID and a canonical reference holder. Resolve the identifier form for persistence and diagnostics; do not treat a numeric ID as globally stable across changed registration order unless the format explicitly guarantees it.
A Holder<T> can be a registry-backed reference, which carries its key and tags, or a direct value, which has no key. HolderSet<T> represents either an ordered direct set or a named tag. Tags group values by meaning so consumers can request a category rather than hard-code every entry. Named tag binding validates member keys and can occur after the registry is frozen, allowing data grouping to be prepared separately from core identity registration.
JSON-backed tags
Section titled “JSON-backed tags”Runtime content can consume tag files under assets/data/<namespace>/tags/<registry>/. TagLoader resolves item, entity type, fluid, and enchantment tags; entries may name an identifier directly or refer to another tag with a # prefix. Optional entries use an object with id and required: false. Nested references are resolved recursively, duplicates are omitted while preserving order, and cycles or malformed files are reported through diagnostics.
{ "replace": false, "values": [ "minecraft:oak_log", "#minecraft:birch_logs", { "id": "minecraft:missing_item", "required": false } ]}When adding a tag, put it in the directory for its registry, use namespaced IDs, and check whether the consumer loads that registry in GameTags.EnsureLoaded. A tag file alone does not make a new registry family active. Missing required values and invalid references are reported; use the domain’s fallback or fix the data rather than assuming the entry was accepted.
When to use a tag
Section titled “When to use a tag”Use a tag when game rules need to ask a category question (for example, membership in a supported block/item family) and multiple definitions should answer it. Use a direct reference when the relationship is specifically to one known definition. Avoid replacing every registry lookup with a tag: tags add flexibility but can obscure a genuinely singular dependency.
Practical trace
Section titled “Practical trace”Follow Identifier parsing → registry registration → holder resolution → tag binding → consumer query. For save formats, check whether they persist the stable key or a numeric ID, then inspect versioning and missing-entry behavior. For registration changes, check initialization order, freeze timing, tags bound afterwards, and any caches derived from registry values.
Primary types are under src/Core/Registry/; item, block, biome, entity, and effect registries are owned by their respective domains. TagLoader and GameTags bridge data files to runtime tag sets. Keep domain-specific registration policy near the domain while reusing the shared identity machinery.