Difference between revisions of "Registration"

From Forge Community Wiki
m (update Link to Resource Location)
(Some improvements of changes in 1.19; skimmed)
 
(23 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Registration is the process of taking the objects of a mod (such as items, blocks, sounds, etc.) and making them known to the game. Registering things is important, as without registration the game will simply not know about these objects, which will cause unexplainable behaviors and crashes.
+
Registration is the process of making an object (such as an item or block) known to the game during runtime with an attached <code>ResourceLocation</code> name. Unregistered objects are a likely cause of game loading crashes or bugs, so it is important to register objects correctly.
  
Most things that require registration in the game are handled by the Forge registries. A registry is an object similar to a map that assigns values to keys. Forge uses registries with [[Using Resources#ResourceLocation|ResourceLocation]] keys to register objects. This allows the <code>ResourceLocation</code> to act as the "registry name" for objects. The registry name for an object may be accessed with <code>#getRegistryName</code>/<code>#setRegistryName</code>. The setter can only be called once; calling it twice results in an exception.
+
Most objects that are known within the game are handled by a Vanilla <code>Registry</code> or a Forge <code>IForgeRegistry</code>. Each registry uniquely defines each of its own objects through a "registry name" via a [[Using Resources#ResourceLocation|ResourceLocation]].
 +
Registries themselves have a name and are registered to the Vanilla root registry or <code>RegistryManager</code>.
 +
It is important to keep these distinct; although both are called registry names.
  
Every type of registrable object has its own registry. To see all registries supported by Forge, see the <code>ForgeRegistries</code> class. All registry names within a registry always be unique; attempting to register with an already existing registry name will cause the newly registered object to override whatever was registered previously. However, names in different registries will not collide. For example, there's a <code>Block</code> registry, and an <code>Item</code> registry. A <code>Block</code> and an <code>Item</code> may be registered with the same name <code>example:thing</code> without colliding; however, if two different <code>Block</code>s or <code>Item</code>s were registered with the same exact name, the second object will override the first.
+
{{Tip|In a global context, each object is universally unique through its <code>ResourceKey</code>: a concatenation of its registry's id and the object's registry name.}}
 +
 
 +
Forge expands Vanilla's registries to add important features for modded environments, such as world saving of integer ids and network syncing of integer ids, to ensure consistency when different mods and entries are present.
 +
 
 +
Forge registries preserves the same loading order of Vanilla registries, with all modded registries fired after Vanilla in alphabetical order by string comparing namespace then path. This loading order determines what order registries have their entries registered. All registries wrapped by Forge can be found within the <code>ForgeRegistries</code> class.
 +
 
 +
{{Tip|All registries have their own set of names and objects, so the same name (e.g. <code>examplemod:object</code>) can be reused in multiple registries, like blocks and items.}}
 +
 
 +
{{Tip/Warning|If two registry objects within the '''same''' registry have the same name, the second object will override the first.}}
  
 
== Methods for Registering ==
 
== Methods for Registering ==
  
There are two proper ways to register objects: the <code>DeferredRegister</code> class, and the <code>RegistryEvent.Register</code> lifecycle event.
+
There are two proper ways to register objects to a Forge registry or Vanilla registry: the <code>DeferredRegister</code> class, and the <code>RegisterEvent</code> lifecycle event.
  
 
=== DeferredRegister ===
 
=== DeferredRegister ===
  
<code>DeferredRegister</code> is the newer and documented way to register objects. It allows the use and convenience of static initialisers while avoiding the issues associated with it. It simply maintains a list of suppliers for entries and registers the objects from those suppliers during the proper <code>RegistryEvent.Register</code> event.
+
<code>DeferredRegister</code> is an abstraction layer over the registry event used to register objects. It maintains a map of "registry name" to their associated suppliers and resolves those suppliers during <code>RegisterEvent</code> for the associated registry. This method is the currently recommended, and documented, way to handle these objects as it provides convenience and safety for those who want to statically initialize objects while avoiding some issues associated with it.
 +
 
 +
{{Tip/Important|When using <code>DeferredRegister</code>s with non-vanilla registries, the registry key or the registry name should be supplied to the <code>create</code> method. These include the custom Forge registries for entity data serializers, global loot modifier serializers, world presets, and biome modifier serializers. Calling <code>Supplier#get</code> on a <code>Supplier<IForgeRegistry<?>></code> when making a DeferredRegister will return null because the registry does not exist yet.}}
  
 
An example of a mod registering a custom block:
 
An example of a mod registering a custom block:
  
<syntaxhighlight lang="java">
+
{{Template:Tabs/Code_Snippets
private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
+
|java=private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
  
public static final RegistryObject<Block> ROCK_BLOCK = BLOCKS.register("rock", () -> new Block(Block.Properties.create(Material.ROCK)));
+
public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));
  
 
public ExampleMod() {
 
public ExampleMod() {
 
     BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
 
     BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
 
}
 
}
</syntaxhighlight>
+
|kotlin=private val BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID)
 +
 
 +
val EXAMPLE_BLOCK: RegistryObject<Block> = BLOCKS.register("example_block") { Block(BlockBehaviour.Properties.of(Material.ROCK)) }
 +
 
 +
internal class ExampleMod {
 +
    init {
 +
        BLOCKS.register(FMLJavaModLoadingContext.get().modEventBus)
 +
    }
 +
}
 +
|scala=object ExampleMod {
 +
    private final val BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID)
 +
 
 +
    final val EXAMPLE_BLOCK = registerBlock("example_block", () => new Block(BlockBehaviour.Properties.of(Material.ROCK)))
 +
}
 +
class ExampleMod {
 +
    BLOCKS.register(FMLJavaModLoadingContext.get.getModEventBus)
 +
}
 +
|groovy=private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
 +
 
 +
public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));
  
=== RegistryEvent.Register ===
+
ExampleMod() {
 +
    BLOCKS.register(FMLJavaModLoadingContext.get().modEventBus);
 +
}
 +
|}}
  
The <code>RegistryEvent</code>s are the second and more flexible way to register objects. These [[Events|events]] are fired after the mod constructors and before the loading of configs.
+
{{Tip|When using a <code>DeferredRegister</code> to register any object, the name inputted will be automatically prefixed with the mod id passed in, giving the above object a "registry name" of <code>examplemod:example_block</code>.}}
  
The event used in registering objects is the <code>RegistryEvent.Register<T></code>, where the type parameter <code>T</code> is the type of the object being registered. Calling <code>#getRegistry</code> will return the registry, upon which objects are registered with <code>#register</code> (pass it a single object that you want to register) or <code>#registerAll</code> (pass it a list of objects).
+
=== RegisterEvent ===
  
The latter is very useful for minimising calls to <code>#register</code>.
+
The <code>RegisterEvent</code> is the second way register objects. This [[Events|event]] is fired for each registry synchronously in vanilla registry order after <code>FMLConstructModEvent</code> and before the configs are loaded. Objects are registered using <code>#register</code> by passing in the registry key, the name of the registry object, and the object itself. There is an additional <code>#register</code> overload which takes in a consumed helper to register an object with a given name. It is recommended to use this method to avoid unnecessary object creation.
  
Here is an example: (the event handler is registered on the *mod event bus*)
+
Here is an example: (the event handler is registered on the '''mod event bus''')
  
<syntaxhighlight lang="java">
+
{{Template:Tabs/Code_Snippets
 +
|java=@SubscribeEvent
 +
public void register(RegisterEvent event) {
 +
    event.register(ForgeRegistries.Keys.BLOCKS,
 +
        helper -> helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
 +
    );
 +
}
 +
|kotlin=@JvmStatic
 
@SubscribeEvent
 
@SubscribeEvent
public void registerBlocks(RegistryEvent.Register<Block> event) {
+
private fun register(event: RegisterEvent) =
     event.getRegistry().registerAll(new Block(...), new Block(...), ...);
+
    event.register(ForgeRegistries.Keys.BLOCKS) {
 +
        helper -> helper.register(ResourceLocation(MODID, "example_block"), Block(...))
 +
    }
 +
|scala=@SubscribeEvent
 +
def register(event: RegisterEvent): Unit =
 +
     event.register(ForgeRegistries.Keys.BLOCKS,
 +
        helper => helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
 +
    )
 +
|}}
 +
 
 +
{{Tip/Important|Since all objects registered must be singleton, some classes cannot by themselves be registered. Instead, <code>*Type</code> classes are registered and used in the formers' constructors to wrap the flyweight objects. For example, a [[Basics_of_Block_Entities|<code>BlockEntity</code>]] is wrapped via <code>BlockEntityType</code>, and <code>Entity</code> is wrapped via <code>EntityType</code>. These <code>*Type</code> classes hold factories that simply create the containing type on demand.
 +
 
 +
These factory holders are created through the use of their <code>*Type$Builder</code> classes. An example: (<code>REGISTER</code> here refers to a <code>DeferredRegister<BlockEntityType<?>></code>)
 +
 
 +
{{Template:Tabs/Code_Snippets
 +
|java=public static final RegistryObject<BlockEntityType<ExampleBlockEntity>> EXAMPLE_BLOCK_ENTITY = REGISTER.register(
 +
    "example_block_entity", () -> BlockEntityType.Builder.of(ExampleBlockEntity::new, EXAMPLE_BLOCK.get()).build(null)
 +
);
 +
|kotlin=val EXAMPLE_BLOCK_ENTITY: RegistryObject<BlockEntityType<ExampleBlockEntity>> = REGISTER.register("example_block_entity") { BlockEntityType.Builder.of(::ExampleBlockEntity, EXAMPLE_BLOCK.get()).build(null)) }
 +
|scala=final val EXAMPLE_BLOCK_ENTITY = REGISTER.register("example_block_entity", () => BlockEntityType.Builder.of(() => new ExampleBlockEntity(), GeneralRegistrar.EXAMPLE_BLOCK.get).build(null))
 +
|}}
 +
}}
 +
 
 +
=== Non-Forge Registries ===
 +
 
 +
Not all vanilla registries are wrapped as a Forge registry. To register objects to any one of these registries, create a <code>DeferredRegister</code> via the <code>#create</code> overload which takes in a resource key of the registry and the mod id to register the entries for. Then simply call <code>#register</code> like any other <code>DeferredRegister</code>.
 +
 
 +
{{Template:Tabs/Code_Snippets
 +
|java=private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);
 +
 
 +
public static final RegistryObject<LootItemConditionType> EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...));
 +
