Fix undo after falling blocks replace double plants 修复下落方块覆盖双格植物后的撤销 - #5128
PigeonNian merged 3 commits into
Conversation
- 记录落点双格植物的完整配对快照,恢复上下两部分并按实际资源结算。 - 撤销前检查历史植物配对完整性,避免混合时点的快照消耗材料后恢复失败。
代码审查摘要 — PR #5128操作: opened(草稿 PR, ✅ 已核实通过(写在这里,便于判断哪些地方是安全的)
|
| 被测目标 | 推荐场景 | 优先级 |
|---|---|---|
BuildingRegionSnapshot.captureBlocks |
落点=植物下半/上半(另一半在区域内/区域外/已被换成非植物方块)三种组合,断言 addedPositions 与 restore() 后的世界状态 |
🔴 |
canRestore 配对门 |
快照仅有半边、快照配对完整、世界侧玩家已改动半边 —— 断言撤销是否应被拒 | 🔴 |
| 材料账目 | 一对双格植物 + 一次落地覆盖 → required.items 恰 1 件;recovery/required 经 cancel() 后净额正确 |
🟡 |
placeFallingBlock 回滚 |
placement 返回 false / 抛异常时,两半均从 addedPositions/blocks/ticks/events 移除 |
🟡 |
结论: COMMENT(草稿 PR,非阻塞式驳回) — 配对捕获与材料结算已逐条核实、方向正确;合并前建议收敛新增配对门的行为(捕获侧不产生半边,或校验侧降级为跳过该格 + 专用文案),并修正描述里的重复 claim。
由 Hermes Agent 审查
|
💾 Self-improvement review: Patched references/falling-block-landing-capture-review.md in skill 'building-rod-blueprint-review' (1 replacement). · Patched SKILL.md in skill 'building-rod-blueprint-review' (1 replacement). |
- 在权限检查后、资源结算前独立校验双格植物快照,保留生存与创造模式的原子拒绝。 - 使用现有资源冲突提示说明不完整或混合时点的植物快照。
Reason: recursive delete Reply |
代码审查摘要 — PR #5128操作: synchronize(opened→synchronize,PR 状态 = Open,未合并) 改动实质
已交叉验证的事实(可复现)
🔴 关键无。
|
| PR 声称 | 状态 | 依据 |
|---|---|---|
| 记录落点双格植物的完整配对快照 | ✅ | L82–93 补记 pos.above()/below(),isInWorldBounds + 同类反侧校验 |
| 恢复上下两部分并按实际资源结算 | ✅ | restore() 逐格还原;上半块经 shouldRecord() 不计费 → 只按下半块结算 |
| 撤销前检查配对完整性,避免先扣料再失败 | ✅ | hasCompletePlants() 在 resources() 前;undo_conflict 提前返回 |
| fixed #5124 | ✅(针对双格植物) | 落点在区域外的半块场景已可撤销;门/床/活塞头同类场景仍拒绝(见 |
结论: APPROVE(建议按
PR 标题「Fix undo after falling blocks replace double plants 修复下落方块覆盖双格植物后的撤销」与改动一致,无需修改(未执行 gh pr edit)。
🧪 测试建议
| 被测目标 | 推荐场景 | 优先级 |
|---|---|---|
captureBlocks() 扩展 |
铁砧落在区域外双格植物下半 / 上半 → 撤销后上下两半均还原、仅消耗 1 个物品 | 🔴 |
captureBlocks() + 邻居更新 |
落点替换下半块后上半块被 updateShape 打成空气 → 撤销恢复完整植株、退回的物料正确 |
🔴 |
hasCompletePlants() |
混合时点(配对半块现场已是铁砧)→ 返回 undo_conflict 且背包物品数不变 |
🔴 |
hasCompletePlants() 误拒边界 |
植株被包围盒 minY/maxY 切开(配对半块在框外且现场完好)→ 期望成功撤销(当前会拒绝,见 |
🟡 |
| 创造模式撤销 | 创造下走新拒绝路径 / 走正常恢复,均不产生物料增减 | 🟡 |
| 多部件同类回归 | 门 / 床 / 延伸活塞头落在区域外 → 现行为(undo_conflict)不被本改动影响(并作为 |
🟡 |
| 世界边界 | 植物位于 y 边界(partner 超出世界边界)→ 不抛异常、added 无重复项 |
🟢 |
由 Hermes Agent 审查
|
💾 Self-improvement review: Patched references/falling-block-landing-capture-review.md in skill 'building-rod-blueprint-review' (1 replacement). · Patched SKILL.md in skill 'building-rod-blueprint-review' (1 replacement). · Patched references/shell-only-diff-navigation.md in skill 'github-webhook-pr-review' (1 replacement). |
- 仅在配对方块没有历史快照时检查现场状态,允许创造模式安全恢复区域边界的完整植物。 - 保留已有快照的配对冲突校验,不覆盖区域外方块,也不放宽生存模式的资源检查。
代码审查摘要 — PR #5128操作: 🔎 根因确认(#5124 症状与代码路径吻合)
修复方向正确: 🔴 关键问题无。
|
| 被落点覆盖的方块 | contains(part) 检查的对象 |
结果 |
|---|---|---|
门下半 HALF=LOWER |
pos.above()(door UPPER) |
区域外且未入快照 ⇒ throw |
床尾 PART=FOOT |
pos.relative(FACING)(HEAD) |
同上 |
| 伸出活塞基座 | pos.relative(FACING)(PISTON_HEAD) |
同上 |
(captureBlocks 只对 state.getBlock() instanceof DoublePlantBlock 做配对展开,门/床/活塞都不会被补记 ⇒ resources() 仍抛异常 ⇒ 仍是 undo_conflict,且由于配对位置永远进不了 contains(),玩家无法通过任何操作补救,只能放弃这次撤销。)
建议:把「配对/部件位置推导」下沉到 BlueprintMultiblocks(新增如 partnerOf(pos, state),覆盖 door/bed/piston-head),captureBlocks 与 hasCompletePlants 共用一份实现,即可顺带覆盖这三类。
forEachPart() 不能直接拿来用:对 HALF=UPPER 的方块 shouldRecord() 为 false,它在 accept(pos) 后直接 return,不会给出下方的配对方块;只有 HALF=LOWER 才会展开 → 所以需要的是 core() 反向推导,而不是 forEachPart()。若维护者认为应限定范围,至少建议立刻开 issue 跟踪门/床场景。
💡 建议
2. hasCompletePlants() 的净收益需要写清(避免被当成纯消息优化)
生存模式下 resources() 抛出的异常已被同一个 catch 映射成同一条 undo_conflict 消息,且扣料发生在 catch 之后的 recovery.consume(),所以该守卫在生存模式只是「提前一次、少算一遍资源」;它真正新增的作用域是创造模式(if (!undo.creative) 跳过 resources(),因此旧路径在创造模式不会做任何部件校验)。commit 3 的描述说明这是刻意的,建议在 javadoc 里点明「含创造模式」,否则读代码的人会以为与 checkParts 重复而无意义。
3. 「快照优先于现场」的判定会锁死一类可撤销的快照
hasCompletePlants() 先取 original.get(other),为空才回退现场。若快照本身记录的就是一株残缺植物(例如区域内只有下半块、上方位置在快照中是 AIR;或区域顶边切过植物且上方半块此前已被吃掉),只要该半块后来发生变化,就会 return false 把整场撤销永久禁止——而旧行为在这种「快照忠实复刻残缺世界」的情形下是能正常撤销的。建议区分「快照残缺」与「现场残缺」,或至少让提示可操作(现有文案 "Cannot safely account for the changed resources; undo cancelled without changing the area" 已经不错,但没告诉玩家该拆掉什么)。
4. 配对逻辑三处分散
captureBlocks(H86)与 hasCompletePlants(H156)各写一份 HALF→配对方向;resources() 里又构建了一份同构的 original map。建议合并为 BlueprintMultiblocks 的一个小 helper,避免三处漂移。
5. zh_cn 缺键(非本 PR 引入)
zh_cn.json 缺 message.anvilcraft.building_rod.undo_conflict / undo_missing_materials / undo_missing_containers / undo_partial(base 已在使用),中文客户端会由 en_us 兜底显示英文;可在 Weblate 补译。
🟢 看起来不错
- 跳过条件
!(block.state().getBlock() instanceof DoublePlantBlock) || getBlockState(pos) == block.state()的 De Morgan 展开正确;otherState == null || !otherState.is(...) || otherState.getValue(HALF) == half靠||短路保证了先is()再getValue(),不存在跨方块读属性的风险。 above()/below()前都有isInWorldBounds守卫,世界高度边缘不会越界取态;BlockState用==/is()与既有canRestore()/resources()的惯例一致(状态实例是规范化的)。expanded用new ArrayList<>(positions)拷贝,兼容调用方传入的List.of(pos)不可变列表;distinct()+immutable()保留原顺序语义。- 失败回滚路径完整:
placeFallingBlock的finally里removeCapturedBlocks(added)会同时清掉blocks/ticks/events/addedPositions,配对位置不会残留。 - 影响面扩展后
addedPositions同时惠及replaced()、blockDrops()(配对半块若因邻居更新掉落,其掉落物现在会进derived,撤销时不会再出现「植物恢复了、地上还躺着一个掉落物」的重复)、以及restore()里对新增位点的事件/tick 清理,语义自洽。 restore()只有undo()一个入口,守卫位置覆盖完整;新 import 顺序与行宽(137/135 < style.xml 的 140)合规,CI 双绿。
📋 声称验证表
| 声称 | 状态 | 证据 |
|---|---|---|
| 记录落点双格植物的完整配对快照 | ✅ | captureBlocks 展开 + otherState.is(state.getBlock()) 校验,且在 placement 之前执行 |
| 恢复上下两部分并按实际资源结算 | ✅ | restore() 遍历含配对位的 this.blocks;resources() 对未变化的配对位 continue,不重复扣料 |
| 撤销前检查历史植物配对完整性 | ✅ | hasCompletePlants() + undo() L337 独立守卫,位于权限检查后、资源结算前 |
| 避免混合时点快照消耗材料后恢复失败 | 生存模式本就在 consume() 之前中止,净新增作用域是创造模式与提前拒绝(见建议 2) |
|
| fixed #5124 | ✅ 根因成立 | checkParts→forEachPart 部件包含性抛出 ⇒ 撤销整体中止,与 issue 症状逐字吻合 |
| 覆盖门/床/活塞等同类多部件方块 | ❌ 未覆盖 | BlueprintMultiblocks.isDoubleBlock 含 DoorBlock;captureBlocks 仅处理 DoublePlantBlock(见警告 1) |
结论: APPROVE(改动本身正确,建议跟进 1 项) — 修复方向、时序、序列化/结算语义均无误,#5124 根因已消除;唯一遗留是同类缺陷在门/床/活塞上仍在,泛化成本很低(一处 helper),建议本 PR 内一并覆盖或立刻开 issue 跟踪。
PR 标题已为「英文 + 中文」且准确描述根因(双格植物),无需修改。
🧪 人工验证建议(项目 src 下无 test 目录,无法加单测)
| 场景 | 期望 |
|---|---|
| 生存模式:蓝图含铁砧落在双格植物下半块上 → 落地后撤销 | 上下两半都还原,材料按实际结算,无重复掉落物 |
| 同上但落在上半块 | 同上(配对方向为 below()) |
| 创造模式:同场景 | 不再被 undo_conflict 误拒(commit 3 的现场状态回退路径) |
| 双格植物跨区域顶边(上方半块在区域外且完好) | 撤销正常 |
| 对照:落点覆盖门的一半 / 床尾 / 伸出活塞基座 | 目前仍会 undo_conflict 且无法补救 —— 用于确认警告 1 |
由 Hermes Agent 审查
|
💾 Self-improvement review: Patched SKILL.md in skill 'building-rod-blueprint-review' (1 replacement). · Patched references/pr-class-routing.md in skill 'github-webhook-pr-review' (1 replacement). |
Reason: script execution via -e/-c flag Reply |
🔔 先说明:该 PR 已经合并(本评论为事后复查)
验证方式(匿名公开端点,未使用任何 token):
因此下面没有「合并前需修复」的动作项,结论按 post-merge 观察 + 可选后续项给出;也无需改标题(标题准确,且已合并)。 代码审查摘要 — PR #5128范围: 2 个文件(2 Java / 0 新增 / 0 删除)/ 79 行 diff(+38 −2)/ 4 hunks 📋 声称验证表
🟢 实现正确性(逐点核对)
💡 建议(可选,非阻塞)
📋 合并状态说明PR 已合并,本评论仅作 post-merge 记录;第 1 🧪 测试建议
结论: 已合并,无需动作 — 核心修复(捕获配对 + 门禁前置)逻辑自洽、副作用为零、资源不重复计数;上述 5 条均为 post-merge 观察与可选后续项,其中第 4、5 条建议实测确认后再决定是否跟进。 由 Hermes Agent 审查(webhook 自动投递,未执行任何 gh 评论命令) |
|
💾 Self-improvement review: Patched references/merged-state-and-ci-verification.md in skill 'github-webhook-pr-review' (1 replacement). · Patched SKILL.md in skill 'building-rod-blueprint-review' (1 replacement). |
PR #5128 审查
范围: 2 个文件(+38/−2), 🟢 修复机理核实(正确)
|
| 声称 | 状态 | 依据 |
|---|---|---|
| 记录落点双格植物的完整配对快照 | ✅ | captureBlocks 配对扩展 → addedPositions |
| 恢复上下两部分 | ✅ | 两半均在 this.blocks,restore() 逐格 set |
| 按实际资源结算 | ✅ | shouldRecord(UPPER)==false ⇒ 材料只计 1 份 |
| 撤销前检查历史植物配对完整性 | DoublePlantBlock |
门/床/活塞/大蛋糕未覆盖(风险 A) |
| fixed #5124 | 双格植物场景已修;同类多方块仍可能"落地方块撤不掉" |
🧪 验证建议(仓库无 src/test,只能游戏内验证)
| 场景 | 步骤 | 期望 |
|---|---|---|
| 双格植物跨界·生存 | 含下落铁砧实体的蓝图,落点旁/下方是区域外高花 | Ctrl+Z 成功:铁砧方块消失、上下两半还原、只扣 1 份花 |
| 同上·创造 | 同上,创造模式 | 不出现"单独下半株" |
| 混合时点 | 落地后手动拆掉配对方块,再 Ctrl+Z | undo_conflict,区域不变 |
| 配对格被占用 | 落地后在配对格放箱子(含物品)再 Ctrl+Z | 当前会静默覆写且内容物丢失 → 确认风险 B |
| 门/床/活塞/蛋糕 | 下落方块压碎半件、另一半在区域外 | 目前仍 undo_conflict → 复现风险 A |
结论: APPROVE(已合并) — 修复方向、原子性位置与资源结算均正确,回滚路径无回归;A、B 两点作为 post-merge 跟进建议。PR 标题准确,无需改动(且已合并,未执行 gh pr edit)。
由 Hermes Agent 审查(base 9cb2569999 → merged 9e750b31cc;head e10793f6d8)
|
💾 Self-improvement review: Patched references/merged-state-and-ci-verification.md in skill 'github-webhook-pr-review' (1 replacement). · Patched SKILL.md in skill 'pr-fix-claim-verification' (1 replacement). |
fix(building): 补齐覆盖双格植物的下落方块撤销 (#5124)