Skip to main content
General Mechanics registers three custom recipe types. All three follow the standard NeoForge RecipeType / RecipeSerializer pattern and integrate with JEI automatically.

Recipe types

Recipe typeRecipe classSerializer constant
gm:fabricationFabricationRecipeCoreRecipes.FABRICATION_SERIALIZER
gm:crushingCrushingRecipeCoreRecipes.CRUSHING_SERIALIZER
gm:fluid_mixingFluidMixingRecipeCoreRecipes.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.

JSON format

{
  "type": "gm:fabrication",
  "ingredients": [
    { "item": "minecraft:iron_ingot" },
    { "item": "minecraft:redstone" },
    { "tag": "c:ingots/copper" }
  ],
  "result": {
    "id": "mymod:circuit_board",
    "count": 1
  }
}
ingredients
array
required
List of 1–3 ingredient objects. Order does not matter during matching.
result
ItemStack
required
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
)

JSON format

{
  "type": "gm:crushing",
  "ingredient": { "item": "minecraft:iron_ore" },
  "result": {
    "id": "gm:iron_dust",
    "count": 2
  }
}
ingredient
Ingredient
required
A single ingredient object (item or tag).
result
ItemStack
required
Output item stack.

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.

JSON format

{
  "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
  }
}
inputs
array
required
List of 1–3 sized fluid ingredient objects, each with fluid (registry ID of the source fluid) and amount in millibuckets.
result
FluidStack
required
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.

Build docs developers (and LLMs) love