|kotlin=private val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)
 +
 
 +
val EXAMPLE_LOOT_ITEM_CONDITION_TYPE : RegistryObject<LootItemConditionType> = REGISTER.register("example_loot_item_condition_type") {
 +
    LootItemConditionType(...)
 
}
 
}
</syntaxhighlight>
+
|scala=private final val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)
 +
 
 +
final val EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () => new LootItemConditionType(...))
 +
|groovy=private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);
 +
 
 +
public static final RegistryObject<LootItemConditionType> EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...));
 +
|}}
 +
 
 +
If you attempt to make one of these instances require an instance of another registry object, you should use the lazy initialization method mentioned above to register the object in the correct order.
 +
 
 +
=== Data Driven Entries ===
  
{{Tip/Important|Some classes cannot by themselves be registered; instead, <code>*Type</code> classes are registered, and used in the formers' constructors. For example, [[Tile_Entities|<code>TileEntity</code>]] has <code>TileEntityType</code>, and <code>Entity</code> has <code>EntityType</code>. These <code>*Type</code> classes are factories that simply create the containing type on demand.
+
Registries are considered to be data driven if they are located within <code>RegistryAccess</code> with the exception of <code>LevelStem</code> and <code>Level</code>.
  
These factories are created through the use of their <code>*Type.Builder</code> classes. An example: (<code>REGISTER</code> here refers to a <code>DeferredRegister<TileEntityType<?>></code>)
+
These registry objects only need to be registered within code if they are to be used within a pre-existing registry object (e.g. a <code>PlacedFeature</code> for ore generation within an overworld <code>Biome</code>). Otherwise, their instance can be purely registered using a JSON file.
  
<syntaxhighlight lang="java">
+
If a data driven registry object has to be registered within code, a dummy object should be supplied to hold a "registry name" and then constructed within a JSON file.
public static final RegistryObject<TileEntityType<ExampleTile>> EXAMPLE_TILE = REGISTER.register(
 
    "example_tile", () -> TileEntityType.Builder.create(ExampleTile::new, EXAMPLE_BLOCK.get()).build(null)
 
);
 
</syntaxhighlight>}}
 
  
 
