Skip to content

Recipes ​

To write EmakiCooking recipes, open the folder for the station (the block you cook with) you are changing: 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 ​

FolderStationTypical use
recipes/chopping_board/Chopping BoardSingle input, accepts the whole main-hand stack, then uses input amount and repeated cuts for sliced output
recipes/wok/WokMultiple ingredients, heat, stirring, and failure branches
recipes/grinder/GrinderInput item plus processing time
recipes/steamer/SteamerInput item plus steaming time or chained steps
recipes/oven/OvenSingle input resolved by baking time, perfect heat ratio, and overbake stage
recipes/juicer/JuicerSingle input pressed into fluid, then bottled by serving capacity
recipes/fermentation_barrel/Fermentation BarrelMultiple inputs resolved by fermentation time, early collection, and over-fermentation

Current default resources use CoreLib Item Source lists and result branches:

yaml
result:
  success:
    outputs:
      - item_source: "minecraft-baked_potato"
        amount: 1
    actions:
      - 'send_message text="<gold>Cooking complete.</gold>"'

Rules:

  • The input side uses item_sources lists, such as minecraft-carrot or minecraft-glass_bottle
  • The output side uses a scalar item_source, such as item_source: "minecraft-baked_potato". The legacy item_sources form is still read but must hold exactly one source, otherwise the output is dropped with a warning; declaring both keys is also rejected with a warning. A matcher on an output node is rejected and the output is dropped
  • result.<branch>.outputs is always a list, even for one output item
  • result.<branch>.actions is placed beside outputs under 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, or fermentation.over_output

Common fields ​

FieldRequiredDescription
idYesUnique recipe id. Prefer matching the file name
display_nameYesDisplay name. Supports MiniMessage
permissionNoPermission required to use this recipe. Empty means no gate
conditionNoCompletion/collection condition block. Can include on_pass.actions, on_fail.actions, and on_fail.block_output
availability_conditionNoCondition 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_presentationNoRecipe-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>.outputsYesOutput list for this branch
result.<branch>.outputs[].item_sourceYesThe scalar item source of this output. The legacy item_sources form is still read but must hold exactly one source
result.<branch>.outputs[].amountNoOutput amount, default 1; values below 1 are treated as 1
result.<branch>.outputs[].chanceNoDrop chance for this output, 0-100, default 100. <=0 never grants it, >=100 always does. Each output is rolled independently
result.<branch>.outputs[].amount_rangeNoRandom amount range, written as the min / max subkeys, defaulting to min=1 and max=min. When present it overrides amount; if min > max the two are swapped automatically
result.<branch>.actionsNoActions 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 bare item_sources examples all work this way

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[] also keeps item_sources as its matching source list; slot identity comes from slot_id / count_key, and /ec convert-legacy deliberately retains the legacy key on inputs[] so already-persisted states keep working. See How matcher relates to item_sources

Config pathPurpose
ingredients[].matcherRecipe ingredients
inputs[].matcherInputs of a multi-input recipe
input.matcherInput of a single-input recipe
container.matcherContainer item such as a bottle
stations.chopping_board.tool.matcherTool detection
stations.wok.spatula.matcherSpatula detection
stations.juicer.container.matcherStation container detection
stations.oven.fuels[].matcherOven fuel (config.yml)
stations.steamer.fuels[].matcherSteamer fuel (config.yml)
stations.steamer.moisture_rules[].input.matcherSteamer moisture rule input (config.yml)
nutrition.food_sources[].matcherFood-to-nutrition mapping (config.yml, see Nutrition)

Each path pairs with an item_sources at the same level

yaml
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_source: "minecraft-golden_carrot"
        amount: 1

DANGER

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 ​

StationCommon branches
Chopping Boardsuccess
Grindersuccess
Steamersuccess; chained recipes may continue through steps
Woksuccess, undercooked, overcooked, invalid
Ovensuccess, perfect, overbaked
Juicersuccess
Fermentation Barrelsuccess, 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 ​

yaml
id: "cut_carrot"
display_name: "Cut Carrot"

input:
  item_sources:
    - "minecraft-carrot"
  amount: 2

cuts_required: 1
tool_damage: 1

result:
  success:
    outputs:
      - item_source: "minecraft-golden_carrot"
        amount: 1
    actions:
      - 'send_message text="<green>Chopping complete.</green>"'
FieldDescription
input.item_sourcesRequired (at least one of this and input.matcher). Matching input sources
input.matcherOptional non-item-source condition, ANDed with the sibling input.item_sources
input.amountInput amount required and consumed for each completed cut cycle. Defaults to 1; values below 1 are treated as 1
cuts_requiredRequired. Click count required to complete one cut cycle
tool_damageDurability damage applied to the tool per cut, default 1; values below 1 are treated as 1
damage_override.chanceOverrides config.yml > stations.chopping_board.cut_damage.chance for this recipe
damage_override.valueOverrides 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 ​

yaml
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_source: "minecraft-rabbit_stew"
        amount: 1
    actions:
      - 'send_message text="<green>Served successfully.</green>"'
  undercooked:
    outputs:
      - item_source: "minecraft-mushroom_stew"
        amount: 1
    actions: []
  overcooked:
    outputs:
      - item_source: "minecraft-dried_kelp"
        amount: 1
    actions: []
  invalid:
    outputs:
      - item_source: "minecraft-stone"
        amount: 1
    actions: []
