The big Registry system update is here
Introduction
A big update to the registry system was just released in NeoForge version 20.2.59-beta! Our primary goal is to simplify the code as much as possible, and align it with the registry system in vanilla Minecraft.
This blog post will go through the most important changes that were made, to act as a migration guide for modders.
This rework is the first of the three big reworks to land after our initial 20.2 release. The other two systems that will receive an overhaul in the coming weeks are capabilities and networking. Once these also land we will be aiming for a 20.2 stable release.
Using the registries
Previously, the NeoForge registry system was completely separate from vanilla’s. Now, we use the existing registry system in vanilla, with a few additions that are relevant to mod support.
This means that IForgeRegistry
is replaced by the vanilla Registry
.
NeoForge adds a few methods to the vanilla Registry
type via IRegistryExtension
.
Here is an overview of the changed methods:
IForgeRegistry |
Registry |
---|---|
getValue(ResourceLocation) |
get(ResourceLocation) |
getKeys() |
keySet() |
getValues().stream() |
stream() |
getHolder(T)Optional<Holder> |
wrapAsHolder(T)Holder? |
tags() |
Use getTag(TagKey) and the HolderSet API |
The registries that are defined by Minecraft itself should now be accessed via BuiltInRegistries
:
- ForgeRegistries.ITEMS.getValue(new ResourceLocation("minecraft:diamond"));
+ BuiltInRegistries.ITEM.get(new ResourceLocation("minecraft:diamond"));
The registries that are defined by NeoForge are accessible via NeoForgeRegistries
.
They are not wrapped in a Supplier
anymore, and can be used directly:
- ForgeRegistries.FLUID_TYPES.get().getValue(new ResourceLocation("mymod:fancyfluid"));
+ NeoForgeRegistries.FLUID_TYPES.get(new ResourceLocation("mymod:fancyfluid"));
Registration
Registration still happens in the RegisterEvent
,
and we still recommend that modders use DeferredRegister
to abstract over the events.
RegistryObject
was replaced by DeferredHolder
, which implements vanilla’s Holder
interface.
When registering objects to a deferred register, we recommended two options:
- If you don’t need any of the
Holder
functions, you can use aSupplier
for the type of your field. - Otherwise, use
DeferredHolder
with the two generic parameters (one for the registry and one for your object type).
Here are is an example:
private static final DeferredRegister<Enchantment> ENCHANTMENTS = DeferredRegister.create(Registries.ENCHANTMENT, "mymod");
- public static final RegistryObject<Enchantment> MAGIC =
- ENCHANTMENTS.register("magic", () -> new MagicEnchantment(/* create enchantment */));
// Supplier only:
+ public static final Supplier<MagicEnchantment> MAGIC =
+ ENCHANTMENTS.register("magic", () -> new MagicEnchantment(/* create enchantment */));
// Access to both Holder and the exact object type:
+ public static final DeferredHolder<Enchantment, MagicEnchantment> MAGIC =
+ ENCHANTMENTS.register("magic", () -> new MagicEnchantment(/* create enchantment */));
NeoForge also provides DeferredHolder
and DeferredRegister
specializations for items and blocks
that implement ItemLike
. For example:
private static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("mymod");
private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems("mymod");
public static final DeferredBlock<MyBlock> MY_BLOCK = BLOCKS.register("my_block", () -> new MyBlock(/* create block */));
public static final DeferredItem<MyItem> MY_ITEM = ITEMS.register("my_item", () -> new MyItem(/* create item */));
// There are also a few extra helper functions, for example `registerBlockItem`:
public static final DeferredItem<BlockItem> MY_BLOCK_ITEM = ITEMS.registerBlockItem(MY_BLOCK);
As usual, don’t forget to pass your mod bus to your DeferredRegister
s:
@Mod("mymod")
public class MyMod {
// In case you missed it, mod constructors can now receive a number of optional arguments,
// including the mod's event bus. Unrelated to registries, but still pretty cool. ;)
public MyMod(IEventBus modEventBus) {
ENCHANTMENTS.register(modEventBus);
BLOCKS.register(modEventBus);
ITEMS.register(modEventBus);
}
}
Custom registries
Custom registries are created with a RegistryBuilder
, and must be registered to the NewRegistryEvent
.
They can now be held in static fields, just like the registries in BuiltInRegistries
or NeoForgeRegistries
.
Here is a registration example, using the helper methods provided by DeferredRegister
:
// Create a registry key - we don't have a registry yet so give the key to DeferredRegister.
public static final ResourceKey<Registry<Custom>> CUSTOM_REGISTRY_KEY =
ResourceKey.createRegistryKey(new ResourceLocation("mymod:custom"));
// Create the DeferredRegister with our registry key.
private static final DeferredRegister<Custom> CUSTOMS =
DeferredRegister.create(CUSTOM_REGISTRY_KEY, "mymod");
// We can register objects as usual...
public static final Holder<Custom> CUSTOM_OBJECT =
CUSTOMS.register("custom_object", () -> new Custom());
// And here is how to create the registry!
public static final Registry<Custom> CUSTOM_REGISTRY =
CUSTOMS.makeRegistry(builder -> /* use builder to configure registry if needed */);
// Remember to register CUSTOMS in the mod constructor!
Another way is to directly create the registry with a RegistryBuilder
, and manually register it:
// We still need a registry key.
public static final ResourceKey<Registry<Custom>> CUSTOM_REGISTRY_KEY =
ResourceKey.createRegistryKey(new ResourceLocation("mymod:custom"));
// Create the registry directly...
public static final Registry<Custom> CUSTOM_REGISTRY = new RegistryBuilder<>(CUSTOM_REGISTRY_KEY)
// configure the builder if you want, for example with .sync(true)
// then build the registry
.build();
// Remember to tell NeoForge about your registry! For example:
modEventBus.addListener(NewRegistryEvent.class, event -> event.register(CUSTOM_REGISTRY));
That’s it!
As usual, ask on our Discord server in the #modder-support-1.20
channel if you have any question.