== Referencing Registered Objects ==
 
== Referencing Registered Objects ==
  
Registered objects should not be stored in fields when they are created and registered. They are to be always newly created and registered whenever their respective <code>RegistryEvent.Register</code> event is fired. This is to allow dynamic loading and unloading of mods in a future version of Forge.
+
Each forge registered object should not be statically initialized nor reference another instance being registered. They must always be a new, singleton instance that is resolved during when <code>RegisterEvent</code> is called for their registry. This is to maintain a sane loading order for registries and their objects along with dynamic loading/unloading of mods.
 +
 
 +
Forge registered objects must always be referenced through a <code>RegistryObject</code> or a field with <code>@ObjectHolder</code>.
  
Registered objects must always be referenced through a <code>RegistryObject</code> or a field with <code>@ObjectHolder</code>.
+
===Using RegistryObjects===
 +
<code>RegistryObject</code>s can be used to retrieve references to registered objects once they become available. Their references are updated along with all <code>@ObjectHolder</code> annotations after the registry that <code>RegisterEvent</code> is called for is dispatched and frozen.
  
=== Using RegistryObjects ===
+
A <code>RegistryObject</code> can be retrieved as a result of using <code>DeferredRegister</code> or calling the static factory <code>RegistryObject#create</code>. Each static factory takes in the "registry name" of the object being referenced and one of the following: a <code>IForgeRegistry</code>, a registry name of the type <code>ResourceLocation</code>, or a registry key of the type <code>ResourceKey<? extends Registry<?>></code>. The <code>RegistryObject</code> can be stored within some field and retrieve the registered object using <code>#get</code>.
  
