-
Notifications
You must be signed in to change notification settings - Fork 35
Custom Builders
The problem: Registrate doesn't yet support the registry type I want to build, or I want to build mod-specific objects that I have a custom registry for.
Good news! Registrate is 100% open to extension, you just need to implement the builder class for your registry objects. The builders shipped with Registrate are not special in any way, other than that they get convenience methods in Registrate
. You can extend the Registrate
class to add your own helper methods.
To create custom builders, it's recommended to extend AbstractBuilder
, as it provides a lot of convenience methods and basic functionality. But it is also possible to implement Builder
directly, if needed.
A simple builder implementation looks like this:
public class CustomObjectBuilder<T extends CustomObject, P> extends AbstractBuilder<CustomObject, T, P, CustomObjectBuilder<T, P>> {
private final Supplier<? extends T> factory;
public CustomObjectBuilder(Registrate owner, P parent, String name, BuilderCallback callback, Supplier<? extends T> factory) {
super(owner, parent, name, callback, CustomObject.class);
this.factory = factory;
}
@Override
protected T createEntry() {
return factory.get();
}
}
You may notice that Builder
(and AbstractBuilder
) has a lot of generic type variables. It may seem like a lot at first glance, but there's nothing very complicated here. Let's go over each type.
-
R
, the first type, is used for the "registry type" of the builder. This is the concrete base class that all registry objects must extend, and the type used for the forge registry itself. It's most likely your class that extendsForgeRegistryEntry
. In this case, it'sCustomObject
. -
T
is the "actual type". This is redefined by the current class so that it can be whatever the type of the object currently being built is. -
P
is the type of the builder's parent. This is simply the type of object that will be returned back frombuild()
. Most of the time it will beRegistrate
, but can be different when builders create other builders. -
S
, finally, is simply a self type. It recursively references the current type, so that the right builder type can be returned from methods defined in superclasses, such asaddData
.
Now that you have a custom builder, it can be plugged in to Registrate using the entry
methods.
public static final RegistryObject<CustomObject> CUSTOM_OBJECT = REGISTRATE.object("custom_object")
.entry((name, callback) -> new CustomObjectBuilder<>(REGISTRATE, REGISTRATE, name, callback, CustomObject::new))
.register();
Of course, this is a lot of boilerplate. You can make things easier by adding utility methods in your own custom Registrate
subclass, such as:
public <T extends CustomObject> CustomObjectBuilder<T, Registrate> customObject(Supplier<? extends T> factory) {
return customObject(this, factory);
}
public <T extends CustomObject, P> CustomObjectBuilder<T, P> customObject(P parent, Supplier<? extends T> factory) {
return customObject(parent, currentName(), factory);
}
public <T extends CustomObject, P> CustomObjectBuilder<T, P> customObject(P parent, String name, Supplier<? extends T> factory) {
return entry(name, callback -> new CustomObjectBuilder<>(this, parent, name, callback, factory));
}
Or whatever is most convenient. Now this can be used just like any other builder.
public static final RegistryObject<CustomObject> CUSTOM_OBJECT = CUSTOM_REGISTRATE.object("custom_object")
.customObject(CustomObject::new)
.register();