General Mechanics registers three custom recipe types. All three follow the standard NeoForge RecipeType / RecipeSerializer pattern and integrate with JEI automatically.
Recipe types
| Recipe type | Recipe class | Serializer constant |
|---|
gm:fabrication | FabricationRecipe | CoreRecipes.FABRICATION_SERIALIZER |
gm:crushing | CrushingRecipe | CoreRecipes.CRUSHING_SERIALIZER |
gm:fluid_mixing | FluidMixingRecipe | CoreRecipes.FLUID_MIXING_SERIALIZER |
CoreRecipes
CoreRecipes holds the DeferredHolder references for all serializers and recipe types.
// Serializers
DeferredHolder<RecipeSerializer<?>, RecipeSerializer<FabricationRecipe>> FABRICATION_SERIALIZER
DeferredHolder<RecipeSerializer<?>, RecipeSerializer<CrushingRecipe>> CRUSHING_SERIALIZER
DeferredHolder<RecipeSerializer<?>, RecipeSerializer<FluidMixingRecipe>> FLUID_MIXING_SERIALIZER
// RecipeType instances (created with RecipeType.simple)
DeferredHolder<RecipeType<?>, RecipeType<FabricationRecipe>> FABRICATION_RECIPE_TYPE
DeferredHolder<RecipeType<?>, RecipeType<CrushingRecipe>> CRUSHING_RECIPE_TYPE
DeferredHolder<RecipeType<?>, RecipeType<FluidMixingRecipe>> FLUID_MIXING_RECIPE_TYPE
Each recipe type is registered via RecipeType.simple(ResourceLocation) where the ResourceLocation is gm:<name>.
gm:fabrication
FabricationRecipe takes up to 3 unordered ingredients and produces one item output.
public record FabricationRecipe(
NonNullList<Ingredient> inputItems, // 1–3 ingredients
ItemStack output
)
Matching is order-independent: each required ingredient is matched against the provided items in the input container, and each candidate item is consumed from the pool once matched.
{
"type": "gm:fabrication",
"ingredients": [
{ "item": "minecraft:iron_ingot" },
{ "item": "minecraft:redstone" },
{ "tag": "c:ingots/copper" }
],
"result": {
"id": "mymod:circuit_board",
"count": 1
}
}
List of 1–3 ingredient objects. Order does not matter during matching.
Output item stack with id and optional count.
gm:crushing
CrushingRecipe takes a single ingredient and produces one item output. Used for ore processing and pulverizing.
public record CrushingRecipe(
Ingredient input,
ItemStack output
)
{
"type": "gm:crushing",
"ingredient": { "item": "minecraft:iron_ore" },
"result": {
"id": "gm:iron_dust",
"count": 2
}
}
A single ingredient object (item or tag).
gm:fluid_mixing
FluidMixingRecipe takes 1–3 fluid inputs (as SizedFluidIngredient) and produces a FluidStack output. Item output is not supported; the result is always a fluid.
public record FluidMixingRecipe(
List<SizedFluidIngredient> inputs, // 1–3 entries
FluidStack result
)
Input matching is order-independent, following the same pool-depletion algorithm as FabricationRecipe.
{
"type": "gm:fluid_mixing",
"inputs": [
{
"fluid": "gm:source_sulfuric_acid",
"amount": 500
},
{
"fluid": "gm:source_distilled_water",
"amount": 500
}
],
"result": {
"fluid": "gm:source_hydrochloric_acid",
"amount": 1000
}
}
List of 1–3 sized fluid ingredient objects, each with fluid (registry ID of the source fluid) and amount in millibuckets.
Output fluid with fluid registry ID and amount in millibuckets.
Datapack placement
Place recipe JSON files under:
data/<namespace>/recipe/<filename>.json
For example, a fabrication recipe in your mod mymod would go at:
data/mymod/recipe/circuit_board.json
The recipe directory (without an s) is the correct NeoForge 1.21+ path. Earlier versions used recipes.
Querying recipes at runtime
Use RecipeManager to look up recipes during server-side tick logic:
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeHolder;
import general.mechanics.recipes.FabricationRecipe;
import general.mechanics.recipes.FabricationRecipe.FabricationRecipeInput;
import general.mechanics.registries.CoreRecipes;
public void findRecipe(Level level, List<ItemStack> inputItems) {
RecipeManager manager = level.getRecipeManager();
FabricationRecipeInput input = FabricationRecipeInput.of(inputItems);
Optional<RecipeHolder<FabricationRecipe>> match =
manager.getRecipeFor(
CoreRecipes.FABRICATION_RECIPE_TYPE.get(),
input,
level
);
match.ifPresent(holder -> {
FabricationRecipe recipe = holder.value();
ItemStack result = recipe.assemble(input, level.registryAccess());
// use result ...
});
}
The same pattern applies to CrushingRecipe (using CRUSHING_RECIPE_TYPE and CrushingRecipeInput) and FluidMixingRecipe (using FLUID_MIXING_RECIPE_TYPE and MixingInput).
JEI integration
All three recipe types are displayed automatically in JEI through the mod’s built-in JEI plugin. No additional registration is required from addon mods; adding a valid JSON recipe file is sufficient for it to appear in JEI’s recipe lookup.