<code>RegistryObject</code>s can be used to retrieve references to registered objects once they are available. These are used by <code>DeferredRegister</code> to return a reference to registered objects. Their references are updated after their corresponding registry's <code>RegistryEvent.Register</code> event is fired, along with the <code>@ObjectHolder</code> annotations.
+
An example using <code>RegistryObject</code>:
 +
{{Template:Tabs/Code_Snippets
 +
|java=public static final RegistryObject<Item> EXAMPLE_ITEM = RegistryObject.create(new ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS);
  
To get a <code>RegistryObject</code>, call <code>RegistryObject.of</code> with a <code>ResourceLocation</code> and the <code>IForgeRegistry</code> of the registrable object. Custom registries can also be used through giving a supplier of the custom object's class. Store the <code>RegistryObject</code> in a <code>public static final</code> field, and call <code>#get</code> whenever you need the registered object.
+
// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
 +
public static final RegistryObject<ExampleRegistry> EXAMPLE_OBJECT = RegistryObject.create(new ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod");
 +
|kotlin=val EXAMPLE_ITEM: RegistryObject<Item> = RegistryObject.create(ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS)
  
An example of using <code>RegistryObject</code>:
+
// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
 +
val EXAMPLE_OBJECT: RegistryObject<ExampleRegistry> = RegistryObject.create(ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod")
 +
|scala=final val EXAMPLE_ITEM = RegistryObject.create(new ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS)
  
<syntaxhighlight lang="java">
+
// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
public static final RegistryObject<Item> BOW = RegistryObject.of(new ResourceLocation("minecraft:bow"), ForgeRegistries.ITEMS);
+
final val EXAMPLE_OBJECT = RegistryObject.create(new ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod");  
 +
|}}
  
// assume that ManaType is a valid registry, and 'neomagicae:coffeinum' is a valid object within that registry
+
{{Tip/Important|All vanilla objects are bootstrapped and registered before mods are loaded. As such, they can be referenced as is without any issues.}}
public static final RegistryObject<ManaType> COFFEINUM = RegistryObject.of(new ResourceLocation("neomagicae", "coffeinum"), () -> ManaType.class);
 
</syntaxhighlight>
 
  
 
=== Using @ObjectHolder ===
 
=== Using @ObjectHolder ===
  
Registered objects from registries can be injected into the <code>public static</code> fields by annotating classes or fields with <code>@ObjectHolder</code> and supplying enough information to construct a <code>ResourceLocation</code> to identify a specific object in a specific registry.
+
Forge registry objects can also be injected into <code>public static final</code> fields by annotating each field with <code>@ObjectHolder</code>. Note that using <code>RegistryObject</code>s is the preferred strategy as ObjectHolders are verbose, clunky, and easy to mess up.
 +
 
 +
ObjectHolders can only be applied to fields and require 2 pieces of information: the registry name of your target registry and the name of your object entry inside the registry.
 +
 
 +
The registry name can be found inside of <code>ForgeRegistries.Keys</code> or <code>Registry</code>.
 +
For blocks, this would be <code>"minecraft:block"</code>.
 +
For items, this would be <code>"minecraft:item"</code>, etc.
 +
 
 +
The name of your entry is dependent on what you called it and requires your modid to be prefixed.
 +
When using <code>@ObjectHolder</code> inside of your main mod class annotated with <code>@Mod</code>, the modid namespace can be omitted.
  
 
The rules for <code>@ObjectHolder</code> are as follows:
 
The rules for <code>@ObjectHolder</code> are as follows:
  
* If the class is annotated with <code>@ObjectHolder</code>, its value will be the default namespace for all fields within if not explicitly defined
 
 
* If the class is annotated with <code>@Mod</code>, the modid will be the default namespace for all annotated fields within if not explicitly defined  
 
* If the class is annotated with <code>@Mod</code>, the modid will be the default namespace for all annotated fields within if not explicitly defined  
 
* A field is considered for injection if:
 
* A field is considered for injection if:
** it has at least the modifiers <code>public static</code>;
+
** it has at least the modifiers <code>public static</code> and optionally <code>final</code>, and
** one of the following conditions are true:
+
** the '''field''' is annotated with <code>@ObjectHolder</code>, and:
*** the '''enclosing class''' has an <code>@ObjectHolder</code> annotation, and the field is <code>final</code>, and:
+
*** the entry name value is explicitly defined, and
**** the name value is the field's name; and
+
*** the registry name value is explicitly defined
**** the namespace value is the enclosing class's namespace
 
**** _An exception is thrown if the namespace value cannot be found and inherited_
 
*** the '''field''' is annotated with <code>@ObjectHolder</code>, and:
 
**** the name value is explicitly defined; and
 
**** the namespace value is either explicitly defined or the enclosing class's namespace
 
** the field type or one of its supertypes corresponds to a valid registry (e.g. <code>Item</code> or <code>ArrowItem</code> for the <code>Item</code> registry);
 
** ''An exception is thrown if a field does not have a corresponding registry.''
 
 
* ''An exception is thrown if the resulting <code>ResourceLocation</code> is incomplete or invalid (non-valid characters in path)''
 
* ''An exception is thrown if the resulting <code>ResourceLocation</code> is incomplete or invalid (non-valid characters in path)''
* If no other errors or exceptions occur, the field will be injected 7
+
* If no other errors or exceptions occur, the field will be injected
 
* If all of the above rules do not apply, no action will be taken (and a message may be logged)
 
* If all of the above rules do not apply, no action will be taken (and a message may be logged)
  
<code>@ObjectHolder</code>-annotated fields are injected with their values after their corresponding registry's <code>RegistryEvent.Register</code> event is fired, along with the <code>RegistryObject</code>s.
+
<code>@ObjectHolder</code> annotated fields are injected with their associated object values after <code>RegisterEvent</code> is fired for their registry, the same time that <code>RegistryObject</code>s are filled. ObjectHolders will remain empty if the associated registry does not exist.
  
{{Colored box|title=Informational|content=If the object does not exist in the registry when it is to be injected, a debug message will be logged and no value will be injected.}}
+
{{Tip/Warning|If the object does not exist in the registry when it is to be injected, a debug message will be logged, and no value will be injected. If the object is found, but the field cannot be set, a warning message will be logged instead.}}
  
 
As these rules are rather complicated, here are some examples:
 
As these rules are rather complicated, here are some examples:
Line 106: Line 196:
 
<div class="mw-collapsible-content" style="overflow: auto; white-space: nowrap;">
 
<div class="mw-collapsible-content" style="overflow: auto; white-space: nowrap;">
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
@ObjectHolder("minecraft") // Inheritable resource namespace: "minecraft"
+
class Holder {
class AnnotatedHolder {
+
    @ObjectHolder(registryName = "minecraft:enchantment", value = "minecraft:flame")
     public static final Block diamond_block = null; // No annotation. [public static final] is required.
+
     public static final Enchantment flame = null;     // Annotation present. [public static] is required. [final] is optional.
                                                    // Block has a corresponding registry: [Block]
+
                                                      // Registry name is explicitly defined: "minecraft:enchantment"
                                                    // Name path is the name of the field: "diamond_block"
+
                                                      // Resource location is explicitly defined: "minecraft:flame"
                                                    // Namespace is not explicitly defined.
+
                                                      // To inject: "minecraft:flame" from the [Enchantment] registry
                                                    // So, namespace is inherited from class annotation: "minecraft"
 
                                                    // To inject: "minecraft:diamond_block" from the [Block] registry
 
  
    @ObjectHolder("ambient.cave")
+
     public static final Biome ice_flat = null;       // No annotation on the field.
     public static SoundEvent ambient_sound = null; // Annotation present. [public static] is required.
+
                                                      // Therefore, the field is ignored.
                                                    // SoundEvent has a corresponding registry: [SoundEvent]
 
                                                    // Name path is the value of the annotation: "ambient.cave"
 
                                                    // Namespace is not explicitly defined.
 
                                                    // So, namespace is inherited from class annotation: "minecraft"
 
                                                    // To inject: "minecraft:ambient.cave" from the [SoundEvent] registry
 
  
    // Assume for the next entry that [ManaType] is a valid registry.         
+
     @ObjectHolder("minecraft:creeper")
     @ObjectHolder("neomagicae:coffeinum")
+
     public static final Entity creeper = null;       // Annotation present. [public static] is required. [final] is optional.
     public static final ManaType coffeinum = null; // Annotation present. [public static] is required. [final] is optional.
+
                                                      // The registry name has not been specified on the field.
                                                    // ManaType has a corresponding registry: [ManaType] (custom registry)
+
                                                      // Therefore, this will not compile.
                                                    // Resource location is explicitly defined: "neomagicae:coffeinum"
 
                                                    // To inject: "neomagicae:coffeinum" from the [ManaType] registry
 
  
     public static final Item ENDER_PEARL = null;   // No annotation. [public static final] is required.
+
    @ObjectHolder(registryName = "minecraft:potion")
                                                    // Item has a corresponding registry: [Item].
+
     public static final Potion levitation = null;     // Annotation present. [public static] is required. [final] is optional.
                                                    // Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl"
+
                                                      // Registry name is explicitly defined: "minecraft:potion"
                                                    // !! ^ Field name is valid, because they are
+
                                                      // The entry's name value has not been specified on the field.
                                                    //      converted to lowercase automatically.
+
                                                      // Therefore, this will not compile.
                                                    // Namespace is not explicitly defined.
+
}
                                                    // So, namespace is inherited from class annotation: "minecraft"
+
</syntaxhighlight>
                                                    // To inject: "minecraft:ender_pearl" from the [Item] registry
+
</div>
 +
</div>
  
    @ObjectHolder("minecraft:arrow")
+
== Creating Custom Registries ==
    public static final ArrowItem arrow = null;    // Annotation present. [public static] is required. [final] is optional.
 
                                                    // ArrowItem does not have a corresponding registry.
 
                                                    // ArrowItem's supertype of Item has a corresponding registry: [Item]
 
                                                    // Resource location is explicitly defined: "minecraft:arrow"
 
                                                    // To inject: "minecraft:arrow" from the [Item] registry                                                   
 
  
    public static Block bedrock = null;            // No annotation, so [public static final] is required.
+
Creating custom registries for your mod might be useful if you want other mods to add new things to your system. For example, you might have magic spells and want to allow other mods to add new spells. For this you will want to make a registry (eg. "mymagicmod:spells"). This way, other mods will be able to register things to that list, and you won't have to do anything else.
                                                    // Therefore, the field is ignored.
 
   
 
    public static final ItemGroup group = null;    // No annotation. [public static final] is required.
 
                                                    // ItemGroup does not have a corresponding registry.
 
                                                    // No supertypes of ItemGroup has a corresponding registry.
 
                                                    // Therefore, THIS WILL PRODUCE AN EXCEPTION.
 
}
 
  
class UnannotatedHolder { // Note the lack of an @ObjectHolder annotation on this class.
+
Just like with registering a new item or block you have two ways of making a new registry. Each method takes in a <code>RegistryBuilder</code> which is used to build an <code>IForgeRegistry</code>. Each builder should have its name set via <code>#setName</code> before being created.
    @ObjectHolder("minecraft:flame")
 
    public static final Enchantment flame = null;  // Annotation present. [public static] is required. [final] is optional.
 
                                                    // Enchantment has corresponding registry: [Enchantment].
 
                                                    // Resource location is explicitly defined: "minecraft:flame"
 
                                                    // To inject: "minecraft:flame" from the [Enchantment] registry
 
  
    public static final Biome ice_flat = null;      // No annotation on the enclosing class.
+
=== With DeferredRegister ===
                                                    // Therefore, the field is ignored.
 
  
    @ObjectHolder("minecraft:creeper")
+
The first method involves the second static constructor: <code>DeferredRegister#create(ResourceLocation, String)</code>. From there, we can construct the registry using <code>#makeRegistry</code>. This will already populate <code>#setName</code> for us. This method also returns a supplier of the registry which we can use after the <code>NewRegistryEvent</code> is called.
    public static Entity creeper = null;            // Annotation present. [public static] is required.
 
                                                    // Entity does not have a corresponding registry.
 
                                                    // No supertypes of Entity has a corresponding registry.
 
                                                    // Therefore, THIS WILL PRODUCE AN EXCEPTION.
 
  
    @ObjectHolder("levitation")
+
Here is an example:
    public static final Potion levitation = null;  // Annotation present. [public static] is required. [final] is optional.
 
                                                    // Potion has a corresponding registry: [Potion].
 
                                                    // Name path is the value of the annotation: "levitation"
 
                                                    // Namespace is not explicitly defined.
 
                                                    // No annotation in enclosing class.
 
                                                    // Therefore, THIS WILL PRODUCE AN EXCEPTION.
 
}
 
</syntaxhighlight>
 
</div>
 
</div>
 
  
== Creating Custom Registries ==
+
{{Template:Tabs/Code_Snippets
 +
|java=public static final DeferredRegister<ExampleRegistry> EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID);
  
You might want to create a new registry for your mod. This might be usefull if you want other mods to add new things to your system. For example you might have magic spells and want to allow other mods to add new spells. For this you will want to make a registry (eg. "mymagicmod:spells"). This way other mods will be able to register things to that list and you won't have to do anything else.
+
public static final Supplier<IForgeRegistry<ExampleRegistry>> REGISTRY = EXAMPLE.makeRegistry(RegistryBuilder::new);
 +
|kotlin=val EXAMPLE: DeferredRegister<ExampleRegistry> = DeferredRegister.create(ResourceLocation(MODID, "example_registry"), MODID)
  
Just like with registering a new item or block you have 2 ways of making a new registry.
+
val REGISTRY: IForgeRegistry<ExampleRegistry> by EXAMPLE.makeRegistry(::RegistryBuilder).let {
 +
    lazy {
 +
        it.get()
 +
    }
 +
}
 +
|scala=final val EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID)
  
=== Using RegistryEvent.NewRegistry ===
+
private final val PRIVATE_REGISTRY = EXAMPLE.makeRegistry(() => new RegistryBuilder)
 +
final lazy val REGISTRY = PRIVATE_REGISTRY.get
 +
|}}
  
Custom registries are created by using <code>RegistryBuilder</code> during the <code>RegistryEvent.NewRegistry</code> event. The class <code>RegistryBuilder</code> takes certain parameters (such as the registry name, the <code>Class</code> of its values, and various callbacks for different events happening on the registry). Calling <code>RegistryBuilder#create</code> will result in the registry being built, registered to the <code>RegistryManager</code>, and returned to the caller for additional processing.
+
=== Using <code>NewRegistryEvent</code> ===
  
The <code>Class</code> of the value of the registry must implement <code>IForgeRegistryEntry</code>, which defines that <code>#setRegistryName</code> and <code>#getRegistryName</code> can be called on the objects of that class. For most custom objects, is recommended to extend the default implementation of <code>ForgeRegistryEntry</code>, instead of implementing the interface directly. When <code>#setRegistryName(String)</code> is called with a string, and that string does not have an explicit namespace, its namespace will be set to the current modid.
+
The second method can be done during the <code>NewRegistryEvent</code> event. Using <code>NewRegistryEvent#create</code>, you can pass in a <code>RegistryBuilder</code> directly. This method will return a <code>Supplier<IForgeRegistry<V>></code> that can be stored and queried after the event is fired to gain access to your <code>IForgeRegistry</code> instance.
  
The Forge registries can be accessed through the <code>ForgeRegistries</code> class. All registries, Forge-provided or custom, can be retrieved by calling <code>GameRegistry.findRegistry(Class)</code> with the appropriate class for the registry. For example, the registry for <code>Block</code>s can be retrieved by calling <code>GameRegistry.findRegistry(Block.class)</code>.
+
Here is an example: (the event handler is registered on the '''mod event bus''')
  
 
<syntaxhighlight lang="Java">
 
<syntaxhighlight lang="Java">
 +
public static Supplier<IForgeRegistry<ExampleRegistry>> registrySupplier = null;
 +
 
@SubscribeEvent
 
@SubscribeEvent
public static void onNewRegistry(RegistryEvent.NewRegistry registry){
+
public void onNewRegistry(NewRegistryEvent event){
     RegistryBuilder<ArcaneFuelType> registryBuilder = new RegistryBuilder<ArcaneFuelType>();
+
     RegistryBuilder<ExampleRegistry> registryBuilder = new RegistryBuilder<>();
     registryBuilder.setName(ArcaneRituals.location("arcane_fuel"));
+
     registryBuilder.setName(new ResourceLocation(MODID, "example_registry");
     registryBuilder.setType(ArcaneFuelType.class);
+
     registrySupplier = event.create(registryBuilder);
    registryBuilder.create();
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== With DeferredRegister ===
+
== Handling Missing Entries ==
 +
 
 +
When loading a pre-existing world after removing mods or updating versions, there are cases where certain registry objects will cease to exist. In these cases, it is possible to specify actions to remove a mapping, prevent the world from loading, or remap the name as needed. This can be done through the third of the registry events: <code>MissingMappingsEvent</code>. Within the event, you can grab an immutable list of missing mappings associated with a mod id for a given registry via <code>#getMappings</code> or a list of all mappings via <code>#getAllMappings</code>.
 +
 
 +
For each <code>Mapping</code>, you can either execute one of the following methods:
 +
* <code>#ignore</code> which abandons the entry when loading
 +
* <code>#warn</code> which warns the user about the missing entry but continues loading
 +
* <code>#fail</code> which prevents the world from loading
 +
* <code>#remap</code> which remaps the entry to the specified non-null object in the same registry
 +
 
 +
If none of the above are specified, then the default action of notifying the user about the missing mappings occur.
 +
 
 +
Here is an example:  (the event handler is registered on the '''forge event bus''')
 +
 
 +
{{Template:Tabs/Code_Snippets
 +
|java=// This will ignore any missing test items from the specified world
 +
@SubscribeEvent
 +
public void onMissing(final MissingMappingsEvent event) {
 +
    event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
 +
        .filter(mapping -> mapping.key.getPath().contains("test"))
 +
            .forEach(Mapping::ignore);
 +
}
 +
|groovy=// This will ignore any missing test items from the specified world
 +
@SubscribeEvent
 +
void onMissing(final MissingMappingsEvent event) {
 +
    event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
 +
        .filter(mapping -> mapping.key.path.contains("test"))
 +
            .forEach(Mapping::ignore);
 +
}
 +
|}}
 +
 
  
TODO
+
[[Category:Common Concepts]]

Latest revision as of 17:06, 8 September 2022

Registration is the process of making an object (such as an item or block) known to the game during runtime with an attached ResourceLocation name. Unregistered objects are a likely cause of game loading crashes or bugs, so it is important to register objects correctly.

Most objects that are known within the game are handled by a Vanilla Registry or a Forge IForgeRegistry. Each registry uniquely defines each of its own objects through a "registry name" via a ResourceLocation. Registries themselves have a name and are registered to the Vanilla root registry or RegistryManager. It is important to keep these distinct; although both are called registry names.

In a global context, each object is universally unique through its ResourceKey: a concatenation of its registry's id and the object's registry name.

Forge expands Vanilla's registries to add important features for modded environments, such as world saving of integer ids and network syncing of integer ids, to ensure consistency when different mods and entries are present.

Forge registries preserves the same loading order of Vanilla registries, with all modded registries fired after Vanilla in alphabetical order by string comparing namespace then path. This loading order determines what order registries have their entries registered. All registries wrapped by Forge can be found within the ForgeRegistries class.

All registries have their own set of names and objects, so the same name (e.g. examplemod:object) can be reused in multiple registries, like blocks and items.

Warning

If two registry objects within the same registry have the same name, the second object will override the first.

Methods for Registering

There are two proper ways to register objects to a Forge registry or Vanilla registry: the DeferredRegister class, and the RegisterEvent lifecycle event.

DeferredRegister

DeferredRegister is an abstraction layer over the registry event used to register objects. It maintains a map of "registry name" to their associated suppliers and resolves those suppliers during RegisterEvent for the associated registry. This method is the currently recommended, and documented, way to handle these objects as it provides convenience and safety for those who want to statically initialize objects while avoiding some issues associated with it.

Important

When using DeferredRegisters with non-vanilla registries, the registry key or the registry name should be supplied to the create method. These include the custom Forge registries for entity data serializers, global loot modifier serializers, world presets, and biome modifier serializers. Calling Supplier#get on a Supplier<IForgeRegistry<?>> when making a DeferredRegister will return null because the registry does not exist yet.

An example of a mod registering a custom block:

private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);

public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));

public ExampleMod() {
    BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
}
private val BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID)

