Changes

3,081 bytes removed ,  17:24, 11 June 2022
Update to 1.19
Line 3: Line 3:  
Registration is the process of making an object (such as an item or block) known to the game during runtime. If some objects are not registered, this could cause crashes even before the game is fully loaded or arbitrary behaviors such as bottlenecking mod compatibility for world generation.
 
Registration is the process of making an object (such as an item or block) known to the game during runtime. If some objects are not registered, this could cause crashes even before the game is fully loaded or arbitrary behaviors such as bottlenecking mod compatibility for world generation.
   −
Most objects that are known within the game are handled by a <code>Registry</code>. Each registry uniquely defines each object through a "registry name" via a [[Using Resources#ResourceLocation|ResourceLocation]]. This "registry name" can be accessed with its respective getter and setter: <code>#getRegistryName</code> and <code>#setRegistryName</code>. You can only set the "registry name" of a given object once; otherwise, an exception will be thrown.
+
Most objects that are known within the game are handled by a <code>Registry</code>. Each registry uniquely defines each object through a "registry name" via a [[Using Resources#ResourceLocation|ResourceLocation]].
    
{{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.}}
 
{{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.}}
Line 13: Line 13:  
== Methods for Registering ==
 
== Methods for Registering ==
   −
There are two proper ways to register objects within an associated wrapped Forge registry: the <code>DeferredRegister</code> class, and the <code>RegistryEvent$Register</code> lifecycle event.
+
There are two proper ways to register objects within an associated wrapped Forge registry: the <code>DeferredRegister</code> class, and the <code>RegisterEvent</code> lifecycle event.
 
  −
For objects with '''no''' associated Forge registry, you can register the associated entry during the <code>FMLCommonSetupEvent</code> lifecycle event. In some cases, although not recommended, you may also statically initialize and register these entries.
      
=== DeferredRegister ===
 
=== DeferredRegister ===
   −
<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 the proper <code>RegistryEvent$Register</code> event. 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.  
+
<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.  
    
An example of a mod registering a custom block:
 
An example of a mod registering a custom block:
Line 59: Line 57:  
{{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>.}}
 
{{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>.}}
   −
=== RegistryEvent.Register ===
+
=== RegisterEvent ===
 
  −
The <code>RegistryEvent</code>s are another, more slightly flexible way to register objects. These [[Events|events]] are fired synchronously after <code>FMLConstructModEvent</code> and before the configs are loaded.
  −
 
  −
The event used to register objects is <code>RegistryEvent.Register<T></code>, where the type parameter <code>T</code> is the object type being registered. You can grab the associated registry using <code>#getRegistry</code> and register the objects within using either <code>#register</code> (pass in a single object) or <code>#registerAll</code> (pass in ''varargs'' or an array of objects). The latter is useful for minimizing calls to <code>#register</code>, although it provides no benefit time-complexity wise.
     −
{{Tip/Important|The type parameter specified must be the exact class used within the Forge registry, not its superclass nor its subclass. If the class specified is not referenced as a type parameter within the associated Forge registries, then the event will not be called.}}
+
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''')
Line 71: Line 65:  
{{Template:Tabs/Code_Snippets
 
{{Template:Tabs/Code_Snippets
 
|java=@SubscribeEvent
 
|java=@SubscribeEvent
public void registerBlocks(RegistryEvent.Register<Block> event) {
+
public void register(RegisterEvent event) {
     event.getRegistry().registerAll(new Block(...).setRegistryName(new ResourceLocation(MODID, "example_block1")), new Block(...).setRegistryName(new ResourceLocation(MODID, "example_block2")), ...);
+
     event.register(ForgeRegistries.Keys.BLOCKS,
 +
        helper -> helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
 +
    );
 
}
 
}
 
|kotlin=@JvmStatic
 
|kotlin=@JvmStatic
 
@SubscribeEvent
 
@SubscribeEvent
private fun registerBlocks(event: RegistryEvent.Register<Block>) =
+
private fun register(event: RegisterEvent) =
     event.registry.registerAll(Block(...).setRegistryName(new ResourceLocation(MODID, "example_block1")), Block(...).setRegistryName(new ResourceLocation(MODID, "example_block2")), ...)
+
     event.register(ForgeRegistries.Keys.BLOCKS) {
 +
        helper -> helper.register(ResourceLocation(MODID, "example_block"), Block(...))
 +
    }
 
|scala=@SubscribeEvent
 
|scala=@SubscribeEvent
def registerBlocks(event: RegistryEvent.Register[Block]): Unit =
+
def register(event: RegisterEvent): Unit =
     event.getRegistry.registerAll(new Block(...).setRegistryName(new ResourceLocation(MODID, "example_block1")), new Block(...).setRegistryName(new ResourceLocation(MODID, "example_block2")), ...)
+
     event.register(ForgeRegistries.Keys.BLOCKS,
 +
        helper => helper.register(new ResourceLocation(MODID, "example_block"), new Block(...))
 +
    )
 
|}}
 
|}}
   Line 98: Line 98:  
=== Non-Forge Registries ===
 
=== Non-Forge Registries ===
   −
Not all vanilla registries are wrapped as a Forge registry. This is because the registry is fully independent from any other registry, completely data driven, or just has not been wrapped yet.
+
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>.
 
  −
These registries include:
  −
* Custom Stats (a <code>ResourceLocation</code> registry)
  −
* <code>RuleTestType</code>
  −
* <code>PosRuleTestType</code>
  −
* <code>RecipeType</code>
  −
* <code>GameEvent</code>
  −
* <code>PositionSourceType</code>
  −
* <code>VillagerType</code>
  −
* <code>LootPoolEntryType</code>
  −
* <code>LootItemFunctionType</code>
  −
* <code>LootItemConditionType</code>
  −
* <code>LootNumberProviderType</code>
  −
* <code>LootNbtProviderType</code>
  −
* <code>LootScoreProviderType</code>
  −
* <code>FloatProviderType</code>
  −
* <code>IntProviderType</code>
  −
* <code>HeightProviderType</code>
  −
* <code>StructurePieceType</code>
  −
* <code>TrunkPlacerType</code>
  −
* <code>FeatureSizeType</code>
  −
* A <code>Codec</code> of <code>BiomeSource</code>
  −
* A <code>Codec</code> of <code>ChunkGenerator</code>
  −
* <code>StructureProcessorType</code>
  −
* <code>StructurePoolElementType</code>
  −
* All registries within <code>BuiltinRegistries</code> excluding <code>Biome</code>
  −
 
  −
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
 
{{Template:Tabs/Code_Snippets
|java=private static final DeferredRegister<RecipeType<?>> RECIPE_TYPES = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, MODID);
+
|java=private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);
   −
public static final RegistryObject<RecipeType<ExampleRecipe>> EXAMPLE_RECIPE = RECIPE_TYPES.register("example_recipe", () -> new RecipeType<>() {});
+
public static final RegistryObject<LootItemConditionType> EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...));
|kotlin=private val RECIPE_TYPES = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, MODID)
+
|kotlin=private val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)
   −
val EXAMPLE_RECIPE: RegistryObject<RecipeType<ExampleRecipe>> = RECIPE_TYPES.register("example_recipe") {
+
val EXAMPLE_LOOT_ITEM_CONDITION_TYPE : RegistryObject<LootItemConditionType> = REGISTER.register("example_loot_item_condition_type") {
     RecipeType<>() {}
+
     LootItemConditionType(...)
 
}
 
}
|scala=private final val RECIPE_TYPES = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, MODID)
+
|scala=private final val REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID)
   −
final val EXAMPLE_RECIPE = RECIPE_TYPES.register("example_recipe", () => new RecipeType<>() {})
+
final val EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () => new LootItemConditionType(...))
|groovy=private static final DeferredRegister<RecipeType<?>> RECIPE_TYPES = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, MODID);
+
|groovy=private static final DeferredRegister<LootItemConditionType> REGISTER = DeferredRegister.create(Registry.LOOT_ITEM_REGISTRY, MODID);
   −
public static final RegistryObject<RecipeType<ExampleRecipe>> EXAMPLE_RECIPE = RECIPE_TYPES.register("example_recipe", () -> new RecipeType<>() {});
+
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 must use the lazy initialization method mentioned above to register the object in the correct order.
+
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 ===
 
=== Data Driven Entries ===
Line 151: Line 123:  
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>.
 
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>.
   −
The following registries are data driven:
+
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.
* <code>ConfiguredSurfaceBuilder</code>
  −
* <code>ConfiguredWorldCarver</code>
  −
* <code>ConfiguredFeature</code>
  −
* <code>ConfiguredStructureFeature</code>
  −
* <code>StructureProcessorList</code>
  −
* <code>StructureTemplatePool</code>
  −
* <code>Biome</code>
  −
* <code>NoiseGeneratorSettings</code>
  −
* <code>DimensionType</code>
  −
* <code>LevelStem</code>
  −
* <code>Level</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>ConfiguredFeature</code> for ore generation within an overworld <code>Biome</code>). 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.
 
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.
Line 170: Line 129:  
== Referencing Registered Objects ==
 
== 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 their respective <code>RegistryEvent$Register</code> event. This is to maintain a sane loading order for registries and their objects along with dynamic loading/unloading of mods.
+
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>.
 
Forge registered objects must always be referenced through a <code>RegistryObject</code> or a field with <code>@ObjectHolder</code>.
    
===Using RegistryObjects===
 
===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 associated <code>RegistryEvent$Register</code> has been dispatched and frozen.
+
<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.
    
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>.
 
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>.
Line 221: Line 180:  
* 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 associated object values after their corresponding registry's <code>RegistryEvent$Register</code> event is fired, along with <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, along with <code>RegistryObject</code>s.
    
{{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.}}
 
{{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.}}
Line 311: Line 270:  
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.
 
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 <code>RegistryBuilder</code> which is used to build an <code>IForgeRegistry</code> for an object class that implements <code>IForgeRegistryEntry</code>. Each builder should have its name and type set via <code>#setName</code> and <code>#setType</code> respectively before being created.
+
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.
 
  −
For the class that implements <code>IForgeRegistryEntry</code>, it is recommended in most cases to extend the default implementation of <code>ForgeRegistryEntry</code>. For interfaces, it should extend <code>IForgeRegistryEntry</code> with its implementations extending <code>ForgeRegistryEntry</code>.
      
=== With DeferredRegister ===
 
=== With DeferredRegister ===
   −
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> and <code>#setType</code> for us. This method also returns a supplier of the registry which we can use after the <code>NewRegistryEvent</code> is called.
+
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.
    
Here is an example:
 
Here is an example:
Line 324: Line 281:  
|java=public static final DeferredRegister<ExampleRegistry> EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID);
 
|java=public static final DeferredRegister<ExampleRegistry> EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID);
   −
public static final Supplier<IForgeRegistry<ExampleRegistry>> REGISTRY = EXAMPLE.makeRegistry(ExampleRegistry.class, RegistryBuilder::new);
+
public static final Supplier<IForgeRegistry<ExampleRegistry>> REGISTRY = EXAMPLE.makeRegistry(RegistryBuilder::new);
 
|kotlin=val EXAMPLE: DeferredRegister<ExampleRegistry> = DeferredRegister.create(ResourceLocation(MODID, "example_registry"), MODID)
 
|kotlin=val EXAMPLE: DeferredRegister<ExampleRegistry> = DeferredRegister.create(ResourceLocation(MODID, "example_registry"), MODID)
    
val REGISTRY: IForgeRegistry<ExampleRegistry> by lazy {
 
val REGISTRY: IForgeRegistry<ExampleRegistry> by lazy {
     EXAMPLE.makeRegistry(ExampleRegistry::class.java, ::RegistryBuilder).get()
+
     EXAMPLE.makeRegistry(::RegistryBuilder).get()
 
}
 
}
 
|scala=final val EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID)
 
|scala=final val EXAMPLE = DeferredRegister.create(new ResourceLocation(MODID, "example_registry"), MODID)
   −
final lazy val REGISTRY = EXAMPLE.makeRegistry(classOf[ExampleRegistry], () => new RegistryBuilder).get
+
final lazy val REGISTRY = EXAMPLE.makeRegistry(() => new RegistryBuilder).get
 
|}}
 
|}}
   Line 355: Line 312:  
== Handling Missing Entries ==
 
== 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>RegistryEvent$MissingMappings<T></code>, where the type parameter <code>T</code> is the object type being registered. Within the event, you can grab an immutable list of missing mappings associated with a mod id via <code>#getMappings</code> or a list of all mappings via <code>#getAllMappings</code>.
+
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:
 
For each <code>Mapping</code>, you can either execute one of the following methods:
Line 365: Line 322:  
If none of the above are specified, then the default action of notifying the user about the missing mappings occur.
 
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 '''mod event bus''')
+
Here is an example:  (the event handler is registered on the '''forge event bus''')
    
{{Template:Tabs/Code_Snippets
 
{{Template:Tabs/Code_Snippets
 
|java=// This will ignore any missing test items from the specified world
 
|java=// This will ignore any missing test items from the specified world
 
@SubscribeEvent
 
@SubscribeEvent
public void onMissingItems(final RegistryEvent.MissingMappings<Item> event) {
+
public void onMissing(final MissingMappingsEvent event) {
     event.getMappings(MODID).stream()
+
     event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
 
         .filter(mapping -> mapping.key.getPath().contains("test"))
 
         .filter(mapping -> mapping.key.getPath().contains("test"))
 
             .forEach(Mapping::ignore);
 
             .forEach(Mapping::ignore);
Line 377: Line 334:  
|groovy=// This will ignore any missing test items from the specified world
 
|groovy=// This will ignore any missing test items from the specified world
 
@SubscribeEvent
 
@SubscribeEvent
void onMissingItems(final RegistryEvent.MissingMappings<Item> event) {
+
void onMissing(final MissingMappingsEvent event) {
     event.getMappings(MODID).stream()
+
     event.getMappings(ForgeRegistries.Keys.ITEMS, MODID).stream()
 
         .filter(mapping -> mapping.key.path.contains("test"))
 
         .filter(mapping -> mapping.key.path.contains("test"))
 
             .forEach(Mapping::ignore);
 
             .forEach(Mapping::ignore);