FieldDescription
ingredientsRequired. Ingredient list; the recipe refuses to load without it
ingredients[].item_sourcesMatching ingredient sources
ingredients[].amountRequired amount
ingredients[].stir_ruleSuggested add/stir interval, such as 1-3
heat_levelRequired. Required heat level
stir_total.min/maxBoth subkeys required. Valid total stir count range
fault_toleranceRequired. Allowed mistake count

Grinder example ​

yaml
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_source: "minecraft-bone_meal"
        amount: 3
    actions:
      - 'send_message text="<gray>Grinding finished.</gray>"'
FieldDescription
input.item_sourcesRequired (at least one of this and input.matcher). Matching input sources
grind_time_secondsRequired. Seconds required to grind. The grinder advances on the config.yml > stations.grinder.check_delay_ticks cycle

Steamer example ​

yaml
id: "steamed_cod"
display_name: "Steamed Cod"

input:
  item_sources:
    - "minecraft-cod"

required_steam: 40

permission: "emakicooking.recipe.steamed_cod"

result:
  success:
    outputs:
      - item_source: "minecraft-cooked_cod"
        amount: 1
    actions:
      - 'send_message text="<aqua>Steaming finished.</aqua>"'
FieldDescription
input.item_sourcesRequired (at least one of this and input.matcher). Matching input sources
required_steamRequired. Total steam that must be consumed to finish the recipe
requires_previous_stepOptional. 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 ​

yaml
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_source: "minecraft-baked_potato"
        amount: 1
    actions:
      - 'send_message text="<gold>Baking complete.</gold>"'
  perfect:
    outputs:
      - item_source: "minecraft-golden_carrot"
        amount: 1
  overbaked:
    outputs:
      - item_source: "minecraft-charcoal"
        amount: 1

Required 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

FieldRequiredDefaultDescription
bake_time_secondsYes—Seconds required for a normal bake; values below 0 are treated as 0
baking.perfect_heat.minNoconfig.yml > stations.oven.heat.min (default 20)Lower bound of the perfect heat range
baking.perfect_heat.maxNoconfig.yml > stations.oven.heat.max (default 80)Upper bound of the perfect heat range; values below the minimum are raised to it
baking.perfect_required_ratioNo1.0Fraction of time that must be spent inside the perfect heat range, clamped to 0-1
baking.overbake_secondsNo0Seconds of continued heating after normal completion before the item overbakes

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 ​

yaml
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_source: "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

FieldRequiredDefaultDescription
presses_requiredYes—Presses needed to finish; values below 0 are treated as 0
fluid.idNo""Fluid id. Empty means the recipe produces no fluid
fluid.display_nameNoEquals fluid.idFluid display name
fluid.amount_mlNo0Fluid added per press, in ml; values below 0 are treated as 0
container.item_sourcesNo—Serving container item sources, ANDed with the sibling container.matcher
container.matcherNoNoneExtra container conditions (component, PDC, lore only)
container.serving_mlNoconfig.yml > stations.juicer.default_serving_ml (default 250)Fluid consumed per serving, in ml; values below 1 are treated as 1

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 ​

yaml
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_source: "minecraft-honey_bottle"
        amount: 1
    actions:
      - 'send_message text="<gold>Fermentation complete.</gold>"'
  early:
    outputs:
      - item_source: "minecraft-potion"
        amount: 1
    actions:
      - 'send_message text="<yellow>You collected a half-fermented apple drink.</yellow>"'
  over:
    outputs:
      - item_source: "minecraft-honey_bottle"
        amount: 1
    actions:
      - 'send_message text="<gold>The cider continued fermenting into a sweet vinegar.</gold>"'
FieldDescription
inputsRequired. Multiple required inputs; each entry uses sibling item_sources + matcher, ANDed
inputs[].item_sourcesItem sources for this input, ANDed with the sibling matcher
inputs[].amountRequired amount for this input, default 1; values below 1 are treated as 1
inputs[].slot_idPersisted 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_keyQuantity aggregation and consumption identity. Multiple stable slots may share one key, but the key must remain stable
inputs[].matcherOptional non-item-source condition, ANDed with the sibling item_sources
fermentation_time_secondsRequired. Time required for normal completion; values below 0 are treated as 0
fermentation.early_collect.min_progress_ratioMinimum progress ratio for early collection, default 1.0, clamped to 0-1. It only applies when a result.early branch exists; otherwise early collection is unavailable
fermentation.over_time_secondsTime after normal completion before over-fermentation, default 0. It only applies when a result.over branch exists
result.early.outputsOutputs for early collection; each output uses scalar item_source
result.over.outputsOutputs for the over-fermented branch; each output uses scalar item_source
result.success.outputsNormal completion outputs; each output uses scalar item_source

Fermentation state migration and failure protection ​

When an old fermentation-barrel state has no slot identities, restoration matches each persisted item against the current recipe: every slot must resolve to exactly one non-blank slot_id/count_key pair, and resolved slot_id values must be unique. Missing item data, a missing recipe, blank identities, ambiguous candidates, or duplicate slots reject the migration; the state is not written as canonical, and restore, interaction, and break paths log a warning and stop processing. A state that already has slot identities but an older schema is written back to the current schema only after successful restoration. These behaviours are covered by source inspection and local JVM tests, not by a live Paper/Folia server test

Debugging and validation ​

  • Use /ecooking reload after editing recipes
  • Use /ecooking inspect hand to 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, and invalid
  • 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