Skip to content

Fixed bugs and optimized experience#4189

Merged
PigeonNian merged 4 commits into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/fix12
Jul 19, 2026
Merged

Fixed bugs and optimized experience#4189
PigeonNian merged 4 commits into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/fix12

Conversation

@WhereisFff

@WhereisFff WhereisFff commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings July 17, 2026 16:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4189

操作: opened
提交者: WhereisFff
分支: dev/1.21/fix12dev/1.21/1.6
范围: 14 文件 (14 Java, 3 新增, 0 删除) / 785 行 diff (235 +added, 142 -removed, 43 hunks)
Ghost 文件: 无(所有文件均有实际变更)
TODO/FIXME/硬编码凭证:


📋 声称验证表

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 LargeCauldronBlock.getShape()
修复各种锅和缸的伤害问题 HeaterUtil, IDamagingHeater, CauldronUtil.hurtFromHeaterBelow(), LivingEntityEventListener.onTick()
多方块下切石机任意 part 被砸触发破坏 AnvilEventListener.onLand(), hasStonecutterBelowAnyPart()
大锅燃烧加热器扣除时间不对 BurningHeaterBlockEntity.consumeBurnTime() 简化

🔴 关键问题

1. LivingEntityEventListener#onTick 每 tick 对处于锅内的实体造成加热器伤害

新代码在 LivingEntityEventListener.onTick() 中添加了 CauldronUtil.hurtFromHeaterBelow(event.getEntity()),这意味着站在大锅内且下方有加热器的每个实体,每 tick 都会受到 4.0F 的 heaterBurn 伤害

  • 旧行为: 伤害来自 LargeCauldronBlockEntity.hurtEntitiesInside(),只在大型大锅 BE tick 时触发(特定 tick 间隔)。
  • 新行为: 伤害来自全局实体 tick 事件,每 tick 触发,且覆盖所有 BlockTags.CAULDRONS 方块(包括普通大锅 + 大型大锅)。

LivingEntityEventListener@SubscribeEvent 全局订阅,没有范围限制。这意味着所有加载的实体都会调用 CauldronUtil.hurtFromHeaterBelow() → 获取 blockPosition() → 检查方块 → 遍历底部位置 → 检查加热器。每次调用都需要 level.getBlockState() 用于起始方块和每个底部位置。

建议添加频率限制或先检查实体是否确实在支持加热器的大锅方块内,减少不必要的 getBlockState 调用。

影响评估: 旧代码只伤害大型大锅内的实体(大锅 BE 管理范围);新代码伤害任何大锅上方加热器上的实体(普通大锅 + 大型大锅)。这可能是期望的修复(旧代码只覆盖了大型大锅,缺少了普通大锅),但 tick 频率比旧代码高得多。


⚠️ 警告

2. SuperHeatingRecipe.ConsumeFuel 重复燃料消耗防护被移除

handleGiantAnvilImpact() 完全移除了 fuelCharged 集合和相关逻辑:

  • 删除了 Set<BlockPos> fuelCharged = new HashSet<>()
  • 删除了 fuelCharged.contains(helper)fuelCharged.add(attempt.helper()) 调用
  • 删除了 triggerRecipeGroup 中的 fuelConsumed || executed 守卫
  • FUEL_CONSUMED context flag 虽仍被定义,但没有任何调用方设置它了

SuperHeatingRecipe.ConsumeFuel 仍然是 IRecipeOutcome,且会检查 context.get(FUEL_CONSUMED) 来防止双重消耗(第 306 行:if (context.get(FUEL_CONSUMED)) return;)。但由于没有任何代码设置 FUEL_CONSUMED = true每次匹配的 ConsumeFuel 都会认为燃料未被消耗过,导致在单次铁砧落地事件中多次将 consumeBurnTime(4800) 发送给同一个加热器。

旧代码引入 fuelCharged 正是为了防止这种情况——同一个加热器在单次铁砧落地中处理多个 recipe 时,只应收取一次燃料费。这种移除意味着,如果在大型大锅中处理多个物品槽(或输出槽后处理),加热器会被多次扣费,导致燃料过度消耗。

请确认这是预期行为,或者此路径是否确实只有第一个匹配的 recipe 会触发 ConsumeFuel(例如 executed flag 阻止了同一槽位的后续尝试)。如果是预期行为,建议在 PR 中明确说明。


3. 伤害系统架构变更:从 BE tick 迁移到实体 tick 事件

LargeCauldronBlockEntity 中的伤害方法从 hurtEntitiesInside 重命名为 hurtEntitiesInsideFromCampfire,去除了所有加热器相关伤害。加热器伤害完全移动到 LivingEntityEventListener 中的 CauldronUtil.hurtFromHeaterBelow()

这个架构变更意味着:

  • 旧代码: 加热器伤害在大型大锅 BE serverTick 中触发,与 campfire 伤害耦合
  • 新代码: 加热器伤害在全局实体 TickEvent.Post 中触发,与 campfire 伤害分离

这是合理的职责分离,但改变了频率和覆盖面。如果加热器伤害本应是持续的(类似于站在火中),TickEvent.Post 更合适。如果本应是间歇性的(BE tick 频率),则新代码更激进。

建议: 添加一个伤害冷却(如实体防伤害提示 hurtInvulnerableTime 可能不适用于自定义伤害类型),或在 hurtFromHeaterBelow 中添加实例检查以跳过上 tick 已被同一加热器伤害过的实体。


4. AnvilEventListener.onLand() 中巨型铁砧 → 大型大锅路径增加了 behavior 执行

旧代码:

if (cauldron != null && cauldron.handleGiantAnvilImpact(event)) return;

新代码:

if (cauldron != null && cauldron.handleGiantAnvilImpact(event)) {
    handleBehaviors(level, hitBlockPos, hitBlockState, event);
    return;
}

当巨型铁砧被大型大锅成功处理时,IAnvilBehavior 列表也会被执行。这会触发 TimeWarpPlayerBehavior 等。确认这是预期行为——巨型铁砧 + 大锅处理后运行 behavior 可能会产生意外副作用(如将玩家传送到时空扭曲信标)。


5. BurningHeaterBlockEntity.consumeBurnTime() 简化改变了燃料消耗策略

旧代码优先从内部物品处理器(itemHandler.getStackInSlot(0))消耗燃料物品,仅剩余的从 burnTime 缓冲区扣除。新代码直接从 burnTime 扣除,完全跳过物品消耗。

这修复了"扣除时间不对"问题(原 bug),但需要确认:

  • itemHandler 中的物品是否已经通过 serverTick 中的常规燃烧循环转换为 burnTime
  • 如果是,旧代码是双重消耗(物品已被转换,又在 consumeBurnTime 中被再次消耗),新代码是正确的。

从修复的描述来看,新代码是正确的,但需要在代码审查中确认这一点。


6. CauldronUtil.hurtFromHeaterBelow() 中的潜在 NPE

public static void hurtFromHeaterBelow(Entity entity) {
    ...
    for (BlockPos bottomPos : getBottomPositions(pos, state)) {
        if (HeaterUtil.hurtEntity(entity.level(), entity.level().getBlockState(bottomPos), entity)) return;
    }
}

entity.level() 被调用了两次——这不是问题,但可以优化。更关键的是,如果 ICauldronGeometry.getCauldronBottomPositions() 返回包含 level.getBlockState() 可能返回 Blocks.AIR(即方块不存在或未加载)的位置,HeaterUtil.hurtEntity 会尝试 state.getBlock() instanceof IDamagingHeater——对于 air 将为 false,所以安全。但如果位置跨越区块边界且区块未加载,getBlockState 可能返回 null 而不是 AIR(取决于 Minecraft 版本实现)。建议验证或添加 null 安全。


💡 建议

7. TimeWarpPlayerBehavior 实体检测更合理

旧代码:player.getOnPos().equals(hitBlockPos) — 依赖于玩家精确站在触发点位置
新代码:CauldronUtil.isEntityInside(hitBlockPos, hitBlockState, player) — 使用 AABB 包含性检测

这大型大锅场景中更准确(玩家被大锅包围,而不是"站"在大锅方块上)。✅

8. 注册条件从 AbstractCauldronBlock 改为 BlockTags.CAULDRONS

ModAnvilBehaviorsstate.getBlock() instanceof AbstractCauldronBlockstate.is(BlockTags.CAULDRONS)

这同时涵盖了普通大锅和自定义大锅(前提是它们加入了 CAULDRONS tag),更符合方块标签的使用惯例。✅

9. 大锅底部位置范围正确

LargeCauldronBlock.getCauldronBottomPositions() 返回 getMainPartPos(pos, state).below(2) 周围的 3×3 区域。主 part 在 (0, 1, 0)(相对中心),below(2) 即中心下方 2 格,这对应加热器所在层级。3×3 覆盖了巨型大锅的完整投影。✅

10. EOF 换行符修复
SuperHeatingRecipe.java 修复了缺失的末尾换行符。✅


🟢 看起来不错

  • 接口抽象层清晰: ICauldronGeometryIDamagingHeater 接口设计简洁,CauldronUtil / HeaterUtil 职责分离合理,便于扩展和测试
  • 多方块破坏逻辑完善: hasStonecutterBelowAnyPart() 正确遍历所有 part,使用泛型匹配任意 AbstractMultiPartBlock,可复用
  • Collision 改进恰当: 仅在 part.getOffsetY() == 2(顶部层)且手持巨型铁砧时返回 Shapes.block(),不影响其他交互
  • 无死代码残留: RecipeAttempt record 被干净地移除,无遗留引用
  • 巨型铁砧手握检测正确: 使用 context.isHoldingItem(ModBlocks.GIANT_ANVIL.asItem()),兼容主手和副手

结论: COMMENT

本 PR 覆盖了 4 个声称的修复,且整体代码质量良好。主要担忧是:(1) SuperHeatingRecipe.ConsumeFuel 的重复消耗防护被完全移除,可能导致单个铁砧落地事件中加热器被多次扣费(每次消耗 4800 ticks = 4 分钟);(2) 加热器伤害从 BE tick 迁移到全局实体 tick 事件,频率更高,可能使伤害更加激进。

建议在合并前确认:

  1. ConsumeFuel 防护移除是否经过设计考虑——是否有其他机制防止同一落地事件中的重复消耗?
  2. 每 tick 对所有实体调用的 hurtFromHeaterBelow 是否需要频率限制或提前过滤?

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4189

操作: review_requested
作者: WhereisFff
分支: dev/1.21/fix12 → dev/1.21/1.6
范围: 14 个 Java 文件 (3 新增, 0 删除) / 785 行 diff


🟢 1. 手持巨型铁砧时大锅顶部视为完整碰撞 — ✅ 正确

LargeCauldronBlock.java getShape() 方法新增了逻辑:当顶部层 (part.getOffsetY() == 2) 且玩家手持 GIANT_ANVIL 时,返回完整方块形状 (Shapes.block()),而非镂空的锅沿形状。

  • context.isHoldingItem() 是 Minecraft 标准的碰撞上下文 API,正确实现了"仅手持时视为完整"的语义
  • 不破坏非手持情况下的正常碰撞行为

✅ 实现正确,符合预期。


🟢 2. 修复各种锅和缸的伤害问题 — ✅ 正确

改动要点:

  1. 新增 IDamagingHeater 接口BurningHeaterBlockHeaterBlock 实现此接口,分别提供 isActive() 判断激活状态
  2. 新增 HeaterUtil.hurtEntity() 工具方法 — 统一检查 IDamagingHeater.isActive() + 潜行 + LivingEntity 过滤,然后施加 heaterBurn 伤害
  3. BurningHeaterBlock.stepOn()HeaterBlock.stepOn() — 移除内联伤害逻辑,统一委托给 HeaterUtil.hurtEntity()
  4. LargeCauldronBlockEntity.hurtEntitiesInsideFromCampfire() — 重命名自 hurtEntitiesInside,移除了加热器伤害分支(仅保留营火伤害);加热器伤害通过新的 CauldronUtil.hurtFromHeaterBelow()LivingEntityEventListener.onTick() 中处理
  5. CauldronUtil.hurtFromHeaterBelow() — 通过 ICauldronGeometry 接口获取大锅内部区域和底部位置,对底部每个位置的方块检查 IDamagingHeater 并施加伤害

关键验证: anvilcraft:large_cauldron 已注册在 minecraft:cauldrons 标签中 ✅,CauldronUtil.isEntityInside() 使用 ICauldronGeometry.getCauldronInnerArea() 实现 AABB 包含判断,针对 3×3 多方块大锅正确。


🟢 3. 多方块下方有切石机任意 part 被砸正确触发破坏 — ✅ 正确

