工位详解
EmakiCooking 有四种世界工位,每种都有自己的交互方式、状态持久化逻辑和特殊机制。下面逐一说明。
砧板 (Chopping Board)
砧板是最简单的工位:放上食材,拿刀切,切够次数就出结果。
工作流程
- 玩家左键点击砧板方块,手持食材放置到砧板上(消耗 1 个)
- 砧板上方生成
ItemDisplay实体展示食材 - 玩家手持工具(
tool_sources中定义的物品)左键砧板进行切割 - 每次切割增加
cut_count,工具受到tool_damage耐久损耗 - 当
cut_count >= cuts_required时,配方完成,发放结果 - 玩家可右键砧板取回未完成的食材
交互方式
| 操作 | 条件 | 效果 |
|---|---|---|
| 左键 + 食材 | 砧板为空 | 放置食材到砧板 |
| 左键 + 工具 | 砧板有食材 | 切割一次 |
| 左键 + 空手 | 砧板有食材 | 取回食材 |
| 右键 | 砧板有食材 | 取回食材 |
| 破坏方块 | 砧板有食材 | 掉落食材,清除状态 |
状态文件格式
每个工位的状态都会持久化到磁盘,路径为 data/stations/{world}/{x}_{y}_{z}.yml。砧板的状态文件长这样:
schema_version: 1
station_type: chopping_board
world: world
x: 100
y: 64
z: 200
input_item:
source: "minecraft-carrot"
display_entity:
uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
timestamps:
last_interaction_ms: 1700000000000
chopping_board:
cut_count: 2特殊机制
cut_damage:每次切割有chance% 概率对玩家造成value点伤害,模拟"切到手"的效果。配方可以通过damage_override覆盖全局设置——比如切某些坚硬食材时伤害更高interaction_delay_ms:两次交互之间的最小间隔,防止玩家快速连点刷产出space_restriction:启用后砧板上方必须为空气方块才能交互,适合需要更真实感的服务器tool_sources:只有列表中的物品才能作为切割工具,空手或其他物品不算ItemDisplay:食材放置后在砧板上方生成展示实体,让玩家能直观看到砧板上有什么。取回或完成时自动移除
炒锅 (Wok)
炒锅是四种工位中最复杂的一个。它有食材顺序、翻炒次数、火力等级、超时烧焦等多个维度,配方的结果也不是简单的成功/失败,而是分成四个分支。
工作流程
- 确认炒锅下方有热源方块(
heat_levels中定义),获得火力等级 - 玩家左键手持食材放入炒锅(按顺序添加,相同食材连续放入会合并数量)
- 玩家手持锅铲(
spatula_sources)左键翻炒,每次翻炒增加全局total_stir_count和每个食材的stir_times - 翻炒完成后,玩家手持碗(
need_bowl: true时)或空手(need_bowl: false时)左键出锅 - 系统匹配配方,根据翻炒次数和
stir_rule判定走哪个结果分支
交互方式
| 操作 | 条件 | 效果 |
|---|---|---|
| 左键 + 食材 | 有热源 | 添加食材到炒锅 |
| 左键 + 锅铲 | 有食材 | 翻炒一次 |
| 右键 + 锅铲 | 有食材 | 查看炒锅内容 |
| 左键 + 碗/空手 | 有食材且已翻炒 | 出锅(匹配配方) |
| 左键 + 空手 | 有食材 | 取回最后一个食材(可能受烫伤) |
| 破坏方块 | 有食材 | 掉落所有食材,清除状态 |
状态文件格式
schema_version: 1
station_type: wok
world: world
x: 100
y: 64
z: 200
wok:
total_stir_count: 5
ingredients:
- source: "minecraft-beef"
amount: 2
stir_times: 5
- source: "minecraft-carrot"
amount: 1
stir_times: 3
timestamps:
last_stir_time_ms: 1700000000000
stir_fried_time_ms: 1700000000000特殊机制
heat_levels:炒锅下方方块决定火力等级。不同的热源方块对应不同的火力值,配方可以要求特定火力——比如"大火快炒"需要灵魂营火(火力 2)stir_rule:每个食材可以定义翻炒规则(如>=3),系统会逐食材比较实际翻炒次数和规则要求timeout_ms:最后一次翻炒后超过此时间没有操作,视为烧焦(overcooked)。这个机制让玩家不能放着不管- 结果分支:
success(成功)、undercooked(欠火候)、overcooked(过火)、invalid(无效)。每个分支可以配置不同的产出和动作 scald_damage:空手取食材时,如果已经翻炒过(锅是热的),玩家会受到烫伤伤害failure:即使配方匹配成功,仍有chance% 概率产出output_source指定的失败物品。这个随机失败机制可以增加烹饪的不确定性
研磨机 (Grinder)
研磨机是一个异步工位——放入食材后不需要玩家持续操作,等一段时间自动完成。
工作流程
- 玩家左键手持食材点击研磨机方块,食材被消耗并开始研磨
- 系统启动后台定时器(
check_delay_ticks间隔),持续检查研磨进度 - 研磨期间在方块上方生成烟雾粒子效果
- 经过
grind_time_seconds秒后,研磨完成,发放结果 - 研磨期间其他玩家无法使用同一研磨机
交互方式
| 操作 | 条件 | 效果 |
|---|---|---|
| 左键 + 食材 | 研磨机空闲 | 开始研磨 |
| 左键 + 食材 | 研磨机忙碌 | 提示正在研磨 |
| 破坏方块 | 正在研磨 | 掉落输入物品,清除状态 |
状态文件格式
schema_version: 1
station_type: grinder
world: world
x: 100
y: 64
z: 200
input_item:
source: "minecraft-bone"
grinder:
recipe_id: "bone_meal"
start_time_ms: 1700000000000
player_uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
player_name: "Steve"特殊机制
check_delay_ticks:后台定时器的检查间隔,默认 20 tick(1 秒)。调大可以减少性能开销,但完成时间的精度会降低- 后台 Ticker:研磨机使用
BukkitTask定时器轮询进度。当服务器上没有任何活跃的研磨机时,定时器会自动取消以节省性能 - 研磨完成后会自动通知在线的发起玩家。如果玩家离线,产出物品会掉落在工位上方
蒸锅 (Steamer)
蒸锅是最接近"真实烹饪"的工位,有燃料、水分、蒸汽三套资源系统,还有 GUI 操作。玩家需要同时管理燃料和水分,才能持续产生蒸汽来推进烹饪。
工作流程
- 确认蒸锅下方有热源方块(
heat_sources) - 玩家潜行右键蒸锅打开 GUI,将食材放入 GUI 槽位
- 玩家潜行左键蒸锅下方热源方块,手持燃料添加燃烧时间
- 玩家潜行左键蒸锅,手持水桶等物品添加水分
- 系统每秒 tick:燃烧中 + 有水分 → 产生蒸汽 → 蒸汽驱动食材烹饪进度
- 食材进度达到
required_steam时完成,产出替换原食材或掉落
交互方式
| 操作 | 条件 | 效果 |
|---|---|---|
| 右键蒸锅 | 潜行 | 打开蒸锅 GUI |
| 左键热源 + 燃料 | 潜行 | 添加燃料,延长燃烧时间 |
| 左键蒸锅 + 水桶 | 潜行 | 添加水分 |
| 左键蒸锅 + 空手 | 潜行 | 查看蒸锅状态信息 |
| 破坏方块 | 有内容物 | 掉落所有物品,清除状态 |
状态文件格式
schema_version: 1
station_type: steamer
world: world
x: 100
y: 64
z: 200
steamer:
burning_until_ms: 1700000060000
moisture: 50
steam: 30
player_uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
player_name: "Steve"
gui_slots:
- index: 0
source: "minecraft-cod"
item: { ... } # 序列化的 ItemStack 数据
- index: 1
source: "minecraft-salmon"
slot_progress:
- index: 0
progress: 80
- index: 1
progress: 20特殊机制
fuel系统:燃料提供燃烧时间(burning_until_ms),燃烧期间热源方块会被点燃(营火点亮 / 熔炉燃烧),视觉上也能看出蒸锅在工作moisture系统:水分是蒸汽的原料。每 tick 消耗steam_production_efficiency单位水分转化为蒸汽。水分耗尽后蒸汽就不再产生steam系统:蒸汽驱动烹饪进度。每 tick 基础消耗steam_consumption_efficiency,每个食材额外消耗steam_conversion_efficiency。食材越多,蒸汽消耗越快reset_progress_when_steam_empty:蒸汽耗尽时是否重置所有食材的烹饪进度。开启后玩家必须保持蒸汽不断,否则前功尽弃- GUI:使用 Bukkit 原生容器(默认 HOPPER 类型),支持配置为 CHEST 并自定义槽位数。槽位数决定了一次能蒸多少食材
- 脏状态刷盘:蒸锅的状态变化非常频繁(每 tick 都在更新),所以使用内存缓存 + 定时刷盘机制,避免频繁 IO 拖慢性能
CookingBlockMatcher
CookingBlockMatcher 负责判断世界中的方块是否匹配工位配置的 block_source。这是工位交互的第一步——玩家点击一个方块时,系统需要知道它是不是某个工位。
匹配逻辑
| 来源类型 | 匹配方式 |
|---|---|
VANILLA | 比较方块的 Material 与 block_source 解析出的原版材质 |
CRAFTENGINE | 委托 CraftEngineBlockBridge 进行自定义方块匹配 |
ITEMSADDER | 委托 ItemsAdderBlockBridge 进行自定义方块匹配 |
NEXO | 委托 NexoBlockBridge 进行自定义方块匹配 |
# 原版方块示例
block_source: "minecraft-oak_slab"
# CraftEngine 自定义方块示例
block_source: "craftengine-my_namespace:my_block"
# ItemsAdder 自定义方块示例
block_source: "itemsadder-my_namespace:my_block"
# Nexo 自定义方块示例
block_source: "nexo-my_block_id"注意
自定义方块类型(CRAFTENGINE、ITEMSADDER、NEXO)在对应插件未安装时,匹配始终返回 false。如果你的工位用了自定义方块,务必确保服务器已安装对应插件。
CookingRewardService
CookingRewardService 处理所有工位的烹饪结果发放。不管是砧板切完、炒锅出锅还是研磨机磨好,最终都走这个服务。
发放模式
| 模式 | 条件 | 行为 |
|---|---|---|
| 掉落 (drop) | drop_result: true 或玩家离线 | 在工位上方自然掉落物品 |
| 给予 (give) | drop_result: false 且玩家在线 | 直接放入背包,满则掉落 |
产出字段
| 字段 | 类型 | 说明 |
|---|---|---|
source | string | 物品来源标识 |
amount | int | 固定数量(默认 1) |
amount_range | object | 随机数量范围 {min, max} |
chance | int | 产出概率 0-100(默认 100) |
actions | list | 产出时执行的动作列表 |
提示
amount 和 amount_range 互斥,同时存在时 amount_range 优先。chance 为百分比,100 表示必定产出。
LegacyImportService
如果你之前用的是旧版 JiuWu's Kitchen,LegacyImportService 可以帮你把配置和数据迁移到 EmakiCooking 2.0。
两种模式
| 模式 | 命令 | 行为 |
|---|---|---|
dryrun | /ecooking convert old dryrun | 解析旧版配置,生成导入报告,不写入任何文件 |
apply | /ecooking convert old apply | 备份当前资源 → 写入转换后的配方/工位状态/配置更新 → 重载 |
导入流程
- 从
old/目录读取旧版Config.yml、Recipe/和Data.yml - 转换配方:砧板、炒锅、研磨机、蒸锅配方分别转换为新格式
- 转换工位状态:将旧版坐标格式转换为新版 YAML 文件
- 转换配置:将旧版工位设置合并到新版
config.yml - 转换展示调整:将旧版物品展示设置转换为
item_adjustments/文件 - 生成导入报告至
data/legacy-import-report.yml
导入报告
报告包含以下统计信息:
converted_recipe_count— 成功转换的配方数converted_station_state_count— 成功转换的工位状态数skipped_count— 跳过的条目数conflict_count— 冲突数(如重复 ID)unknown_source_count— 无法识别的物品来源数action_context_issue_count— 命令执行上下文不确定的数量
注意
apply 模式会在写入前自动备份当前资源到 backup/pre-import-{timestamp}/。强烈建议先跑一次 dryrun 看看报告,确认没问题再执行 apply。