-
-
Notifications
You must be signed in to change notification settings - Fork 1
Custom Datagen Base Classes
In order to add your own custom data generators, you can either extend AbstractDataProvider
or AbstractRecipeProvider
, depending on your usecase. The way they work is pretty much the same (see the bottom of this page for the differences). Since this is better shown with an example, let's just say you're adding crushing recipes, because that's something no mod has ever done before. In any place you see a variation of the word "crushing", you can use the thing you're actually datagenning instead.
Our generator consists of two classes: the provider and the builder. The builder is used to build single crushing recipe instances, while the provider gets all the crushing recipe instances added, and in the end writes them to disk. If you want, you can make the builder an inner class of the provider, however this is not required.
Let's start with the builder, because that is generally the easier part:
public class CrushingBuilder extends AbstractRecipeBuilder<CrushingBuilder> {
public CrushingBuilder(ResourceLocation id, CrushingProvider provider) {
super(id, provider);
}
@Override
protected void toJson(JsonObject json) {
}
}
The toJson
method is where the magic happens. In here, everything needed is serialized onto the provided JsonObject
. To get the values needed in toJson
, you can and should add helper methods. For example, if our crushing recipe has a required Ingredient
input and an optional secondary Ingredient
input, the code would look something like this:
public class CrushingBuilder extends AbstractRecipeBuilder<CrushingBuilder> {
private final Ingredient input;
private Ingredient secondaryInput;
public CrushingBuilder(ResourceLocation id, CrushingProvider provider, Ingredient input) {
super(id, provider);
this.input = input;
}
public CrushingBuilder setSecondaryInput(Ingredient secondaryInput) {
this.secondaryInput = secondaryInput;
return this;
}
@Override
protected void toJson(JsonObject json) {
json.add("input", input.toJson());
if (secondaryInput != null) {
json.add("secondary_input", secondaryInput.toJson());
}
}
}
Generally, it is recommended to make required properties final and have them be constructor parameters, and have optional properties be handled by setter methods.
Our provider is comparably simple:
public abstract class CrushingProvider extends AbstractRecipeProvider<CrushingBuilder> {
public CrushingProvider(String namespace, DataGenerator generator) {
super("crushing", namespace, generator);
}
@Override
public String getName() {
return "Crushing[" + namespace + "]";
}
public CrushingBuilder builder(String id, Ingredient input) {
return new CrushingBuilder(new ResourceLocation(namespace, id), this, input); //everything after `this` should match the builder's constructor
}
}
You can add as many builder()
methods as you like. It is not required, but highly recommended to have at least one.
The provider can then be extended, like so:
public class MyCrushingProvider extends CrushingProvider {
public MyCrushingProvider(DataGenerator generator) {
super("mymodid", generator);
}
@Override
public void generate() {
//add things to generate here, for example:
builder("tin_ore_crushing", Ingredient.of(MyItems.TIN_ORE.get()))
.setSecondaryInput(Ingredient.of(Items.CHARCOAL))
.build(); //don't forget the build() call!
}
}
And that mod-specific provider can then be added in the GatherDataEvent
like any other provider.
As you might have noticed, using Ingredient
or Item
doesn't work when using items that are not in any loaded mod. For example, if you wanted to use Create's experience nuggets in a recipe, how are you supposed to get it? PotentiallyAbsentItemStack
solves exactly that problem. Using it instead of ItemStack
s (especially when handling results and the like) effectively fakes an ItemStack
to the generator, allowing the use of unknown item ids when generating. Simply create a new PotentiallyAbsentItemStack(new ResourceLocation("thirdpartymodid:thirdpartymoditem"), 10);
and call toJson()
on it where needed.
There is also PotentiallyAbsentFluidStack
, which similarly fakes a FluidStack
to the generator where needed. Both item and fluid variants each also have a WithChance
subvariant that additionally accepts a chance
parameter and adds it to the JSON when serializing.
You might remember the very beginning of this page, where AbstractDataProvider
was mentioned. AbstractRecipeProvider
is a subclass of AbstractDataProvider
that is intended to be used for (you guessed it) recipes. Similarly, AbstractRecipeBuilder
is a subclass of AbstractDataBuilder
with additional functionality for recipes. The differences are as follows:
-
AbstractRecipeProvider
always outputs into thedata/<namespace>/recipes
folder, whereasAbstractDataProvider
outputs directly into thedata/<namespace>
folder. If the recipe type is not the same as the provider's namespace (which means that it's another mod), the recipe will be put indata/<namespace>/recipes/compat/<recipetypenamespace>/<recipetypepath>
- so for example, if you add Create crushing recipes, then these will go intodata/<namespace>/recipes/compat/create/crushing
. -
AbstractRecipeProvider
automatically adds the recipe type as a property to the recipe JSON. -
AbstractRecipeBuilder
has support for recipe conditions. Add a condition usingaddCondition()
. Aforge:mod_loaded
condition for the mod the recipe type is from is automatically added.
Long story short: If you're adding a new recipe type, use AbstractRecipeProvider
and AbstractRecipeBuilder
. If you're adding other, non-recipe datagennable objects, use AbstractDataProvider
and AbstractDataBuilder
instead.