Recipes
EmakiCooking recipes are stored by station under plugins/EmakiCooking/recipes/<station>/. Each station has its own gameplay flow, so each recipe type has different fields: chopping boards use input amount and cut counts, woks use stirring and heat, ovens use baking stages, juicers use fluid volume, and fermentation barrels use time stages.
Recipe folders
| Folder | Station | Typical use |
|---|---|---|
recipes/chopping_board/ | Chopping Board | Single input, accepts the whole main-hand stack, then uses input amount and repeated cuts for sliced output. |
recipes/wok/ | Wok | Multiple ingredients, heat, stirring, and failure branches. |
recipes/grinder/ | Grinder | Input item plus processing time. |
recipes/steamer/ | Steamer | Input item plus steaming time or chained steps. |
recipes/oven/ | Oven | Single input resolved by baking time, perfect heat ratio, and overbake stage. |
recipes/juicer/ | Juicer | Single input pressed into fluid, then bottled by serving capacity. |
recipes/fermentation_barrel/ | Fermentation Barrel | Multiple inputs resolved by fermentation time, early collection, and over-fermentation. |
Current recommended structure
Current default resources use CoreLib Item Source lists and result branches:
result:
success:
outputs:
- item_sources:
- "minecraft-baked_potato"
amount: 1
actions:
- 'send_message text="<gold>Cooking complete.</gold>"'Rules:
- Inputs and outputs should use
item_sourceslists, such asminecraft-carrotorminecraft-glass_bottle. result.<branch>.outputsis always a list, even for one output item.result.<branch>.actionsis placed besideoutputsunder the same result branch.- Do not use old examples such as
output,result.output,result.outputs,result.actions,perfect_output,overbaked_output,fermentation.early_collect.output, orfermentation.over_output.
Common fields
| Field | Required | Description |
|---|---|---|
id | Yes | Unique recipe id. Prefer matching the file name. |
display_name | Yes | Display name. Supports MiniMessage. |
permission | No | Permission required to use this recipe. Empty means no gate. |
condition | No | Completion/collection condition block. Can include on_pass.actions, on_fail.actions, and on_fail.block_output. |
availability_condition | No | Condition block deciding whether the recipe is available at all. Unlike condition, it is evaluated during matching: if it fails, the recipe does not take part in matching. |
structured_presentation | No | Recipe-level structured presentation config. An output entry may carry its own outputs[].structured_presentation; the two layers are merged, with the output level overriding the recipe level. |
result.<branch>.outputs | Yes | Output list for this branch. |
result.<branch>.outputs[].chance | No | Drop chance for this output, 0-100, default 100. <=0 never grants it, >=100 always does. Each output is rolled independently. |
result.<branch>.outputs[].amount_range | No | Random amount range, written as the min / max subkeys. When present it overrides amount; if min > max the two are swapped automatically. |
result.<branch>.actions | No | Actions executed when this branch outputs. |
General item matcher
Every input-side field in Cooking accepts a matcher that narrows item selection further by component, PDC, or lore. Full syntax is in Item Matcher.
Every matching position is item_sources AND matcher
Recipe inputs and station roles now follow the same rule. Each matching position holds two sibling fields: item_sources for the allowed item sources, matcher for non-item-source conditions (component, PDC, lore, variable). Both must hold; either may be omitted, and only omitting both makes the position never match. That is why the bare item_sources examples on this page all work.
Item source conditions must not be written inside matcher. type: item_source / item_sources / source / sources are rejected at load time with a warning, and such a matcher never matches. The same ban applies to all_of / any_of / none_of child lists.
Station roles now nest their pair under a child node — stations.chopping_board.tool, stations.wok.spatula, stations.juicer.container, stations.steamer.moisture_rules[].input — each holding a standard item_sources + matcher. The old flat keys (tool_matcher, spatula_matcher, container_matcher, input_matcher) are still read at load time for compatibility, but write the nested form in new configs. See Stations.
Fermentation barrel inputs[] keeps item_sources for a second reason too: that key doubles as the slot aggregation key. See How matcher relates to item_sources.
| Config path | Purpose |
|---|---|
ingredients[].matcher | Recipe ingredients. |
inputs[].matcher | Inputs of a multi-input recipe. |
input.matcher | Input of a single-input recipe. |
container.matcher | Container item such as a bottle. |
stations.chopping_board.tool.matcher | Tool detection. |
stations.wok.spatula.matcher | Spatula detection. |
stations.juicer.container.matcher | Station container detection. |
stations.oven.fuels[].matcher | Oven fuel (config.yml). |
stations.steamer.fuels[].matcher | Steamer fuel (config.yml). |
stations.steamer.moisture_rules[].input.matcher | Steamer moisture rule input (config.yml). |
nutrition.food_sources[].matcher | Food-to-nutrition mapping (config.yml, see Nutrition). |
Each path pairs with an item_sources at the same level.
id: fresh_carrot_slice
ingredients:
# "is a carrot" AND "was never renamed", written as two sibling fields
- item_sources:
- "minecraft-carrot"
matcher:
type: component
component: custom_name
operator: absent
amount: 1
result:
success:
outputs:
- item_sources:
- "minecraft-golden_carrot"
amount: 1DANGER
The block side and the output side do not support matchers.
- Station block detection (fields such as
block_item_sources) decides which block counts as the station; it is not input matching. - The output side (
result.<branch>.outputs[].item_sources) creates items rather than matching them.
A matcher in either place has no effect. See Only input-matching fields support matcher.
Result branches
| Station | Common branches |
|---|---|
| Chopping Board | success |
| Grinder | success |
| Steamer | success; chained recipes may continue through steps. |
| Wok | success, undercooked, overcooked, invalid |
| Oven | success, perfect, overbaked |
| Juicer | success |
| Fermentation Barrel | success, early, over |
When condition.on_fail.block_output is true, a failed condition blocks output. When it is false, output can still happen but fail actions run. For fermentation barrels, automatic completion and block-break drops may happen while the player is offline; source behavior is authoritative in those cases.
Chopping board example
id: "cut_carrot"
display_name: "Cut Carrot"
input:
item_sources:
- "minecraft-carrot"
amount: 2
cuts_required: 1
tool_damage: 1
result:
success:
outputs:
- item_sources:
- "minecraft-golden_carrot"
amount: 1
actions:
- 'send_message text="<green>Chopping complete.</green>"'| Field | Description |
|---|---|
input.item_sources | Required (at least one of this and input.matcher). Matching input sources. |
input.amount | Input amount required and consumed for each completed cut cycle. Defaults to 1. |
cuts_required | Required. Click count required to complete one cut cycle. |
tool_damage | Durability damage applied to the tool per cut. |
damage_override.chance | Overrides config.yml > stations.chopping_board.cut_damage.chance for this recipe. |
damage_override.value | Overrides config.yml > stations.chopping_board.cut_damage.value for this recipe. |
damage_override is optional; when omitted, the global cut_damage settings from config.yml are used.
When a player places input on a chopping board, the board takes the whole main-hand stack and stores the accumulated amount in station state; the display entity still shows only one item. Cutting cannot start until the stored amount reaches input.amount. With cuts_required: 1, players can continuously process an already placed batch without re-placing one ingredient at a time.
Wok example
id: "example_recipe"
display_name: "Simple Stew"
ingredients:
- item_sources:
- "minecraft-carrot"
amount: 1
stir_rule: "1-3"
- item_sources:
- "minecraft-cooked_chicken"
amount: 1
stir_rule: "2-3"
heat_level: 1
stir_total:
min: 2
max: 5
fault_tolerance: 0
permission: "emakicooking.recipe.simple_stew"
condition:
type: all_of
entries:
- "%player_level% >= 5"
on_pass:
actions:
- 'send_message text="<green>Cooking skill check passed.</green>"'
on_fail:
actions:
- 'send_message text="<red>Cooking skill level is too low.</red>"'
block_output: true
result:
success:
outputs:
- item_sources:
- "minecraft-rabbit_stew"
amount: 1
actions:
- 'send_message text="<green>Served successfully.</green>"'
undercooked:
outputs:
- item_sources:
- "minecraft-mushroom_stew"
amount: 1
actions: []
overcooked:
outputs:
- item_sources:
- "minecraft-dried_kelp"
amount: 1
actions: []
invalid:
outputs:
- item_sources:
- "minecraft-stone"
amount: 1
actions: []| Field | Description |
|---|---|
ingredients | Required. Ingredient list; the recipe refuses to load without it. |
ingredients[].item_sources | Matching ingredient sources. |
ingredients[].amount | Required amount. |
ingredients[].stir_rule | Suggested add/stir interval, such as 1-3. |
heat_level | Required. Required heat level. |
stir_total.min/max | Both subkeys required. Valid total stir count range. |
fault_tolerance | Required. Allowed mistake count. |
Grinder example
id: "bone_meal"
display_name: "Ground Bone Meal"
input:
item_sources:
- "minecraft-bone"
grind_time_seconds: 6
permission: "emakicooking.recipe.bone_meal"
result:
success:
outputs:
- item_sources:
- "minecraft-bone_meal"
amount: 3
actions:
- 'send_message text="<gray>Grinding finished.</gray>"'| Field | Description |
|---|---|
input.item_sources | Required (at least one of this and input.matcher). Matching input sources. |
grind_time_seconds | Required. Seconds required to grind. The grinder advances on the config.yml > stations.grinder.check_delay_ticks cycle. |
Steamer example
id: "steamed_cod"
display_name: "Steamed Cod"
input:
item_sources:
- "minecraft-cod"
required_steam: 40
permission: "emakicooking.recipe.steamed_cod"
result:
success:
outputs:
- item_sources:
- "minecraft-cooked_cod"
amount: 1
actions:
- 'send_message text="<aqua>Steaming finished.</aqua>"'| Field | Description |
|---|---|
input.item_sources | Required (at least one of this and input.matcher). Matching input sources. |
required_steam | Required. Total steam that must be consumed to finish the recipe. |
requires_previous_step | Optional. Another steamer recipe ID marking this recipe as its follow-up step, used for chained steaming. |
Chained example: recipes/steamer/chain_example_recipe.yml uses requires_previous_step: "example_recipe" to feed the previous output into the next step.
Oven example
id: "baked_potato"
display_name: "Baked Potato"
input:
item_sources:
- "minecraft-potato"
bake_time_seconds: 20
baking:
perfect_heat:
min: 45
max: 60
perfect_required_ratio: 0.7
overbake_seconds: 10
permission: "emakicooking.recipe.baked_potato"
result:
success:
outputs:
- item_sources:
- "minecraft-baked_potato"
amount: 1
actions:
- 'send_message text="<gold>Baking complete.</gold>"'
perfect:
outputs:
- item_sources:
- "minecraft-golden_carrot"
amount: 1
overbaked:
outputs:
- item_sources:
- "minecraft-charcoal"
amount: 1Required fields for an oven recipe are id, display_name, input.item_sources (or input.matcher), and bake_time_seconds; the recipe refuses to load if any of them is missing. The baking block is optional.
The oven chooses output by baking time, perfect heat ratio, and continued heating after completion. For valuable food, show heat and progress clearly in the GUI so players understand why an item became overbaked.
Juicer example
id: "apple_juice"
display_name: "Apple Juice"
input:
item_sources:
- "minecraft-apple"
presses_required: 5
fluid:
id: "apple_juice"
display_name: "Apple Juice"
amount_ml: 180
container:
item_sources:
- "minecraft-glass_bottle"
serving_ml: 250
permission: "emakicooking.recipe.apple_juice"
result:
success:
outputs:
- item_sources:
- "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<aqua>Juicing complete.</aqua>"'Required fields for a juicer recipe are id, display_name, input.item_sources (or input.matcher), and presses_required; the recipe refuses to load if any of them is missing.
Each completed press adds fluid.amount_ml to the station. Bottling consumes container.serving_ml. For example, the default apple juice adds 180ml per press while a bottle needs 250ml, so one press is not enough to serve once.
Fermentation barrel example
id: "example_recipe"
display_name: "Apple Cider"
inputs:
- item_sources:
- "minecraft-apple"
amount: 3
- item_sources:
- "minecraft-sugar"
amount: 1
fermentation_time_seconds: 300
fermentation:
early_collect:
min_progress_ratio: 0.5
over_time_seconds: 600
permission: "emakicooking.recipe.apple_cider"
condition:
type: all_of
entries:
- "%player_level% >= 3"
on_pass:
actions:
- 'send_message text="<green>Fermentation matured.</green>"'
on_fail:
actions:
- 'send_message text="<yellow>Extra fermentation condition failed.</yellow>"'
block_output: false
result:
success:
outputs:
- item_sources:
- "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>Fermentation complete.</gold>"'
early:
outputs:
- item_sources:
- "minecraft-potion"
amount: 1
actions:
- 'send_message text="<yellow>You collected a half-fermented apple drink.</yellow>"'
over:
outputs:
- item_sources:
- "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>The cider continued fermenting into a sweet vinegar.</gold>"'| Field | Description |
|---|---|
inputs | Required. Multiple required inputs; each entry uses sibling item_sources + matcher, ANDed. |
inputs[].slot_id | Persisted slot identity. It must be non-blank and unique within the recipe; do not reuse it for another material when editing a recipe. |
inputs[].count_key | Quantity aggregation and consumption identity. Multiple stable slots may share one key, but the key must remain stable. |
inputs[].matcher | Optional non-item-source condition, ANDed with the sibling item_sources. |
fermentation_time_seconds | Required. Time required for normal completion. |
fermentation.early_collect.min_progress_ratio | Minimum progress ratio for early collection. |
fermentation.over_time_seconds | Time after normal completion before over-fermentation. |
result.early.outputs | Outputs for early collection; each output uses scalar item_source. |
result.over.outputs | Outputs for the over-fermented branch; each output uses scalar item_source. |
result.success.outputs | Normal completion outputs; each output uses scalar item_source. |
Debugging and validation
- Use
/ecooking reloadafter editing recipes. - Use
/ecooking inspect handto verify whether the held item can be resolved by Item Source. - Test each recipe with missing permission, missing ingredients, full inventory, correct branch, failure branch, and station state after server restart.
- For woks, test
success,undercooked,overcooked, andinvalid. - For ovens, test normal completion, perfect completion, and overbaking.
- For juicers, test insufficient fluid, wrong container, and mixed-fluid attempts.
- For fermentation barrels, test early collection, normal completion, and over-fermentation.