The point of this gist is to explain how my mods like BYG register their registry objects(ie. blocks) in a way that avoids duplicate code across all modules and keeps it only in the common module.
In the common module objects we utilize a helper class that contains both the object and the ID like so:
public record BYGRegistryObject<T>(T object, String id) {
}
In our class containing an object type(let's use blocks in this example) we need the following statics:
private static final List<BYGRegistryObject<Block>> BLOCKS = new ArrayList<>();
public static Block createBlock(Block block, String id) {
BLOCKS.add(new BYGRegistryObject<>(block, id));
return block;
}
public static Collection<BYGRegistryObject<Block>> bootStrap() {
return BLOCKS;
}
Which allows us to static final init our blocks:
public static final Block ASPEN_PLANKS = createPlanks("aspen_planks");
public static final Block BAOBAB_PLANKS = createPlanks("baobab_planks");
public static final Block BLUE_ENCHANTED_PLANKS = createPlanks("blue_enchanted_planks");
public static final Block CHERRY_PLANKS = createPlanks("cherry_planks");
public static final Block CIKA_PLANKS = createPlanks("cika_planks");
private static Block createPlanks(String id) {
return createBlock(new Block(BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2.0f, 3.0f)), id);
}
Below are how we use this system in the Common Module
by mod loader:
@Mod(BYG.MOD_ID)
public class BYGForge {
public BYGForge() {
IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();
bootStrap(eventBus);
}
private static void bootStrap(IEventBus eventBus) {
// We want to static init the class during the registry event not during mod class construction, so we use a supplier here.
register(Block.class, eventBus, () -> BYGBlocks.bootStrap());
}
private static <T extends IForgeRegistryEntry<T>> void register(Class clazz, IEventBus eventBus, Supplier<Collection<BYGRegistryObject<T>>> registryObjectsSupplier) {
eventBus.addGenericListener(clazz, (RegistryEvent.Register<T> event) -> {
Collection<BYGRegistryObject<T>> registryObjects = registryObjectsSupplier.get();
IForgeRegistry<T> registry = event.getRegistry();
for (BYGRegistryObject<T> registryObject : registryObjects) {
registryObject.object().setRegistryName(BYG.createLocation(registryObject.id()));
registry.register(registryObject.object());
}
});
}
}
public class BYGFabric implements ModInitializer {
@Override
public void onInitialize() {
registryBootStrap();
}
private void registryBootStrap() {
register(Registry.BLOCK, BYGBlocks.bootStrap());
}
public static <T> void register(Registry<T> registry, Collection<BYGRegistryObject<T>> objects) {
for (BYGRegistryObject<T> object : objects) {
Registry.register(registry, createLocation(object.id()), object.object());
}
}
Minecraft now does registry freezing for us(1.18.2+), any calls we make that are early(before registration) or if we attempt to register after the registries freeze, is an incorrect call and the call in question should use a supplier if possible. The goal for us is we want to avoid use mod loader specific registration classes systems for registration which helps keep duplicate code low. It is also possible that using static finals may assist the Java Internal Compiler
in optimizing calls to these fields, although I have no evidence of said optimization, but one can dream :')