Custom Ingredient Types %5Bminecraft 1.21.11 To 26.2%5D
Note
Version note: This page covers Minecraft 1.21.11, 26.1.2, and 26.2.
Related pages for other README-supported versions:
- Minecraft 1.21 and 1.21.1: Getting Started, Ingredients and Visibility
- Minecraft 1.18.2, 1.19.2, and 1.20.1: Adding/Hiding Items, Item Subtypes, Non-Item Ingredients
- Minecraft 1.16.5: Adding/Hiding Items, Item Subtypes, Non-Item Ingredients
- Minecraft 1.12.2: Non-Item Ingredients, Adding Items, Item Subtypes
Use a custom ingredient type only when item stacks and platform fluid stacks cannot represent what your recipe uses. Examples include spells, gases, research entries, species, or virtual resources.
If your data can be represented as an ItemStack with components, use item subtypes instead.
Register custom ingredient types in registerIngredients.
@Override
public void registerIngredients(IModIngredientRegistration registration) {
registration.register(
SpellIngredient.TYPE,
SpellRegistry.getKnownSpells(),
new SpellIngredientHelper(),
new SpellIngredientRenderer(),
SpellIngredient.CODEC
);
}The registration needs:
- An
IIngredientType<T>. - Every ingredient JEI should display initially.
- An
IIngredientHelper<T>. - An
IIngredientRenderer<T>. - A
Codec<T>for saving and loading bookmarks and JEI data.
The type identifies the Java class and the persistent ingredient type ID.
public record SpellIngredient(String id, Component name) {
public static final IIngredientType<SpellIngredient> TYPE = new IIngredientType<>() {
@Override
public String getUid() {
return "examplemod:spell";
}
@Override
public Class<? extends SpellIngredient> getIngredientClass() {
return SpellIngredient.class;
}
};
public static final Codec<SpellIngredient> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.STRING.fieldOf("id").forGetter(SpellIngredient::id)
).apply(instance, SpellRegistry::getSpell)
);
}Use a stable UID. Changing it breaks saved JEI data for that ingredient type.
The helper provides search, identity, grouping, copying, and error information.
public final class SpellIngredientHelper implements IIngredientHelper<SpellIngredient> {
@Override
public IIngredientType<SpellIngredient> getIngredientType() {
return SpellIngredient.TYPE;
}
@Override
public String getDisplayName(SpellIngredient ingredient) {
return ingredient.name().getString();
}
@Override
public Object getUid(SpellIngredient ingredient, UidContext context) {
return ingredient.id();
}
@Override
public Identifier getIdentifier(SpellIngredient ingredient) {
return Identifier.fromNamespaceAndPath("examplemod", ingredient.id());
}
@Override
public SpellIngredient copyIngredient(SpellIngredient ingredient) {
return ingredient;
}
@Override
public String getErrorInfo(SpellIngredient ingredient) {
return String.valueOf(ingredient);
}
}The UID returned from getUid must have stable equals and hashCode. Do not return mutable objects.
The renderer draws the ingredient in JEI's ingredient list and recipe slots.
public final class SpellIngredientRenderer implements IIngredientRenderer<SpellIngredient> {
@Override
public void render(GuiGraphicsExtractor guiGraphics, SpellIngredient ingredient) {
SpellIconRenderer.draw(guiGraphics, ingredient, 0, 0);
}
@Override
public List<Component> getTooltip(SpellIngredient ingredient, TooltipFlag tooltipFlag) {
return List.of(
ingredient.name(),
Component.translatable("jei.examplemod.spell_id", ingredient.id())
);
}
}The default size is 16 by 16 pixels. Override getWidth and getHeight only when every slot using this renderer is prepared for the larger size.
Add custom ingredients to recipe layouts with your type.
builder.addInputSlot(1, 10)
.setStandardSlotBackground()
.add(SpellIngredient.TYPE, recipe.spell());Add aliases and info pages by passing the custom type.
registration.addAlias(SpellIngredient.TYPE, ModSpells.FROST, "ice");
registration.addIngredientInfo(
ModSpells.FROST,
SpellIngredient.TYPE,
Component.translatable("jei.examplemod.info.frost_spell")
);- Make the codec support normalized ingredients that can be saved and loaded.
- Keep
copyIngredientsafe. JEI may copy ingredients to avoid mutation. - Implement
isValidIngredientif your type has empty or placeholder values. - Implement
getTagStreamif players should be able to search this ingredient by tags. - Use
registerExtraIngredientsor runtime ingredient APIs only after the type itself has been registered.