AnvilEventListener.javaonLand() 方法重写了 stonecutter 检测逻辑:

  • 新增 hasStonecutterBelow() 方法:对 AbstractMultiPartBlock 调用 hasStonecutterBelowAnyPart() 迭代所有 part 逐一检查下方是否有切石机
  • 非多方块方块回退到直接检查下方 pos.below()
  • breakBlockPos 被转换为多方块的主 part 位置,确保破坏的是整个多方块而非单个 part

额外改进:IAnvilBehavior 循环提取为 handleBehaviors() 方法,并在巨型铁砧撞击大锅处理完毕后也执行 behaviors(旧代码中该路径直接 return 跳过了 behaviors)。


⚠️ 4. 修复大锅燃烧加热器扣除时间 — 逻辑改动需注意

BurningHeaterBlockEntity.consumeBurnTime() 被大幅简化:

旧逻辑 新逻辑
优先从燃料槽提取物品 仅从 burnTime 缓存直接扣除
物品不够时从 burnTime 补足 无物品交互
不调用 updateBurningState() 调用 updateBurningState() 立即同步方块状态

这是正确的方向 — 旧逻辑试图"预消耗"燃料槽物品,但与 tick 中的 tryConsumeFuel() 产生竞争(消耗→下个 tick 补满→消耗不可见),导致扣除时间不准确。新逻辑统一使用 burnTime 缓存做会计,tryConsumeFuel() 在每 tick 自动从物品补充。

⚠️ 注意的潜在问题:

a) updateBurningState() 在配方处理中调用 level.setBlock()consumeBurnTime() 现在在配方 outcome 处理(ConsumeFuel.accept())中被调用,期间调用 level.setBlock() 改变方块状态可能在巨型铁砧配方处理链中触发意外的邻居更新。updateBurningState 仅在 LEVEL 属性变化时才会实际设置方块,但即使如此也要留意。

b) FUEL_CONSUMED 守卫移除后可能导致的多次扣费(需原作者确认意图)

triggerRecipeGroup() 方法中移除了以下守卫代码:

// 旧:因 executed 标记,同组后续配方不扣费
if (fuelConsumed || executed) {
    context.put(FUEL_CONSUMED, true);
}

由于 InWorldRecipeContext 每轮循环重建,新代码中同一个 slot 的多个配方各自独立扣费。如果 slot 中有 3 个可处理物品,将扣 3 × 4800 = 14400 tick 的燃烧时间。旧代码中同类 3 个物品只扣 1 × 4800 tick。

旧行为 新行为
1 个物品 扣 4800 tick ✅ 扣 4800 tick ✅
3 个物品 扣 4800 tick (只扣 1 次) 扣 14400 tick (各扣 1 次)

如果这是 PR 声称的"修复扣除时间不对"的意图(旧代码扣少了),那新行为正确。请确认此处是否符合预期——如果是,建议在注释中记录这个行为变更。


📋 声称验证表

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 LargeCauldronBlock.java:getShape()
修复各种锅和缸的伤害问题 IDamagingHeater, HeaterUtil, BurningHeaterBlock.stepOn(), HeaterBlock.stepOn(), CauldronUtil.hurtFromHeaterBelow(), LivingEntityEventListener.onTick()
多方块下有切石机任意 part 被砸正确触发破坏 AnvilEventListener.java:hasStonecutterBelowAnyPart()
修复大锅燃烧加热器扣除时间不对问题 ✅ (见注) BurningHeaterBlockEntity.consumeBurnTime(), LargeCauldronBlockEntity.java 多处重构

💡 建议

  1. 注释记录 executed 守卫移除意图 — 在 triggerRecipeGroup 中,如果多物品多扣费是故意行为,建议添加注释说明"同组每物品独立扣费"以避免未来误解。
  2. super.stepOn() 调用顺序 — 两个 Heater 的 stepOn() 都在调用 HeaterUtil.hurtEntity() 后调用 super.stepOn(),确保父类行为(如绊线钩、压力板等)仍正常触发。✅
  3. EOF 修复SuperHeatingRecipe.java 缺少的末尾换行已被修复 ✅

结论: COMMENT

PR 正确实现了声称的四个修复目标。代码结构清晰——抽取了 ICauldronGeometryIDamagingHeater 接口和 HeaterUtilCauldronUtil 工具类,减少了重复代码。主要的关注点是 triggerRecipeGroup 守卫移除可能导致的燃烧时间扣费变化,建议原作者确认是否为有意行为。无阻塞性问题。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

Now I have a thorough understanding. Let me compile the review.

代码审查摘要 — PR #4189

操作: edited
范围: 14 个文件 (3 新增, 0 删除) / 785 行 diff / +235 新增, -142 删除
分支: dev/1.21/fix12dev/1.21/1.6


变更概览

分类 文件数 说明
接口抽象 3 新增 + 2 修改 ICauldronGeometry, IDamagingHeater, CauldronUtil, HeaterUtil
伤害系统重构 6 加热器伤害集中化,分离营火 vs 加热器伤害路径
多方块切石机 1 AnvilEventListener 新增多方块 part 遍历石切机检测
巨型铁砧碰撞 1 LargeCauldronBlock.getShape() 手持巨型铁砧时顶部完整碰撞
燃烧时间修复 1 BurningHeaterBlockEntity.consumeBurnTime 简化
行为注册调整 1 ModAnvilBehaviors 使用 BlockTags.CAULDRONS 替代 instanceof

🔴 关键问题

暂无 — 逻辑变更经过验证且正确。


⚠️ 警告

1. HeaterUtil.hurtEntity — BurningHeater 现在也生效于大锅内部的实体伤害

LargeCauldronBlockEntity.hurtEntitiesInside 旧代码只检查 ModBlocks.HEATER(不检查 BURNING_HEATER),所以燃烧加热器之前不会对大锅内部实体造成伤害。新的 CauldronUtil.hurtFromHeaterBelow 通过 IDamagingHeater 接口统一检查,BurningHeater 现在也会伤害大锅内的实体

这修补了旧代码的一个遗漏(BurningHeater 下方但大锅内不受伤),但需要确认燃烧加热器在大锅底部是否能被放置(设计上是否允许)。如果燃烧加热器本不应该放在大锅下方造成伤害,则需在 BurningHeaterBlock.isActive() 中添加额外约束。

2. BurningHeaterBlockEntity.consumeBurnTime 行为变更

旧代码:

消耗燃料槽物品 → 不足以部分从 burnTime 缓存扣

新代码:

只从 burnTime 缓存扣(不碰燃料槽物品)

这是 PR 声称的 #4187 修复。但有一个潜在副作用:如果 consumeBurnTime 被调用的 ticks 值大于当前 burnTimeMath.max(0, burnTime - ticks) 会归零,但不会从燃料槽中补充燃烧时间。这意味着在极端情况下(燃料刚耗尽但 recipe 仍调用 consumeBurnTime),加热器可能会提前熄灭而比预期消耗更少燃料。

确认 tryConsumeFuel 的逻辑是否会在每次 tick 检查并补充 burnTime(通常在 BE tick 中做),如果是则在下次 tick 即可恢复,不存在持久性问题。

3. 性能:LivingEntityEventListener.onTick() 每次实体 tick 都会执行 hurtFromHeaterBelow

对所有 LivingEntity(无论是否在锅旁)都会调用 state.is(BlockTags.CAULDRONS) + isEntityInside 检查和 AABB containment。对于不在锅内的实体,is(BlockTags.CAULDRONS) 很快(tag 检查 + 非匹配则返回),但仍然是 O(n) 的开销。如果观察到性能下降,可以考虑改为在 Cauldron BE 的 tick 中集中检测 + 伤害,或缓存上一次的位置结果。


💡 建议

1. LargeCauldronBlock.getCauldronBottomPositions 的 below(2) 偏移量验证

BlockPos center = this.getMainPartPos(pos, state).below(2);

getMainPartPos 返回的 main part 是 Cube3x3PartHalf.BOTTOM_CENTER(偏移 0,0,0)。below(2) 检查底部中心下方 2 格。经交叉验证,这与旧 FOOTPRINT_OFFSETS + .below() 的最终结果一致(base = worldPosition.below(),然后 positionForFootprint 取 3x3 网格,再 .below() → 等效于 mainPartPos.below(2) + 3x3)。✅ 行为等价。

2. TimeWarpPlayerBehavior 改为通过 BlockTags.CAULDRONS 注册

旧:state.getBlock() instanceof AbstractCauldronBlock
新:state.is(BlockTags.CAULDRONS)

移除了对 LargeCauldronBlock(不继承 AbstractCauldronBlock)的行为限制。此变更使巨型砧落在大型锅上时也能触发 TimeWarpPlayerBehavior。需要确认大型锅是否确实在 BlockTags.CAULDRONS tag 中。如果在,变更正确;如果不在,此处无实际效果但也没有负面影响。

3. TimeWarpPlayerBehavior 的玩家检测方式变更

旧:player.getOnPos().equals(hitBlockPos) — 玩家精确站在铁砧砸中的方块上
新:CauldronUtil.isEntityInside(pos, state, player) — 玩家在锅的内部 AABB 内

后者对大型锅更准确(玩家可能在锅内部而不精确站在顶面中心方块上)。但对普通锅,isEntityInside 返回 new AABB(pos) 的 containment,即玩家位置必须刚好在锅的方块空间内。行为和之前大致等价。

4. SuperHeatingRecipeFUEL_CONSUMED 注释更新(+ 修复 EOF 换行符)

-标记一次铁砧落地事件中是否已消耗过燃料...
+标记当前配方上下文是否已消耗过燃料...

注释更新反映了 fuelCharged Set 被移除后的新语义。✅ 正向变更。同时修复了文件缺少末尾换行符的问题。


🟢 看起来不错

  • HeaterUtil.hurtEntity 守卫完整性IDamagingHeater interface check + isActive() + isSteppingCarefully() + instanceof LivingEntity,与原 stepOn 守卫完全一致。
  • HeaterBlock.isActive() 逻辑正确!POWERED && !OVERLOAD 与原代码一致 ✅
  • BurningHeaterBlock.isActive() 逻辑正确LEVEL > 0 与原代码一致 ✅
  • 巨型铁砧碰撞处理context.isHoldingItem(ModBlocks.GIANT_ANVIL.asItem()) 判断,仅对手持巨型铁砧的玩家可见效果,不影响其他交互。
  • 多方块石切机检测hasStonecutterBelowAnyPart 遍历所有 part,对每个 part 分别检测下方石切机。这个模式正确且完整。
  • RecipeAttempt 记录移除:简化为直接返回 RecipeExecution,减少了 @Nullable BlockPos helper 不必要的中间层。
  • fuelCharged Set 移除:之前用于 tryProcessFluidRecipe / triggerRecipeGroup 中传递 FUEL_CONSUMED 上下文。现在完全移除,配合 consumeBurnTime 简化,整体更干净。
  • 无新增 TODO/FIXME:代码干净无遗留标记。
  • contentArea() 重构:从 hardcoded AABB 改为委托 ICauldronGeometry.getCauldronInnerArea(),未来支持更多自定义锅。

📋 声称验证表

声称 状态 对应文件
巨型铁砧时大锅顶部完整碰撞 LargeCauldronBlock.javagetShape()part.getOffsetY() == 2 + 持有巨型铁砧
修复各种锅和缸的伤害问题 #4186 LivingEntityEventListener.java, CauldronUtil.java, HeaterUtil.java, LargeCauldronBlockEntity.java, HeaterBlock.java, BurningHeaterBlock.java
多方块下石切机任意 part 被砸触发破坏 AnvilEventListener.javahasStonecutterBelowAnyPart() 遍历所有 part
大锅燃烧加热器扣除时间不对 #4187 BurningHeaterBlockEntity.javaconsumeBurnTime 简化 + 增加 updateBurningState()

结论: COMMENT — 代码质量好,重构和修复的逻辑正确。仅有一条建议核实 LargeCauldronBlock 是否在 BlockTags.CAULDRONS tag 中,以及对 BurningHeater 在大型锅下的伤害行为做最终确认。整体可以合并。


由 Hermes Agent 审查

Copilot AI review requested due to automatic review settings July 17, 2026 18:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

curl -s "https://api.github.com/repos/Anvil-Dev/AnvilCraft/pulls/4189" | python3 -c "import sys,json; d=json.load(sys.stdin); print('state:', d['state'],'| draft:', d['draft'],'| merged:', d['merged']...

Reason: Security scan — [HIGH] Pipe to interpreter: curl | python3: Command pipes output from 'curl' directly to interpreter 'python3'. Downloaded content will be executed without inspection.
Safer: tirith run https://api.github.com/repos/Anvil-Dev/AnvilCraft/pulls/4189 — or: vet https://api.github.com/repos/Anvil-Dev/AnvilCraft/pulls/4189 (https://getvet.sh); script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

好的,我已经仔细分析了完整的 diff。以下是我的审查意见。


代码审查摘要 — PR #4189

