Datapack Registries

From Forge Community Wiki
Revision as of 10:51, 18 December 2022 by Xan (talk | contribs) (→‎JsonCodecProvider: Fix code error on the last line (GatherDataEvent has no method named addProvider(), this is a member of DataGenerator))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Datapack Registries (sometimes called dynamic registries or worldgen registries) are a set of registries that are able to load data from JSONS when a server starts. These include all vanilla registries enumerated in RegistryAccess.REGISTRIES (such as biomes, placed features, and dimensiontypes) as well as any custom forge registry marked with datapackRegistry() in its registry builder (such as forge's Biome Modifiers and Structure Modifiers).

These registries have the ability to have their objects registered in Java or defined in JSON.

If a mod ships a JSON for a datapack registry element in its builtin datapack, then it is not necessary for that mod to register the object in java; however, it is also possible to create the object during GatherDataEvent and datagenerate the JSON.

All datapack registries are automatically datagenerable; each registry has a Codec registered to it, which defines the serialization, and each registry's registry id determines its json directory. Elements of vanilla registries are loaded from data/{element-namespace}/{registry-path}/{element-path}.json, while elements of custom registries are loaded from data/{element-namespace}/{registry-namespace}/{registry-path}/{element-path}.json, using the namespace and path of the registry and the element.

RegistryOps and RegistryAccess

RegistryOps is a special DynamicOps made specifically for de/serializing datapack registries; it provides additional registry context and enables the use of special codecs that can only be used with RegistryOps. Datageneration of datapack registry elements must always be done using RegistryOps to convert elements to JsonElements.

RegistryOps can be created via RegistryOps.create(JsonElement.INSTANCE, RegistryAccess.builtinCopy()). RegistryAccess.builtinCopy() creates a set of writable datapack registries, which is necessary for datagenerating unregistered objects. All datageneration done in a GatherDataEvent handler must use the same RegistryAccess/RegistryOps instances (trying to encode an object in one set of registries that refers to an object in another set of registries will fail with strange holder errors).


A Holder vaguely resembles a Pair<Key, Value> that either starts with a key and has a value bound later, or starts with a value and may have a key bound later.

Many datapack registry elements must be constructed with Holders that refer to elements of other registries; for example, PlacedFeatures are constructed with a Holder of a ConfiguredFeature. When datagenerating objects, we can use RegistryOps#registry to get a registry and Registry#getOrCreateHolderOrThrow to produce key-only reference holders (the key being the only part of the holder we need, as holder codecs encode only the key of the holder when using a RegistryOps).

Holders referring to datapack registry elements must be retrieved from the RegistryAccess/RegistryOps that will be used for datageneration; holders referring to static registry elements (such as blocks) can come directly from a ForgeRegistry as e.g. there is only ever one registry for blocks.


Forge provides a dataprovider for datapack registry elements that, given a registry key and a map of objects to datagenerate, datagenerates all elements in the map to the locations determined by their keys.

void onGatherData(GatherDataEvent event)
  DataGenerator generator = event.getDataGenerator();
  ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
  RegistryAccess registryAccess = RegistryAccess.builtinCopy();
  RegistryOps<JsonElement> registryOps = RegistryOps.create(JsonOps.INSTANCE, registryAccess);

  ResourceLocation placedFeatureRL = new ResourceLocation("modid", "sponge_everywhere");
  PlacedFeature placedFeature = new PlacedFeature(//etc;)
  // All placed features to be datagenerated can be in the map
  Map<ResourceLocation, PlacedFeature> map = Map.of(placedFeatureRL, placedFeature);

  JsonCodecProvider provider = JsonCodecProvider.forDatapackRegistry(
    dataGenerator, existingFileHelper, "modid", registryOps, Registry.PLACED_FEATURE_REGISTRY, map);

  generator.addProvider(event.includeServer(), provider);