Skip to content

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.

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.

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.

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.

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.