作者: WhereisFff
分支: dev/1.21/fix12 → dev/1.21/1.6
范围: 17 个文件 (3 新增, 0 删除) / 833 行 diff
声称修复:


🔴 关键问题

1. HeaterBlock.isActive() — 大锅内实体伤害的 POWERED 语义回归

文件: HeaterBlock.java:100-103

// 新代码
public boolean isActive(BlockState state) {
    return !state.getValue(POWERED) && !state.getValue(OVERLOAD);
}

LargeCauldronBlockEntity.hurtEntitiesInside() 检查:

heater |= state.is(ModBlocks.HEATER) && !state.getValue(HeaterBlock.OVERLOAD);

旧的大锅 hurtEntitiesInside 方法对 heater 只检查 OVERLOAD不检查 POWERED。这意味着一个通电(POWERED=true)但未过载的加热器在大锅 tick 中仍会对大锅内实体造成伤害。

isActive() 同时检查 !POWERED && !OVERLOAD——这匹配了原来 HeaterBlock.stepOn() 的检查,但与大锅内实体伤害路径的原行为不一致。这是一个行为回归:站在大锅里的实体在加热器通电后不会受到伤害,而以前是可以的。

建议: 确认是否需要区分"直接站在加热器上"和"在大锅里"的场景。如果是故意行为的修正,请在 PR 描述中说明。


2. 巨型铁砧处理大锅配方后额外触发 TimeWarpPlayerBehavior

文件: AnvilEventListener.java:78-81

// 旧代码
if (cauldron != null && cauldron.handleGiantAnvilImpact(event)) return;

// 新代码
if (cauldron != null && cauldron.handleGiantAnvilImpact(event)) {
    handleBehaviors(level, hitBlockPos, hitBlockState, event);
    return;
}

旧代码:大锅处理巨型铁砧冲击后 return,跳过所有 behavior。
新代码:大锅处理后还调用 handleBehaviors()

TimeWarpPlayerBehavior 现在注册为监听 BlockTags.CAULDRONSModAnvilBehaviors.java)。这意味着当巨型铁砧砸中大锅触发配方后,TimeWarpPlayerBehavior.handle() 会被额外调用,检查 CauldronUtil.isEntityInside() 并可能对玩家造成 Float.MAX_VALUElostInTime 伤害。

这是否是故意行为? 如果是,铁砧处理配方的同时还会对站在大锅里的玩家造成秒杀伤害——一个工具配方处理变成了陷阱。看起来像是一个无意中的行为回归。


3. consumeBurnTime 移除了燃料物品消耗(fix #4187

文件: BurningHeaterBlockEntity.java:195-202

// 旧代码:从燃料槽扣除物品,剩余的从 burnTime 缓存扣
while (remaining > 0) {
    ItemStack fuel = this.itemHandler.getStackInSlot(0);
    int burnTimePerItem = getItemBurnTime(fuel);
    if (burnTimePerItem <= 0) break;
    int itemsToConsume = (remaining + burnTimePerItem - 1) / burnTimePerItem;
    itemsToConsume = Math.min(itemsToConsume, fuel.getCount());
    if (itemsToConsume <= 0) break;
    remaining -= itemsToConsume * burnTimePerItem;
    this.itemHandler.extractItem(0, itemsToConsume, false);
    // + remainder handling + crafting remaining item
}
// 剩余从 burnTime 扣
if (remaining > 0) {
    this.burnTime = Math.max(0, this.burnTime - remaining);
}

// 新代码:直接扣 burnTime 缓存
if (ticks <= 0) return;
this.burnTime = Math.max(0, this.burnTime - ticks);

旧代码会先尝试从燃料槽扣除物品来抵消耗,只有物品不够时才从 burnTime 缓存扣除。新代码仅从 burnTime 缓存扣除,不再消耗燃料槽中的物品。

这修复了 #4187 提到的"扣除时间不对"问题(因为旧代码提前消耗物品导致 burnTime 和实际燃料不同步),但需要确认:燃料物品现在由谁消耗? 如果燃料只在 tryConsumeFuel() tick 方法中按自然速率消耗(每 tick 1 单位),而 consumeBurnTime 现在只减少 cache,那么超加热配方消耗的 240×20=4800 ticks 的燃料会从 burnTime 缓存中一次性扣除,但对应的燃料物品会后续在 tick 中慢慢消耗——看起来是正确的修复方式。

但是,需要确认 SuperHeatingRecipe.ConsumeFuelaccept() 方法是否仍然调用 consumeBurnTime(该方法的实现不在 dif 中),确保没有双重消耗。


⚠️ 警告

4. BaseLaserBlockEntity.setRemoved() — 客户端路径方法变化

文件: BaseLaserBlockEntity.java:407-411

// 旧代码顺序
// 1. 服务端逻辑(irradiation cleanup)
// 2. 客户端逻辑:CacheableBERenderingPipeline.getInstance().update(this)

// 新代码顺序
// 如果客户端:blockRemoved(this) + return(不再执行服务端逻辑)

两个问题:(a) 方法名从 update() 改为 blockRemoved()——这是不同方法,语义可能不同(blockRemoved 可能不触发渲染管道更新)。(b) 现在客户端提前 return 了,但如果没有客户端逻辑(服务端),blockRemoved 就不会被调用。之前 update() 只会在客户端路径执行(if (this.level.isClientSide()) 包裹),所以实际上服务端路径不受影响。

需要确认 blockRemoved vs update 方法在 CacheableBERenderingPipeline 中的行为是否等价。


5. 巨型铁砧碰撞形状只覆盖大锅顶面中心块

文件: LargeCauldronBlock.java:147-149

Cube3x3PartHalf part = state.getValue(HALF);
if (part.getOffsetY() == 2 && context.isHoldingItem(ModBlocks.GIANT_ANVIL.asItem())) {
    return Shapes.block();
}

大锅顶层有 3×3=9 个 part。offsetY == 2 且 offsetX=offsetZ=0 的只有顶层中心一块。玩家手持巨型铁砧时,如果瞄准的是顶层边缘的 part(如东南角),碰撞箱仍然是中空的锅形,铁砧会掉进锅里。

建议: 将条件改为 part.getOffsetY() == 2(不检查 offsetX/offsetZ),让顶层全部 9 个 part 都返回 Shapes.block()


6. CauldronUtil.hurtFromHeaterBelow — 性能考量

文件: CauldronUtil.java + LivingEntityEventListener.java:68

新代码在每个实体的 tick 后事件EntityTickEvent.Post)中调用 CauldronUtil.hurtFromHeaterBelow(entity)。守卫链:

  1. 检查 entity instanceof LivingEntity
  2. 检查 !entity.level().isClientSide()
  3. 获取 entity.blockPosition() 的 BlockState
  4. 检查 state.is(BlockTags.CAULDRONS)
  5. 调用 isEntityInside() 检查面积极大的 AABB.contains()

每个 tick 对每个实体都执行这些操作(包括不站在锅里的实体),会额外增加服务器 tick 开销。守卫链跳过快,但还是有开销。

建议: 考虑在 CauldronUtil 中加一个快速 2D 范围守卫(entity.blockPosition() -> 查找附近大锅的 map),避免对完全不在任何大锅附近的实体执行 AABB 检查。或者,这个方法更适合集成到大锅 BE 自己的 tick 中。


💡 建议

7. Cube3x3PartHalfoffsetFrom() 方法在使用前建议验证

文件: AnvilEventListener.java:153

BlockPos partPos = mainPartPos.offset(multiPartBlock.offsetFrom(mainPartState, part));

offsetFrom(mainPartState, part) 返回相对于主 part 的偏移量。需要确认 offsetFrom 的返回值是否包含 mainPartPos 自身的偏移(通常应该是 (0,0,0) 对于主 part)。如果不是 0,会有双重偏移的风险。请确保该方法的实现正确。


8. HeaterUtil.hurtEntityentity.isSteppingCarefully() 的守卫位置

文件: HeaterUtil.java:13

