Skip to content

Recipe Definitions

Crafting recipes live under plugins/EmakiStation/recipes/. One recipe per file; the file name is not used for identification, and the recipe id comes from the id field inside the file.

Matching is unordered set matching: only material kinds and totals matter, never placement. There is no shaped concept.

Full Example

yaml
id: "iron_ingot_block"
display_name: "<white>Iron Block Pressing</white>"

station_ids:
  - "blacksmith"

tags:
  - "blacksmith"

permission: ""
visible: true
duration_seconds: 30

cost:
  currency:
    type: "vault"
    amount: 0

materials:
  - item_sources:
      - "minecraft-iron_ingot"
    amount: 9
    consume: true

result:
  outputs:
    - item_source: "minecraft-iron_block"
      amount: 1
  actions: []

condition:
  invalid_as_failure: true
  entries: []

display_condition:
  invalid_as_failure: false
  entries: []

actions:
  pre: []
  success: []
  failure: []

Fields

FieldDescription
idRecipe id.
display_nameDisplay name; MiniMessage is supported.
station_idsWhich stations this recipe belongs to. Empty means open to every station.
tagsStill usable for other purposes such as API queries, but no longer drives station ownership.
permissionPermission required to use this recipe; empty means unrestricted.
visibleWhether the recipe appears in the catalog. false hides it completely, usually paired with API submission.
duration_secondsDuration of one craft, in seconds. 0 or omitted completes immediately without entering the queue.
costExtra currency charged per craft, multiplied by the batch.
materialsMaterial requirement list.
result.outputsOutput list.
result.actionsAction lines run after settlement.
conditionCondition gate checked before submission.
display_conditionUnlock condition.
actionsPer-stage action lines: pre, success, failure.

The station-side recipes section (include_tags / include_ids / exclude_ids) is deprecated; ownership is now declared recipe-side through station_ids.

Without the permission node, the recipe does not appear in the catalog at all rather than showing greyed out.

cost — Currency Cost

FieldDescription
cost.currency.typevault or excellent (excellenteconomy is equivalent). Any other value logs an issue and is treated as free.
cost.currency.amountAmount. Omitted or 0 means free; a negative value logs an issue and is treated as free.

WARNING

type and amount must live inside the cost.currency sub-section, not directly under cost. When the currency sub-section is missing the plugin reports nothing and simply treats the recipe as free, so the configuration fails silently.

The balance is validated before materials are spent, and the charge happens after they are spent; a failed charge refunds the materials in full. Cancelling a queue entry refunds at the station's cancel_refund_rate.

materials — Material Requirements

FieldDescription
material_idOptional selection identity. When omitted, it is derived as recipe_id.material.<index>. A duplicate with a requirement_id blocks the recipe from loading.
requirement_idOptional requirement-record identity; defaults to material_id.
count_keyOptional quantity aggregation/consumption-record identity; defaults to material_id.
item_sourcesAn "any of these" set whose amount is counted across the whole set. Omit it and the item source is unrestricted.
matcherOptional. Non-item-source conditions (component, PDC, lore, variable), ANDed with the sibling item_sources — see Item Matcher. Note the storage-channel limit below.
amountTotal amount required.
consumefalse validates possession without spending, for requirements like "hold a tool".

For a requirement of 64, 30 coal plus 34 charcoal also satisfies it.

Material kind count per recipe is not limited by GUI slot count. The warehouse channel deducts long amounts directly, while the inventory channel is bounded by the vanilla 64-per-slot limit.

Material allocation is greedy with no backtracking. When one item can satisfy several requirements it goes to the one declared first. In rare cases this makes a theoretically valid recipe fail to match; splitting the recipe avoids it.

General item matcher

item_sources and matcher are sibling fields and both must hold (AND). item_sources carries the allowed item sources, matcher carries only non-item-source conditions such as component, PDC, or lore. Item source conditions written inside a matcher are rejected at load time with a warning and never match. Full syntax is in Item Matcher.

yaml
materials:
  - item_sources:
      - minecraft-iron_ingot
    matcher:
      type: component
      component: custom_name
      operator: absent
    amount: 9
    consume: true

DANGER

Any material that declares a matcher can be supplied from the backpack only; storage lookup skips it.

Storage counts stock by item source and cannot inspect a real item's components, so it cannot prove a component condition. A single storage_unreachable_material WARN is logged at load time for each such material.

Materials that must draw from storage should declare only item_sources, with no matcher.

Note that one matcher-bearing material also switches the whole recipe to the stack model, so the recipe's other materials are debited from the backpack too.

Dismantle recipes (top-level matcher in recipes_dismantle/*.yml) do not have this limit.

The Two Condition Gates

SectionDecidesBehaviour when unsatisfied
conditionWhether it can be craftedThe recipe still shows; clicking is refused.
display_conditionWhether it is unlockedThe recipe shows as a greyed-out placeholder and clicking does nothing.

A greyed-out placeholder rather than hiding lets players know there is more content ahead. To hide something completely, use visible: false or permission.

display_condition is evaluated on every catalog repaint, so heavy conditions slow the window down.