Repository navigation
1.21 Fix final burning heater batch 修复燃烧加热器最后一次加工数量 - #6085
Conversation
- 按配方与配置限制计算鱼缸批量加工数量 - 保持一次铁砧触发只消耗一次燃料,并遵守原料及输出容量限制
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.
Gugle2308
left a comment
There was a problem hiding this comment.
审查结论:APPROVE(无阻塞问题,3 条待确认项)
删除的是一个判断逻辑有误的限制条件,修复方向正确。删除之后,「燃料只剩一次加工量」与「燃料充足」走完全相同的路径,符合 issue #6052 的预期;并且“一次铁砧触发只扣一次燃料”这一点仍然由既有机制保证,不依赖被删掉的那段代码。
范围说明:webhook 只提供 patch diff;本 PR 为 1 文件 / +1 −25,与 API 元数据(changed_files=1 / additions=1 / deletions=25)一致,diff 覆盖完整。结论基于 head 82d5bd95(codeload 拉取完整源码树)与 base 0ea57be7 对照,已逐行核对被删方法体与 base 一致、无夹带改动。未覆盖:运行时实机行为、多人同时触发。CI:build ✅ / checkstyle ✅(commits/82d5bd95/check-runs)。目标分支 dev/1.21/1.6。
✅ 根因定位正确(数值可逐一复算)
原 batchLimit 的算术链:
BurningHeaterBlockEntity.LIT_THRESHOLD = 240 * 20 = 4800(block/entity/BurningHeaterBlockEntity.java:45);SuperHeatingRecipe.ConsumeFuel.FUEL_COST_TICKS也是240 * 20 = 4800(recipe/anvil/wrap/SuperHeatingRecipe.java:311);CauldronProcessing.SUPER_HEATING对燃烧加热器只接受LEVEL == 2(api/recipe/CauldronProcessing.java:35-36),而updateBurningState的档位线就是burnTime >= 4800 → 2(同文件:229-238);- 旧代码模拟的是扣完一次燃料之后的等级:
remaining = burnTime - 4800,要求remaining >= 4800,即burnTime >= 9600。
⇒ 当 burnTime ∈ [4800, 9600) 时,当前确实满足高温熔炼,但旧代码预判“扣一次就掉出该类别”,于是 return Math.min(limit, 1)——正是 issue 描述的“只执行一次物品加工”。燃料充足(≥9600)时该分支不触发,所以这个 bug 只在燃料将尽时出现。逻辑完全自洽。
对称情形也成立:炉温从 LEVEL 2 掉到 1 会脱离 SUPER_HEATING,从 1 掉到 0 会脱离 FAST_COOKING;但因为只有 SuperHeatingRecipe 挂了 ConsumeFuel,被删分支实际只在这条路径上生效。
✅ 删除不会导致“多批次却不扣燃料”
- 燃料按“触发”计费,而不按“批次”计费:
FishTankRecipes的afterCommit写的是
effect.acceptWithChance(effect instanceof SuperHeatingRecipe.ConsumeFuel ? impact : context)——传入的是共享的impact上下文(文件内注释也写明“自动点燃会消耗物品,只在最终库存提交后统一处理”)。而ConsumeFuel.accept的第一句就是
if (context.get(FUEL_CONSUMED)) return; context.put(FUEL_CONSUMED, true);。
第一批置位后,后续批次直接跳过 ⇒ 无论limit是多少,一次铁砧触发只扣 4800。成本与批次数解耦,用剩余燃料去限制批次数量在语义上本来就是错的。 - 匹配时对燃料状态的限制没有丢:
HasCauldronProcessor(…, SUPER_HEATING)仍由Property注入(AbstractProcessRecipe.java:882-884),并且SuperHeatingRecipe的inputBlocks显式带.with(BurningHeaterBlock.LEVEL, 2)。所以不会出现“加热器已熄灭却仍开新配方”——消失的只是“熄灭前这一次也不许满批”这个错误前置条件。 - 输入/输出容量限制未被触碰:
ItemBatchPlan.shared/separate+SlotCapacity = tank.getOutputHandler()::getSlotLimit这行 diff 里没动,无新增溢出路径。 - 残留清零:
batchLimit/Math.min(limit, 1)全树 0 命中;getCauldronProcessor()全树 0 调用者(由Property上的 Lombok@Getter提供,无人用也不影响编译);被删的 3 个 import(CauldronProcessing/BurningHeaterBlock/BurningHeaterBlockEntity)在 head 文件中零引用,AbstractProcessRecipe(:50)/IRecipeOutcome(:115)仍在使用 ⇒ 无未使用 import。新增行长度约 110 <style.xml的LineLength max=140。
⚠️ ConsumeFuel 的注释与实际计费方式不符(本 PR 让这处矛盾变得玩家可见)
SuperHeatingRecipe.ConsumeFuel 的常量注释写的是
/** 每次合成消耗的燃烧时间(tick),240秒 = 4800tick */
public static final int FUEL_COST_TICKS = 240 * 20;但实际实现是每次铁砧触发扣一次(FUEL_CONSUMED 挂在共享 impact 上),不是“每次合成/每批”扣一次。旧代码那段 burnTime 模拟,本质上是按“每批都要扣一次燃料”的假设写的——这也是它能自圆其说的唯一理由;删掉它之后,同一份燃料换来的产物数量就完全取决于 maxEfficiency 与原料数量,注释里的“每次合成”与代码的“每次触发”正式分岔。
建议二选一(属设计确认,非代码错误):
- 若认可“一次触发一次燃料” ⇒ 把注释改成“每次铁砧触发消耗的燃烧时间”,使文档与实现对齐;
- 若本意确实是“每批消耗 4800” ⇒ 那这次删除并非完整修复,需要改成按
plan.batches()计费(这是另一个更大的改动,不建议在本 PR 内做)。
💡 玩家可见的效率变化,建议写进描述或更新日志
改动后,burnTime ∈ [4800, 9600) 的加热器一次触发即可产出 min(maxEfficiency, CONFIG.inWorldRecipeMaxEfficiency) 批、消耗仍是 4800。也就是说,低燃料不再受惩罚,单位燃料的产出曲线从“越低越亏”变成恒定。这是 issue 的期望(“正确执行正确数量的物品加工”),但属于收益提升,写明原因可以避免以后被当作“燃料白嫖”回滚。若设计上确实想限制单次触发最多烧炼量,正确的落点是 SuperHeatingRecipe 的 maxEfficiency,而不是恢复这里的 clamp。
💡 maxEfficiency 对 super_heating 恒为 Integer.MAX_VALUE
SuperHeatingRecipe 走 super(createProperty(...)) ⇒ AbstractProcessRecipe(Property) 把 maxEfficiency 定为 Integer.MAX_VALUE(AbstractProcessRecipe.java:74-76),生成的 24 个 super_heating 配方 JSON 也没有 max_efficiency 字段。因此 Math.min(process.maxEfficiency(), CONFIG.inWorldRecipeMaxEfficiency) 实际等于仅取配置值。补一行注释可以避免后续维护者误以为“每个配方自带上限”。
📋 声称验证表
| PR 声称 | 状态 | 证据 |
|---|---|---|
| 按配方与配置限制计算鱼缸批量加工数量 | ✅ | FishTankRecipes.java:64 = Math.min(process.maxEfficiency(), AnvilLibRecipe.CONFIG.inWorldRecipeMaxEfficiency),与旧方法首行逐字相同;ItemBatchPlan 两分支都用这个 limit |
| 保持一次铁砧触发只消耗一次燃料 | ✅ | FishTankRecipes.java:151 把 ConsumeFuel 路由到共享 impact;ConsumeFuel.accept 的 FUEL_CONSUMED 幂等守卫(SuperHeatingRecipe.java:326-327) |
| 遵守原料及输出容量限制 | ✅ | SlotCapacity/ItemBatchPlan 未改动;getSlotLimit 委托到 FishTankBlockEntity:164-178 |
fixed #6052 |
✅(跨线) | issue 报在 26.1.2 / snapshot2862,本 PR 目标 dev/1.21/1.6;同一 bug 在 1.21 线同样存在,逻辑等价修复成立。26.1 线的对应 PR 是 #6082(26.1 Fix final burning heater batch,head ee039c28,同为 1 文件 +1 −25),两份 diff 结构完全一致,唯一差别是 outcome 类型名(1.21 为内嵌 SuperHeatingRecipe.ConsumeFuel + 静态 FUEL_COST_TICKS,26.1 已提为独立 record ConsumeBurningHeaterFuel + fuelCostTicks() 字段)⇒ 语义等价。两线需一起合入,否则另一线仍可复现。 |
另附旁证:同仓另一处批量上限 anvil/MassInjectBehavior.java:34 一直直接使用 inWorldRecipeMaxEfficiency、从未有燃烧时间守卫 ⇒ 本次删除是向既有约定对齐,而非引入新模式。
🧪 建议验证
- 原始复现:燃烧加热器燃至
burnTime略高于 4800(旧代码必现只加工 1 次),鱼缸放足够原料 + 空输出 → 落砧一次,应加工满批且燃料只扣 4800; - 回归:
burnTime ≥ 9600时批次数与改动前一致; - 边界:
burnTime恰为 9600(旧代码最后一个不触发 clamp 的值)与恰为 4800(旧代码第一个触发 clamp 的值,改动后应满批并把加热器烧至LEVEL 0)各测一次; - 不应加工:
burnTime略低于 4800(LEVEL 1)→ 高温熔炼不匹配,落砧无产出(确认没被顺手放宽到燃烧加热器任意等级); - 燃料归一:连续多次落砧,确认每次触发各扣一次 4800,不会因批次数多扣;
- 电加热器路径不受影响:加热器下方放普通电加热器(
HEATER,无ConsumeFuel实际消耗)跑高温熔炼,批次数应与改动前一致; - 输出溢出:输出槽接近满时落砧,产物只进槽内、不留掉落物。
fix(recipe): 修复燃烧加热器最后一次加工数量
按配方与配置限制计算鱼缸批量加工数量
保持一次铁砧触发只消耗一次燃料,并遵守原料及输出容量限制
fixed [Bug] 当燃烧加热器剩余时间仅够一次加工时,加工物品数量错误 #6052