Adding Recipes
LCE has three recipe types: shaped (grid pattern matters), shapeless (any arrangement), and furnace (smelting). All crafting recipes go through the Recipes singleton, while smelting recipes use FurnaceRecipes.
Architecture overview
Section titled “Architecture overview”| Class | File | Role |
|---|---|---|
Recipy | Minecraft.World/Recipy.h | Abstract base for all crafting recipes |
ShapedRecipy | Minecraft.World/ShapedRecipy.h | Grid-pattern recipes (e.g. pickaxe) |
ShapelessRecipy | Minecraft.World/ShapelessRecipy.h | Order-independent recipes (e.g. eye of ender) |
Recipes | Minecraft.World/Recipes.h | Singleton manager, owns the recipe list |
FurnaceRecipes | Minecraft.World/FurnaceRecipes.h | Singleton manager for smelting recipes |
Recipe groups
Section titled “Recipe groups”Every recipe belongs to a group that controls where it shows up in the console crafting UI:
| Code | Group | Constant |
|---|---|---|
'S' | Structure | eGroupType_Structure |
'T' | Tool | eGroupType_Tool |
'F' | Food | eGroupType_Food |
'A' | Armour | eGroupType_Armour |
'M' | Mechanism | eGroupType_Mechanism |
'V' | Transport | eGroupType_Transport |
'D' | Decoration | eGroupType_Decoration (default) |
If you use an unrecognized character or forget the group entirely, it defaults to Decoration.
Shaped recipes
Section titled “Shaped recipes”Shaped recipes use Recipes::addShapedRecipy(), which is a C variadic function. The arguments follow a specific encoding because C++ variadics cannot carry type information the way Java reflection can. A type string is passed as the second variadic argument to describe what comes next.
The type string encoding
Section titled “The type string encoding”The type string is a wide-character string where each character tells the parser what the next variadic argument is:
| Char | Meaning | Variadic type |
|---|---|---|
s | A row of the crafting grid | wchar_t * (wide string literal) |
w | A string array (reads until empty string) | wstring * |
a | A row (alternate, same as s) | wchar_t * |
c | A mapping character (key) | wchar_t |
z | Mapped to an ItemInstance * | ItemInstance * |
i | Mapped to an Item * | Item * |
t | Mapped to a Tile * | Tile * |
g | The recipe group | wchar_t (one of S, T, F, A, M, V, D) |
The type string must end with g followed by the group character. The parser reads types left-to-right and grabs variadic args accordingly.
Important: t vs z for tile ingredients
Section titled “Important: t vs z for tile ingredients”When you use t (Tile), the parser creates new ItemInstance(tile, 1, ANY_AUX_VALUE). This means any aux/data value will match. When you use z (ItemInstance), you provide the instance yourself and can specify an exact aux value. Use z when you need a specific variant (like birch planks vs oak planks).
How shaped recipes work
Section titled “How shaped recipes work”- Row strings (
sentries) define the crafting grid. Each character in a row maps to an ingredient. Spaces mean empty slots. - After all rows,
c+i/t/zpairs define the character-to-ingredient mappings. g+ group char finishes the recipe.- The parser figures out
widthandheightfrom the row strings, builds anItemInstance**array, and creates aShapedRecipy(width, height, ids, result, group).
Example: 3x3 shaped recipe (enchanting table)
Section titled “Example: 3x3 shaped recipe (enchanting table)”From Recipes.cpp:
addShapedRecipy(new ItemInstance(Tile::enchantTable, 1), L"sssctcicig", // type string: 3 rows, then char+tile, char+item, char+item, group L" B ", // row 0 L"D#D", // row 1 L"###", // row 2 L'#', Tile::obsidian, // '#' -> obsidian (Tile*) L'B', Item::book, // 'B' -> book (Item*) L'D', Item::diamond, // 'D' -> diamond (Item*) L'S'); // group: StructureReading the type string sssctcicig:
sss= three grid rowsct= char#maps to a Tileci= charBmaps to an Itemci= charDmaps to an Itemg= group follows
Example: 2x2 shaped recipe (snow block)
Section titled “Example: 2x2 shaped recipe (snow block)”addShapedRecipy(new ItemInstance(Tile::snow, 1), L"sscig", // 2 rows, char+item, group L"##", // row 0 L"##", // row 1 L'#', Item::snowBall, L'S');Example: 1x1 shaped recipe (planks from log)
Section titled “Example: 1x1 shaped recipe (planks from log)”addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), L"sczg", // 1 row, char+ItemInstance(z), group L"#", // single slot L'#', new ItemInstance(Tile::treeTrunk, 1, 0), // specific aux value L'S');Using z (ItemInstance) instead of t (Tile) lets you specify an aux data value for the ingredient. With t, the mapping automatically uses ANY_AUX_VALUE (-1), so any wood type would match.
The w type for sub-manager shapes
Section titled “The w type for sub-manager shapes”The recipe sub-managers (ToolRecipies, WeaponRecipies, ArmorRecipes) use w instead of s. The w type reads a wstring * array and keeps reading entries until it finds an empty string. This is how those classes pass their pre-built shape arrays:
// Inside ToolRecipies::addRecipes()wchTypes[0]=L'w'; // shape arraywchTypes[1]=L'c'; // char keywchTypes[2]=L'i'; // stick (Item)wchTypes[3]=L'c'; // char keywchTypes[4]=L'i'; // material (Item)wchTypes[5]=L'g'; // groupr->addShapedRecipy(new ItemInstance(target), wchTypes, shapes[t], L'#', Item::stick, L'X', pObjMaterial->item, L'T');The shapes array has an empty string L"" as a terminator after the last row.
Keeping NBT tags
Section titled “Keeping NBT tags”Some recipes need to keep the NBT tag from an ingredient (e.g. carrot on a stick inherits the fishing rod’s damage). Chain ->keepTag() on the return value:
addShapedRecipy(new ItemInstance(Item::carrotOnAStick, 1), L"sscicig", L"# ", L" X", L'#', Item::fishingRod, L'X', Item::carrots, L'T')->keepTag();The addShapedRecipy() function returns a ShapedRecipy * pointer, so you can chain keepTag() on it. This sets the _keepTag flag, and when the recipe is assembled, the NBT tag from the first non-null ingredient is copied to the result.
Shapeless recipes
Section titled “Shapeless recipes”Shapeless recipes use Recipes::addShapelessRecipy(). The type string is simpler since there is no grid, just a list of ingredients:
| Char | Meaning |
|---|---|
i | Next arg is Item * |
t | Next arg is Tile * |
z | Next arg is ItemInstance * |
g | Group char follows (must be last) |
Note: for shapeless recipes, t (Tile) does not add ANY_AUX_VALUE. It creates a plain new ItemInstance(tile). This is different from shaped recipes where t always adds ANY_AUX_VALUE.
Example: eye of ender
Section titled “Example: eye of ender”addShapelessRecipy(new ItemInstance(Item::eyeOfEnder, 1), L"iig", // two Items, then group Item::enderPearl, // ingredient 1 Item::blazePowder, // ingredient 2 L'T'); // group: ToolExample: fire charge (with aux data)
Section titled “Example: fire charge (with aux data)”addShapelessRecipy(new ItemInstance(Item::fireball, 3), L"iizg", // two Items + one ItemInstance, group Item::sulphur, Item::blazePowder, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), // charcoal specifically L'T');Example: book
Section titled “Example: book”addShapelessRecipy(new ItemInstance(Item::book, 1), L"iiiig", // four Items, group Item::paper, Item::paper, Item::paper, Item::leather, L'D'); // group: DecorationExample: mushroom stew (mix of types)
Section titled “Example: mushroom stew (mix of types)”addShapelessRecipy(new ItemInstance(Item::mushroomStew), L"ttig", // two Tiles + one Item, group Tile::mushroom1, Tile::mushroom2, Item::bowl, L'F');Furnace (smelting) recipes
Section titled “Furnace (smelting) recipes”Furnace recipes are registered through FurnaceRecipes, a separate singleton. The API is simple:
void addFurnaceRecipy(int itemId, ItemInstance *result, float xpValue);| Parameter | Description |
|---|---|
itemId | The tile/item ID of the input (use Tile::xxx_Id or Item::xxx_Id) |
result | The output ItemInstance |
xpValue | XP awarded per smelt (0.1 for basic, 0.35 for food, 0.7 for iron, 1.0 for gold/diamond) |
Existing furnace recipes
Section titled “Existing furnace recipes”From FurnaceRecipes.cpp:
addFurnaceRecipy(Tile::ironOre_Id, new ItemInstance(Item::ironIngot), 0.7f);addFurnaceRecipy(Tile::goldOre_Id, new ItemInstance(Item::goldIngot), 1.0f);addFurnaceRecipy(Tile::diamondOre_Id, new ItemInstance(Item::diamond), 1.0f);addFurnaceRecipy(Tile::sand_Id, new ItemInstance(Tile::glass), 0.1f);addFurnaceRecipy(Item::porkChop_raw_Id, new ItemInstance(Item::porkChop_cooked), 0.35f);addFurnaceRecipy(Tile::treeTrunk_Id, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), 0.15f);Adding a custom furnace recipe
Section titled “Adding a custom furnace recipe”To smelt a custom ore into a custom ingot:
// In FurnaceRecipes constructor or your mod initFurnaceRecipes::getInstance()->addFurnaceRecipy( myCustomOre_Id, // input tile ID new ItemInstance(Item::myCustomIngot), // output 0.8f // XP value);Recipe category modules
Section titled “Recipe category modules”LCE organizes recipes into dedicated classes that each register their own set. The Recipes constructor calls them in a specific order to control crafting menu layout:
pToolRecipies->addRecipes(this); // Pickaxes, shovels, axes, hoes, shearspFoodRecipies->addRecipes(this); // Food crafting recipespStructureRecipies->addRecipes(this); // Sandstone, workbench, furnace, chest, etc.// ... inline recipes (bed, enchanting table, stairs, etc.) ...pArmorRecipes->addRecipes(this); // Armor pieces (chain commented out)pClothDyeRecipes->addRecipes(this); // Wool dyeing, dye mixing, carpets// ... more inline recipes (TNT, slabs, rails, etc.) ...pWeaponRecipies->addRecipes(this); // Swords// ... more inline recipes (bow, arrow, bucket, torch, etc.) ...pOreRecipies->addRecipes(this); // Storage block conversionsHow sub-managers work internally
Section titled “How sub-managers work internally”All sub-managers (except FoodRecipies and ClothDyeRecipes) use the same pattern:
- A
maparray ofvector<Object *>where row 0 holds materials and subsequent rows hold result items. - An
_init()method fills the map withADD_OBJECT(map[row], value)calls. - An
addRecipes()loop iterates over each material column and builds recipes dynamically.
The Object class (defined in Recipes.h) is a union that can hold Tile *, Item *, or ItemInstance *. The GetType() method tells the loop whether to use t or i in the type string.
| Class | File | Category | Map Rows |
|---|---|---|---|
ToolRecipies | ToolRecipies.cpp | Pickaxes, shovels, axes, hoes | 5 (material + 4 tool types) |
WeaponRecipies | WeaponRecipies.cpp | Swords | 2 (material + sword) |
ArmorRecipes | ArmorRecipes.cpp | All armor pieces | 5 (material + 4 slots) |
OreRecipies | OreRecipies.cpp | Storage block conversions | 2 per entry (block + items) |
FoodRecipies | FoodRecipies.cpp | Food items, golden items | Direct registration |
ClothDyeRecipes | ClothDyeRecipes.cpp | Wool dyeing, dye mixing | Loop + direct |
StructureRecipies | StructureRecipies.cpp | Building blocks | Direct registration |
ToolRecipies internals
Section titled “ToolRecipies internals”MAX_TOOL_RECIPES = 5. Four shape patterns stored in a static wstring array:
Pickaxe: XXX Shovel: X Axe: XX Hoe: XX # # X# # # # # #Five material columns: Planks (Tile), Cobblestone (Tile), Iron Ingot (Item), Diamond (Item), Gold Ingot (Item).
The loop also registers shears at the end as a 2x2 shaped recipe.
WeaponRecipies internals
Section titled “WeaponRecipies internals”MAX_WEAPON_RECIPES = 2. One shape pattern: X / X / #.
Same five material columns as tools. Bow and arrow recipes are commented out here and registered inline in Recipes.cpp instead, to avoid display issues in the crafting menu.
ArmorRecipes internals
Section titled “ArmorRecipes internals”MAX_ARMOUR_RECIPES = 5. Four shape patterns:
Helmet: XXX Chestplate: X X Leggings: XXX Boots: X X X X XXX X X X X XXX X XChain armor is commented out. The 4J comment says: “removing the chain armour, since we show all possible recipes in the xbox game, and it’s not one you can make.” So only 4 materials are active: leather, iron, diamond, gold.
Also provides GetArmorType(int itemId) for 4J’s quick equip feature. This maps any armor piece ID to its slot type (helmet, chestplate, leggings, boots).
OreRecipies internals
Section titled “OreRecipies internals”MAX_ORE_RECIPES = 5. Each entry is a block/item pair. The loop creates both directions:
- 9 items in a 3x3 grid makes 1 block (group
'D') - 1 block in a 1x1 grid makes 9 items (group
'D')
Current entries: Gold, Iron, Diamond, Emerald, Lapis.
Adding a new recipe: step by step
Section titled “Adding a new recipe: step by step”-
Decide the recipe type: shaped (pattern matters), shapeless (any order), or furnace (smelting).
-
Choose where to register it: either inline in
Recipes::Recipes()inRecipes.cpp, or in one of the category classes. If it fits an existing category (tools, armor, ores), add to that sub-manager. Otherwise, add inline. -
Build the type string: for shaped recipes, count your rows (
seach), then each character-ingredient pair (c+i/t/z), and end withg. -
Call the registration function:
// Shaped: diamond blockaddShapedRecipy(new ItemInstance(Tile::diamondBlock, 1),L"ssscig",L"###",L"###",L"###",L'#', Item::diamond,L'D');// Shapeless: mushroom stewaddShapelessRecipy(new ItemInstance(Item::mushroomStew, 1),L"ttig",Tile::mushroom1, Tile::mushroom2, Item::bowl,L'F'); -
Rebuild. Recipes are registered during
Recipes::staticCtor()at startup. After the constructor runs,buildRecipeIngredientsArray()gets called automatically to index all recipes for the console crafting UI.
Plugging into sub-managers
Section titled “Plugging into sub-managers”If you are adding a new material tier (like ruby), you can plug directly into the existing sub-managers instead of writing recipes by hand.
Adding to ToolRecipies
Section titled “Adding to ToolRecipies”In ToolRecipies::_init(), add entries to each row:
ADD_OBJECT(map[0], Item::ruby); // materialADD_OBJECT(map[1], Item::pickAxe_ruby); // pickaxeADD_OBJECT(map[2], Item::shovel_ruby); // shovelADD_OBJECT(map[3], Item::hatchet_ruby); // axeADD_OBJECT(map[4], Item::hoe_ruby); // hoeAdding to WeaponRecipies
Section titled “Adding to WeaponRecipies”ADD_OBJECT(map[0], Item::ruby); // materialADD_OBJECT(map[1], Item::sword_ruby); // swordAdding to ArmorRecipes
Section titled “Adding to ArmorRecipes”ADD_OBJECT(map[0], Item::ruby); // materialADD_OBJECT(map[1], Item::helmet_ruby); // helmetADD_OBJECT(map[2], Item::chestplate_ruby); // chestplateADD_OBJECT(map[3], Item::leggings_ruby); // leggingsADD_OBJECT(map[4], Item::boots_ruby); // bootsAlso update GetArmorType() with the new armor IDs so quick equip works.
Adding to OreRecipies
Section titled “Adding to OreRecipies”Bump MAX_ORE_RECIPES in the header, then add:
ADD_OBJECT(map[5], Tile::rubyBlock);ADD_OBJECT(map[5], new ItemInstance(Item::ruby, 9));This creates both the 9-to-block and block-to-9 recipes automatically.
Key source files
Section titled “Key source files”Minecraft.World/Recipy.hfor the abstract recipe base classMinecraft.World/ShapedRecipy.hfor the shaped recipe classMinecraft.World/ShapelessRecipy.hfor the shapeless recipe classMinecraft.World/Recipes.h/Recipes.cppfor the recipe manager and all built-in recipesMinecraft.World/FurnaceRecipes.h/FurnaceRecipes.cppfor the furnace recipe manager
Related guides
Section titled “Related guides”- Crafting & Recipes for the full internals of the recipe system
- Adding Items to create items for your recipes
- Making a Full Ore for an end-to-end example using all recipe types