FabricMC / fabric

Essential hooks for modding with Fabric.
Apache License 2.0
2.38k stars 419 forks source link

Unobvious recipe datagen logic (not an issue, more of a discussion) #4252

Open maityyy opened 2 hours ago

maityyy commented 2 hours ago

I'm creating this issue primarily to share my fix and let you know that this is a known issue, but also to possibly make a change.

I'm using mojmap.

Vanilla, for creating advancements that unlock recipes, uses the namespace of the item rather than the recipe itself. You can see that in RecipeBuilder::save.

This quite unobvious logic results in recipes for items not of this mod having no real working unlock advancements!

Should the FabricRecipeProvider report this somewhere in the docs?

I previously used FabricRecipeProvider::getRecipeIdentifier when exporting a recipe, but this is no longer possible in newer versions as recipes are now created outside of FabricRecipeProvider (in RecipeGenerator impl, or in an anonymous class).

Anyway, my simple fix:

@Override
public void buildRecipes() {
    Item recipeResult = ...; // item not of this mod
    recipeBuilder(recipeResult)
        .save(output, useModNamespace(recipeResult)) // RecipeBuilder::save
}

private ResourceKey<Recipe<?>> useModNamespace(Item item) {
    return ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath("modid", RecipeBuilder.getDefaultRecipeId(item).getPath()));
}
Linguardium commented 2 hours ago

it still works as long as you design your provider in a given fashion:

public class RecipeProvider extends FabricRecipeProvider {

    public RecipeProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
        super(output, registriesFuture);
    }

    @Override
    protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup wrapperLookup, RecipeExporter recipeExporter) {
        return new RecipeGenerator(wrapperLookup, recipeExporter) {

            @Override
            public void generate() {
                createShaped(RecipeCategory.MISC, Items.ACACIA_BOAT)
                        .pattern("XXX")
                        .input('X',Items.COBBLESTONE)
                        .criterion("somecrit",conditionsFromItem(Items.COBBLESTONE))
//                        .criterion("someothercrit",conditionsFromItem(Items.FLINT))
                        .offerTo(exporter, RegistryKey.of(RegistryKeys.RECIPE, getRecipeIdentifier(someId));

            }
        };
    }

    @Override
    public String getName() {
        return "";
    }

}
maityyy commented 2 hours ago

We can also use Interface Injection and add our own version of RecipeBuilder::save that would use (somehow) the mod namespace (or just static util class)