Table of Contents

Creating a Custom Recipe Type

In this page, we will create our own custom recipe type, including the shapeless version! We won't use any vanilla Minecraft classes, it will be a fully custom recipe type.

You can find and learn how to make other recipe types. Creating a custom cooking recipe type

Adding the Block

We will need to create a block to open the recipe gui. Some of the classes will be created later.

TestRecipeBlock.java
public class TestRecipeBlock extends Block {
	private static final Text TITLE = new TranslatableText("container.test_crafting");
	public TestRecipeBlock(Settings settings) {
		super(settings);
	}
 
	@Override
	public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
		if(world.isClient) {
			return ActionResult.SUCCESS;
		} else {
			player.openHandledScreen(state.createScreenHandlerFactory(world, pos));
			player.incrementStat(Stats.INTERACT_WITH_CRAFTING_TABLE);
			return ActionResult.CONSUME;
		}
	}
 
	@Override
	public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
		return new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> {
			return new TestCraftingScreenHandler(i, playerInventory, ScreenHandlerContext.create(world, pos));
		}, TITLE);
	}
}

Now we need to register the Block and BlockItem.

ExampleCustomRecipeMod.java
public class ExampleCustomRecipeMod implements ModInitializer {
	public static final TestRecipeBlock TEST_RECIPE_BLOCK = new TestRecipeBlock(FabricBlockSettings.of(Material.METAL));
 
	@Override
	public void onInitialize() {  
    		// Resuming later...
    	}
}

Creating the Recipe Class

Now we will do the most fun part of this page, creating the recipe class.

Creating the Shaped Recipe Class

Shaped recipes are a recipe type that requires an item to be in that specific slot or pattern. It is mostly used on grid recipes such as Crafting Recipe.

The Recipe interface expects an Inventory class as a type parameter. Any inventory that contains the ingredients for the recipe would work. But we will be using CraftingInventory.

TestRecipe.java
public class TestRecipe implements Recipe<CraftingInventory> {
	//You can add as much inputs as you want here.
	//It is important to always use Ingredient, so you can support tags.
	private final Ingredient inputA;
	private final Ingredient inputB;
	private final ItemStack result;
	private final Identifier id;
 
	public TestRecipe(Identifier id, ItemStack result, Ingredient inputA, Ingredient inputB) {
		this.id = id;
		this.inputA = inputA;
		this.inputB = inputB;
		this.result = result;
	}
 
	public Ingredient getInputA() {
		return this.inputA;
	}
 
	public Ingredient getInputB() {
		return this.inputB;
	}
 
	@Override
	public ItemStack getOutput() {
		return this.result;
	}
 
	@Override
	public Identifier getId() {
		return this.id;
	}
	//[...]
}

We would need to care about craft, because it is needed to craft the result. In craft, it is a good idea to return this.getOutput().copy(). fits should return true.

TestRecipe.java
public class TestRecipe implements Recipe<CraftingInventory> {
	//[...]
	@Override
	public ItemStack craft(CraftingInventory inv) {
		return this.getOutput().copy();
	}
 
	@Override
	public boolean fits(int width, int height) {
		return true;
	}
}

In matches, we need to say if a given inventory satisfies a recipe's input. We will return true if the first and second slot matches first and second input.

TestRecipe.java
public class TestRecipe implements Recipe<CraftingInventory> {
	//[...]
	@Override 
	public boolean matches(CraftingInventory inv, World world) {
		if(inv.size(0) < 2) return false;
		return inputA.test(inventory.getStack(0)) && inputB.test(inventory.getStack(1));
	}
}

Now we need the Type of the recipe. All you have to do is to create an instance of a class that extends RecipeType<TestRecipe>.

TestRecipe.java
public class TestRecipe implements Recipe<CraftingInventory> {
	//[...]
	public static class Type implements RecipeType<TestRecipe> {
		private Type() {}
		public static final Type INSTANCE = new Type();
		public static final String ID = "test_recipe";
	}
	@Override
	pubilc RecipeType<?> getType() {
		return Type.INSTANCE;
	}
}

What's still missing is the RecipeSerializer, for now try to figure it out using the SOURCE link below.

SOURCE: Defining Custom Crafting Recipes