val EXAMPLE_BLOCK: RegistryObject<Block> = BLOCKS.register("example_block") { Block(BlockBehaviour.Properties.of(Material.ROCK)) }

internal class ExampleMod {
    init {
        BLOCKS.register(FMLJavaModLoadingContext.get().modEventBus)
    }
}
object ExampleMod {
    private final val BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID)

    final val EXAMPLE_BLOCK = registerBlock("example_block", () => new Block(BlockBehaviour.Properties.of(Material.ROCK)))
}
class ExampleMod {
    BLOCKS.register(FMLJavaModLoadingContext.get.getModEventBus)
}
private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);

public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of(Material.STONE)));

ExampleMod() {
    BLOCKS.register(FMLJavaModLoadingContext.get().modEventBus);
}
When using a DeferredRegister to register any object, the name inputted will be automatically prefixed with the mod id passed in, giving the above object a "registry name" of examplemod:example_block.

RegisterEvent

The RegisterEvent is the second way register objects. This event is fired for each registry synchronously in vanilla registry order after FMLConstructModEvent and before the configs are loaded. Objects are registered using #register by passing in the registry key, the name of the registry object, and the object itself. There is an additional #register overload which takes in a consumed helper to register an object with a given name. It is recommended to use this method to avoid unnecessary object creation.

