English
Recipes
A recipe defines what an attempt needs, how it is matched, what is consumed, what is produced, and which actions run on success or failure. You place recipe files in recipes/*.yml; the id comes from id, not the file name, and a duplicate id in the same directory is rejected
Root fields
| Field | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
id | string | Yes | — | Non-blank string | Unique recipe id. A blank value skips the file with BLANK_ID |
display_name | string | No | id | MiniMessage text | Recipe display name used by the recipe book and the forge GUI title |
gui_template | string | No | blank | A template id under gui/, e.g. forge_gui | GUI template used by this recipe. Blank falls back to the bundled forge_gui |
blueprint_requirements | list<object> | No | empty list | — | Blueprint requirements; presence is checked, nothing is consumed. Per-entry fields are in Blueprints and Materials |
materials | list<object> | No | empty list | — | Material entries. Per-entry fields are in Blueprints and Materials |
forge_capacity | integer | No | 0 | >= 0 (negatives clamp to 0) | Capacity limit for the sum of material capacity_cost. A value of 0 makes the GUI show the "waiting for blueprint" state |
optional_material_limit | integer | No | 0 | >= 0 (negatives clamp to 0) | Maximum number of optional material types per attempt |
condition | object | No | all_of / 0 / empty | — | Condition group, see Condition node |
quality | object | No | disabled | — | Recipe-level quality override; the full key set is in Quality |
result | object | No | empty | — | Result definition, see Result definition |
actions | object | No | empty | — | Flow actions, see Action nodes |
permission | string | No | blank | A permission node string | Permission required to use the recipe. Blank means no permission check |
success_rate | number | No | 100 | 0 ~ 100 (clamped) | Forge success rate in percent. Below 100 the forge can fail and failure_outcomes applies |
failure_outcomes | list<object> | No | empty list | — | Failure plans, meaningful only when success_rate < 100 |
Shipped default: the bundled recipes/example_recipe.yml is a complete sample (blueprint requirement, 7 materials, quality, result, and actions). A minimal copyable recipe:
yaml
id: flame_sword
display_name: '<red>Flame Sword Forging'
blueprint_requirements:
- item_sources:
- "minecraft-paper"
amount: 1
materials:
- item_sources:
- "minecraft-blaze_rod"
amount: 3
capacity_cost: 5
forge_capacity: 10
optional_material_limit: 2
result:
success:
outputs:
- item_source: "minecraft-diamond_sword"Condition node
condition is parsed by the CoreLib condition group and supports five combination modes:
| Field | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
condition.type | string | No | all_of | all_of / any_of / none_of / at_least / exactly | Combination mode. Any other value rejects the recipe with INVALID_CONDITION_MODE |
condition.required_count | integer | No | 0 | >= 0 (negatives clamp to 0) | Comparison basis for at_least (at least N hold) and exactly (exactly N hold); ignored by the other modes |
condition.entries | list | No | empty list | — | Condition entries. Each is a CoreLib condition expression or a nested condition group |
yaml
condition:
type: at_least
required_count: 2
entries:
- "level >= 30"
- "permission: blacksmith"
- "world: world_nether"WARNING
The CoreLib condition block also accepts condition.invalid_as_failure, condition.on_pass.actions, condition.on_fail.actions, condition.on_fail.block_output, and condition.on_fail.message, but Forge's forge flow only reads type / required_count / entries. Whether an invalid condition counts as a failure is controlled globally by condition.invalid_as_failure in config.yml, and on_pass / on_fail do not participate in forge validation. Writing them in a recipe has no effect
You can combine permission and condition to restrict who uses a recipe, for example "blacksmith profession only", "level 30 or above", or "must carry a PDC marker"
Result definition
| Field | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
result.success.outputs | list<object> | No | empty list | — | Success-branch outputs; Forge uses only the first entry as the primary result. When empty the recipe needs a target item slot in the GUI (requiresTargetInput()), otherwise no result can be produced |
result.success.outputs[].item_source | string | Yes (when the entry exists) | — | A single item source | Source of the produced item. It must be scalar: a list reports OUTPUT_SOURCE_NOT_SINGLE and omission reports OUTPUT_SOURCE_MISSING |
result.success.outputs[].item_sources | list | No | — | Exactly one valid source | Legacy form, read for compatibility with a WARNING (OUTPUT_LEGACY_SOURCE). Declaring it together with item_source reports OUTPUT_SOURCE_FIELD_CONFLICT |
result.success.outputs[].matcher | — | No | — | Not allowed | An output entry may not declare matcher; doing so reports OUTPUT_MATCHER_FORBIDDEN |
result.success.outputs[].amount | integer | No | ignored | — | The current implementation does not read this key; the primary result is always produced with amount 1. A wrong value neither errors nor takes effect |
result.success.actions | list<string> | No | empty list | CoreLib action lines | Actions executed after the result is generated |
result.success.name_actions | list<object> | No | empty list | CoreLib name operations | Name operation chain for the result item |
result.success.lore_actions | list<object> | No | empty list | CoreLib lore operations | Lore operation chain for the result item |
When result.success.outputs declares an output source, a successful forge creates the first entry as the primary result item and attaches the forge layer snapshot. This suits crafting a new weapon from materials, producing fixed equipment from a blueprint, or emitting EmakiItem and third-party plugin items
yaml
result:
success:
outputs:
- item_source: "minecraft-diamond_sword"
name_actions:
- action: "append_suffix"
value: " <gray>[%quality_name%]</gray>"
lore_actions:
- action: "append"
content:
- ""
- "<gray>Quality: <white>%quality_name%</white></gray>"
- "<gray>Multiplier: <white>%quality_multiplier%</white></gray>"IMPORTANT
The bundled forge_gui template only provides blueprint and material slots; it has no target equipment input slot. When using the bundled GUI you must configure result.success.outputs, otherwise no forge result can be produced
Recipe result actions, material effect actions, and quality item_meta actions are merged and then applied to the result item together
Success rate and failure outcomes
success_rate defaults to 100, meaning the forge always succeeds. Set it below 100 and forging can fail, in which case one plan is picked from failure_outcomes by weight
| Field | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
failure_outcomes[].type | string | No | return_materials | return_materials / partial_consume / consume_materials (other values do not error but behave as a full refund) | Failure handling plan |
failure_outcomes[].weight | integer | No | 1 | >= 1 (values below 1 are raised to 1) | Selection weight of this plan |
failure_outcomes[].params | object | No | empty object | — | Plan parameters, read per type; undeclared keys use the source defaults |
failure_outcomes[].params.return_rate | number | No | 1.0 for return_materials; 0.5 for partial_consume | 0 ~ 1 | Refund ratio. Read only by those two type values |
yaml
success_rate: 70
failure_outcomes:
- type: "return_materials"
weight: 50
params:
return_rate: 1.0
- type: "partial_consume"
weight: 30
params:
return_rate: 0.5
- type: "consume_materials"
weight: 20NOTE
Without failure_outcomes, the default is return_materials with a full refund. Other type values do not raise an error but are treated as a full material refund
Quality configuration
See Quality for the full quality documentation. A typical recipe block:
yaml
quality:
enabled: true
custom_pool:
- "Common-70-1.0"
- "Fine-25-1.05"
- "Epic-5-1.15"
guarantee:
enabled: true
attempts: 10
minimum: "Fine"NOTE
The recipe-level pool field is custom_pool and the pity threshold field is attempts; the matching global keys in config.yml are tiers and threshold. custom_pool uses the "name-weight-multiplier" string form, and an empty pool falls back to the global pool
Guidance:
- Give common tiers a large weight and rare tiers a small one
- Give high-value recipes a pity so players do not go long without positive feedback
- Keep quality ids consistent across presentation, attributes, and action config
Action nodes
| Node | Type | Trigger |
|---|---|---|
actions.pre | list<string> | Before recipe execution. If a pre-stage action fails, forging aborts and failure runs |
actions.success | list<string> | After a successful forge |
actions.failure | list<string> | After a failed forge |
result.success.actions | list<string> | When the result is generated or applied (before actions.success) |
When you load a recipe, all four chains — actions.pre, result.success.actions, actions.success, and actions.failure — are trial-compiled with the CoreLib action engine; if any line fails to compile the recipe is rejected with INVALID_ACTION_LINE
The successful result chain runs in this order: result.success.actions → actions.success → quality actions. Actions inside each list run serially, and each stage waits for the previous stage to finish. Forge reads the final stack from the mutable item_target holder and delivers it only after all three stages settle. This lets EmakiItem component actions safely mutate the forge result without an older stack being delivered early. Failed action batches are logged but do not discard an already generated forge result
yaml
actions:
pre:
- 'send_message text="<gray>Forging begins..."'
success:
- 'send_title title="<green>Forged" subtitle="<gray>Your equipment was recast"'
- 'play_sound sound=BLOCK_ANVIL_USE volume=1 pitch=1'
failure:
- 'send_message text="<red>The forge failed and the materials were ruined."'
- 'play_sound sound=ENTITY_VILLAGER_NO volume=1 pitch=1'NOTE
When actions.pre fails, the error message can use %reason%, %action%, and %line%
Available template variables
Available in result.success.name_actions value and result.success.lore_actions content:
| Variable | Description |
|---|---|
%quality% | Quality tier name. Injected only when a quality tier resolved |
%quality_name% | Quality display name, same value as %quality% |
%quality_multiplier% | Quality multiplier, formatted as 0.## |
%multiplier% | Short alias of the quality multiplier |
%<stat_id>% | Any variable injected through material variables effects, already scaled by material amount and quality multiplier |
The complete variable set (including material effect and GUI variables) is in Placeholders
Minimal example
yaml
id: "flame_sword"
display_name: "<red>Flame Sword</red>"
permission: emakiforge.recipe.flame_sword
forge_capacity: 10
blueprint_requirements:
- item_sources:
- "minecraft-paper"
amount: 1
materials:
- item_sources:
- "minecraft-blaze_rod"
amount: 3
capacity_cost: 5
effects:
- type: "variables"
variables:
fire_damage: 10
- type: "lore_action"
lore_actions:
- action: "append"
content:
- "<red>Fire Damage: +%fire_damage%</red>"
optional_material_limit: 2
condition:
type: all_of
entries: []
quality:
enabled: true
custom_pool:
- "Common-80-1.0"
- "Fine-20-1.05"
result:
success:
outputs:
- item_source: "minecraft-diamond_sword"
name_actions:
- action: "append_suffix"
value: " <gray>[%quality_name%]</gray>"
lore_actions:
- action: "append"
content:
- ""
- "<gray>Quality: <white>%quality_name%</white></gray>"
actions:
success:
- 'send_message text="<green>Flame power infused into the weapon."'