From 9f10fdee65833cf4a466e2089d7592278f8c3bb3 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sat, 23 May 2020 12:56:08 +0800 Subject: [PATCH 01/10] Rewrite registries documentation --- docs/concepts/registries.md | 172 ++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 57 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 10d0203c..bb450627 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -1,34 +1,22 @@ Registries ========== -Registration is the process of taking the objects of a mod (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 in a mod and will exhibit great amounts of unexplainable behavior (and probably crash). Some examples of things that need to be registered are `Block`s, `Item`s, `Biome`s. +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 in a mod and will cause unexplainable behavior and crashes. -Most things that require registration in the game are handled by the Forge registries. A registry is a simple object similar to a map that assigns values to keys. Additionally, they automatically assign integer IDs to values. Forge uses registries with [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act like a "registry name" for the object. The registry name for an object may be accessed with `get`/`setRegistryName`. The setter can only ever be called once, and calling it twice results in an exception. Every type of registrable object has its own registry, and names in two different registries will not collide. (E.g. there's a registry for `Block`s, and a registry for `Item`s, and a `Block` and an `Item` may be registered with the same name `mod:example` without colliding. However, if two blocks were registered with that name, an exception would be thrown.) +Most things that require registration in the game are handled by the Forge registries. A registry is a object similar to a map that assigns values to keys. Forge uses registries with [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act as the "registry name" for objects. The registry name for an object may be accessed with `#getRegistryName`/`#setRegistryName`. The setter can only be called once; calling it twice results in an exception. -Registering Things ------------------- - -The recommended way to register things is through the `RegistryEvent`s. These [events][] are fired after mod constructors are called and before configs are loaded. In `RegistryEvent.NewRegistry`, registries should be created. Later, `RegistryEvent.Register` is fired once for each registered registry. Because `Register` is a generic event, the event handler should set the type parameter to the type of the object being registered. The event will contain the registry to register things to (`getRegistry`), and things may be registered with `register` (or `registerAll`) on the registry. Here's an example of an event handler that registers blocks: +Every type of registrable object has its own registry. To see all registries supported by Forge, see the `ForgeRegistries` class. All registry names within a registry must be unique. However, names in different registries will not collide. For example, there's a `Block` registry, and an `Item` registry. A `Block` and an `Item` may be registered with the same name `example:thing` without colliding; however, if two `Block`s or two `Item`s were registered with the same exact name, an exception will occur. -```java -@SubscribeEvent -public void registerBlocks(RegistryEvent.Register event) { - event.getRegistry().registerAll(block1, block2, ...); -} -``` - -The order in which `RegistryEvent.Register` events fire is alphabetically, with the exception that `Block` will *always* fire first, and `Item` will *always* fire second, right after `Block`. After the `Register` event has fired, all [`ObjectHolder`][ObjectHolder] annotations are refreshed, and after `Register` has fired they are refreshed again. They are refreshed for a third time after *all* of the other `Register` events have fired. +Methods for Registering +------------------ -`RegistryEvent`s are currently supported for the following types: `Block`, `Item`, `Potion`, `Biome`, `SoundEvent`, `PotionType`, `Enchantment`, `IRecipe`, `VillagerProfession`, `EntityEntry` +There are two proper ways to register objects: the `DeferredRegister` class, and the `RegistryEvent.Register` lifecycle event. -There is another, older way of registering objects into registries, using `GameRegistry.register`. Anytime something suggests using this method, it should be replaced with an event handler for the appropriate registry event. This method simply finds the registry corresponding to an `IForgeRegistryEntry` with `IForgeRegistryEntry::getRegistryType`, and then registers the object to the registry. There is also a convenience overload that takes an `IForgeRegistryEntry` and a `ResourceLocation`, which is equivalent to calling `IForgeRegistryEntry::setRegistryName`, followed by a `GameRegistry.register` call. +### DeferredRegister -!!! information - Registering an `Entity` or `TileEntity` might be a little bit confusing at first as it doesn't use the `Entity` or `TileEntity` classes, but an `EntityType` or `TileEntityType`. These are created by making use of `EntityType.Builder` or `TileEntityType.Builder`. - The `String` parameter of the builder's `build` method is a data-fixer id. Data fixers do not work with mods (yet) so you should pass `null` in. +`DeferredRegister` is the newer, documented, and recommeded 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 `Register` event. -### DeferredRegister -`DeferredRegister` is a new way of registering objects that replicates the simplicity of static initialisers while supporting registry overrides and avoiding the issues caused by static initialisation. `DeferredRegister` is well documented (check it's javadocs). It simply maintains a list of suppliers for entries and registers the objects from those suppliers them during the proper Register event. These suppliers should return **new** instances every time. Here's an example of a mod that uses `DeferredRegister` to register blocks: +An example of a mod registering a custom block: ```java private static final DeferredRegister BLOCKS = new DeferredRegister<>(ForgeRegistries.BLOCKS, MODID); @@ -40,63 +28,133 @@ public ExampleMod() { } ``` -Creating Registries +### `Register` events + +The `RegistryEvent`s are the second and more flexible way to register objects. These [events][] are fired after the mod constructors and before the loading of configs. + +The event used in registering objects is the `RegistryEvent.Register`. The type parameter `T` should be set to the type of the object being registered. Calling `#getRegistry` will return the registry, upon which objects are registered with `#register` or `#registerAll`. + +`RegistryEvent.Register` events are fired in this order: first, the `Block` registry, then the `Item` registry, and then all other registries in alphabetical order. + +Here is an example: (note that the event handler must be registered on the *mod event bus* in the mod constructor) + +```java +@SubscribeEvent +public void registerBlocks(RegistryEvent.Register event) { + event.getRegistry().registerAll(block1, block2, ...); +} +``` + +!!! note + `TileEntity` and `Entity` cannot be registered; instead, `TileEntityType` and `EntityType` are registered, and used in `TileEntity`/`Entity` constructors. These are created through the use of `TileEntityType.Builder` and `EntityType.Builder`, respectively. An example: (`REGISTER` refers to a `DeferredRegister`) + ```java + public static final RegistryObject> EXAMPLE_TILE = REGISTER.register( + "example_tile", () -> TileEntityType.Builder.create(ExampleTile::new, EXAMPLE_BLOCK.get()).build(null) + ); + ``` + +Creating Custom Registries ------------------- -There's a global registry where all the other registries are stored. By taking a `Class` that a registry is supposed to store or its `ResourceLocation` name, one can retrieve a registry from this registry. For example, one can use `GameRegistry.findRegistry(Block.class)` to get the registry for blocks. Any mod can create their own registries, and any mod can register things to registries from any other mod. Registries are created by using `RegistryBuilder` inside a `RegistryEvent.NewRegistry` event handler. This class takes certain parameters for the registry it will generate, such as the name, the `Class` of it's values, and various callbacks for when the registry is changed. Upon calling `RegistryBuilder::create`, the registry is built, registered to the metaregistry, and returned to the caller. +Custom registries are created by using `RegistryBuilder` during the `RegistryEvent.NewRegistry` event. The class `RegistryBuilder` takes certain parameters (such as the name, the `Class` of its values, and various callbacks for different events happening on the registry). Calling `RegistryBuilder#create` will result in the registry being built, registered to the `RegistryManager`, and returned to the caller for additional processing. -In order for a class to have a registry, it needs to implement `IForgeRegistryEntry`. This interface defines `getRegistryName(ResourceLocation)`, `setRegistryName(ResourceLocation)`, and `getRegistryType()`. `getRegistryType` is the base `Class` of the registry the object is to be registered to. It is recommended to extend the default `IForgeRegistryEntry.Impl` class instead of implementing `IForgeRegistryEntry` directly. This class also provides two convenience implementations of `setRegistryName`: one where the parameter is a single string, and one where there are two string parameters. The overload that takes a single string checks whether the input contains a `:` (i.e. it checks whether the passed in stringified `ResourceLocation` has a namespace), and if it doesn't, it uses the current modid as the resource namespace. The two argument overload simply constructs the registry name using the `modID` as the namespace and `name` as the path. +The `Class` of the value of the registry must implement `IForgeRegistryEntry`, which defines that `#setRegistryName` and `#getRegistryName` can be called on the objects of that class. It is recommended to extend `ForgeRegistryEntity`, the default implementation instead of implementing the interface directly. When `#setRegistryName(String)` is called with a string, and that string does not have an explicit namespace, its namespace will be set to the current modid. -Injecting Registry Values Into Fields +The Forge registries can be accessed through the `ForgeRegistries` class. All registries, Forge-provided or custom, can be retrieved by calling `GameRegistry.findRegistry(Class)` with the appropriate class for the registry. For example, the registry for `Block`s can be retrieved by calling `GameRegistry.findRegistry(Block.class)`. + +Injecting Values Using @ObjectHolder ------------------------------------- -It is possible to have Forge inject values from registries into `public static final` fields of classes. This is done by annotating classes and fields with `@ObjectHolder`. If a class has this annotation, all the `public static final` fields within are taken to be object holders too, and the value of the annotation is the namespace of the holder (i.e. every field uses it as the default namespace for the registry name of the object to inject). If a field has this annotation, and the value does not contain a namespace, the namespace is chosen from the surrounding class's `@ObjectHolder` annotation. If the class is not annotated in this situation, the field is ignored with a warning. If it does contain a namespace, then the object to inject into the field is the object with that name. If the class has the annotation and one of the `public static final` fields does not, then the resource path of the object's name is taken to be the field's name. The type of the registry is taken from the type of the field. +It is possible to have Forge inject registered object from registries into the `public static` fields of classes. This is done by annotating classes or fields with `@ObjectHolder` and supplying enough information to construct a `ResourceLocation` that identifies a specific object in a specific registry. + +The rules for `@ObjectHolder` are as follows: + + * If the class is annotated with `@ObjectHolder`, its value will be the default namespace for all fields within if not explicitly defined + * 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`; + * the field type corresponds to a valid registry (e.g. `Item` for the `Item` registry); + * *An exception is thrown if the field type does not correspond to a valid registry* + * the **field** is annotated with `@ObjectHolder`, then: + * the name value is explicitly defined; and + * the namespace value is either explicitly defined or the enclosing class's namespace + * the **enclosing class** has an `@ObjectHolder` annotation, and the field is `final`, then: + * 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* + * *An exception is thrown if the resulting `ResourceLocation` is 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` annotations are refreshed and their fields are injected with their values three times: after the `Block` registry event, after the `Item` registry event, and once after all other registries. !!! note - If an object is not found, either because the object itself hasn't been registered or because the registry does not exist, a debug message is logged and the field is left unchanged. + If the object does not exist in the registry when it is to be injected, a debug message will be logged and no futher action taken. As these rules are rather complicated, here are some examples: - ```java -@ObjectHolder("minecraft") // Resource namespace "minecraft" +@ObjectHolder("minecraft") // Inheritable resource namespace: "minecraft" class AnnotatedHolder { - public static final Block diamond_block = null; // public static final is required. - // Type Block means that the Block registry will be queried. - // diamond_block is the field name, and as the field is not annotated it is taken to be the resource path. - // As there is no explicit namespace, "minecraft" is inherited from the class. - // Object to be injected: "minecraft:diamond_block" from the Block registry. - - @ObjectHolder("ender_eye") - public static final Item eye_of_ender = null; // Type Item means that the Item registry will be queried. - // As the annotation has the value "ender_eye", that overrides the field's name. - // As the namespace is not explicit, "minecraft" is inherited from the class. - // Object to be injected: "minecraft:ender_eye" from the Item registry. - + public static final Block diamond_block = null; // No annotation. [public static final] is required. + // Registry to be queried is [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. + // Registry to be queried is [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; // Type ManaType means that the ManaType registry will be queried. This is obviously a registry made by a mod. - // As the annotation has the value "neomagicae:coffeinum", that overrides the field's name. - // The namespace is explicit, and is "neomagicae", overriding the class's "minecraft" default. - // Object to be injected: "neomagicae:coffeinum" from the ManaType registry. - - public static final Item ENDER_PEARL = null; // Note that the actual name is "minecraft:ender_pearl", not "minecraft:ENDER_PEARL". - // However, since constructing a ResourceLocation lowercases the value, this will work. + public static final ManaType coffeinum = null; // Annotation present. [public static] is required. [final] is optional. + // Registry to be queried is [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. + // Registry to be queried is [Item]. + // Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl" + // !! ^ Field name is valid, because ResourceLocations + // lowercase their values automatically. + // Namespace is not explicitly defined. + // So, namespace is inherited from class annotation: "minecraft" + // To inject: "minecraft:ender_pearl" from the [Item] registry + + public static Block bedrock = null; // No annotation, so [public static final] is required. + // Therefore, the field is ignored. } -class UnannotatedHolder { // Note lack of annotation on this class. +class UnannotatedHolder { // Note the lack of an @ObjectHolder annotation on this class. @ObjectHolder("minecraft:flame") - public static final Enchantment flame = null; // No annotation on the class means that there is no preset namespace to inherit. - // Field annotation supplies all the information for the object. - // Object to be injected: "minecraft:flame" from the Enchantment registry. + public static final Enchantment flame = null; // Annotation present. [public static] is required. [final] is optional. + // Registry to be queried is [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, so [public static final] is required. + // No annotation on the enclosing class. + // Therefore, the field is ignored. - public static final Biome ice_flat = null; // No annotation on the class or the field. - // Therefore this just gets ignored. + @ObjectHolder("minecraft:creeper") + public static Entity creeper = null; // Annotation present. [public static] is required. + // No valid registry exists for [Entity]. + // Therefore, THIS WILL PRODUCE AN EXCEPTION. @ObjectHolder("levitation") - public static final Potion levitation = null; // No resource namespace in annotation, and no default specified by class annotation. - // Therefore, THIS WILL FAIL. The field annotation needs a namespace, or the class needs an annotation. + public static final Potion levitation = null; // Annotation present. [public static] is required. [final] is optional. + // Registry to be queried is [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. } ``` [ResourceLocation]: resources.md#resourcelocation [events]: ../events/intro.md -[ObjectHolder]: #injecting-registry-values-into-fields From 33796e6e9caf3e709743a6a7d7ca2bedb8cb2252 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sun, 24 May 2020 15:23:07 +0800 Subject: [PATCH 02/10] Move ObjectHolder section to middle, fix typos, add ContainerType --- docs/concepts/registries.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index bb450627..cd099315 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -3,9 +3,9 @@ Registries 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 in a mod and will cause unexplainable behavior and crashes. -Most things that require registration in the game are handled by the Forge registries. A registry is a object similar to a map that assigns values to keys. Forge uses registries with [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act as the "registry name" for objects. The registry name for an object may be accessed with `#getRegistryName`/`#setRegistryName`. The setter can only be called once; calling it twice results in an exception. +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 [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act as the "registry name" for objects. The registry name for an object may be accessed with `#getRegistryName`/`#setRegistryName`. 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 `ForgeRegistries` class. All registry names within a registry must be unique. However, names in different registries will not collide. For example, there's a `Block` registry, and an `Item` registry. A `Block` and an `Item` may be registered with the same name `example:thing` without colliding; however, if two `Block`s or two `Item`s were registered with the same exact name, an exception will occur. +Every type of registrable object has its own registry. To see all registries supported by Forge, see the `ForgeRegistries` class. All registry names within a registry must be unique. However, names in different registries will not collide. For example, there's a `Block` registry, and an `Item` registry. A `Block` and an `Item` may be registered with the same name `example:thing` without colliding; however, if two different `Block`s or `Item`s were registered with the same exact name, the second object will override the first. Methods for Registering ------------------ @@ -14,7 +14,7 @@ There are two proper ways to register objects: the `DeferredRegister` class, and ### DeferredRegister -`DeferredRegister` is the newer, documented, and recommeded 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 `Register` event. +`DeferredRegister` 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 `Register` event. An example of a mod registering a custom block: @@ -36,7 +36,7 @@ The event used in registering objects is the `RegistryEvent.Register`. The ty `RegistryEvent.Register` events are fired in this order: first, the `Block` registry, then the `Item` registry, and then all other registries in alphabetical order. -Here is an example: (note that the event handler must be registered on the *mod event bus* in the mod constructor) +Here is an example: (the event handler is registered on the *mod event bus*) ```java @SubscribeEvent @@ -46,22 +46,15 @@ public void registerBlocks(RegistryEvent.Register event) { ``` !!! note - `TileEntity` and `Entity` cannot be registered; instead, `TileEntityType` and `EntityType` are registered, and used in `TileEntity`/`Entity` constructors. These are created through the use of `TileEntityType.Builder` and `EntityType.Builder`, respectively. An example: (`REGISTER` refers to a `DeferredRegister`) + [`TileEntity`][tileentity], `Entity`, and `Container` cannot be registered; instead, `TileEntityType`, `EntityType`, and `ContainerType` respectively are registered, and used in the formers' constructors. These `*Type` classes are factories that simply create the containing type on demand. + + These factories are created through the use of their `*Type.Builder` classes. An example: (`REGISTER` refers to a `DeferredRegister`) ```java public static final RegistryObject> EXAMPLE_TILE = REGISTER.register( "example_tile", () -> TileEntityType.Builder.create(ExampleTile::new, EXAMPLE_BLOCK.get()).build(null) ); ``` -Creating Custom Registries -------------------- - -Custom registries are created by using `RegistryBuilder` during the `RegistryEvent.NewRegistry` event. The class `RegistryBuilder` takes certain parameters (such as the name, the `Class` of its values, and various callbacks for different events happening on the registry). Calling `RegistryBuilder#create` will result in the registry being built, registered to the `RegistryManager`, and returned to the caller for additional processing. - -The `Class` of the value of the registry must implement `IForgeRegistryEntry`, which defines that `#setRegistryName` and `#getRegistryName` can be called on the objects of that class. It is recommended to extend `ForgeRegistryEntity`, the default implementation instead of implementing the interface directly. When `#setRegistryName(String)` 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 `ForgeRegistries` class. All registries, Forge-provided or custom, can be retrieved by calling `GameRegistry.findRegistry(Class)` with the appropriate class for the registry. For example, the registry for `Block`s can be retrieved by calling `GameRegistry.findRegistry(Block.class)`. - Injecting Values Using @ObjectHolder ------------------------------------- @@ -156,5 +149,15 @@ class UnannotatedHolder { // Note the lack of an @ObjectHolder annotation on thi } ``` +Creating Custom Registries +------------------- + +Custom registries are created by using `RegistryBuilder` during the `RegistryEvent.NewRegistry` event. The class `RegistryBuilder` takes certain parameters (such as the name, the `Class` of its values, and various callbacks for different events happening on the registry). Calling `RegistryBuilder#create` will result in the registry being built, registered to the `RegistryManager`, and returned to the caller for additional processing. + +The `Class` of the value of the registry must implement `IForgeRegistryEntry`, which defines that `#setRegistryName` and `#getRegistryName` can be called on the objects of that class. It is recommended to extend `ForgeRegistryEntry`, the default implementation instead of implementing the interface directly. When `#setRegistryName(String)` 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 `ForgeRegistries` class. All registries, Forge-provided or custom, can be retrieved by calling `GameRegistry.findRegistry(Class)` with the appropriate class for the registry. For example, the registry for `Block`s can be retrieved by calling `GameRegistry.findRegistry(Block.class)`. + [ResourceLocation]: resources.md#resourcelocation [events]: ../events/intro.md +[tileentity]: ../tileentities/tileentity.md \ No newline at end of file From 618a593fe6c19e7701ee38c327c517b42fd1c48e Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sat, 20 Jun 2020 23:40:27 +0800 Subject: [PATCH 03/10] Update phrasing --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index cd099315..b7470a6c 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -1,7 +1,7 @@ Registries ========== -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 in a mod and will cause unexplainable behavior and crashes. +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 [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act as the "registry name" for objects. The registry name for an object may be accessed with `#getRegistryName`/`#setRegistryName`. The setter can only be called once; calling it twice results in an exception. From fc20bbea33abe8a1e110eab2edf9594244c6f170 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sun, 21 Jun 2020 00:07:29 +0800 Subject: [PATCH 04/10] Generalize note on registering *Type classes --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index b7470a6c..8c6e6b7e 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -46,7 +46,7 @@ public void registerBlocks(RegistryEvent.Register event) { ``` !!! note - [`TileEntity`][tileentity], `Entity`, and `Container` cannot be registered; instead, `TileEntityType`, `EntityType`, and `ContainerType` respectively are registered, and used in the formers' constructors. These `*Type` classes are factories that simply create the containing type on demand. + Some classes cannot by themself be registered; instead, `*Type` classes are registered, and used in the formers' constructors. For example, [`TileEntity`][tileentity] has `TileEntityType`, and `Entity` has `EntityType`. These `*Type` classes are factories that simply create the containing type on demand. These factories are created through the use of their `*Type.Builder` classes. An example: (`REGISTER` refers to a `DeferredRegister`) ```java From 11d6a02a04ac70cb0df573e658c05d8a0bd6a292 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sun, 21 Jun 2020 00:28:59 +0800 Subject: [PATCH 05/10] Fix typo --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 8c6e6b7e..a7f9ef88 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -46,7 +46,7 @@ public void registerBlocks(RegistryEvent.Register event) { ``` !!! note - Some classes cannot by themself be registered; instead, `*Type` classes are registered, and used in the formers' constructors. For example, [`TileEntity`][tileentity] has `TileEntityType`, and `Entity` has `EntityType`. These `*Type` classes are factories that simply create the containing type on demand. + Some classes cannot by themselves be registered; instead, `*Type` classes are registered, and used in the formers' constructors. For example, [`TileEntity`][tileentity] has `TileEntityType`, and `Entity` has `EntityType`. These `*Type` classes are factories that simply create the containing type on demand. These factories are created through the use of their `*Type.Builder` classes. An example: (`REGISTER` refers to a `DeferredRegister`) ```java From 1cc9eb87caf3defb5137ecd6f30c58d8b5477c3e Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sun, 21 Jun 2020 17:01:17 +0800 Subject: [PATCH 06/10] Remove paragraph about registry load order --- docs/concepts/registries.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index a7f9ef88..0cf7e196 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -34,8 +34,6 @@ The `RegistryEvent`s are the second and more flexible way to register objects. T The event used in registering objects is the `RegistryEvent.Register`. The type parameter `T` should be set to the type of the object being registered. Calling `#getRegistry` will return the registry, upon which objects are registered with `#register` or `#registerAll`. -`RegistryEvent.Register` events are fired in this order: first, the `Block` registry, then the `Item` registry, and then all other registries in alphabetical order. - Here is an example: (the event handler is registered on the *mod event bus*) ```java From b919838d81c94b75c27fb74a5c207a2d257ddfc4 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Wed, 1 Jul 2020 01:50:39 +0800 Subject: [PATCH 07/10] Update DeferredRegister creation --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 0cf7e196..dd74af63 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -19,7 +19,7 @@ There are two proper ways to register objects: the `DeferredRegister` class, and An example of a mod registering a custom block: ```java -private static final DeferredRegister BLOCKS = new DeferredRegister<>(ForgeRegistries.BLOCKS, MODID); +private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); public static final RegistryObject ROCK_BLOCK = BLOCKS.register("rock", () -> new Block(Block.Properties.create(Material.ROCK))); From 00a2f4a384c9a496621b36bb9c1312f739d4a9a6 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Wed, 1 Jul 2020 02:06:15 +0800 Subject: [PATCH 08/10] Convert and fix tabs to spacing --- docs/concepts/registries.md | 90 ++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index dd74af63..f98c417a 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -24,7 +24,7 @@ private static final DeferredRegister BLOCKS = DeferredRegister.create(Fo public static final RegistryObject ROCK_BLOCK = BLOCKS.register("rock", () -> new Block(Block.Properties.create(Material.ROCK))); public ExampleMod() { - BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus()); + BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus()); } ``` @@ -60,9 +60,9 @@ It is possible to have Forge inject registered object from registries into the ` The rules for `@ObjectHolder` are as follows: - * If the class is annotated with `@ObjectHolder`, its value will be the default namespace for all fields within if not explicitly defined - * 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: +* If the class is annotated with `@ObjectHolder`, its value will be the default namespace for all fields within if not explicitly defined +* 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`; * the field type corresponds to a valid registry (e.g. `Item` for the `Item` registry); * *An exception is thrown if the field type does not correspond to a valid registry* @@ -73,9 +73,9 @@ The rules for `@ObjectHolder` are as follows: * 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* - * *An exception is thrown if the resulting `ResourceLocation` is 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) +* *An exception is thrown if the resulting `ResourceLocation` is 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` annotations are refreshed and their fields are injected with their values three times: after the `Block` registry event, after the `Item` registry event, and once after all other registries. @@ -87,63 +87,63 @@ As these rules are rather complicated, here are some examples: @ObjectHolder("minecraft") // Inheritable resource namespace: "minecraft" class AnnotatedHolder { public static final Block diamond_block = null; // No annotation. [public static final] is required. - // Registry to be queried is [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 + // Registry to be queried is [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") + @ObjectHolder("ambient.cave") public static SoundEvent ambient_sound = null; // Annotation present. [public static] is required. - // Registry to be queried is [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 + // Registry to be queried is [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. + // 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. - // Registry to be queried is [ManaType] (custom registry). - // Resource location is explicitly defined: "neomagicae:coffeinum" - // To inject: "neomagicae:coffeinum" from the [ManaType] registry + // Registry to be queried is [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. - // Registry to be queried is [Item]. - // Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl" - // !! ^ Field name is valid, because ResourceLocations - // lowercase their values automatically. - // Namespace is not explicitly defined. - // So, namespace is inherited from class annotation: "minecraft" - // To inject: "minecraft:ender_pearl" from the [Item] registry - + // Registry to be queried is [Item]. + // Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl" + // !! ^ Field name is valid, because ResourceLocations + // lowercase their values automatically. + // Namespace is not explicitly defined. + // So, namespace is inherited from class annotation: "minecraft" + // To inject: "minecraft:ender_pearl" from the [Item] registry + public static Block bedrock = null; // No annotation, so [public static final] is required. - // Therefore, the field is ignored. + // Therefore, the field is ignored. } 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. - // Registry to be queried is [Enchantment]. - // Resource location is explicitly defined: "minecraft:flame" - // To inject: "minecraft:flame" from the [Enchantment] registry + // Registry to be queried is [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, so [public static final] is required. - // No annotation on the enclosing class. - // Therefore, the field is ignored. + // 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. - // No valid registry exists for [Entity]. - // Therefore, THIS WILL PRODUCE AN EXCEPTION. + @ObjectHolder("minecraft:creeper") + public static Entity creeper = null; // Annotation present. [public static] is required. + // No valid registry exists for [Entity]. + // Therefore, THIS WILL PRODUCE AN EXCEPTION. @ObjectHolder("levitation") public static final Potion levitation = null; // Annotation present. [public static] is required. [final] is optional. - // Registry to be queried is [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. + // Registry to be queried is [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. } ``` From adb7cfa3e679386217df26da948cce27ad29b239 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Wed, 1 Jul 2020 02:27:32 +0800 Subject: [PATCH 09/10] Clarify ObjectHolder rules --- docs/concepts/registries.md | 51 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index f98c417a..d316c687 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -64,16 +64,17 @@ 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`; - * the field type corresponds to a valid registry (e.g. `Item` for the `Item` registry); - * *An exception is thrown if the field type does not correspond to a valid registry* - * the **field** is annotated with `@ObjectHolder`, then: - * the name value is explicitly defined; and - * the namespace value is either explicitly defined or the enclosing class's namespace - * the **enclosing class** has an `@ObjectHolder` annotation, and the field is `final`, then: - * 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* -* *An exception is thrown if the resulting `ResourceLocation` is invalid (non-valid characters in path)* + * one of the following conditions are true: + * the **enclosing class** has an `@ObjectHolder` annotation, and the field is `final`, 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 `@ObjectHolder`, 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. `Item` or `ArrowItem` for the `Item` registry); + * _An exception is thrown if a field does not have a corresponding registry._ +* _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) @@ -87,7 +88,7 @@ As these rules are rather complicated, here are some examples: @ObjectHolder("minecraft") // Inheritable resource namespace: "minecraft" class AnnotatedHolder { public static final Block diamond_block = null; // No annotation. [public static final] is required. - // Registry to be queried is [Block]. + // 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" @@ -95,7 +96,7 @@ class AnnotatedHolder { @ObjectHolder("ambient.cave") public static SoundEvent ambient_sound = null; // Annotation present. [public static] is required. - // Registry to be queried is [SoundEvent]. + // 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" @@ -104,12 +105,12 @@ class AnnotatedHolder { // 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. - // Registry to be queried is [ManaType] (custom registry). + // 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. - // Registry to be queried is [Item]. + // Item has a corresponding registry: [Item]. // Name path is the name of the field: "ENDER_PEARL" -> "ender_pearl" // !! ^ Field name is valid, because ResourceLocations // lowercase their values automatically. @@ -117,29 +118,41 @@ class AnnotatedHolder { // 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. - // Registry to be queried is [Enchantment]. + // 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, so [public static final] is required. - // No annotation on the enclosing class. + 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. - // No valid registry exists for [Entity]. + // 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. - // Registry to be queried is [Potion]. + // 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. From 5620f0f29851e311d966f063f8054f4190d130d6 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Wed, 1 Jul 2020 02:32:35 +0800 Subject: [PATCH 10/10] Clarify when ObjectHolder fields are refreshed --- docs/concepts/registries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index d316c687..68c0468e 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -78,10 +78,10 @@ The rules for `@ObjectHolder` are as follows: * 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` annotations are refreshed and their fields are injected with their values three times: after the `Block` registry event, after the `Item` registry event, and once after all other registries. +`@ObjectHolder`-annotated fields are injected with their values after their corresponding registry's `RegistryEvent.Register` event is fired. !!! note - If the object does not exist in the registry when it is to be injected, a debug message will be logged and no futher action taken. + 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: ```java