简体中文
配方系统
EmakiCooking 的配方按工位 (世界里用来做菜的那块方块) 分类放在 recipes/<station>/ 里, 每类工位的字段和交互都不一样
你要改哪类工位的配方, 就进对应的目录: plugins/EmakiCooking/recipes/<station>/. 每类工位的交互不同, 字段也不同: 砧板按输入数量与切割次数, 炒锅按翻炒与火候, 烤炉按烘烤阶段, 榨汁机按流体容量, 发酵桶按时间阶段
配方目录
| 目录 | 工位 | 典型玩法 |
|---|---|---|
recipes/chopping_board/ | 砧板 | 单输入, 一次收下手中的整叠物品, 按输入数量与切割次数产出切片或半成品 |
recipes/wok/ | 炒锅 | 多输入, 结果由火候, 翻炒次数和容错决定 |
recipes/grinder/ | 研磨机 | 放进材料, 等研磨时间到了就产出粉末 |
recipes/steamer/ | 蒸锅 | 放入材料, 按蒸制时间或链式步骤产出熟制品 |
recipes/oven/ | 烤炉 | 单输入, 结果由烘烤时间, 完美火力比例和过烤阶段决定 |
recipes/juicer/ | 榨汁机 | 单输入, 多次压榨生成流体, 再按容器容量盛取 |
recipes/fermentation_barrel/ | 发酵桶 | 多输入, 结果由发酵时间, 提前收取和过度发酵决定 |
当前推荐写法
当前默认资源统一使用 CoreLib Item Source 输入列表, 单值产出字段和结果分支结构:
yaml
result:
success:
outputs:
- item_source: "minecraft-baked_potato"
amount: 1
actions:
- 'send_message text="<gold>料理完成。</gold>"'关键约定:
- 输入侧写
item_sources列表, 例如minecraft-carrot,minecraft-glass_bottle - 产出侧写单值
item_source, 例如item_source: "minecraft-baked_potato". 旧写法item_sources仍被读取, 但必须恰好包含一个来源, 否则该产物被丢弃并告警; 两键同时出现也会告警并丢弃该产物. 产出节点上写matcher会被拒绝并丢弃 result.<branch>.outputs永远是列表, 只有一个产物也写列表result.<branch>.actions与outputs同级, 位于同一个结果分支下- 不再使用旧示例里的
output,result.output,result.outputs,result.actions,perfect_output,overbaked_output,fermentation.early_collect.output,fermentation.over_output
通用字段
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 配方唯一的 ID, 建议和文件名一致 |
display_name | 是 | 展示名称. 支持 MiniMessage |
permission | 否 | 使用该配方所需权限; 为空表示不限制 |
condition | 否 | 完成或收取时判定的条件块, 可以配 on_pass.actions, on_fail.actions 和 on_fail.block_output |
availability_condition | 否 | 配方是否可用 (能否被匹配到) 的条件块. 与 condition 不同: 它在匹配阶段判定, 不通过这个配方就直接不参与匹配 |
structured_presentation | 否 | 配方级结构化展示配置. 产物条目下也可写一份 outputs[].structured_presentation, 两层会合并, 产物级覆盖配方级 |
result.<branch>.outputs | 是 | 当前分支产物列表 |
result.<branch>.outputs[].item_source | 是 | 该产物的单个物品来源. 旧写法 item_sources 仍被读取, 但必须恰好包含一个来源 |
result.<branch>.outputs[].amount | 否 | 该产物数量, 默认 1, 小于 1 按 1 处理 |
result.<branch>.outputs[].chance | 否 | 该产物的掉落概率, 取值 0~100, 默认 100. <=0 恒不发放, >=100 恒发放. 逐个产物独立判定 |
result.<branch>.outputs[].amount_range | 否 | 产物数量随机区间, 写 min / max 两个子键, 默认 min=1, max=min. 存在时覆盖 amount; min > max 会自动交换 |
result.<branch>.actions | 否 | 当前分支产出时执行的动作列表 |
通用物品匹配器 (matcher)
Cooking 的输入侧字段都支持 matcher, 按组件, PDC, Lore 等条件进一步收窄物品判定. 完整语法 (类型全集, operator, path, 嵌套写法) 见 物品匹配器
item_sources 与 matcher 是同级并行字段
所有输入判定位都由这两个字段合成, 取 AND:
- 只写
item_sources: 按物品源判定, 不加额外条件. 文中大量裸item_sources示例都是这种形态 - 只写
matcher: 只按组件 / PDC / Lore 等条件判定, 不限来源 - 两者都写: 必须同时成立
- 两者都不写: 该条目永不命中 (配方会被跳过)
matcher 内部不能写物品源条件. type: item_source / item_sources / source / sources 会被解析器拒绝并告警, 该 matcher 恒不匹配. 物品源一律写到同级的 item_sources
工位角色侧 (工具, 锅铲, 工位容器, 加水规则输入) 现在是嵌套子节点, 每个子节点下同样是标准的 item_sources + matcher: stations.chopping_board.tool, stations.wok.spatula, stations.juicer.container, moisture_rules[].input. 旧的扁平前缀键 (tool_matcher, spatula_matcher, container_matcher, input_matcher) 仍被加载期兼容读取, 但新配置请写嵌套子节点. 详见 工位系统
| 配置位置 | 说明 |
|---|---|
ingredients[].matcher | 炒锅配方原料 |
inputs[].matcher | 发酵桶多输入项 |
input.matcher | 单输入配方的输入项 |
container.matcher | 容器物品 (如瓶子) |
stations.chopping_board.tool.matcher | 工具识别 (config.yml) |
stations.wok.spatula.matcher | 锅铲识别 (config.yml) |
stations.juicer.container.matcher | 工位容器识别 (config.yml) |
stations.oven.fuels[].matcher | 烤炉燃料 (config.yml) |
stations.steamer.fuels[].matcher | 蒸锅燃料 (config.yml) |
stations.steamer.moisture_rules[].input.matcher | 蒸锅湿度规则输入 (config.yml) |
nutrition.food_sources[].matcher | 食物到营养的映射 (config.yml, 见营养系统) |
表中每一项都与同级的一个 item_sources 配对
yaml
id: fresh_carrot_slice
ingredients:
# "是胡萝卜"写 item_sources, "没被重命名过"写 matcher, 两者 AND
- item_sources:
- "minecraft-carrot"
matcher:
type: component
component: custom_name
operator: absent
amount: 1
result:
success:
outputs:
- item_source: "minecraft-golden_carrot"
amount: 1DANGER
方块侧与产出侧不支持 matcher
- 工位方块识别 (
block_item_sources一类字段) 是"哪个方块算这个工位", 不是输入匹配 - 产出侧 (
result.<branch>.outputs[].item_sources) 是"生成什么物品", 属于构造而非匹配
这两处写 matcher 不会生效. 详见 只有输入匹配点支持 matcher
结果分支
| 工位 | 常用结果分支 |
|---|---|
| 砧板 | success |
| 研磨机 | success |
| 蒸锅 | success, 链式配方可按步骤继续处理 |
| 炒锅 | success, undercooked, overcooked, invalid |
| 烤炉 | success, perfect, overbaked |
| 榨汁机 | success |
| 发酵桶 | success, early, over |
condition.on_fail.block_output 为 true 时, 条件不满足会阻止产物输出; 为 false 时仍可输出, 但会执行失败动作. 发酵桶在自动完成或破坏掉落等玩家可能离线的场景下, 条件处理以源码运行时逻辑为准
砧板示例
yaml
id: "cut_carrot"
display_name: "切制胡萝卜"
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>砧板处理完成。</green>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填 (与 input.matcher 二者至少写一个) 可匹配的输入来源 |
input.matcher | 可选. 只写组件 / PDC / Lore 等非物品源条件, 与同级 input.item_sources 取 AND |
input.amount | 每次完成切割需要并消耗的输入数量; 未配置时按 1 处理, 小于 1 按 1 处理 |
cuts_required | 必填. 完成一轮切割所需点击次数 |
tool_damage | 每次切割对工具造成的耐久损耗, 默认 1, 小于 1 按 1 处理 |
damage_override.chance | 覆盖 config.yml > stations.chopping_board.cut_damage.chance, 即本配方切割时扣耐久的概率 |
damage_override.value | 覆盖 config.yml > stations.chopping_board.cut_damage.value, 即本配方每次扣除的耐久值 |
damage_override 是可选段; 未写时使用 config.yml 中的全局 cut_damage 设置
砧板放入输入时会一次收下玩家主手那一整叠, 并在工位状态中累计数量; 展示实体仍然只显示 1 个物品. 累计数量不足 input.amount 时不能开始切割. 若设置 cuts_required: 1, 玩家可以对已经放入的一批食材连续完成多轮切割, 不用每次重新放入 1 个输入
炒锅示例
yaml
id: "example_recipe"
display_name: "简易炖菜"
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>厨艺达标,出锅成功。</green>"'
on_fail:
actions:
- 'send_message text="<red>厨艺等级不足,出锅失败。</red>"'
block_output: true
result:
success:
outputs:
- item_source: "minecraft-rabbit_stew"
amount: 1
actions:
- 'send_message text="<green>成功出锅。</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: []| 字段 | 说明 |
|---|---|
ingredients | 必填. 食材列表, 缺失则该配方拒绝载入 |
ingredients[].item_sources | 可匹配的食材来源 |
ingredients[].amount | 需要数量 |
ingredients[].stir_rule | 建议加入或翻炒区间, 例如 1-3 |
heat_level | 必填. 配方要求的火力等级 |
stir_total.min/max | 两个子键都必填. 有效总翻炒次数范围 |
fault_tolerance | 必填. 允许错误次数 |
研磨机示例
yaml
id: "bone_meal"
display_name: "骨粉研磨"
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>研磨结束。</gray>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填 (与 input.matcher 二者至少写一个) 可匹配的输入来源 |
grind_time_seconds | 必填. 研磨所需秒数. 研磨机按 config.yml > stations.grinder.check_delay_ticks 周期推进 |
蒸锅示例
yaml
id: "steamed_cod"
display_name: "清蒸鳕鱼"
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>蒸制完成。</aqua>"'| 字段 | 说明 |
|---|---|
input.item_sources | 必填 (与 input.matcher 二者至少写一个) 可匹配的输入来源 |
required_steam | 必填. 完成该配方需要累计消耗的蒸汽量 |
requires_previous_step | 可选. 填写另一个蒸锅配方 ID, 表示本配方是该配方的后续步骤, 用于链式蒸制 |
链式示例: recipes/steamer/chain_example_recipe.yml 用 requires_previous_step: "example_recipe" 把上一步产物接到下一步
烤炉示例
yaml
id: "baked_potato"
display_name: "烤马铃薯"
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>烤制完成。</gold>"'
perfect:
outputs:
- item_source: "minecraft-golden_carrot"
amount: 1
overbaked:
outputs:
- item_source: "minecraft-charcoal"
amount: 1烤炉配方的必填字段是 id, display_name, input.item_sources (或 input.matcher) 和 bake_time_seconds, 缺任意一项该配方直接拒绝载入. baking 段为可选
| 字段 | 必填 | 默认值 | 说明 |
|---|---|---|---|
bake_time_seconds | 是 | — | 正常烘烤所需秒数, 小于 0 按 0 处理 |
baking.perfect_heat.min | 否 | config.yml > stations.oven.heat.min (默认 20) | 完美火力区间下限 |
baking.perfect_heat.max | 否 | config.yml > stations.oven.heat.max (默认 80) | 完美火力区间上限; 小于下限时被抬到下限 |
baking.perfect_required_ratio | 否 | 1.0 | 判定为完美所需的处于完美火力区间的比例, 截断到 0~1 |
baking.overbake_seconds | 否 | 0 | 正常完成后继续加热进入过烤的秒数 |
烤炉会根据烘烤时间, 处于完美火力区间的比例, 以及完成后继续加热时间决定结果. 高价值料理建议给玩家清楚展示火力和进度, 避免"看不懂为什么过烤"
榨汁机示例
yaml
id: "apple_juice"
display_name: "苹果汁"
input:
item_sources:
- "minecraft-apple"
presses_required: 5
fluid:
id: "apple_juice"
display_name: "苹果汁"
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>榨汁完成。</aqua>"'榨汁机配方的必填字段是 id, display_name, input.item_sources (或 input.matcher) 和 presses_required, 缺任意一项该配方直接拒绝载入
| 字段 | 必填 | 默认值 | 说明 |
|---|---|---|---|
presses_required | 是 | — | 完成压榨所需次数, 小于 0 按 0 处理 |
fluid.id | 否 | "" | 流体 ID. 为空表示该配方不产出流体 |
fluid.display_name | 否 | 等于 fluid.id | 流体显示名 |
fluid.amount_ml | 否 | 0 | 每次压榨加入的流体量 (ml), 小于 0 按 0 处理 |
container.item_sources | 否 | — | 盛取容器的物品来源; 与同级 container.matcher 取 AND |
container.matcher | 否 | 无 | 盛取容器的附加条件, 只写组件 / PDC / Lore 等非物品源条件 |
container.serving_ml | 否 | config.yml > stations.juicer.default_serving_ml (默认 250) | 每次盛取消耗的流体量 (ml), 小于 1 按 1 处理 |
一次压榨会向工位加入 fluid.amount_ml 的流体; 盛取时消耗 container.serving_ml. 例如默认苹果汁每次压榨 180ml, 玻璃瓶盛取需要 250ml, 所以玩家至少需要压榨两轮才可盛取一次
发酵桶示例
yaml
id: "example_recipe"
display_name: "苹果酒"
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>发酵成熟。</green>"'
on_fail:
actions:
- 'send_message text="<yellow>发酵未满足额外条件。</yellow>"'
block_output: false
result:
success:
outputs:
- item_source: "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>发酵完成。</gold>"'
early:
outputs:
- item_source: "minecraft-potion"
amount: 1
actions:
- 'send_message text="<yellow>提前收取了半发酵苹果饮。</yellow>"'
over:
outputs:
- item_source: "minecraft-honey_bottle"
amount: 1
actions:
- 'send_message text="<gold>苹果酒已经继续转化为酸甜果醋。</gold>"'| 字段 | 说明 |
|---|---|
inputs | 必填. 发酵桶需要投入的多种材料; 每项使用同级 item_sources + matcher, 两者取 AND |
inputs[].item_sources | 该输入项的物品来源列表; 与同级 matcher 取 AND |
inputs[].amount | 该输入项需要的数量, 默认 1, 小于 1 按 1 处理 |
inputs[].slot_id | 持久化槽位身份. 当前配方中必须非空且唯一; 不要在改配方时把同一个身份再用到另一种材料上 |
inputs[].count_key | 数量聚合与消费身份. 可让多个稳定槽位共享同一聚合键, 但必须保持长期稳定 |
inputs[].matcher | 可选. 只写非物品源条件, 并与同级 item_sources 取 AND |
fermentation_time_seconds | 必填. 正常完成发酵所需秒数, 小于 0 按 0 处理 |
fermentation.early_collect.min_progress_ratio | 允许提前收取的最低进度比例, 默认 1.0, 截断到 0~1. 仅当存在 result.early 分支时生效, 否则视为不可提前收取 |
fermentation.over_time_seconds | 正常完成后继续放置多久进入过度发酵, 默认 0. 仅当存在 result.over 分支时生效 |
result.early.outputs | 提前收取产物. 产出项使用单值 item_source |
result.over.outputs | 过度发酵产物. 产出项使用单值 item_source |
result.success.outputs | 正常完成产物. 产出项使用单值 item_source |
发酵桶状态迁移与失败保护
旧的发酵桶状态如果没有槽位身份, 恢复时会按当前配方逐个匹配已经保存的物品: 每个槽位必须只得到一个非空的 slot_id/count_key 候选, 而且解析出来的 slot_id 不能重复. 缺少物品, 配方不存在, 身份为空, 候选不唯一或槽位重复时, 迁移被拒绝, 状态不会写成规范格式的结果; 恢复, 交互和破坏这几条路径都会记录 warning 并停止继续处理. 已经有槽位身份, 但状态 schema (状态文件的结构) 还是旧版时, 只在成功恢复后才回写当前 schema. 以上都是源码与本地 JVM 测试覆盖的行为, 不等于在 Paper/Folia 上实机验证过
调试与验证
- 使用
/ecooking reload重载配方 - 使用
/ecooking inspect hand检查手里物品能否被 Item Source 识别 - 每个配方至少测试: 权限不足, 材料不足, 背包满, 正确分支, 失败分支, 服务器重启后的工位状态
- 炒锅额外测试
success,undercooked,overcooked,invalid - 烤炉额外测试普通完成, 完美完成, 过烤
- 榨汁机额外测试流体不足, 容器不匹配和不同流体混合
- 发酵桶额外测试提前收取, 正常完成和过度发酵