Here is an example: (the event handler is registered on the mod event bus)

@SubscribeEvent
public void register(RegisterEvent event) {
    event.register(ForgeRegistries.Keys.BLOCKS,
        helper -> helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
    );
}
@JvmStatic
@SubscribeEvent
private fun register(event: RegisterEvent) =
    event.register(ForgeRegistries.Keys.BLOCKS) {
        helper -> helper.register(ResourceLocation(MODID, "example_block"), Block(...))
    }
@SubscribeEvent
def register(event: RegisterEvent): Unit =
    event.register(ForgeRegistries.Keys.BLOCKS,
        helper => helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
    )

Important

Since all objects registered must be singleton, some classes cannot by themselves be registered. Instead, *Type classes are registered and used in the formers' constructors to wrap the flyweight objects. For example, a BlockEntity is wrapped via BlockEntityType, and Entity is wrapped via EntityType. These *Type classes hold factories that simply create the containing type on demand.

These factory holders are created through the use of their *Type$Builder classes. An example: (REGISTER here refers to a DeferredRegister<BlockEntityType<?>>)

public static final RegistryObject<BlockEntityType<ExampleBlockEntity>> EXAMPLE_BLOCK_ENTITY = REGISTER.register(
    "example_block_entity", () -> BlockEntityType.Builder.of(ExampleBlockEntity::new, EXAMPLE_BLOCK.get()).build(null)
);
val EXAMPLE_BLOCK_ENTITY: RegistryObject<BlockEntityType<ExampleBlockEntity>> = REGISTER.register("example_block_entity") { BlockEntityType.Builder.of(::ExampleBlockEntity, EXAMPLE_BLOCK.get()).build(null)) }
final val EXAMPLE_BLOCK_ENTITY = REGISTER.register("example_block_entity", () => BlockEntityType.Builder.of(() => new ExampleBlockEntity(), GeneralRegistrar.EXAMPLE_BLOCK.get).build(null))

Non-Forge Registries

Not all vanilla registries are wrapped as a Forge registry. To register objects to any one of these registries, create a DeferredRegister via the #create overload which takes in a resource key of the registry and the mod id to register the entries for. Then simply call #register like any other DeferredRegister.

private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);

public static final RegistryObject<LootItemConditionType> EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...));
private val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)

val EXAMPLE_LOOT_ITEM_CONDITION_TYPE : RegistryObject<LootItemConditionType> = REGISTER.register("example_loot_item_condition_type") {
    LootItemConditionType(...)
}
private final val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)

final val EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () => new LootItemConditionType(...))
private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);

public static final RegistryObject<LootItemConditionType> EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...));

If you attempt to make one of these instances require an instance of another registry object, you should use the lazy initialization method mentioned above to register the object in the correct order.

Data Driven Entries

Registries are considered to be data driven if they are located within RegistryAccess with the exception of LevelStem and Level.

These registry objects only need to be registered within code if they are to be used within a pre-existing registry object (e.g. a PlacedFeature for ore generation within an overworld Biome). Otherwise, their instance can be purely registered using a JSON file.

If a data driven registry object has to be registered within code, a dummy object should be supplied to hold a "registry name" and then constructed within a JSON file.

Referencing Registered Objects

Each forge registered object should not be statically initialized nor reference another instance being registered. They must always be a new, singleton instance that is resolved during when RegisterEvent is called for their registry. This is to maintain a sane loading order for registries and their objects along with dynamic loading/unloading of mods.

Forge registered objects must always be referenced through a RegistryObject or a field with @ObjectHolder.

Using RegistryObjects

RegistryObjects can be used to retrieve references to registered objects once they become available. Their references are updated along with all @ObjectHolder annotations after the registry that RegisterEvent is called for is dispatched and frozen.

A RegistryObject can be retrieved as a result of using DeferredRegister or calling the static factory RegistryObject#create. Each static factory takes in the "registry name" of the object being referenced and one of the following: a IForgeRegistry, a registry name of the type ResourceLocation, or a registry key of the type ResourceKey<? extends Registry<?>>. The RegistryObject can be stored within some field and retrieve the registered object using #get.

An example using RegistryObject:

public static final RegistryObject<Item> EXAMPLE_ITEM = RegistryObject.create(new ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS);

// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
public static final RegistryObject<ExampleRegistry> EXAMPLE_OBJECT = RegistryObject.create(new ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod");
val EXAMPLE_ITEM: RegistryObject<Item> = RegistryObject.create(ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS)

// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
val EXAMPLE_OBJECT: RegistryObject<ExampleRegistry> = RegistryObject.create(ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod")
final val EXAMPLE_ITEM = RegistryObject.create(new ResourceLocation("examplemod", "example_item"), ForgeRegistries.ITEMS)

// Assume that 'examplemod:example_registry' is a valid registry, and 'examplemod:example_object' is a valid object within that registry
final val EXAMPLE_OBJECT = RegistryObject.create(new ResourceLocation("examplemod", "example_object"), new ResourceLocation("examplemod", "example_registry"), "examplemod");

Important

All vanilla objects are bootstrapped and registered before mods are loaded. As such, they can be referenced as is without any issues.

Using @ObjectHolder

Forge registry objects can also be injected into public static final fields by annotating each field with @ObjectHolder. Note that using RegistryObjects is the preferred strategy as ObjectHolders are verbose, clunky, and easy to mess up.

ObjectHolders can only be applied to fields and require 2 pieces of information: the registry name of your target registry and the name of your object entry inside the registry.

The registry name can be found inside of ForgeRegistries.Keys or Registry. For blocks, this would be "minecraft:block". For items, this would be "minecraft:item", etc.

The name of your entry is dependent on what you called it and requires your modid to be prefixed. When using @ObjectHolder inside of your main mod class annotated with @Mod, the modid namespace can be omitted.

The rules for @ObjectHolder are as follows:

  • If the class is annotated with @Mod, the modid will be the default namespace for all annotated fields within if not explicitly defined
  • A field is considered for injection if:
    • it has at least the modifiers public static and optionally final, and
    • the field is annotated with @ObjectHolder, and:
      • the entry name value is explicitly defined, and
      • the registry name value is explicitly defined
  • An exception is thrown if the resulting ResourceLocation is incomplete or invalid (non-valid characters in path)
  • If no other errors or exceptions occur, the field will be injected
  • If all of the above rules do not apply, no action will be taken (and a message may be logged)

@ObjectHolder annotated fields are injected with their associated object values after RegisterEvent is fired for their registry, the same time that RegistryObjects are filled. ObjectHolders will remain empty if the associated registry does not exist.

Warning

If the object does not exist in the registry when it is to be injected, a debug message will be logged, and no value will be injected. If the object is found, but the field cannot be set, a warning message will be logged instead.

As these rules are rather complicated, here are some examples:

Example uses of @ObjectHolder
class Holder {
    @ObjectHolder(registryName = "minecraft:enchantment", value = "minecraft:flame")
    public static final Enchantment flame = null;     // Annotation present. [public static] is required. [final] is optional.
                                                      // Registry name is explicitly defined: "minecraft:enchantment"
                                                      // Resource location is explicitly defined: "minecraft:flame"
                                                      // To inject: "minecraft:flame" from the [Enchantment] registry

    public static final Biome ice_flat = null;        // No annotation on the field.
                                                      // Therefore, the field is ignored.

    @ObjectHolder("minecraft:creeper")
    public static final Entity creeper = null;        // Annotation present. [public static] is required. [final] is optional.
                                                      // The registry name has not been specified on the field.
                                                      // Therefore, this will not compile.

    @ObjectHolder(registryName = "minecraft:potion")
    public static final Potion levitation = null;     // Annotation present. [public static] is required. [final] is optional.
                                                      // Registry name is explicitly defined: "minecraft:potion"
                                                      // The entry's name value has not been specified on the field.
                                                      // Therefore, this will not compile.
}

Creating Custom Registries

Creating custom registries for your mod might be useful if you want other mods to add new things to your system. For example, you might have magic spells and want to allow other mods to add new spells. For this you will want to make a registry (eg. "mymagicmod:spells"). This way, other mods will be able to register things to that list, and you won't have to do anything else.

Just like with registering a new item or block you have two ways of making a new registry. Each method takes in a RegistryBuilder which is used to build an IForgeRegistry. Each builder should have its name set via #setName before being created.

With DeferredRegister

The first method involves the second static constructor: DeferredRegister#create(ResourceLocation, String). From there, we can construct the registry using #makeRegistry. This will already populate #setName for us. This method also returns a supplier of the registry which we can use after the NewRegistryEvent is called.

Here is an example:

public static final DeferredRegister<ExampleRegistry> EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID);

public static final Supplier<IForgeRegistry<ExampleRegistry>> REGISTRY = EXAMPLE.makeRegistry(RegistryBuilder::new);
val EXAMPLE: DeferredRegister<ExampleRegistry> = DeferredRegister.create(ResourceLocation(MODID, "example_registry"), MODID)

val REGISTRY: IForgeRegistry<ExampleRegistry> by EXAMPLE.makeRegistry(::RegistryBuilder).let {
    lazy {
        it.get()
    }
}
final val EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID)

private final val PRIVATE_REGISTRY = EXAMPLE.makeRegistry(() => new RegistryBuilder)
final lazy val REGISTRY = PRIVATE_REGISTRY.get

Using NewRegistryEvent

The second method can be done during the NewRegistryEvent event. Using NewRegistryEvent#create, you can pass in a RegistryBuilder directly. This method will return a Supplier<IForgeRegistry<V>> that can be stored and queried after the event is fired to gain access to your IForgeRegistry instance.

Here is an example: (the event handler is registered on the mod event bus)

public static Supplier<IForgeRegistry<ExampleRegistry>> registrySupplier = null;

@SubscribeEvent
public void onNewRegistry(NewRegistryEvent event){
    RegistryBuilder<ExampleRegistry> registryBuilder = new RegistryBuilder<>();
    registryBuilder.setName(new ResourceLocation(MODID, "example_registry");
    registrySupplier = event.create(registryBuilder);
}

Handling Missing Entries

When loading a pre-existing world after removing mods or updating versions, there are cases where certain registry objects will cease to exist. In these cases, it is possible to specify actions to remove a mapping, prevent the world from loading, or remap the name as needed. This can be done through the third of the registry events: MissingMappingsEvent. Within the event, you can grab an immutable list of missing mappings associated with a mod id for a given registry via #getMappings or a list of all mappings via #getAllMappings.

For each Mapping, you can either execute one of the following methods:

  • #ignore which abandons the entry when loading
  • #warn which warns the user about the missing entry but continues loading
  • #fail which prevents the world from loading
  • #remap which remaps the entry to the specified non-null object in the same registry

If none of the above are specified, then the default action of notifying the user about the missing mappings occur.

Here is an example: (the event handler is registered on the forge event bus)

// This will ignore any missing test items from the specified world
@SubscribeEvent
public void onMissing(final MissingMappingsEvent event) {
    event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
        .filter(mapping -> mapping.key.getPath().contains("test"))
            .forEach(Mapping::ignore);
}


// This will ignore any missing test items from the specified world
@SubscribeEvent
void onMissing(final MissingMappingsEvent event) {
    event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
        .filter(mapping -> mapping.key.path.contains("test"))
            .forEach(Mapping::ignore);
}