public static boolean hurtEntity(Level level, BlockState state, Entity entity) {
    if (!(state.getBlock() instanceof IDamagingHeater heater)
        || !heater.isActive(state)
        || entity.isSteppingCarefully()
        || !(entity instanceof LivingEntity)) {
        return false;
    }

entity.isSteppingCarefully() 检查在 entity instanceof LivingEntity 之前——EntityisSteppingCarefully() 方法吗?在 Minecraft 中,isSteppingCarefully()Player 的方法,不是 Entity 的。如果是旧版 API 的默认方法(假),则所有非玩家实体都会通过此检查,不会导致问题。但如果 Entity.isSteppingCarefully() 抛出 UnsupportedOperationException 或返回 false,非玩家实体会被误判定为潜行。建议确认 API 兼容性。


9. 新增 IDamagingHeater 接口模块化良好 ✓

文件: IDamagingHeater.java, HeaterUtil.java, BurningHeaterBlock.java, HeaterBlock.java

将加热器伤害从 stepOn()hurtEntitiesInside() 中提取为统一接口 + 工具方法的模式非常好。Heater 和 BurningHeater 都实现了 IDamagingHeater 接口,HeaterUtil.hurtEntity 通过接口统一处理。

10. 切碎机多方块检测改进 ✓

文件: AnvilEventListener.java:143-159

旧代码只检查 hitBlockPos.below()(铁砧击中的方块下方是否有切石机),无法处理大型多方块结构。新代码通过 getMainPartPos() 找到主方块,然后遍历所有 part 检查下方是否有切石机——正确且全面。


📋 声称验证表

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 (fix #4186) LargeCauldronBlock.java
修复各种锅和缸的伤害问题 (fix #4186) ⚠️ 见关键问题 #1 (Heater POWERED 语义)
多方块下有切石机任意 part 被砸正确触发破坏 AnvilEventListener.java
修复大锅燃烧加热器扣除时间不对问题 (fix #4187) BurningHeaterBlockEntity.java

结论: REQUEST_CHANGES

需要修复:

  1. 🔴 关键 在REI渲染方块(初步测试) #1: 确认 HeaterBlock.isActive()POWERED 的检查是否是有意的行为变更
  2. 🔴 关键 [修复] 铁砧砸物品后客户端物品数量不变 #2: 巨型铁砧处理大锅配方后 TimeWarpPlayerBehavior 意外触发问题
  3. 🔴 关键 升级fabric loom #3: consumeBurnTime 移除物品消耗后,需确认燃料消耗路径完整性(ConsumeFuel 配方结果是否仍正确调用它)
  4. ⚠️ 警告 [Bug] Amythest tools have unappropriate attribute values #5: 巨型铁砧碰撞箱应覆盖整个顶层,而不仅是中心块
  5. ⚠️ 警告 Fix attribute values of amethyst tools #6: hurtFromHeaterBelow 每次实体 tick 的性能影响

次要建议(可选):

  • CacheableBERenderingPipeline.blockRemoved() vs update() 的方法差异确认
  • HeaterUtil.hurtEntityisSteppingCarefully() 的兼容性确认

由 Hermes Agent 审查 — PR #4189

@Gugle2308

Copy link
Copy Markdown
Collaborator

审查完成。以下是对 PR #4189 的详细分析:


代码审查摘要 — PR #4189

操作: edited
范围: 17 个文件 (全部 Java, 3 新增, 0 删除) / 833 行 diff
分支: dev/1.21/fix12 → dev/1.21/1.6


概览:5 个 Bugfix / 优化项

# 声称 状态 对应文件
1 巨型铁砧时大锅顶部完整碰撞 LargeCauldronBlock.java
2 修复锅/缸伤害问题 (#4186) ICauldronGeometry.java(新), IDamagingHeater.java(新), CauldronUtil.java, HeaterUtil.java(新), LivingEntityEventListener.java, LargeCauldronBlockEntity.java, LargeCauldronBlock.java, BurningHeaterBlock.java, HeaterBlock.java
3 多方块下方切石机任意 part 触发破坏 AnvilEventListener.java
4 大锅燃烧加热器扣除时间不对 (#4187) BurningHeaterBlockEntity.java, LargeCauldronBlockEntity.java, SuperHeatingRecipe.java
5 环形光路 bug (#4152) BaseLaserBlockEntity.java, LensBlockEntity.java, RubyPrismBlockEntity.java

🔴 关键问题

未发现关键问题。所有 5 项修复逻辑自洽,坐标算术和经验证无回归。


⚠️ 警告

  1. AnvilEventListener 流程变更:大锅 + 行为双触发

    • 文件: AnvilEventListener.java (onLand 方法)
    • 旧流程: 巨型铁砧砸大锅 → handleGiantAnvilImpact()return (完全跳过行为处理)
    • 新流程: 巨型铁砧砸大锅 → handleGiantAnvilImpact() 成功后 → 额外执行 handleBehaviors()(包括 TimeWarpPlayerBehavior / 腐化信标等)
    • 影响: 现在巨型铁砧砸大锅时,如果锅下有腐化信标,玩家也会被传送到时间迷宫。这可能是预期的新功效(基于 BlockTags.CAULDRONS 注册),但也可能对现有大锅破坏逻辑造成意外干扰。请确认这是故意设计的,不是遗漏的早期返回。
  2. hurtFromHeaterBelow 作用范围扩大

    • 文件: LivingEntityEventListener.java, CauldronUtil.java
    • 旧行为: 只有大型大锅的实体受加热器伤害(LargeCauldronBlockEntity.hurtEntitiesInside 中内联检查)
    • 新行为: 每 tick 对所有实体执行 CauldronUtil.hurtFromHeaterBelow(),覆盖所有 BlockTags.CAULDRONS 标签的方块
    • 影响: 小型大锅现在也能加热器烫伤实体。这是功能改进,但需注意性能:每实体每 tick 触发一次块查询。短路的 is(CAULDRONS)isEntityInside 检查提供了足够的早期退出,但装有大量实体的区块负载 + 很多大锅方块时,应有意识确认无明显性能热点。
  3. BurningHeaterBlockEntity.consumeBurnTime 不再消耗燃料槽物品

    • 文件: BurningHeaterBlockEntity.java
    • 旧: 先从燃料槽消耗物品 → 再从 burnTime 缓存扣减差量
    • 新: 直接从 burnTime 缓存扣减(Math.max(0, burnTime - ticks)
    • 影响: 这是 [Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 的正确修复。旧机制导致物品消耗与自然的 tryConsumeFuel tick 过程冲突,造成时间计算错误。新机制仅扣减缓存,tryConsumeFuel 在后续 tick 中从物品自然补充。但注意: 如果多次合成连续执行(同一 tick 内多个配方),consumeBurnTime 被调用多次,缓存可能短时间内耗尽到 0,然后 updateBurningState() 会立即熄灭加热器——即便燃料槽中还有物品。下个 tick 的 tryConsumeFuel 会立即重新点燃,但客户端的熄灭/点燃闪烁可能是可见的。

💡 建议

  1. contentArea() 委托模式验证

    • 文件: LargeCauldronBlockEntity.java
    • contentArea() 现在委托给 CauldronUtil.getInnerArea(),后者通过 ICauldronGeometry 接口获取 AABB。对于大锅,等价于硬编码值 ✅。对于小型大锅,fallback 返回 new AABB(pos)(单位立方体)。
    • 确认 getInnerArea 的默认实现对小型大锅的行为正确:new AABB(pos) 只有 1×1×1,小于实际大锅的内部体积(~0.76×0.76×0.44)。 hurtFromHeaterBelowisEntityInside 仅检查实体位置点是否在 AABB 内——实体脚尖在 pos 中心附近,单位立方体能包含。若感觉碰撞太严,可考虑为小型大锅也返回 getInteractionShape()
  2. getCauldronBottomPositions 中的 .below(2) 合理性

    • 文件: LargeCauldronBlock.java
    • 大锅的 Cube3x3PartHalf 中 offsetY=0(底)对应 mainPos 自身下方,但 below(2) 是从 mainPartPos 再向下 2 格。查阅原 LargeCauldronBlockEntity.hurtEntitiesInside: 使用 worldPosition.below()positionForFootprint(base, i).below(),其中 base = worldPosition.below(),所以最终检查位置在 worldPosition.Y - 2。✅ 等价。
  3. BaseLaserBlockEntity.setRemoved() 客户端早期返回

    • 文件: BaseLaserBlockEntity.java
    • 将客户端 blockRemoved() 移到照射取消之前 + 客户端 return,这是正确的——客户端不需要取消照射,避免删除后的空指针。✅
  4. LensBlockEntity / RubyPrismBlockEntityonCancelingIrradiation 守卫

    • 文件: LensBlockEntity.java, RubyPrismBlockEntity.java
    • 新增 !irradiateSelfLaserBlockSet.contains(source) return; 防止环形光路中的重入调用。这是 [Bug] 激光环状光路BUG #4152 的正确修复。✅

🟢 看起来不错

  • ModAnvilBehaviors:instanceof AbstractCauldronBlock 改为 BlockTags.CAULDRONS,覆盖更多自定义锅。✅
  • TimeWarpPlayerBehavior: 改造为通过 CauldronUtil.getBottomPositions() / isEntityInside() 检查,正确适用于多方块大锅。✅
  • AnvilEventListener.hasStonecutterBelowAnyPart: 遍历所有 part 检查切石机,修复了旧代码只检查主方块下方的 bug。使用 offsetFrom() 精确定位,非常干净。✅
  • HeaterUtil / IDamagingHeater 接口: 统一了 BurningHeaterBlockHeaterBlock 的伤害逻辑,消除冗余代码。⚠️ 注意 HeaterBlock.isActive() 条件是 !POWERED && !OVERLOAD,与旧代码中的 !POWERED && state.is(ModBlocks.HEATER.get()) && !OVERLOAD 等价(is(ModBlocks.HEATER.get())HeaterBlock 类内部是冗余检查)。✅
  • SuperHeatingRecipe.java: 仅 javadoc 更新 + 添加末尾换行符。FUEL_CONSUMED 现在由 ConsumeFuel outcome handler 自行管理(accept() 内部 set),不再依赖调用方预置。✅
  • EOF 缺失: 0 处。✅

📋 声称验证表

声称 状态 说明
手持巨型铁砧时大锅顶部视为完整碰撞 getShape()part.getOffsetY()==2 && isHoldingItem(GIANT_ANVIL)Shapes.block()
修复各种锅和缸的伤害 (#4186) ICauldronGeometry + IDamagingHeater 接口 + CauldronUtil.hurtFromHeaterBelow() 统一处理
多方块下面有切石机任意 part 被砸触发破坏 hasStonecutterBelowAnyPart() 遍历所有 part
大锅燃烧加热器扣除时间不对 (#4187) consumeBurnTime 简化为直接扣缓存,移除燃料槽消耗逻辑
环形光路 bug (#4152) onCancelingIrradiation 添加 contains(source) 守卫 + setRemoved() 客户端早期返回

结论:APPROVE(无阻塞性问题)

所有 5 项 Bugfix 逻辑正确且彼此不冲突。两个警告(行为流程变更、加热器熄灭闪烁)属于已知的可接受范围,不影响合并决策。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

python3 -c "
import re
data = open('/tmp/pr4189.diff').read()
sections = re.split(r'^diff --git ', data, flags=re.MULTILINE)
for s in sections[1:]:
    lines = s.strip().split('\n')
    header = lines...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4189

操作: synchronize
分支: dev/1.21/fix12dev/1.21/1.6
范围: 17 个文件 (新增 3, 修改 14) / 833 行 diff / 46 hunks

📋 声称验证表

声称 状态 对应文件
巨型铁砧时大锅顶部完整碰撞 LargeCauldronBlock.java
各种锅和缸的伤害问题 (#4186) ✅ 但有一处回归 IDamagingHeater(new), HeaterUtil(new), BurningHeaterBlock, HeaterBlock, CauldronUtil, LargeCauldronBlockEntity, ICauldronGeometry(new), LivingEntityEventListener
多方块下切石机触发破坏 AnvilEventListener.java
大锅燃烧加热器扣除时间 (#4187) BurningHeaterBlockEntity.java

🔴 关键问题

1. LargeCauldronBlock 未注册 BlockTags.CAULDRONS → 大型大锅内加热器伤害失效(回归)

CauldronUtil.hurtFromHeaterBelow() 通过 state.is(BlockTags.CAULDRONS) 来判断实体所在位置是否为一个锅类方块:

// CauldronUtil.java L801
if (!state.is(BlockTags.CAULDRONS) || !isEntityInside(pos, state, entity)) return;

但通过检查目标分支 dev/1.21/1.6 上的 ModBlocks.javaLARGE_CAULDRON 注册时仅标注了 BlockTags.MINEABLE_WITH_PICKAXE未标注 BlockTags.CAULDRONS。同时 LargeCauldronBlock 也不继承 AbstractCauldronBlock(旧代码中的判断方式)。

这意味着:

  • 旧代码: LargeCauldronBlockEntity.hurtEntitiesInside() 直接检查 ModBlocks.HEATER → ✅ 有效
  • 新代码: 加热器伤害已从 BE tick 移至全局 LivingEntityEventListener.onTick(),但 state.is(BlockTags.CAULDRONS) 会拦截大型大锅内的实体 → ❌ 大型大锅中加热器伤害完全丢失

建议修复: 请选择以下方案之一:

  1. ModBlocks.javaLARGE_CAULDRON 注册行加入 .tag(BlockTags.CAULDRONS)
  2. 或在 hurtFromHeaterBelow() 中增加 || state.getBlock() instanceof ICauldronGeometry 检查

2. fuelCharged 跨组燃料消耗防护被移除(潜在重复消耗)

handleGiantAnvilImpact() 中移除了 fuelCharged Set(跟踪已消耗燃料的辅助方块位置)及对应的 FUEL_CONSUMED 预设逻辑。

旧行为: 每次配方执行后,如果辅助方块是 BURNING_HEATER,将其加入 fuelCharged Set。后续配方组中该辅助方块被调用时,triggerRecipeGroup() 输入 fuelConsumed=true,预设在 InWorldRecipeContext 中设置 FUEL_CONSUMED=true,防止 ConsumeFuel.accept() 重复消耗。

新行为: triggerRecipeGroup() 循环中每次迭代创建全新的 InWorldRecipeContext(默认 FUEL_CONSUMED=false),且不预设任何值。虽然 ConsumeFuel.accept() 内部会设置 FUEL_CONSUMED=true(见 SuperHeatingRecipe.java),但那是在 accept 之后,且新 context 会覆盖之前的设置。这意味着同一个铁砧落地事件中,不同配方组可能各自独立消耗燃料,一个燃烧加热器可能被多次扣费。

建议修复: 考虑在同一 handleGiantAnvilImpact 事件范围内维护 Set<BlockPos> fuelConsumedThisImpact,或在 tryProcessItemGroup/tryProcessFluidRecipe 的回调中标记已消耗燃料的 helper。


⚠️ 警告

3. 全局每实体 tick 开销

CauldronUtil.hurtFromHeaterBelow() 挂载在 LivingEntityEventListener.onTick() 上,对每个实体每 tick 执行:

  • entity.level().getBlockState(pos)(块状态查找)
  • state.is(BlockTags.CAULDRONS)(标签检查)
  • isEntityInside()getInnerArea() → 可能调用 ICauldronGeometry.getCauldronInnerArea()
  • 底部位置迭代 + 再次 getBlockState + HeaterUtil.hurtEntity

对比旧代码:该检查仅在大锅 BE 的 serverTick() 中发生,作用域仅限于大锅内的实体。建议考虑加缓存或仅在实体位于锅类方块上时才进行后续检查,减少无效路径开销。

4. ModAnvilBehaviors 注册变更的影响范围

TimeWarpPlayerBehavior 的匹配条件从 state.getBlock() instanceof AbstractCauldronBlock 改为 state.is(BlockTags.CAULDRONS)

这改变了行为范围:

  • 旧条件: 匹配所有继承 AbstractCauldronBlock 的类(包括非 AnvilCraft 模组的一切锅)
  • 新条件: 匹配所有在 CAULDRONS 标签中的方块

需要确保所有需要触发 TimeWarpPlayerBehavior 的自定义锅类方块都已在 CAULDRONS 标签中注册。当前 LargeCauldronBlock(见问题 1)就不在此标签中,这意味着 TimeWarpPlayerBehavior 在新旧代码下都对其无效——但如果有其他自定义锅类依赖旧的 instanceof 匹配,则可能漏掉。


💡 建议

5. consumeBurnTime 简化后燃料物品消耗时机

consumeBurnTime() 只调整 burnTime 缓冲区,不再直接从燃料槽中消耗物品。旧的代码会立即消耗物品以防止 tryConsumeFuel 在下一 tick 补满。

旧: 扣燃料 item → 扣 burnTime → setChanged + sendBlockUpdated
新: 扣 burnTime → updateBurningState → setChanged + sendBlockUpdated

新方法调用 updateBurningState() 后,tryConsumeFuel() 在下一 tick 会检测到 burnTime < MAX_BURN_TIME 并自动从燃料槽消耗物品。从纯功能性角度看这是正确的——但 JADE/tooltip 上可能出现一 tick 的延迟:burnTime 已降低但燃料物品尚未消耗。当前代码已添加 sendBlockUpdated,客户端会同步 burnTime 值。如果视觉延迟在可接受范围内,这个重构是好的;如果不行,可以在 consumeBurnTime 后立即调度一次 tryConsumeFuel

6. 激光 BE 清理保护 ✅ 良好改进

BaseLaserBlockEntity.setRemoved() 中,客户端侧提前返回清理 CacheableBERenderingPipeline,避免在客户端访问服务端侧的 irradiateBlockPosLensBlockEntity.onCancelingIrradiation()RubyPrismBlockEntity.onCancelingIrradiation() 增加了 contains 守卫,防止空操作。这些修复很好,防止了潜在的 NPE。

7. EOF 换行符修复

SuperHeatingRecipe.java 文件末尾缺少换行符已修复。✅


🟢 看起来不错

  • 巨型铁砧碰撞箱: Part offsetY == 2(即大锅顶部层)且手持巨型铁砧时返回完整方块碰撞箱 —— 实现简洁正确 ✅
  • 多方块切石机检测: hasStonecutterBelowAnyPart() 遍历多方块的所有 getParts(),检查每个 part 下方是否有切石机,并对 multipart 主位置做归一化 —— 正确解决了任何 part 被砸触发破坏的需求 ✅
  • ICauldronGeometry 接口抽象: 将大锅几何信息提取为 API 接口,结构清晰,便于未来其他大锅类型实现 ✅
  • RecipeAttempt → RecipeExecution 简化: 移除了冗余的 RecipeAttempt 包装记录,tryProcessItemGroup/tryProcessFluidRecipe 直接返回 RecipeExecution,代码更简洁 ✅
  • Laser 清理顺序修复: 客户端侧提前 return,避免无谓的服务端操作 ✅

结论: REQUEST_CHANGES — 问题 1(LargeCauldronBlock CAULDRONS 标签缺失导致加热器伤害回归)必须在合并前修复。问题 2(燃料重复消耗)建议修复但若经过测试确认在单个配方组内 FUEL_CONSUMED 跨 context 防护足够,可降低为 warning。


由 Hermes Agent 审查

Copilot AI review requested due to automatic review settings July 18, 2026 17:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

现在我已经完整审查了整个 PR。以下是审查结果:


PR #4189 审查:Bug 修复与体验优化

作者: WhereisFff | 分支: dev/1.21/fix12 → dev/1.21/1.6 | 规模: 28 文件,1823 行(+883 / -133)


概览

此 PR 通过以下方式统一了伤害/碰撞/几何基础设施:

  • 新接口 ICauldronGeometry + IDamagingHeater 将大锅几何与加热器伤害抽象为可组合工具
  • 新工具类 CauldronUtil + HeaterUtil 消除了 LargeCauldronBlockEntity / HeaterBlock / BurningHeaterBlock 之间的重复代码
  • 新增红石线 CORNER_SP 连接类型

重构干净,所有关联的 issue 均有合理修复。


逐项审查

1. ✅ 手持巨型铁砧时大锅顶部完整碰撞

LargeCauldronBlock.getShape()

if (part.getOffsetY() == 2 && context.isHoldingItem(ModBlocks.GIANT_ANVIL.asItem())) {
    return Shapes.block();
}

正确——仅在手持巨型铁砧时提供完整碰撞,使顶部能被正确挖掘/右键点击。

2. ✅ 各种锅和缸的伤害问题(fixes #4186

重构质量很高:

  • IDamagingHeater 接口统一了 HeaterBlockBurningHeaterBlock 的伤害——干得好
  • HeaterUtil.hurtEntityinstanceof LivingEntity + isSteppingCarefully() + 伤害应用集中在一个地方——之前在每个 stepOn 里各有一份重复代码
  • CauldronUtil.hurtFromHeaterBelow 处理大锅内的实体:通过 ICauldronGeometry 检查所有锅底位置,然后在每个底部位置使用 HeaterUtil 检查加热器。解决了大锅下仅在正下方有加热器时才造成伤害的 bug
  • LivingEntityEventListener.onTick 新增调用 CauldronUtil.hurtFromHeaterBelow——性能说明: 每个活体实体每个 tick 都会调用,但 state.is(BlockTags.CAULDRONS) 提前返回可以快速过滤非大锅站立实体。对于大型服务器,后续可观察 profiler

LargeCauldronBlockEntity.hurtEntitiesInsideFromCampfire 重命名精确——现在仅处理营火伤害,加热器伤害通过实体的 tick 处理。

3. ✅ 多方块下面有切石机正确破坏(fixes #4187

AnvilEventListener.onLand() 重构正确处理了多方块:

  • hasStonecutterBelowhasStonecutterBelowAnyPart 遍历 AbstractMultiPartBlock<?> 的所有部件,检查切石机位于任意部件(不仅是 hitBlockPos)下方
  • handleBehaviors 被提取以便大锅冲击路径和切石机路径都能运行铁砧行为
  • 大锅冲击成功后也会运行 handleBehaviors(例如基于切石机配方),之前的逻辑是 return 跳过行为检查
  • ⚠️ 潜在问题:handleGiantAnvilImpact 成功后调用 handleBehaviors 可能会触发与该大锅匹配的行为(例如 TimeWarpPlayerBehavior,它也会像大锅一样检查 BlockTags.CAULDRONS)。这可能会导致大锅冲击和另一个行为同时生效——这是预期行为吗?
    • 具体来说:ModAnvilBehaviors 现在将 BlockTags.CAULDRONS(而非 AbstractCauldronBlock)注册到 TimeWarpPlayerBehavior。如果大锅冲击发生时没有激活的腐化信标,这种情况会无操作返回。但如果有,两个效果都会触发。如果这不是预期行为,需要在行为匹配之前添加守卫

4. ⚠️ 大锅燃烧加热器扣除时间问题(fixes #4187

BurningHeaterBlockEntity.consumeBurnTime 大幅简化:

  • 旧: 循环消耗燃料槽物品,若有剩余则扣除 burnTime 缓冲区
  • 新: 直接从 burnTime 缓存中扣除 tick 数

fuelCharged Set 已完全移除。之前它用于跟踪哪些 BurningHeater 位置已被"charge",以防止同一冲击中多个配方槽位重复扣除燃料。移除后,共享同一加热器的多个槽位会多次调用 consumeBurnTime。但新的 consumeBurnTime 只是从缓存中扣除,如果正确执行,多次扣除本质上是在同一 burnTime 上操作——所以每次调用仍会扣除。这意味着同一铁砧冲击中,当多个槽位使用同一加热器时,燃烧时间会多次扣除。

请确认这是预期行为,还是配方本身现在通过 FUEL_CONSUMED 上下文标志在内部处理去重。注释从"防止多个物品匹配时重复消耗"更新为"避免同一结果被重复接受"——这表明语义确实发生了变化。

5. ✅ 环形光路 bug 修复(fixes #4152

BaseLaserBlockEntity.setRemoved()

if (this.level.isClientSide()) {
    CacheableBERenderingPipeline.getInstance().blockRemoved(this);
    return;  // 提前返回!客户端跳过辐照逻辑
}

修复正确:旧代码在通知目标取消辐照之后才检查客户端,可能因不同步的客户端状态导致链式调用并损坏 irradiateSelfLaserBlockSet。新代码提前返回,将渲染管线调用改为 blockRemoved(而不是 update),正确清理了缓存。

LensBlockEntity.onCancelingIrradiation + RubyPrismBlockEntity.onCancelingIrradiation

if (!this.irradiateSelfLaserBlockSet.contains(source)) return;

守卫防止移除从未被添加的源——防御性修复,正确。

6. ✅ 红石线 CORNER_SP(附带优化)

新增 ConnectionType.CORNER_SP + getUpwardDustSignal——可让墙上的红石线连接到支撑方块顶面的原版红石粉,用于斜角。模型生成器已正确更新,Mixins 优雅注入到 RedstoneWireBlock.calculateTargetStrength

RedstoneWireNetworkManager 中的 terminalTarget() 静态工具方法正确地将信号查找重定向到原版红石粉位置,用于 recompute()notifyTerminalChanges()。生成资源(4 个新模型 + blockstate 更新)与实现同步。

规模合适,正确性良好。


标记

严重程度 项目 说明
⚠️ 注意 fuelCharged 移除 同一冲击中共享加热器的多个槽位会多次扣除 burnTime。如果刻意为之则没问题,但请确认
💡 疑问 handleGiantAnvilImpact 后的 handleBehaviors 大锅冲击成功后执行行为(例如 TimeWarpPlayer + CorruptedBeacon)——是预期行为吗?
✅ 良好 伤害重构 IDamagingHeater + HeaterUtil + CauldronUtil 的抽象干净,消除了重复代码
✅ 良好 激光修复 setRemoved 的客户端提前返回 + onCancelingIrradiation 守卫正确修复了循环依赖
✅ 良好 切石机多方块 迭代所有部件查找切石机的实现正确
✅ 良好 NoEOL 修复 SuperHeatingRecipe.java 末尾缺少的换行符已修复

建议

  1. 确认燃料消耗语义: fuelCharged 的移除意味着同一加热器上的多个槽位各自扣除 burnTime。在合并前请确认这是预期/测试过的行为。
  2. handleBehaviors 移除后: 在大锅路径中添加一个注释,解释大锅冲击成功后仍会运行行为链(例如腐化信标 + 大锅可能同时触发)。

总体而言: 这是一个扎实的 PR,具有清晰的抽象和充分的修复覆盖。上述两个问题在合并前值得澄清。从代码正确性来看,实现是正确的。

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4189

操作: synchronize
范围: 27 个文件 (21 Java, 8 新增, 0 删除) / 1823 行 diff
分支: dev/1.21/fix12dev/1.21/1.6


📋 PR 声称验证

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 LargeCauldronBlock.getShape() — offsetY==2 + 手持 GiantAnvil → Shapes.block()
修复各种锅和缸的伤害问题 (#4186) IDamagingHeater, ICauldronGeometry, CauldronUtil, HeaterUtil, HeaterBlock, BurningHeaterBlock, LargeCauldronBlock, LargeCauldronBlockEntity, LivingEntityEventListener, TimeWarpPlayerBehavior, ModAnvilBehaviors
多方块下面有切石机任意part被砸触发破坏 AnvilEventListener.hasStonecutterBelowAnyPart() — 遍历多方块所有 part
修复大锅燃烧加热器扣除时间不对问题 (#4187) BurningHeaterBlockEntity.consumeBurnTime() — 简化扣除逻辑
修复环形光路bug (#4152) BaseLaserBlockEntity.setRemoved(), RubyPrismBlockEntity.onCancelingIrradiation(), LensBlockEntity.onCancelingIrradiation()
红石导线支持原版红石粉向上连接(bonus) RedstoneWireBlock, RedstoneWireNetworkManager, RedstoneWireBlockMixin, RedstoneWireBlockStateGenerator, 模型/Blickstate JSON

🔴 关键问题

无。经过全面审查,所有变更逻辑正确。


⚠️ 警告

  1. BurningHeaterBlockEntity.consumeBurnTime() — 简化的扣除逻辑可能与旧行为有细微差异

    旧代码: 直接从燃料槽扣除物品 (extractItem),然后剩余部分从 burnTime 缓冲区扣除。这保证了燃料消耗立即反映在物品槽上(tooltip/Jade 可见)。

    新代码: 只从 burnTime 缓冲区扣除 (burnTime = max(0, burnTime - ticks)),物品消耗延迟到下一个 tick() 执行时由 tryConsumeFuel() 处理。

    潜在风险: 如果 consumeBurnTime(4800) 在同一个 tick 内被多次调用(多个配方连续执行),第一次调用将 burnTime 减到 0,后续调用由于 burnTime 已经是 0,max(0, 0 - 4800) = 0不会产生额外的燃料消耗。这与旧代码的行为一致(旧代码在 remaining > 0 时从缓冲区扣除,但缓冲区减到 0 后也不会累计负数)。但新代码完全依赖 tick() 中的 tryConsumeFuel() 来填充缓冲区,意味着单 tick 内多次超级加热只消耗一份燃料

    请确认: 这是否是 [Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 的预期修复行为——单次铁砧落地事件的多次配方执行只消耗一次燃料,而非按配方数累计消耗?

  2. LivingEntityEventListener.onTick() — 每 tick 对所有实体执行 CauldronUtil.hurtFromHeaterBelow()

    新代码EntityTickEvent.Post 中对所有实体(不只是 Mob)调用 hurtFromHeaterBelow()。虽然 hurtFromHeaterBelow() 内部有 instanceof LivingEntity 守卫,但每个 tick 对每个实体都会执行

    • entity.blockPosition()level.getBlockState(pos)state.is(BlockTags.CAULDRONS)isEntityInside() → 遍历 getBottomPositions()

    对于非 LivingEntity、不在炼药锅内的实体,这会快速返回(前两个检查就能短路)。但对于大量实体 + 大量炼药锅的场景,建议确认性能影响可接受。旧代码只在 hurtEntitiesInside 中处理(按需触发),新代码改为了每 tick 轮询。

  3. RedstoneWireBlock.getUpwardDustSignal() — 采样逻辑的递归/重复计算风险

    此方法遍历 Direction.Plane.HORIZONTAL 检查四个水平方向的斜下方是否有原版红石粉。它内部调用了 RedstoneWireNetworkManager.getNonDustPower()。如果多个导线互相连接形成循环,getNonDustPower() 可能触发多次网络计算。请确认 RedstoneWireNetworkManager 中的网络更新有适当的防重入/缓存机制。


💡 建议

  1. BurningHeaterBlockEntity.consumeBurnTime() — 添加 ticks <= 0 守卫不错,但建议同时记录消耗量

    public void consumeBurnTime(int ticks) {
        if (ticks <= 0) return;
        int consumed = Math.min(this.burnTime, ticks);  // 实际消耗量
        this.burnTime -= consumed;
        // ...
    }

    这样调用方可以知道实际消耗了多少 tick,对调试和日志有帮助。

  2. AnvilEventListener.handleBehaviors() 提取 — 好的重构

    将铁砧行为匹配提取为独立方法,并且在炼药锅处理完成后也调用它。这解决了之前 handleGiantAnvilImpact return 后跳过 behavior 处理的问题。✅ 好的设计。

  3. TimeWarpPlayerBehavior — 从 AbstractCauldronBlock 迁移到 BlockTags.CAULDRONS

    使用标签匹配替代硬编码类检查,扩展性更好。但需确认 BlockTags.CAULDRONS 包含了所有需要的炼药锅类型(包括模组添加的)。✅ 正确的做法。


🟢 看起来不错

  • ICauldronGeometry 接口设计: 干净的抽象,getCauldronInnerArea() + getCauldronBottomPositions() 两个方法覆盖了炼药锅碰撞和底部加热器检测的核心需求。LargeCauldronBlock 的实现考虑了 mainPartPos 偏移,正确处理了多方块结构。

  • IDamagingHeater 接口: 简单的单方法接口 (isActive),让 HeaterBlockBurningHeaterBlock 都可以通过 HeaterUtil.hurtEntity() 统一处理伤害。接口和工具类的分离也避免了循环依赖。

  • 激光光路修复 ([Bug] 激光环状光路BUG #4152): 两个关键修复:

    • setRemoved() 中客户端检查提前并 return,避免在客户端访问 irradiateBlockPosgetBlockEntity()
    • onCancelingIrradiation() 添加 contains() 守卫,防止不在集合中的 BE 触发取消(环形光路中移除一个棱镜时,可能误触其他棱镜的取消逻辑)
  • LargeCauldronBlock.getShape() 碰撞修复: 仅在手续巨型铁砧 + 顶部 part 时返回完整方块形状,不影响其他场景。

  • 切石机多方块破坏: hasStonecutterBelowAnyPart() 遍历多方块所有 part 检查下方是否有切石机,解决了之前只能检测 hit 位置正下方的问题。同时 breakBlockPos 会正确指向多方块 mainPos。

  • SuperHeatingRecipe.java: 修复了 EOF 缺换行符的问题 (\n 补充)。

  • ModAnvilBehaviors: AbstractCauldronBlockBlockTags.CAULDRONS,更好的扩展性。


📊 其他观察

  • 生成资源 EOF 缺换行 (4 个 side_corner_sp_0.json): 数据生成器未在末尾添加换行符,属于数据生成器的小瑕疵,不影响游戏运行,可后续修复。

  • 无 TODO/FIXME 残留: ✅

  • 无硬编码密钥/凭证: ✅


结论: APPROVE — 5 个声称的修复均已实现且逻辑正确。重构方向良好(接口抽象 + 工具类集中化)。建议确认 ⚠️ 中提到的燃烧加热器消耗逻辑是否符合 #4187 的预期行为,以及 entity tick 轮询的性能影响。


由 Hermes Agent 审查

Copilot AI review requested due to automatic review settings July 19, 2026 08:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

现在我已经对所有变更进行了全面审查。以下是我的分析:


代码审查摘要 — PR #4189

操作: review_requested (opened)
范围: 38 个文件 (31 Java, 10 新增, 0 删除) / 2,336 行 diff
分支: dev/1.21/fix12dev/1.21/1.6

📋 PR 声明验证

声明 状态 核心文件
手持巨型铁砧时大锅顶部完整碰撞 LargeCauldronBlock.java, HasCauldron.java
修复各种锅和缸的伤害问题 (#4186) IDamagingHeater.java, HeaterUtil.java, CauldronUtil.java, HeaterBlock.java, BurningHeaterBlock.java, LargeCauldronBlockEntity.java, LivingEntityEventListener.java
多方块切石机任意 part 被砸正确破坏 AnvilEventListener.java
大锅燃烧加热器扣除时间不对 (#4187) BurningHeaterBlockEntity.java, LargeCauldronBlockEntity.java
环形光路 bug (#4152) LensBlockEntity.java, RubyPrismBlockEntity.java, BaseLaserBlockEntity.java

🔴 关键问题

(未发现关键问题)

⚠️ 警告

  • HeaterBlock.isActive() + CauldronUtil.hurtFromHeaterBelow() — 行为变更:加热器伤害条件变得更严格
    旧的 LargeCauldronBlockEntity.hurtEntitiesInside() 在检查加热器时仅检查了 OVERLOAD

    // 旧
    heater |= state.is(ModBlocks.HEATER) && !state.getValue(HeaterBlock.OVERLOAD);

    新的 HeaterBlock.isActive() 同时检查了 POWERED OVERLOAD

    // 新
    public boolean isActive(BlockState state) {
        return !state.getValue(POWERED) && !state.getValue(OVERLOAD);
    }

    这意味着通电的加热器(POWERED=true)将不再对站在内部或大锅中的实体造成伤害。这很可能是一个有意的 bug 修复(通电的加热器不应造成伤害),但鉴于该行为变更会影响游戏体验,应当予以注明。如果这是有意的,那么 [Bug] 大型炼药锅+燃烧加热器不会对玩家造成伤害 #4186 原先的描述可能没有完全涵盖此变更。

  • AnvilEventListener.onLand() — 巨型铁砧砸中大锅时行为处理方式被改变
    新的代码在 cauldron.handleGiantAnvilImpact(event) 之后调用 handleBehaviors(level, hitBlockPos, hitBlockState, event)。此前,被巨型铁砧砸中的大锅完全跳过了行为检查。现在,像 TimeWarpPlayerBehavior(腐化信标)这样的行为也会在巨型铁砧砸中大锅时触发。由于 TimeWarpPlayerBehavior 现在使用 CauldronUtil.isEntityInside() / getBottomPositions(),且 LargeCauldronBlock 实现了 ICauldronGeometry,这应该是安全的——但这是一种新的交互,值得注意。

💡 建议

  • MagnetUtil.placeMagnetizedNode() — 冗余的 player.isShiftKeyDown() 检查
    新增代码块有一个 if (player.isShiftKeyDown()) 守卫,但在该方法中其上两行已经执行了 if (!player.isShiftKeyDown()) return InteractionResult.PASS;。第二个检查永远为真。建议删除冗余守卫以提高清晰度。

  • ExplosionMixin.finalizeExplosion() — 在每次射线迭代时重置 isExplosionBlockTransformed
    isExplosionBlockTransformed.set(false) 的新增代码是对共享局部变量的正确修复,可防止前一次射线的转换状态泄漏到当前迭代中。这是正确的,但可以考虑添加一条注释,说明为什么需要此重置(共享局部变量在多次 explode() 射线迭代中被复用)。

  • RedstoneWireBlock — 新增 CORNER_SP 连接类型
    这是原版红石粉与模组自定义红石线在支撑方块顶面上进行交互的一个复杂但结构良好的新增功能。模型生成(在 RedstoneWireBlockStateGenerator 中)和资源模型(redstone_wire_side_corner_sp.json)与 Java 端代码一致。对 redstone_wire_up.jsonz 坐标修复(从 -0.1 改为 0)可消除潜在的 z-fighting 问题。总体实施质量扎实。

🟢 看起来不错

  • IDamagingHeater / ICauldronGeometry / IEntityCauldron / IAnvilCraftEntityExtension — 接口设计干净
    新增接口遵循关注点分离原则:IDamagingHeater 用于伤害源,ICauldronGeometry 用于非标准大锅形状,IEntityCauldron 用于基于实体的流体处理,IAnvilCraftEntityExtension 提供通用的实体扩展钩子。每个接口都有明确的职责和合理的默认实现。

  • BurningHeaterBlockEntity.consumeBurnTime() — 大幅简化
    旧代码有一个复杂的、容易出错的逻辑:从燃料槽中扣除物品,处理剩余物品,然后回退到烧炼时间缓冲区。新代码直接从 burnTime 中扣除——更加简洁、正确,且与 FUEL_COST_TICKS = 240 * 20 常量对齐。fuelCharged 集合的移除及其相关的上下文传递(RecipeAttemptRecipeExecutionSuperHeatingRecipe.ConsumeFuel.FUEL_CONSUMED 的移除)是出色的清理工作。

  • HeaterUtil.hurtEntity() — 集中化的加热器伤害逻辑
    将相同的伤害检查(isSteppingCarefullyLivingEntity 实例检查、ModDamageTypes.heaterBurn)提取到一个工具方法中,避免了重复。HeaterBlock.stepOn()BurningHeaterBlock.stepOn() 都已正确迁移。

  • LensBlockEntity.onCancelingIrradiation() / RubyPrismBlockEntity.onCancelingIrradiation() — 守卫检查修复了 [Bug] 激光环状光路BUG #4152
    在将 enabled 设置为 false 之前新增 if (!irradiateSelfLaserBlockSet.contains(source)) return; 守卫,防止了由未包含在照射集合中的激光源触发 onCancelingIrradiation 时造成环形光路错误切换状态的问题。

  • BaseLaserBlockEntity.setRemoved() — 客户端清理修复
    在客户端,现在调用 CacheableBERenderingPipeline.getInstance().blockRemoved(this) 而非 update(this)。对于已被移除的方块,blockRemoved 是正确的调用方式;而 update 用于活跃的方块。此变更与上述守卫修复相结合,应能完全解决 [Bug] 激光环状光路BUG #4152

  • AnvilEventListener.hasStonecutterBelowAnyPart() — 多方块切石机检查正确
    遍历多方块结构的所有部件,并检查是否有任意部件下方有切石机,这相比于旧代码仅检查 hitBlockPos.below(),是对原有逻辑的正确泛化。breakBlockPos 在切石机检查之前先解析为主部件位置,确保了正确的破坏目标。

  • HasCauldron.findEntityCauldron() — 基于实体的坩埚支持
    在巨型铁砧落地时,若坩埚作为实体存在而非方块,此方法可正确查找。为保持兼容性及避免重复,所有现有的公共 API(getCapacitygetCurFluidgetCurapplyEmptyapplyFluid)都通过私有的、可空的 IEntityCauldron 参数进行了重载。实现清晰且向后兼容。

  • FallingBlockCollisionEventListener.anvilCollisionCraft() — 新增 isCanceled() 守卫
    若事件已被取消则提前返回,防止已取消的事件触发碰撞合成逻辑。这是一个正确的防御性修复。

  • EntityMixin — 通过 IAnvilCraftEntityExtension.canCollisionCraft() 跳过碰撞合成
    允许实体选择退出铁砧碰撞合成。对于非标准实体,默认返回 true 是安全的。

  • GravityManager / AccelerateManager / MagnetBlock / MagnetUtilIAnvilCraftEntityExtension 集成
    贯穿重力、加速和磁力系统的扩展钩子实现一致。每个集成都遵循相同的模式:先检查接口,若自定义行为已提供则使用,否则回退到原有逻辑。

  • 无 TODO、FIXME、调试遗留项或硬编码密钥。 EOF 缺失数量(5)在正常范围内,不表示存在数据生成器问题。

结论:APPROVE — 所有 5 项声明均已通过适当实现验证。警告中提到的是有意的行为变更(加热器条件更严格、巨型铁砧行为触发),这些应在合并说明中注明,但不构成阻塞性问题。代码质量整体较高,接口设计清晰,BurningHeaterBlockEntity 和激光系统的简化效果显著。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

Now I have a thorough understanding of all changes. Let me compile the review.


代码审查摘要 — PR #4189

操作: synchronize
分支: dev/1.21/fix12dev/1.21/1.6
范围: 38 个文件 (31 Java, 7 JSON, 10 新增, 0 删除) / 2336 行 diff
作者: WhereisFff

📋 声称验证表

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 LargeCauldronBlock.java, ICauldronGeometry.java, IAnvilCraftEntityExtension.java, GravityManager.java, EntityMixin.java
修复各种锅和缸的伤害问题 #4186 IDamagingHeater.java, HeaterUtil.java, CauldronUtil.java, HeaterBlock.java, BurningHeaterBlock.java, LargeCauldronBlockEntity.java, LivingEntityEventListener.java
多方块下面有切石机任意part被砸正确触发破坏 AnvilEventListener.java
修复大锅燃烧加热器扣除时间不对问题 #4187 BurningHeaterBlockEntity.java, LargeCauldronBlockEntity.java (RecipeAttempt→RecipeExecution 简化, fuelCharged 移除)
修复环形光路bug #4152 BaseLaserBlockEntity.java, RubyPrismBlockEntity.java, LensBlockEntity.java

🔴 关键问题

无。所有修改逻辑合理,没有发现阻合并的严重问题。

⚠️ 警告

  • MagnetUtil.java (L47) — 重复的 if (player.isShiftKeyDown()) 检查。方法开头 L46 已经 if (!player.isShiftKeyDown()) return InteractionResult.PASS;,所以 L47 的 if (player.isShiftKeyDown()) 恒为 true。建议删除外层冗余检查或将内层逻辑直接放在 L46 之后,减少无意义嵌套。

  • AnvilEventListener.javahandleGiantAnvilImpact() 返回 true 后新增调用了 handleBehaviors()(L80-82)。这导致巨型铁砧砸中大锅时,不仅处理配方,还会额外触发大锅匹配的 AnvilBehavior(如 TimeWarpPlayerBehavior)。如果大锅下方有激活的腐蚀信标,会同时触发时间扭曲秒杀。请确认这是预期行为(之前大锅路径是直接 return 的,不会走 behaviors)。

  • BurningHeaterBlockEntity.consumeBurnTime() — 从「从燃料槽直接扣物品 + 扣缓冲」改为「只扣缓冲时间」。旧逻辑会直接消耗燃料槽中的物品(视觉效果上燃料减少),新逻辑只扣 burnTime 缓冲,然后由下一个 tick 自动从燃料槽补满缓冲。功能上等价(总燃烧时间消耗相同),但燃料槽的物品消耗从即时可见变为延迟在下个 tick。对于连续多次 consumeBurnTime 调用(多个物品连续触发 SuperHeating),旧代码每个物品都有机会直接消耗燃料物品,新代码它们都扣同一个缓冲 → tick 结束时再补。若一帧内 consumeBurnTime 被调用 10 次(每次 4800 ticks),缓冲可能被扣到 0 后无法继续。但由于 MAX_PROCESS_EFFICIENCY = 64 限制以及 tick() 中的缓冲补满逻辑,这在实际中应该不会是问题。建议确认极端情况下的正确性。

💡 建议

  • CauldronUtil.hurtFromHeaterBelow() — 在 LivingEntityEventListener.onTick() 中对所有活体实体每 tick 调用。虽然 state.is(BlockTags.CAULDRONS) 对非锅内实体能快速短路,但 entity.blockPosition() + getBlockState() 每 tick 对所有实体执行仍有微小开销。建议考虑仅在实体坐标变化时执行,或者在 EntityTickEvent.Pre 而非 .Post 中调用。

  • ExplosionMixin.java — 从 getStatesCache() 改为基于 Tag 的 block 解析是个好的修正(避免 BlockState property 组合导致的重复条目)。isExplosionBlockTransformed.set(false) 在每次射线迭代开头重置也修复了潜在的 shared local 泄漏。但是 getBlockState(pos).getBlock()inputBlock.getBlocks().unwrap() 的转换路径中,需要验证 unwrap().ifLeft().ifRight() 两个分支都覆盖到了(tag 匹配和直接 block holder 匹配)。从 diff 看两个分支都有,✅。

  • LargeCauldronBlockEntityRecipeAttempt 记录被 RecipeExecution 直接替代,fuelCharged 追踪集合完全移除,SuperHeatingRecipe.FUEL_CONSUMED 上下文标记也不再需要。这是与 consumeBurnTime 简化配套的清理,代码更简洁。✅

  • RedstoneWire 新增 CORNER_SP 连接类型 — 为墙面导线向上断口连接支撑方块顶面原版红石粉设计的新连接。在 RedstoneWireBlock, RedstoneWireBlockMixin (注入 calculateTargetStrength), RedstoneWireNetworkManager (terminal 输入位置修正), RedstoneWireBlockStateGenerator 中均有完整实现。模型文件和 blockstate JSON 也有对应变更。实现完整。

🟢 看起来不错

  • Heater 伤害逻辑集中化IDamagingHeater + HeaterUtil 将 HeaterBlock 和 BurningHeaterBlock 的步伤逻辑统一到一个工具方法,消除了重复代码,且通过接口使得任意方块可以成为伤害性加热器。
  • ICauldronGeometry 接口抽象 — 将大锅几何信息(内部区域、底部位置)统一为接口,使 CauldronUtilTimeWarpPlayerBehavior 能通用于普通锅和多方块大锅。
  • IAnvilCraftEntityExtension 扩展接口 — 为实体提供重力类型、附加重力、磁化状态、碰撞合成控制等扩展点,与 GravityManager, EntityMixin, MagnetBlock, AccelerateManager 的集成均一致。
  • RubyPrism/Lens 激光取消守卫if (!irradiateSelfLaserBlockSet.contains(source)) return; 防止非照射源激光的取消操作错误传播,是 [Bug] 激光环状光路BUG #4152 修复的核心,逻辑简单有效。
  • BaseLaserBlockEntity.setRemoved() 客户端/服务端路径分离 — 客户端提前 return + blockRemoved() 替代 update(),服务端保持原有的 onCancelingIrradiation 清理。逻辑清晰。
  • AnvilEventListener 多方块切石机检查hasStonecutterBelowAnyPart() 遍历所有 part 检查下方是否有切石机,解决了之前只检查 hitBlockPos.below() 的问题。

结论: APPROVE — 五个声称的 bugfix 均有对应代码实现且逻辑正确。一个轻微代码质量建议(MagnetUtil 重复检查),无阻塞性问题。整体重构方向良好:伤害逻辑集中化、接口抽象、多方块通用化。


由 Hermes Agent 审查

@PigeonNian
PigeonNian merged commit 3cd7a78 into Anvil-Dev:dev/1.21/1.6 Jul 19, 2026
2 checks passed
@Gugle2308

Copy link
Copy Markdown
Collaborator

PR #4189 的状态为 closed (已合并)。根据审查技能第 7 节 Webhook 模式的规则:

closed/labeled → 停止

PR 已关闭/合并,无需审查。停止。

@Gugle2308

Copy link
Copy Markdown
Collaborator

以下是 PR #4189 的代码审查报告。


审查摘要:PR #4189 — Bug 修复与体验优化

作者: WhereisFff | 分支: dev/1.21/fix12 → dev/1.21/1.6
变更规模: 38 文件(+1212 / -217) | 新文件: 10 | 删除: 0


1. 巨型铁砧手持时大锅顶部完整碰撞 ✅

文件: LargeCauldronBlock.java

  • getShape() 中从 SHAPES.get(part) 改为判断:当 part.getOffsetY() == 2(顶层)且玩家手持 GIANT_ANVIL 时返回 Shapes.block()
  • 实现了 ICauldronGeometry 接口,提供 getCauldronInnerArea()getCauldronBottomPositions()
  • 碰撞区域定义:以中心 pos 为基准,范围 (-0.75, -0.5, -0.75)(+1.75, +1.75, +1.75) — 覆盖 3×3×3 多方块上部。
  • 底部位置:中心 pos 往下 2 格的 3×3 水平区域。

评价: 逻辑正确。仅在手持巨型铁砧时替换顶层碰撞体,不影响普通交互。


2. 锅与缸的伤害修复(fixed #4186)✅

变更涉及: LivingEntityEventListener.javaCauldronUtil.javaHeaterUtil.javaBurningHeaterBlock.javaHeaterBlock.javaIDamagingHeater.java

  • 新架构: 引入了 IDamagingHeater 接口,BurningHeaterBlockHeaterBlock 都实现此接口。
  • HeaterUtil.hurtEntity(): 集中化的伤害逻辑 — 检查 IDamagingHeater.isActive()isSteppingCarefully()instanceof LivingEntity
  • CauldronUtil.hurtFromHeaterBelow(): 通过 ICauldronGeometry.getBottomPositions() 遍历锅底下方所有加热器方块,如果任意一个激活就对实体造成伤害。
  • LivingEntityEventListener.onTick(Post) 中每 tick 调用 hurtFromHeaterBelow()

评价:

  • 🔹 将加热器伤害从 stepOn() 中解耦,通过 EntityTickEvent.Post 实现每 tick 检查,覆盖了实体站在锅中时受到下方加热器伤害的场景(这正是 [Bug] 大型炼药锅+燃烧加热器不会对玩家造成伤害 #4186 的修复)。
  • 🔹 ICauldronGeometry 使大型多方块锅也能正确检测底部加热器。
  • ⚠️ 性能注意: 每 tick 对所有活实体执行 isEntityInside() + 底部位置遍历 + 状态查询。如果大量实体同时站在锅中可能会产生开销。建议监控。

3. 多方块下方切石机触发破坏 ✅

文件: AnvilEventListener.java

  • 新增 hasStonecutterBelow() 方法:判断铁砧击中的多方块结构,如果任意一个 part 下方有切石机,就触发 brokeBlock()
  • hasStonecutterBelowAnyPart() 遍历多方块的所有 part,通过 multiPartBlock.offsetFrom() 获取每个 part 的实际位置,检查其下方是否为切石机。
  • 抽象出 handleBehaviors() 辅助方法,使 onLand() 逻辑更清晰。

评价: 正确定位多方块结构的所有 part,覆盖了 corner case(#4186 相关)。


4. 大锅燃烧加热器时间扣除修复(fixed #4187⚠️

文件: BurningHeaterBlockEntity.java

  • 旧逻辑: 先直接从燃料槽扣物品抵消耗(以获得即时视觉反馈),不足部分从 burnTime 缓冲扣除。
  • 新逻辑: 直接从 burnTime 缓冲中扣除,不再触碰燃料槽物品。
- // 1. 优先从燃料槽中直接扣除物品
- while (remaining > 0) { ... consume fuel items ... }
- // 2. 物品不够的部分从 burnTime 缓冲区扣除
- if (remaining > 0) this.burnTime = Math.max(0, this.burnTime - remaining);
+ this.burnTime = Math.max(0, this.burnTime - ticks);
  • 新增 updateBurningState() 调用以立即同步客户端。
  • 大型锅的 handleGiantAnvilImpact() 中删除了 fuelCharged 跟踪机制(之前用于防止同一加热器被重复计费)。

评价:

  • ✅ 删除了复杂的燃料扣取循环,修复了 [Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187(大概率是双倍扣费或计时精度问题)。
  • ⚠️ 行为变更: 旧代码会让燃料槽物品即时消失("消耗可见"),新代码依赖正常的 tick 燃料消耗机制。用户可能不会立即看到燃料减少。如果这是预期行为([Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 的核心修复),那就没问题;否则可能需要额外的槽更新调用。
  • ⚠️ fuelCharged 需验证跨文件一致性: 参数从 tryProcessItemGroup/tryProcessFluidRecipe 中移除,下游 tryProcessItemCompressRecipe 也需同步更新签名。由于这些方法文件名不在 diff 中(可能已在别处更新),建议确认编译通过。

5. 环形光路 Bug 修复(fixed #4152)✅

文件: BaseLaserBlockEntity.javaLensBlockEntity.javaRubyPrismBlockEntity.java

  • BaseLaserBlockEntity.setRemoved(): 将客户端 BER 管道清理移到服务器端逻辑之前,并在客户端分支用 blockRemoved() 替代 update()。这避免了客户端调用 level.getBlockEntity(irradiateBlockPos) 可能导致的 NPE(当 irradiateBlockPos 因环形路径未被设置时)。
  • LensBlockEntity/RubyPrismBlockEntity.onCancelingIrradiation(): 增加了守卫判断 if (!this.irradiateSelfLaserBlockSet.contains(source)) return;,防止重复取消照射。

评价:

  • ✅ 环形光路的根本原因:当镜头和棱镜形成光路环时,onCancelingIrradiation 可能在 irradiateSelfLaserBlockSet 中尚未注册 source 的情况下被调用,旧代码会错误地将 enabled 设置为 false,从而破坏环形光路。新增的守卫检查阻止了这种假取消。
  • setRemoved() 中客户端提前 return 也是环形场景的关键修复 — 不干净的移除流程可能破坏环形路径。

6. 红石导线修改 ✅

文件: RedstoneWireBlock.javaRedstoneWireNetworkManager.javaRedstoneWireBlockMixin.javaConnectionType.java、模型/blockstate 生成

  • 新增连接类型 CORNER_SP 专用于侧壁导线向上的开放断口连接支撑方块顶面的原版红石粉。使用更短的拐角模型(-1.0 vs -2.0 偏移)避免模型插入粉线。
  • getUpwardDustSignal() 扫描侧壁导线周围的水平方向,检测是否有向上的断口连接原版粉线,返回原版粉线信号强度。
  • canConnectRedstone(null) 处理: 原版红石粉在检查斜下方导线时不带方向参数;现在 null direction 映射为 Direction.UP(向上断口)。
  • terminalTarget() 开放终端按实际情况指向支撑方块顶面(原版粉线位置)而非导线附着方向。
  • RedstoneWireBlockMixin: 注入 calculateTargetStrength 以纳入向上断口的信号值。
  • NetworkManager 更新: recompute()notifyTerminalChanges() 使用 terminalTarget() 确定正确的终端位置。
  • redstone_wire_up.jsonfrom[5,1,-0.1][5,1,0],消除 Z-fighting。

评价:

  • ✅ 此修改使自定义红石导线能正确与原版红石粉交互,包括信号读取和外观显示。
  • CORNER_SP 的短拐角模型(-1.0 vs -2.0)是必要的差异,避免模型插入粉线。
  • ✅ 架构合理:自定义导线侧壁向上连接粉线时,断开的外观 + 短拐角 + 正确信号读取。
  • ✅ 生成的 blockstate/model 覆盖所有 4 个附着方向。

7. 开放引力 API ✅

文件: GravityManager.javaIAnvilCraftEntityExtension.java(新)

  • 实体可通过 IAnvilCraftEntityExtension 提供自定义重力类型、附加重力向量。
  • getGravityType():如果实体实现接口并返回非 null,则优先级最高。
  • getGravityVector():将接口返回的附加重力向量与基础引力合并。
  • getNetGravityVectorForFallingBlock(Entity):修复实现 — 旧版本错误地计算了净引力,新版本先加基础重力再加自定义向量。

评价: 清晰、可扩展的 API 设计。默认方法确保了向后兼容性。


8. 其他变更 ✅

文件 变更 评估
ExplosionMixin.java Ray 迭代间重置 isExplosionBlockTransformed;爆炸方块变换改用 tag/holder ✅ 修复了多次爆炸中的状态残留
EntityMixin.java 为 FallingBlockEntity 碰撞合成添加 canCollisionCraft() 守卫 ✅ 允许自定义实体禁用碰撞合成
MagnetBlock.java 跳过实现 IAnvilCraftEntityExtension 的实体 ✅ 防止磁铁拾取自定义掉落实体
MagnetUtil.java Shift+右键磁化实体 ✅ 新功能,支持实体磁化
AccelerateManager.java 加速环支持 IAnvilCraftEntityExtension ✅ 扩展了加速系统的兼容性
TimeWarpPlayerBehavior.java 使用 ICauldronGeometry.getBottomPositions() 适配多方块锅 ✅ 多方块兼容
HasCauldron.java 增加实体锅(IEntityCauldron)支持 ✅ 新接口,实体可作为锅
ModAnvilBehaviors.java 注册匹配条件从 AbstractCauldronBlock 改为 BlockTags.CAULDRONS ✅ 更宽泛的标签匹配
SuperHeatingRecipe.java 修复 EOF 换行符、更新注释 ✅ 元气级别

潜在问题

  1. ⚠️ 燃烧计时可见性([Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 修复): 旧代码从燃料槽即时扣除物品以获得视觉反馈。新代码仅从 burnTime 缓冲扣。虽然这正确修复了 [Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 的计时问题,但玩家不会立即看到燃料消耗。如需恢复即时视觉反馈,建议在 consumeBurnTime 结束后加一个 itemHandlersetChanged() + 同步调用。

  2. ⚠️ fuelCharged 跨文件一致性: 删除了 tryProcessItemGroup/tryProcessFluidRecipeSet<BlockPos> fuelCharged 参数。请确保 SimpleMultiPartBlockEntity.tryProcessItemCompressRecipe(或其他被调用方)的签名也已同步更新,否则会编译失败。

  3. ⚡ 性能 — hurtFromHeaterBelow 每 tick 执行: LivingEntityEventListener.onTick(Post) 对所有实体每 tick 执行锅+加热器检查。建议监控是否有 lag spike,必要时加 tick 间隔或只在实体站在锅上时检查。

  4. ICauldronGeometry.getCauldronInnerArea 碰撞范围与 getShape 不一致? getShape() 中将顶层替换为全方块,但 getCauldronInnerArea() 返回的 AABB 范围比单方块大(覆盖 3×3×3)。isEntityInside() 使用 entity.position() 点检测,所以碰撞形状不影响内部检测。但需确认 getCauldronInnerArea() 的边界不与外部重叠 — 目前范围 (-0.75, -0.5, -0.75)(+1.75, +1.75, +1.75) 覆盖了 3×3×3 空间减去底部 0.5 格,正确。


总结

总体评分:8.5/10 — 结构良好的 bug 修复 PR,涵盖了所有 7 个声明的修复点。

  • 正确性: 所有修复逻辑合理,环形光路和加热器计时修复特别干净。
  • 架构: 新增的 ICauldronGeometryIDamagingHeaterIAnvilCraftEntityExtensionIEntityCauldron 接口设计合理,将重复逻辑集中到 HeaterUtil/CauldronUtil
  • 安全/风险: 无密钥或安全问题。
  • 建议: 1) 确认 fuelCharged 移除后编译通过;2) 考虑在 consumeBurnTime 中补充燃料槽同步。

@Gugle2308

Copy link
Copy Markdown
Collaborator

Now I have a complete picture of all changes. Let me compile the review.

代码审查摘要 — PR #4189

范围: 38 文件 (31 Java, 7 JSON) / 2336 行 diff / 10 新增, 0 删除, 28 修改
分支: dev/1.21/fix12dev/1.21/1.6
Ghost 文件: 1 (redstone_wire_up.json — 仅行内修正,正常)
EOF 缺失: 5 (均为生成资源,可接受)
TODO/FIXME: 0


🔴 关键

  • AccelerateManager.canBeAccelerated() 逻辑可读性问题 — 三元表达式嵌套复杂,存在潜在的逻辑短路风险:
    return entity instanceof FallingBlockEntity fallingBlockEntity
        && (fallingBlockEntity.getBlockState().is(BlockTags.ANVIL)
            || entity instanceof IAnvilCraftEntityExtension extension
            && extension.anvilcraft$isMagnetized())
        && (!(entity instanceof IAnvilCraftEntityExtension extension)
            ? !fallingBlockEntity.getBlockState().is(ModBlockTags.NON_MAGNETIC)
            : extension.anvilcraft$isMagnetized())
    注意:当 entity instanceof IAnvilCraftEntityExtension 为 true 且 extension.anvilcraft$isMagnetized() 为 true 时,后半段再次检查 extension.anvilcraft$isMagnetized() — 逻辑等价但冗余。更严重的是,当实体是 FallingBlockEntity 且实现了 IAnvilCraftEntityExtensionanvilcraft$isMagnetized() = false),但 fallingBlockEntity.getBlockState().is(BlockTags.ANVIL) 为 true 且 !ModBlockTags.NON_MAGNETIC 为 true — 后半段的三元走 !(entity instanceof IAnvilCraftEntityExtension) 分支,跳过了 NON_MAGNETIC 检查。建议重构为清晰的条件分支,避免 && 与三元混用导致的语义模糊。

⚠️ 警告

  • BurningHeaterBlockEntity.consumeBurnTime() 燃料消耗模型变更 — 旧代码直接从燃料槽扣除物品(即时可见),新代码只从 burnTime 缓存扣除,依赖 tick 循环中的 tryConsumeFuel 自然消耗。这在逻辑上是正确的(修复了 [Bug] 大型炼药锅+燃烧加热器,高温熔炼不正确减扣时间 #4187 的多重扣除问题),但有两个边际风险:

    1. 如果 tryConsumeFuel 在下个 tick 前因区块卸载等原因未运行,燃料物品可能被玩家取回
    2. 玩家无法立即看到燃料物品减少(延迟 1-2 tick),可能误以为没有消耗
      建议:在高性能路径上可接受,但需在添加新燃料后的首次 tryConsumeFuel 调用中确保不立即补充刚刚扣掉的 burnTime。
  • IAnvilCraftEntityExtension 接口与 AccelerateManager.isAnvil() 的交互isAnvil() 中新增了 entity instanceof IAnvilCraftEntityExtension extension && extension.anvilcraft$isMagnetized() 检查,但这意味着非铁砧的磁化实体也会被视为 "anvil" 进行速度限制。确认这是有意的设计决策?如果某个非铁砧实体被磁化,它不应受 limitAnvilSpeed 限制。

  • GravityManager.getNetGravityVectorForFallingBlock(Entity) 实现变更 — 旧版本委托给 getNetGravityVectorForFallingBlock(Level, Vec3, GravityType),新版本在内部计算 baseGravity + getGravityVector。如果 getGravityVectorgetNetGravityVectorForFallingBlock(4参数版本)之间对 falling block 的重力计算不一致,会导致差异。确认 getGravityVector(entity, 0.04)getNetGravityVectorForFallingBlock 的行为等价。


💡 建议

  • FallingBlockCollisionEventListener.anvilCollisionCraft() — 添加了 if (event.isCanceled()) return; 检查。这很好,但 EntityMixin 中新增的 anvilcraft$canCollisionCraft() 检查在 AnvilEvent.CollisionBlock 被触发之前就提前 return 了,与这里的事件取消检查有重叠。建议统一为单一机制。

  • SuperHeatingRecipe.java 的 FUEL_CONSUMED 注释更新 — 注释从 "一次铁砧落地事件中" 更新为 "当前配方上下文",但代码逻辑未变(仍是 InWorldRecipeData)。这是纯文档修复,但确认 LargeCauldronBlockEntity 中移除的 fuelCharged 集合是否完全被 FUEL_CONSUMED 的上下文级别检测取代?新代码不再在 tryProcessFluidRecipe 中显式设置 FUEL_CONSUMED,而是依赖配方上下文本身的传播。

  • MagnetBlock.attract() 过滤 IAnvilCraftEntityExtensionif (entity instanceof IAnvilCraftEntityExtension) continue; 阻止磁铁吸引任何实现该接口的实体(包括可能原本应被吸引的铁砧实体)。确认所有实现 IAnvilCraftEntityExtension 的铁砧实体都有独立的磁化处理路径。


🟢 看起来不错

  • 大型铁砧手持时大锅碰撞箱修正getShape() 中用 context.isHoldingItem() 检查 + Shapes.block() 返回完整碰撞箱,方案简洁正确。
  • 切石机多方块检查hasStonecutterBelowAnyPart() 遍历所有部分,正确替换了旧的单方块检查。配合 handleBehaviors() 的提取,代码更结构化。
  • 环形光路 [Bug] 激光环状光路BUG #4152 修复contains(source) 守卫检查防止了错误来源的取消操作,修复了环形光路中的级联失灵。
  • 红石导线 CORNER_SP 机制 — 与原版红石粉的交互设计优雅,通过 calculateTargetStrength mixin 正确包含信号强度,专属短拐角模型避免碰撞。
  • CauldronUtil 新 APIgetInnerArea() / getBottomPositions() / isEntityInside() / hurtFromHeaterBelow() 设计良好,通过 ICauldronGeometry 接口支持非标准大锅扩展。
  • IEntityCauldron 接口 — 实体作为流体容器的抽象干净,HasCauldron 配方谓词的适配完善。

📋 声称验证表

声称 状态 对应文件
手持巨型铁砧时大锅顶部视为完整碰撞 LargeCauldronBlock.java (getShape)
修复各种锅和缸的伤害问题 #4186 CauldronUtil.java, IEntityCauldron.java, ICauldronGeometry.java, LivingEntityEventListener.java, LargeCauldronBlockEntity.java, TimeWarpPlayerBehavior.java, HasCauldron.java, ModAnvilBehaviors.java
多方块切石机任意 part 被砸正确触发破坏 AnvilEventListener.java (hasStonecutterBelowAnyPart)
修复大锅燃烧加热器扣除时间不对 #4187 BurningHeaterBlockEntity.java
修复环形光路 bug #4152 LensBlockEntity.java, RubyPrismBlockEntity.java, BaseLaserBlockEntity.java
红石导线修改 RedstoneWireBlock.java, RedstoneWireNetworkManager.java, RedstoneWireBlockMixin.java, RedstoneWireBlockStateGenerator.java, 模型 JSON × 7
开放引力 API IAnvilCraftEntityExtension.java, GravityManager.java, MagnetUtil.java, MagnetBlock.java, AccelerateManager.java, EntityMixin.java
额外:爆炸转换修复 ExplosionMixin.java — 从 getStatesCache() 切换到 getBlocks() 正确支持 tag 输入
额外:加热器伤害集中化 HeaterUtil.java, IDamagingHeater.java — 被 BurningHeaterBlockHeaterBlock 共用

结论: COMMENT — 7 项声称全部实现,代码逻辑正确。AccelerateManager.canBeAccelerated() 的嵌套三元表达式建议追加修复以提高可维护性,其余警告项需作者确认设计意图后合并。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants