17,405 bytes added
, 17:30, 22 October 2020
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.
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 [[Resources#ResourceLocation|</code>ResourceLocation</code>]] 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.
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.
== Methods for Registering ==
There are two proper ways to register objects: the <code>DeferredRegister</code> class, and the <code>RegistryEvent.Register</code> lifecycle event.
=== 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.
An example of a mod registering a custom block:
<syntaxhighlight lang="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 ExampleMod() {
BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());
}
</syntaxhighlight>
=== RegistryEvent.Register ===
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.
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).
The latter is very useful for minimising calls to <code>#register</code>.
Here is an example: (the event handler is registered on the *mod event bus*)
<syntaxhighlight lang="java">
@SubscribeEvent
public void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(new Block(...), new Block(...), ...);
}
</syntaxhighlight>
{{Colored box|title=Important|content=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.
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>)
<syntaxhighlight lang="java">
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 ==
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.
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 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.
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.
An example of using <code>RegistryObject</code>:
<syntaxhighlight lang="java">
public static final RegistryObject<Item> BOW = RegistryObject.of(new ResourceLocation("minecraft:bow"), ForgeRegistries.ITEMS);
// assume that ManaType is a valid registry, and 'neomagicae:coffeinum' is a valid object within that registry
public static final RegistryObject<ManaType> COFFEINUM = RegistryObject.of(new ResourceLocation("neomagicae", "coffeinum"), () -> ManaType.class);
</syntaxhighlight>
=== 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.
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
* A field is considered for injection if:
** it has at least the modifiers <code>public static</code>;
** one of the following conditions are true:
*** the '''enclosing class''' has an <code>@ObjectHolder</code> annotation, and the field is <code>final</code>, and:
**** the name value is the field's name; and
**** 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)''
* If no other errors or exceptions occur, the field will be injected 7
* 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.
{{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.}}
As these rules are rather complicated, here are some examples:
<div class="mw-collapsible mw-collapsed" style="border: solid 2px; padding: 2px 5px; margin-top: 3px">
<div style="font-weight:bold;line-height:1.6;">Example uses of <code>@ObjectHolder</code></div>
<div class="mw-collapsible-content" style="overflow: auto; white-space: nowrap;">
<syntaxhighlight lang="java">
@ObjectHolder("minecraft") // Inheritable resource namespace: "minecraft"
class AnnotatedHolder {
public static final Block diamond_block = null; // No annotation. [public static final] is required.
// Block has a corresponding registry: [Block]
// Name path is the name of the field: "diamond_block"
// Namespace is not explicitly defined.
// So, namespace is inherited from class annotation: "minecraft"
// To inject: "minecraft:diamond_block" from the [Block] registry
@ObjectHolder("ambient.cave")
public static SoundEvent ambient_sound = null; // Annotation present. [public static] is required.
// 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("neomagicae:coffeinum")
public static final ManaType coffeinum = null; // Annotation present. [public static] is required. [final] is optional.
// ManaType has a corresponding registry: [ManaType] (custom registry)
// 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.
// Item has a corresponding registry: [Item].
// Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl"
// !! ^ Field name is valid, because they are
// converted to lowercase automatically.
// Namespace is not explicitly defined.
// So, namespace is inherited from class annotation: "minecraft"
// To inject: "minecraft:ender_pearl" from the [Item] registry
@ObjectHolder("minecraft:arrow")
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.
// 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.
@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.
// Therefore, the field is ignored.
@ObjectHolder("minecraft:creeper")
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")
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 ==
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.
Just like with registering a new item or block you have 2 ways of making a new registry.
=== Using RegistryEvent.NewRegistry ===
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.
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 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>.
<syntaxhighlight lang="Java">
@SubscribeEvent
public static void onNewRegistry(RegistryEvent.NewRegistry registry){
RegistryBuilder<ArcaneFuelType> registryBuilder = new RegistryBuilder<ArcaneFuelType>();
registryBuilder.setName(ArcaneRituals.location("arcane_fuel"));
registryBuilder.setType(ArcaneFuelType.class);
registryBuilder.create();
}
</syntaxhighlight>
=== With DeferredRegister ===
TODO