Skip to content

Powerful overseer. - #4354

Draft
alpha-hhh wants to merge 6 commits into
Anvil-Dev:dev/26.1/1.6from
alpha-hhh:26.1/1.6/overseer
Draft

Powerful overseer.#4354
alpha-hhh wants to merge 6 commits into
Anvil-Dev:dev/26.1/1.6from
alpha-hhh:26.1/1.6/overseer

Conversation

@alpha-hhh

@alpha-hhh alpha-hhh commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
  • resolved [Feature] 原版加载变得非常简单,监督者该加强了,几个监督者加强建议 #1582
  • 修改了监督者的加载方式,现在监督者的一个底座方块对应一个区块
  • 监督者现在最高为4级
  • 使更多方块可以作为监督者的底座方块
  • 现在铁块、金块、绿宝石块和钻石块可以用来提升监督者的等级,但不进行加载
  • 皇家钢系列方块为原版默认加载方式,即强加载区块且运算随机刻,浮霜系列方块则强加载区块但不运算随机刻
  • 下界合金块和余烬系列方块强加载对应区块、运算随机刻、启用火焰蔓延,多相物质块强加载对应区块、不运算随机刻、启用火焰蔓延
  • 超限合金块强加载对应区块、启用区块内生物自然刷新和刷怪笼生物刷新(生物自然刷新要求该区块位于玩家2048区块内)
  • 现在/overseer命令可以显示每个监督者的等级,加载区块数量和所有监督者加载区块总数

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

timeout 20 curl -s "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" -o /tmp/vm.json && python3 -c "
import json
d=json.load(open('/tmp/vm.json'))
vs=[v['id'] for v in d['versions']]
p...

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

⚠️ Dangerous command requires approval:

curl -sL "https://api.github.com/repos/Anvil-Dev/AnvilCraft/issues/1582" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('body','')[:2500])" 2>/dev/null || curl -sL "https://api.git...

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/issues/1582 — or: vet https://api.github.com/repos/Anvil-Dev/AnvilCraft/issues/1582 (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

All verifications complete. Compile-level references check out (all 16 series blocks exist on the target branch). Here is the review.

代码审查摘要 — PR #4354

操作: edited
范围: 35 个文件 (25 Java, 19 新增, 2 删除) / 1905 行 diff
主题: 监督者重做 — 底座方块→区块映射、4 级金字塔、按底座方块类型区分的区块特性(随机刻/火焰蔓延/生物刷新)、/overseer 命令

🔴 关键

  • OverseerBlockEntity.java — 结构损坏后重建同结构,区块加载永不恢复(状态残留 bug)
    tick()!checkOverseerBlocks() 分支执行 LevelLoadManager.unregister() 后直接 return没有重置 oldLevel / oldTierHash。场景:玩家敲掉顶部/中间的一节监督者方块(底部 BE 仍在 tick)→ 已注销;随后原样补回 → scanPyramidBase() 得到相同 level + 相同 tierHash → levelChangedtiersChanged 均为 false → 提前 return,永远不会重新 register,区块加载静默失效(直到再次改动结构)。修复:在无效分支里将 oldLevel = -1; oldTierHash = 0;(或取消哈希短路,始终重同步)。

  • OverseerBlockEntity.scanPyramidBase + LoadChunkData.createLoadChunkData — 区块边界处底座映射错位 ±1 个区块
    映射记录的是底座方块相对监督者的方块偏移 (dx, dz)(dx∈[-4,4]),但 createLoadChunkData 把它当区块偏移用:new ChunkPos(centerChunk.x() + m.chunkOffsetX(), ...)。监督者位于区块边界 4 格以内时(每轴约 12.5% 的摆放位置),底座方块实际所在区块与计算结果相差 1:真实区块不会被强加载、拿不到特性标志;反而多加载了一个没有底座的空区块,特性标志(不随机刻/火焰蔓延/生物刷新)全部作用到错误区块。例如 x mod 16 = 14 时 dx=+2 的方块实际在 cx+1,代码却映射到 cx+2。修复:按实际方块坐标计算 ChunkPos.containing(centerPos.offset(dx, 0, dz)),或把记录字段改成绝对坐标而非区块偏移。

⚠️ 警告

  • ChunkFeatureManager.java — 静态表只以 ChunkPos 为键,不含维度
    CHUNK_SOURCESResourceKey<Level> 维度键,而 LevelLoadManager.CHUNK_REF_COUNT 是分维度的——两处不对称。若两个维度在同一区块坐标都有监督者,特性(跳过随机刻/火焰蔓延/刷怪笼强制激活/自然刷新)会跨维度串扰(如主世界刷怪笼因下界同坐标的超限底座而强制激活)。建议键改为 (维度, ChunkPos) 或查找时传入 level。

  • SourceFlags.TRANSCENDIUM = NO_RANDOM_TICK | ALLOW_NATURAL_SPAWN | ALLOW_SPAWNER_SPAWN — 与 PR 描述不符
    描述只提到超限合金块"启用生物自然刷新和刷怪笼生物刷新",未提随机刻;代码同时禁用了随机刻。若是有意设计(刷怪塔免作物生长)请在描述/注释中说明,否则需移除 NO_RANDOM_TICK

  • ServerLevelFireSpreadMixin.java — 无条件强制 canSpreadFireAround 返回 true
    对已托管且允许火焰蔓延的区块,无论原值直接返回 true。请确认 26.1 中该方法语义:若它同时承担 doFireTick 游戏规则或方块可替代性判定,此覆盖会绕过 gamerule、甚至可能让火焰判定通过不可燃位置(是否实际起火仍受邻块可燃性限制)。另需确认"任一来源命中即生效"的合并策略:同一区块同时被浮霜(禁随机刻)和皇家钢(要随机刻)监督者覆盖时,随机刻被跳过——是否符合预期?

  • LevelLoadManager.getAllOverseerForcedChunkCount(ServerLevel level) — 参数未使用
    统计的是所有维度的 OVERSEER 加载区块总数,而 /overseer 列表按查询维度过滤。摘要行显示的总数与列表范围不一致,易误导。应过滤 data.getServerLevel().dimension() 或删除参数。

  • ChunkFeatureManager.clear() 是死代码
    全库无调用点(已 grep 确认)。虽然正常流程下 discard 会清空条目,但建议在 ServerStoppedEvent 显式调用 clear(),同时清理 OverseerUtil.placedOverseers,避免同 JVM 内跨世界残留。

💡 建议

  • 性能scanPyramidBase() 每 tick 全量扫描,满级最坏 9+25+49+81 = 164 次 getBlockState + tag 查询/监督者/tick。建议改为周期重扫(如每 N tick 或 NeighborChanged 时)再比对哈希。
  • NaturalSpawnerMixin.java:ThreadLocal 在 HEAD 设置、仅 RETURN 清除,spawn 过程中抛异常会残留脏值;建议 try/finally。
  • ItemTooltipManager.java:英文文本混入全角标点 "9x9 range), different base blocks..."( 已原样进入 en_ud),请改用 ASCII 标点;"9x9 range" 建议明确为 "9x9 chunk range"(方块 vs 区块易混淆)。
  • MobCategoryMixin.javashouldAllowNaturalSpawn(null) 依赖 HashMap.get(null) 语义返回 false,虽安全但脆弱,建议显式判空。

🟢 看起来不错

  • 旧 typo 类清理彻底:LoadChuckDataLoadChunkDataRandomChuckTickLoadManager 删除,所有引用点(OverseerBlockEntity / WormholeStabilizerHandler / ServerLifecycleEventListener)同步更新。
  • forceChunk 按维度引用计数 + applied 幂等守卫,修复了旧版多个加载源同区块互相顶掉的问题,reload() 先 discard 后 apply 的顺序也正确。
  • 新设计(mixin 跳过随机刻)比旧的 RandomChuckTickLoadManager.tick() 手动补 tick 更干净——旧实现对 isNeedRandomTick=true 的区块存在双重随机刻的嫌疑。
  • ServerLevelRandomTickMixin 的重入守卫(@Unique 字段 + 取消后以 tickSpeed=0 重入)正确,只跳过随机刻、保留实体/BE/计划刻。
  • 标签体系清晰:tier_0(铁/金/绿宝/钻石)只提升等级不加载 ✓,系列标签(royal/frost/ember)可复用;en_us/en_ud 同步再生成。
  • /overseer 命令:权限门控、按维度追踪(OverseerUtil)、坐标点击复制,实现完整。
  • 已交叉验证目标分支 dev/26.1/1.6TRANSCENDIUM_BLOCKMULTIPHASE_MATTER_BLOCK、16 个系列方块、SimpleMultiPartBlock.isMainPart 均存在,无编译级引用问题。

📋 声称验证表

声称 状态 对应实现
resolved #1582(监督者加强) 4 级金字塔、底座扩展、特性区分、/overseer 命令
一个底座方块对应一个区块 ✅(边界 ±1 偏移 bug 除外) LoadChunkData.createLoadChunkData + scanPyramidBase
最高 4 级 TIER_RADIUS {1,2,3,4}、MAX_LEVEL=4
铁/金/绿宝石/钻石升级但不加载 tier_0 tag + blockTier >= 1 过滤
皇家钢 = 强加载 + 随机刻 DEFAULT=0
浮霜 = 强加载不随机刻 FROST=NO_RANDOM_TICK
下界合金/余烬 = +火焰蔓延 FIRE
多相物质 = 不随机刻 + 火焰蔓延 MULTIPHASE
超限合金 = 自然刷新 + 刷怪笼刷新 ⚠️ TRANSCENDIUM 额外禁用了随机刻(描述未提)
/overseer 显示等级/区块数/总数 ⚠️ 总数跨维度统计与列表维度不一致

🧪 测试建议

被测目标 推荐测试场景 优先级
OverseerBlockEntity.tick() 敲掉顶部方块→原样补回→断言重新 register(复现 stale 状态 bug) 🔴
LoadChunkData.createLoadChunkData() 监督者摆放在 x/z mod 16 ∈ {13,14,15,0,1,2} 边界位置,断言各底座方块实际区块被加载 🔴
ChunkFeatureManager 两个维度同坐标区块,断言特性不串扰 🟡
ServerLevelRandomTickMixin 浮霜/皇家钢同区块重叠时随机刻行为(确认 any-source-wins 是否符合预期) 🟡
BaseSpawnerMixin / ChunkMapMixin 无玩家在场时刷怪笼是否持续刷怪;玩家 >128 但 <2048 区块时自然刷新 🟡

结论: REQUEST_CHANGES — 架构与方向正确,但存在两个必须修复的逻辑缺陷:结构重建后不重新注册(静默丢失加载),以及区块边界处底座→区块映射错位。另有跨维度串扰与描述不一致问题建议一并确认。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #4354

操作: opened
范围: 35 个文件(25 Java,19 新增,2 删除)/ 1905 行 diff

整体架构清晰:LoadChuckData/RandomChuckTickLoadManager 合并为 LoadChunkData + ChunkFeatureManager,底座方块按层级(3x3→9x9)逐层扫描、每方块映射一个区块、按方块类型给区块打 feature 标志位,8 个 mixin 分别实现"跳过随机刻 / 允许火焰蔓延 / 允许自然刷新 / 允许刷怪笼刷新 / 扩大清除距离"。方向正确,但跨维度隔离存在两个关键缺陷

🔴 关键

  • ChunkFeatureManager 完全没有维度隔离ChunkFeatureManager.java + 全部 8 个 mixin)
    CHUNK_SOURCES 仅以 ChunkPos 为键,所有 shouldSkipRandomTick/shouldAllowFireSpread/shouldAllowNaturalSpawn/shouldAllowSpawnerSpawn/getAllNaturalSpawnChunks 均不含维度参数,mixin 调用时也只传 ChunkPos。同一个区块坐标的 feature 标志会在所有维度同时生效:下界某坐标放一个浮霜底座监督者 → 主世界同坐标区块随机刻被静默取消;主世界超限合金块启用自然刷新 → 下界同坐标区块也强制刷怪、怪物清除距离被放大到 32768 格。LevelLoadManagerCHUNK_REF_COUNT 反而是按 ResourceKey<Level> 分键的——同一个 PR 内两处处理不一致。建议给 ChunkFeatureManager 全部方法加维度参数(mixin 侧 ServerLevel/ChunkMap 都能拿到 level)。

  • LevelLoadManager.removeAll() 在任意维度卸载时清空所有维度的加载数据LevelLoadManager.java
    LevelEventListener.onLevelUnload每个维度的卸载都会调用 removeAll(level),而它遍历的是全局 LOAD_DATA_MAP(所有维度),用被卸载维度的 level 去 discard 其他维度的数据,随后 LOAD_DATA_MAP.clear() + CHUNK_REF_COUNT.clear()。后果链:

    1. 玩家离开下界 → 下界卸载(常规行为)→ 主世界所有监督者的登记被清空;
    2. 主世界监督者的 BE 每 tick 仍在跑,但 tickif (!levelChanged && !tiersChanged) return;(第 906 行)不检查 LevelLoadManager.checkRegistered(pos) → 永远不会重新注册;
    3. 已强制加载的区块 ticket 残留在主世界 chunkmap 里,登记数据却已丢失 → 这些区块本会话内永远无法解除强制加载(区块泄漏),且随机刻/火焰/刷怪等覆盖全部失效。
      修复建议:removeAll 按维度清理;tick 早退条件加上 && LevelLoadManager.checkRegistered(pos)LOAD_DATA_MAP 改按 dimension + BlockPos 复合键(当前仅按 BlockPos 分键,两个维度同坐标的监督者会互相覆盖登记)。

⚠️ 警告

  • OverseerBlockEntity 结构破损路径不重置缓存checkOverseerBlocks() 为 false 时只 unregister 就 return,oldLevel/oldTierHash 保留原值;若上部方块被短暂拆掉再原样复原(底座未变),下一 tick 早退,区块加载永久丢失。与上面的"外部清空后不重注册"是同一类问题,建议失败路径把 oldLevel = -1; oldTierHash = 0; 重置。
  • 底座方块偏移 → 区块偏移是近似映射LoadChunkData.createLoadChunkData)— BlockOffsetMapping 里存的是方块偏移(±4),却直接当作区块偏移加到 centerChunk 上。监督者位于区块边缘(如 x=12,偏移 4 的方块已跨入下一区块)时,加载区域和 feature 标志会整体漂移 ±1 区块:应改为 ChunkPos.containing(centerPos.offset(dx, 0, dz))
  • 混合底座时标志合并语义与描述不符 — 同偏移 (dx,dz) 在不同层级都存在方块(同心方环共享偏移),一个区块会聚合最多 4 个来源的标志,且"任一来源生效"。例:3x3 皇家钢核心 + 5x5 浮霜环 → 内圈 3x3 区块同时收到 DEFAULT 和 FROST → shouldSkipRandomTick 返回 true → 皇家钢核心的随机刻被浮霜环压制,与"皇家钢运算随机刻"的描述相悖。建议确认:随机刻是否应改为"存在任一来源要求运算则不跳过"。
  • 等级显示与加载面积可背离 — 任意合法底座方块(含 tier-0 铁/金/钻/绿宝石)都能补完任意层级(含 tier-3 9x9 层),全铁底座可显示 4 级但只加载中心 1 个区块;tier-0 方块不产生映射是本 PR 的设计,但"层级完整性"不区分方块 tier 会放大显示与实际的落差,建议在 tooltip/文档中说明。
  • zh_cn.json 未同步src/main/resources/assets/anvilcraft/lang/zh_cn.jsontooltip.anvilcraft.item.overseer(.shift) 仍是"3x3 皇家钢 / 最高 3 层 / 5x5"旧文案,command.anvilcraft.overseer.entry 仍显示"随机刻",且缺 command.anvilcraft.overseer.summary 新键。
  • en_us tooltip 混入全角逗号 — "...(max 4 layers, 9x9 range), different base blocks..." 的","应为半角 ", "(ItemTooltipManager.java 与生成的 lang 都要改)。

💡 建议

  • BlockOffsetMapping 字段名 chunkOffsetX/Z 实为方块偏移,易误导;tier 字段在 createLoadChunkData 中完全未参与计算(仅用于变更哈希),建议删除或实际使用。
  • NaturalSpawnerMixin 用两个独立 @Inject 设置/清除 CURRENT_SPAWNING_CHUNK,异常路径会泄漏 ThreadLocal 值,建议 try/finally。
  • OverseerBlockEntity.tick 每 tick 无条件全量扫描底座(最多 9x9x4≈324 次 getBlockState + 哈希),全部底座方块都是合法方块时可以先做轻量变更检测。
  • getAllOverseerForcedChunkCount(ServerLevel level) 忽略 level 参数,统计的是所有维度的监督者区块数,而命令 list 是按维度列出的,总数与明细口径不一致。
  • ChunkFeatureManager.clear() 无调用方,同 JVM 内服务器重启(集成服)会残留旧状态,建议接入 removeAll/停止事件。
  • 超限合金块的 TRANSCENDIUM 标志同时禁用了随机刻NO_RANDOM_TICK | ALLOW_NATURAL_SPAWN | ALLOW_SPAWNER_SPAWN),PR 描述未提及,请确认是否有意。
  • 8 个新 mixin 的目标方法(尤其 ServerLevel.canSpreadFireAroundDistanceManager.getSpawnCandidateChunksChunkMap.playerIsCloseEnoughForSpawning)建议在开发环境实际加载验证,mixin 目标失效会在类加载时直接崩溃。

🟢 看起来不错

  • CHUNK_REF_COUNT 按维度做引用计数、forceChunk 在 count 0↔1 边界才真正调 setChunkForced,多监督者重叠区块的引用管理正确。
  • ServerLevelRandomTickMixin 用实例标志位防重入、取消后以 tickChunk(chunk, 0) 重入——只跳过随机刻而保留 inhabited time 等其余逻辑,设计巧妙且正确。
  • LoadChunkDataapplied/removed 双标志 + lazy() 延迟队列使 apply/discard 幂等,服务器启动时序处理得当。
  • 旧 API(LoadChuckData/RandomChuckTickLoadManager)全部调用方迁移完毕,WormholeStabilizerHandler 改用 createSimpleLoadChunkData(1, pos, level) 后行为与原 isNeedRandomTick=false 等价(无回归)。
  • 4 个底座 tier tag + 3 个系列 tag 的注册、生成 JSON、ModBlocks 引用三方一致;MAX_LEVEL=4 与 blockstate 属性范围吻合。

📋 声称验证表

声称 状态 对应实现
resolved #1582 ⚠️ 无法从 diff 验证 issue 内容
一个底座方块对应一个区块 createLoadChunkData 方块偏移→区块偏移(区块边缘 ±1 漂移见警告)
最高 4 级 TIER_RADIUS {1,2,3,4}MAX_LEVEL=4、tooltip 更新
更多方块可作底座 overseer_base_tier_0..3 四个 tag
铁/金/绿宝石/钻石提升等级但不加载 blockTier >= 1 过滤,tier-0 不产生映射
皇家钢:强加载+随机刻 SourceFlags.DEFAULT
浮霜:强加载、不随机刻 FROST + ServerLevelRandomTickMixin
下界合金/余烬:+随机刻+火焰蔓延 FIRE
多相物质:不随机刻+火焰蔓延 MULTIPHASE
超限合金:自然刷新+刷怪笼刷新(2048 区块内) TRANSCENDIUM + ChunkMapMixin/DistanceManagerMixin/MobMixin 等(附赠禁用随机刻,待确认)

结论: REQUEST_CHANGES — 核心机制与版本描述一致,但 ChunkFeatureManager 无维度隔离、removeAll 跨维度清空 + BE 不重注册这两个问题会让多维度使用场景(本模组的常态)出现区块泄漏和加载失效,建议修复后再合并。

🧪 测试建议

被测目标 推荐测试场景 优先级
LevelLoadManager.removeAll 下界无玩家卸载后,主世界监督者仍保持加载/可解除加载 🔴
ChunkFeatureManager.* 主世界 + 下界同坐标各放一个监督者,验证 feature 不串维度 🔴
OverseerBlockEntity.tick 拆掉上部方块再复原(底座不变),验证重新加载 🟡
LoadChunkData.createLoadChunkData 监督者放区块边界处,验证加载区块与底座方块实际所在区块一致 🟡
ServerLevelRandomTickMixin 浮霜与皇家钢混合底座,验证内圈核心随机刻行为 🟡
OverseerCommand list 多维度监督者时总数与分维度列表口径一致 🟢

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

@alpha-hhh
alpha-hhh marked this pull request as draft August 6, 2026 11:28
@Gugle2308

Copy link
Copy Markdown
Collaborator

交叉引用验证完毕,信息足够。整理审查意见。

代码审查摘要 — PR #4354

操作: converted_to_draft(按流程仅报严重问题,comment 级别)
范围: 35 个文件(25 Java, 19 新增, 2 删除)/ ~1900 行 diff
目标: 监督者(Overseer)区块加载系统重做 — 底座方块→区块映射、4 级金字塔、按方块系列区分加载特性、8 个新 mixin

🔴 关键

  1. OverseerBlockEntity.tick() — 塔身短暂损坏恢复后永远不再注册
    checkOverseerBlocks() 失败分支里 unregister 后直接 return没有重置 oldLevel / oldTierHash。若 MID/TOP 部分被活塞推动或临时替换后恢复原状,恢复后的 tick 中 levelChanged=false && tiersChanged=false → 提前 return → 该监督者永久停止加载区块,直到玩家改动底座才会恢复。修复:失败分支中同时 this.oldLevel = -1; this.oldTierHash = 0;(旧代码同样有此隐患,但本 PR 重写了整个 tick,正是修复时机)。

⚠️ 警告

  1. ChunkFeatureManager / LOAD_DATA_MAP 无维度限定 — 跨维度同坐标区块特性串扰
    CHUNK_SOURCESChunkPos 为键、LOAD_DATA_MAPBlockPos 为键,均不含 ResourceKey<Level>。新增的 8 个 mixin 全部通过 ChunkFeatureManager(只看 ChunkPos)查特性。两个维度在相同区块坐标各有一个监督者时:register/unregister 互相覆盖,且主世界的浮霜监督者会禁用末地同坐标区块的随机刻/刷怪等特性。CHUNK_REF_COUNT 已经按维度键控了,建议 feature 表也把维度加进键。

  2. MobCategoryMixin + ThreadLocal 全局副作用
    getDespawnDistance()spawnForChunk 期间对所有 MobCategory 返回 32768。NaturalSpawner 内部任何依赖该值的逻辑(如 mobcap 统计范围)都会在超限合金区块的刷怪 pass 中被放大,可能饱和全局刷怪容量。且它和 MobMixin 的 per-mob WrapOperation 功能重叠——建议确认 NaturalSpawner 的调用点后评估能否删掉这个 mixin,只保留 MobMixin。

  3. WormholeStabilizerHandler 行为变化(需确认是否有意)
    createLoadChuckData(1, pos, false, level)isNeedRandomTick=false(无随机刻);新 createSimpleLoadChunkData(1, pos, level) → sourceFlags=0 → 随机刻默认开启。虫洞稳定器加载的区块现在会运算随机刻,行为反转。

  4. OverseerCommand 汇总数跨维度串数
    getAllOverseerForcedChunkCount(level)level 参数从未使用,直接对全局 LOAD_DATA_MAP 统计——/overseer list <dim> 的 "Loads N chunk(s) in total" 是所有维度的总和,与所选维度无关。建议按 data.getServerLevel() == level 过滤。

  5. DistanceManagerMixin.getSpawnCandidateChunks — 每调用全量拷贝
    每次调用把原始 LongIterator 整体拷贝进 LongOpenHashSet 再遍历全部监督者区块。若该方法由 ServerLevel.tick 每 tick 调用,大视野服务器会每 tick 产生大分配。建议确认调用频率并考虑缓存。

  6. 混合材料金字塔的特性 OR 合并
    同一 (dx,dz) 位置的金字塔各层方块都映射到同一区块,特性按 OR 合并——混合材料的 4 级金字塔其中心 3x3 区块会同时获得全部特性(无随机刻+火焰+自然刷新+刷怪笼)。若意图是各层方块独立生效,需确认这是设计而非遗漏。

💡 建议

  • ItemTooltipManager — en_us tooltip 混入全角逗号:"9x9 range), different..."(en_ud 已同步,源头需改)
  • ChunkFeatureManagerclear() 无任何调用者(dead code)
  • LoadChunkDataBlockOffsetMapping.tier 字段在 createLoadChunkData 中未使用(仅参与 hash),如无用途可删

🟢 看起来不错

  • forceChunk 引用计数按维度键控,多监督者重叠加载同一区块时正确(count==0 才 force,count<=1 才 unforce)
  • 8 个新 mixin 全部注册进 anvilcraft.mixins.json
  • LoadChuckDataLoadChunkData 重命名完整无残留(全仓 grep 确认),RandomChuckTickLoadManager 删除干净
  • ServerLevelRandomTickMixin 的 reentrancy 守卫 + tickSpeed=0 重入方案正确:只跳过随机刻,tickChunk 其余逻辑照常
  • 特性按 tag 驱动(royal_series/ember_series/frost_series + 4 个 tier tag),数据驱动设计清晰

📋 声称验证表

声称 状态 对应实现
一个底座方块对应一个区块 scanPyramidBaseBlockOffsetMapping(dx,dz)ChunkPos(center+dx, center+dz)
监督者最高 4 级 TIER_RADIUS={1,2,3,4}MAX_LEVEL=4
更多方块可作为底座 4 个 overseer_base_tier_N tag
铁/金/绿宝石/钻石提升等级但不加载 tier_0 tag;仅 blockTier>=1 加入 offsetMappings
皇家钢强加载+随机刻 DEFAULT=0 → 不跳过随机刻
浮霜强加载不运算随机刻 FROST + ServerLevelRandomTickMixin
下界合金/余烬强加载+随机刻+火焰蔓延 FIRE + ServerLevelFireSpreadMixin
多相物质强加载+不随机刻+火焰蔓延 MULTIPHASE
超限合金强加载+自然刷新+刷怪笼刷新(2048 区块) TRANSCENDIUM + NaturalSpawner/ChunkMap/DistanceManager/Mob/BaseSpawner mixins
/overseer 显示等级、加载区块数、总数 ⚠️ 等级与单监督者区块数 ✅;总数跨维度串数(见 #5

结论: COMMENT — 整体设计与声称一致,实现质量良好。建议转正式 review 前优先处理 🔴#1(塔身恢复后不重新注册)和 ⚠️#2(维度键控),其余可合并前再定。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

分析完成。所有交叉引用验证完毕,现在输出审查意见。


代码审查摘要 — PR #4354

操作: synchronize
范围: 35 个文件(25 Java,8 新增 Mixin,6 新增 tag JSON,2 语言文件)/ 1950 行 diff
特性: 监督者(Overseer)区块加载系统重写 — 解决 #1582,底座分层(最高 4 级)+ 按底座方块类型区分随机刻/火焰蔓延/生物刷新/刷怪笼行为 + /overseer 命令

🔴 关键

  • ChunkFeatureManager.java + NaturalSpawnerMixin.java — CURRENT_SPAWNING_DIMENSION ThreadLocal 从未被 set,超限合金块的"生物自然刷新"特性在远离玩家处完全失效。 该 ThreadLocal 在 ChunkFeatureManager 中声明(L202),被 DistanceManagerMixin.onHasPlayersNearby(L1605)、DistanceManagerMixin.onGetSpawnCandidateChunks(L1620)、MobCategoryMixin.onGetDespawnDistance(L1662)三处读取,但全 diff 中只有 CURRENT_SPAWNING_CHUNK.set()(NaturalSpawnerMixin L1738)。dimension 恒为 null → shouldAllowNaturalSpawn(null, chunkPos) 恒返回 false → 这三个注入全部静默失效。PR 声称"生物自然刷新要求该区块位于玩家2048区块内",但实际超出的只有 ChunkMapMixin(用 player.level().dimension() 直接取值,可用)和 MobMixinself.level().dimension(),可用)——hasPlayersNearby/候选区块扩展这两个关键闸门是死代码,刷怪仍被限制在原版玩家附近距离。修复:在 NaturalSpawnerMixin 的 spawnForChunk HEAD/RETURN 中同时 set/remove CURRENT_SPAWNING_DIMENSION

⚠️ 警告

  • ServerLevelRandomTickMixin — 依赖一个行为假设翻转:强加载区块会原生调用 tickChunk 旧实现用 RandomChuckTickLoadManager.tick() 手动调用 tickChunk 正是因为在旧版本强加载区块默认不运算随机刻。新设计只做"抑制"(FROST 区块重入 tickSpeed=0),若 26.1 中强加载区块不原生走 tickChunk,则皇家钢(DEFAULT)的"运算随机刻"声称也会失效,且 FROST 抑制变成空操作。需运行时验证强加载区块确实会触发 tickChunk。
  • LevelLoadManager.register/unregister — LOAD_DATA_MAP 仅以 BlockPos 为键,跨维度碰撞会破坏引用计数并造成永久强加载泄漏。 两个维度同坐标各放一个监督者时,第二个 register 被 containsKey 静默丢弃;其 tick 中 checkRegistered(pos) 误判为已注册 → unregister(pos, 本维度level) 会把另一个维度的数据 markRemoved 并以错误维度执行 discard → CHUNK_REF_COUNT 错乱、被误删数据的区块永远无法 setChunkForced(false)。建议键改为 (dimension, pos) 组合。
  • OverseerBlockEntity.tick — 结构破坏后 oldLevel 不重置。 checkOverseerBlocks() 失败路径直接 return,未将 oldLevel 复位为 -1(旧代码在重新注册前会重置)。/overseer list 会显示陈旧的 "Lv.3, loads: 0 chunk(s)"。
  • scanPyramidBase — 底座方块偏移(块)直接当作区块偏移(chunk)使用。 BlockOffsetMapping(dx, dz, ...) 中 dx/dz 是方块级偏移,createLoadChunkDatacenterChunk.x() + m.chunkOffsetX() 按区块粒度相加——3x3 底座加载 3x3 区块(9 chunks),9x9 底座加载 9x9(81 chunks),加载区以监督者所在区块为中心做偏移映射,并非底座方块实际所在区块。若"一个底座方块对应一个区块"的本意是"加载包含该底座的区块",应改用 ChunkPos.containing(blockPos)。请确认设计意图。
  • removeAll 从未被调用 + 静态状态无清理。 LevelLoadManager.removeAll 在目标分支上没有任何调用点,且 ServerStopped 未清理 LOAD_DATA_MAP/CHUNK_REF_COUNTOverseerUtil.placedOverseers 同样无清理(其他管理器如 PowerGrid/FluidNetworkManager 都有 clear())。服务端重启后 /overseer 命令会遍历陈旧坐标并刷 "Invalid overseer" warn 日志。
  • WormholeStabilizerHandler — 虫洞加载行为变化。 LoadChuckData.createLoadChuckData(1, pos, false, level)(isNeedRandomTick=false → 无随机刻)改为 createSimpleLoadChunkData(1, pos, level)(DEFAULT flags → 随机刻开启)。若旧行为确实是无随机刻,这是回归;需确认是否有意。
  • 19 个文件缺 EOF 换行(含全部新 Java 文件、6 个新 tag JSON、改动的 LevelLoadManager)。仓库现有生成文件均以 \n 结尾,建议与 datagen 输出保持一致。
  • en_us.json / ItemTooltipManager — 英文 tooltip 混入中文全角逗号: "max 4 layers, 9x9 range), different base blocks...",应为半角 ,

💡 建议

  • OverseerBlockEntity.tick — 每 tick 全量扫描底座(4 级满配时 9+25+49+81=164 次 getBlockState/tag 判断),可考虑每 N tick 扫描或缓存结果。
  • MobCategoryMixin 与 MobMixin 功能重复:MobMixin 已用 WrapOperation 覆盖 checkDespawn 的 despawnDistance,MobCategoryMixin 实际是死代码(其读取的 CURRENT_SPAWNING_CHUNK 只在 spawnForChunk 期间有效,checkDespawn 路径上不可用)。建议删除或合并。
  • LoadChunkData L218: serverLevel,Source.SIMPLE 缺空格(纯格式)。
  • OverseerCommand: 成功返回 0、失败返回 1,与 brigadier 惯例相反(成功应返回非 0),建议统一为现有命令的风格。

🟢 看起来不错

  • 引用计数设计CHUNK_REF_COUNT 按 (dimension, chunk) 计数,多个加载源重叠时不会重复 setChunkForced(false),reload 顺序改为先 discard 后 apply 也正确。
  • 随机刻抑制实现isReentering 守卫 + 重入 tickChunk(chunk, 0) 的方案正确,且与现有 ServerLevelMixin(只注入 addEntity/addFreshEntity)无冲突。
  • 标签分层清晰:tier 0-3 + royal/frost/ember series 的嵌套结构,BlockTagLoader 与生成的 6 个 JSON 完全一致。
  • 命令权限写法(permissions().hasPermission)、HoverEvent.ShowText/CopyToClipboardsendSuccess 均与仓库现有命令模式一致;lang 三处(en_us/en_ud/CommandLang)同步 ✓。

📋 声称验证表

声称 状态 对应实现
解决 #1582,重写加载方式 LoadChunkData(新)/ LevelLoadManager / ChunkFeatureManager
一个底座方块对应一个区块 ⚠️ 偏移映射实现(见警告),语义需确认
监督者最高 4 级 TIER_RADIUS {1,2,3,4},tooltip "max 4 layers, 9x9 range"
更多方块可作底座 tier 0-3 tags(铁/金/钻/绿宝石 → 皇家/浮霜 → 下界合金/余烬/多相 → 超限)
铁金绿钻提升等级但不加载 getBlockTier >= 1 过滤 + getBlockSourceFlags
皇家钢默认加载(强加载+随机刻) ⚠️ DEFAULT flags — 依赖强加载区块原生 tickChunk 的假设(见警告)
浮霜强加载不随机刻 FROST=NO_RANDOM_TICK + ServerLevelRandomTickMixin
下界合金+余烬:随机刻+火焰蔓延 FIRE flag + ServerLevelFireSpreadMixin
多相物质块:不随机刻+火焰蔓延 MULTIPHASE flags
超限合金块:自然刷新+刷怪笼刷新(2048区块内) 刷怪笼 ✅(BaseSpawnerMixin);自然刷新 🔴 失效(ThreadLocal 未 set)
/overseer 显示等级、加载区块数、总数 OverseerCommand + getOverseerChunkCount + getAllOverseerForcedChunkCount

结论: REQUEST_CHANGES — 必须修复 🔴(CURRENT_SPAWNING_DIMENSION 从未 set 导致超限合金块的自然刷新特性静默失效),并确认强加载区块随机刻的运行时行为;其余警告项请逐条确认。

🧪 测试建议

被测目标 推荐测试场景 优先级
OverseerBlockEntity.tick 跨维度同坐标监督者并存;结构破坏后 /overseer 显示 🔴
ServerLevelRandomTickMixin 皇家钢 vs 浮霜区块随机刻差异;randomTickSpeed=0 游戏规则 🔴
ChunkFeatureManager 自然刷怪链路 超限合金块区块在玩家 >128 块外是否刷怪、怪物防消失 🔴
LevelLoadManager.forceChunk 两个监督者重叠加载同一区块的增减计数 🟡
LoadChunkData.createLoadChunkData 混合底座(tier0 填充 + tier1 加载)的区块集合与 flags 🟡

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

分析完成。以下是审查意见。


代码审查摘要 — PR #4354

操作: synchronize
范围: 35 个文件(25 Java, 19 新增, 2 删除)/ 1948 行 diff
主题: 监督者(Overseer)区块加载系统重构 — 金字塔底座分级、按方块类型分区加载特性、/overseer 命令

架构总览

  • LoadChuckData(拼写错误)→ 重命名为 LoadChunkData,旧 RandomChuckTickLoadManager 删除,新增 ChunkFeatureManager 统一管理按维度×区块×来源的加载特性(跳过随机刻/火焰蔓延/自然刷怪/刷怪笼刷怪)
  • LevelLoadManager 新增 CHUNK_REF_COUNT 引用计数,多个加载源重叠同一区块时只 setChunkForced 一次,全部释放才卸载 — 实现正确
  • 底座改为 4 层倒金字塔(3×3 → 5×5 → 7×7 → 9×9),每层半径 TIER_RADIUS={1,2,3,4},每个 tier≥1 的底座方块映射一个区块偏移("一个底座方块对应一个区块")
  • 8 个新 mixin + 2 个事件监听扩展 + 1 个新命令

🔴 关键

  1. ChunkFeatureManager.CURRENT_SPAWNING_DIMENSION 从未被赋值 → DistanceManagerMixin 两个注入全部失效(死代码),超限合金"2048 区块内自然刷怪"功能未实现

    • 全仓库 grep 确认:CURRENT_SPAWNING_DIMENSION 只有定义和 .get()DistanceManagerMixin 两处、MobCategoryMixin 一处),没有任何 .set() 调用NaturalSpawnerMixin 只 set/remove CURRENT_SPAWNING_CHUNK
    • 后果链:DistanceManager.hasPlayersNearby 注入中 shouldAllowNaturalSpawn(null, chunkPos)CHUNK_SOURCES.get(null) → null → 返回 false → 超限合金区块的 hasPlayersNearby 永远保持 FALSEgetSpawnCandidateChunks 注入中 getAllNaturalSpawnChunks(null) → 空集 → 超限合金区块不会进入自然刷怪候选列表
    • 而自然刷怪候选区块的判定(ChunkMap.tickupdatingChunkMap + hasPlayersNearby)正是依赖这个被写死的路径。ChunkMapMixin.playerIsCloseEnoughForSpawning(用 player.level().dimension(),有效)只影响已是候选区块的距离放宽,救不了候选列表。
    • 结果:"超限合金块启用区块内生物自然刷新"实际上只在玩家处于原版判定距离内时生效,"2048 区块"扩展不工作MobCategoryMixin 同理——getDespawnDistanceMob.checkDespawn(实体 tick 栈)中调用,不在 spawnForChunk 栈内,CURRENT_SPAWNING_CHUNK 此时也是 null,该注入永远不生效(despawn 距离扩展实际靠 MobMixin 的 WrapOperation 用实体自身位置实现,那部分是有效的)。
    • 修复建议:在 NaturalSpawnerMixin HEAD 注入中同时 CURRENT_SPAWNING_DIMENSION.set(level.dimension())(RETURN 一并 remove),或给 DistanceManagerMixin 注入 ChunkMap 的 level 字段。同时可删除冗余的 MobCategoryMixin
  2. OverseerBlockEntity.tick():监督者上部方块被破坏后修复,永远不会重新注册加载

    • checkOverseerBlocks() 失败分支(BE 所在 BOTTOM 方块仍在,MID/TOP 被破坏)执行 unregister 后直接 return不重置 oldLevel/oldTierHash
    • 玩家修复 MID/TOP 方块后:扫描结果与旧值完全相同 → levelChanged=false && tiersChanged=false → 提前 return → 区块加载永不恢复(直到底座被改动触发 hash 变化)。
    • 旧代码同样存在此问题,但本次是重写,应顺手修复:失败分支中把 oldLevel=-1; oldTierHash=0 与 unregister 一起重置(或统一走 unregister+reset 公共路径)。

⚠️ 警告

  • ItemTooltipManager.java:597 + 生成数据 en_us.json/en_ud.json — 英文 tooltip 混入全角逗号:"...(max 4 layers, 9x9 range), different base blocks provide different effects",应为半角 , 。源字符串在 SHIFT.put 中,生成文件需重新 runData。
  • OverseerCommand.java:6 — 残留编辑标记注释:import net.minecraft.commands.Commands; // ← 新增:普通导入,提交前应删除。
  • ChunkFeatureManager.clear() 定义后从未被调用OverseerUtil.placedOverseers 也没有服务器停止清理路径(ServerLifecycleEventListener.onServerStopped 未清)。虽然 BE unload 事件会逐条移除,但异常终止/世界整体卸载时可能残留跨会话脏数据。建议在 ServerStoppedEventLevelEvent.Unload 中统一清理。
  • OverseerBlockEntity.VALID_BASE_TAGS — raw TagKey[] + @SuppressWarnings("unchecked"),建议 TagKey<?>[] 或逐字段声明,避免压制警告掩盖问题。

💡 建议

  • NaturalSpawnerMixinCURRENT_SPAWNING_CHUNK HEAD set / RETURN remove 建议改为 try/finally(spawnForChunk 中途异常时 RETURN 注入不执行,ThreadLocal 残留旧值;虽然后续会被覆盖,但更稳妥)。
  • LevelLoadManager.getAllOverseerForcedChunkCount.distinct().mapToInt(cp -> 1).sum() 可直接 .distinct().count()
  • LOAD_DATA_MAP/CHUNK_REF_COUNTBlockPos 为 key 不含维度——wormhole 稳定器与监督者(或跨维度同坐标)共用一个注册表,存在理论冲突。旧代码遗留,非本 PR 引入,但重写注册表时可顺带改为维度感知 key。
  • scanPyramidBase 每 tick 全量扫描最多 164 次 getBlockState(9×9+7×7+5×5+3×3),可接受但属热路径,后续可考虑邻接更新缓存。
  • 多底座重叠区块的特性为 OR 语义(任一 frost 覆盖即跳过随机刻、任一 ember 覆盖即允许火焰),是合理设计取舍,建议在 wiki/tooltip 说明,避免玩家困惑。

🟢 看起来不错

  • 引用计数 forceChunk 正确处理多加载源重叠:count==0setChunkForced(true)count<=1 才 false,无重复强制/提前释放。
  • ServerLevelRandomTickMixin 的重入标志 + tickChunk(chunk, 0) 技巧干净:取消后以 tickSpeed=0 重入,跳过随机刻但保留调度刻/BE tick,且 tickSpeed<=0 守卫防止原版本就无随机刻时的无谓重入。
  • LoadChuckDataLoadChunkData 重命名 + RandomChuckTickLoadManager 删除,语义更清晰;标签分层(tier_0..3 + royal/frost/ember_series)数据驱动,扩展性好。
  • LoadChunkData.apply/discardapplied 幂等标志 + FIFO 延迟队列顺序保证,启动期注册/卸载不会错序。
  • BaseSpawnerMixin/ServerLevelFireSpreadMixin 的守卫(先查原版结果、isChunkManaged 前置)正确,未破坏非超限合金区块行为。

📋 声称验证表

声称 状态 对应实现
一个底座方块对应一个区块 scanPyramidBaseBlockOffsetMapping(dx,dz,...) → chunk 偏移
监督者最高 4 级 TIER_RADIUS={1,2,3,4}MAX_LEVEL=4、tooltip "max 4 layers, 9x9 range"
更多方块可作为底座 overseer_base_tier_0..3 标签(铁/金/钻/绿宝石 + 系列方块 + 下界合金 + 多相 + 超限合金)
铁/金/绿宝石/钻石提升等级但不加载 blockTier>=1 才加入 offsetMappings,tier_0 方块只过 isTierComplete
皇家钢默认加载(强加载+随机刻) SourceFlags.DEFAULT=0 → 不跳过随机刻
浮霜强加载但不运算随机刻 FROST=NO_RANDOM_TICK + ServerLevelRandomTickMixin
下界合金+余烬:随机刻+火焰蔓延 FIRE=ALLOW_FIRE_SPREAD(不含 NO_RANDOM_TICK)+ ServerLevelFireSpreadMixin
多相物质:不随机刻+火焰蔓延 MULTIPHASE=NO_RANDOM_TICK|ALLOW_FIRE_SPREAD
超限合金:自然刷新+刷怪笼刷新(玩家 2048 区块内) ⚠️ 部分 刷怪笼 ✅(BaseSpawnerMixin);自然刷怪 ❌(CURRENT_SPAWNING_DIMENSION 从未 set,DistanceManagerMixin 失效);despawn 距离 ✅(MobMixin
/overseer 命令显示等级/区块数/总数 OverseerCommand + getOverseerChunkCount/getAllOverseerForcedChunkCount
resolved #1582 功能重做方向一致(issue 内容无法从 diff 验证)

结论: REQUEST_CHANGES — 两个关键问题:① 超限合金"自然刷怪 2048 区块扩展"因 CURRENT_SPAWNING_DIMENSION 从未赋值而整体失效(功能声称未落地);② 监督者上部方块破坏后修复无法恢复区块加载(用户可见的恢复性 bug)。修复后建议实测验证:超限合金底座上无玩家(但 2048 区块内)时怪物是否自然刷新、监督者 MID/TOP 破坏再修复后是否恢复强加载。

🧪 测试建议

被测目标 推荐测试场景 优先级
OverseerBlockEntity.tick MID/TOP 方块破坏→修复后注册恢复;底座方块替换为同 tier/flags 方块(hash 不变不重复注册);tier_0 铁底座 4 层 vs royal 4 层的加载范围差异 🔴
ChunkFeatureManager 特性聚合 同一区块被 frost+royal 重叠加载 → 随机刻被跳过;ember+royal 重叠 → 火焰允许;多维度同坐标不串扰 🔴
LevelLoadManager.forceChunk 引用计数 两个监督者重叠加载同一区块,逐个移除 → 最后一个移除才 unforce 🟡
BaseSpawnerMixin 超限合金区块内无玩家时刷怪笼激活;royal 区块内刷怪笼保持原版行为 🟡
ServerLevelRandomTickMixin 重入后调度刻/BE tick 仍执行;游戏规则 randomTickSpeed=0 时不干预 🟢

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).


import dev.dubhe.anvilcraft.api.world.load.ChunkFeatureManager;
import net.minecraft.world.level.BaseSpawner;
import net.minecraft.core.BlockPos;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ [Checkstyle] <com.puppycrawl.tools.checkstyle.checks.imports.CustomImportOrderCheck> reported by reviewdog 🐶
Wrong lexicographical order for 'net.minecraft.core.BlockPos' import. Should be before 'net.minecraft.world.level.BaseSpawner'.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

curl -sL "https://api.github.com/repos/Anvil-Dev/AnvilCraft/issues/1582" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('body','')[:2000])" 2>/dev/null || curl -sL "https://api.git...

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/issues/1582 — or: vet https://api.github.com/repos/Anvil-Dev/AnvilCraft/issues/1582 (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

All analysis complete. Here's my review:


代码审查摘要 — PR #4354

操作: synchronize
范围: 35 个文件 (25 Java, 19 新增, 2 删除) / 1947 行 diff
架构: 监督者重构为「金字塔底座扫描 + 逐块→逐区块映射 + 按维度/区块/来源注册的 ChunkFeatureManager + 引用计数强加载」,并用 8 个新 mixin 实现随机刻/火焰蔓延/生物刷新控制,删除了旧的 LoadChuckDataRandomChuckTickLoadManager

🔴 关键问题

1. CURRENT_SPAWNING_DIMENSION 从未被赋值 → 超限合金块「生物自然刷新」整条链路失效
ChunkFeatureManager.java:17 声明了 CURRENT_SPAWNING_DIMENSION,但整个 PR 树中只有 3 处 .get()DistanceManagerMixin:27,42MobCategoryMixin:23),没有任何一处 .set()NaturalSpawnerMixin 只 set/remove 了 CURRENT_SPAWNING_CHUNK)。因此三个消费者永远读到 nullshouldAllowNaturalSpawn(null, ...) 永远返回 false:

  • DistanceManagerMixin.anvilcraft$onGetSpawnCandidateChunksgetAllNaturalSpawnChunks(null) 返回空集 → 超限合金块区块永远不会被加入刷怪候选列表NaturalSpawner.spawnForChunk 根本不会对这些区块执行 → 无自然刷新;
  • DistanceManagerMixin.anvilcraft$onHasPlayersNearby → 永不生效;
  • MobCategoryMixin.anvilcraft$onGetDespawnDistance → 永不生效(反离/不消失由 MobMixin 按实体实际位置兜底,但那只解决"不消失",不解决"生成")。

⚠️ 即使把 set() 补进 NaturalSpawnerMixin 也修不好:getSpawnCandidateChunks()ServerChunkCache.tickspawnForChunk 循环之前调用,而 ThreadLocal 在每次 spawnForChunk RETURN 就被 remove,调用候选列表时仍是 null。建议:

  • ServerChunkCache.tick(或 DistanceManager.tick)的候选区块迭代外层 set/remove 维度,或
  • DistanceManagerMixin 在 ThreadLocal 为 null 时遍历 CHUNK_SOURCES 全部维度取并集(最简单稳妥)。

ChunkMapMixin.playerIsCloseEnoughForSpawning 是唯一能取到维度(player.level().dimension())的路径,但它只在候选区块的生成流程内被调用——候选列表为空时它也不会被触发。

⚠️ 警告

  • OverseerBlockEntity.java tick 失效路径状态残留checkOverseerBlocks() 失败时只 unregister 就 return,oldLevel/oldTierHash 不重置。若 MID/TOP 监督者方块被短暂移除后恢复(如活塞推动、玩家拆装),重新扫描得到相同的 level+hash → 提前 return → 永不重新注册,区块保持不加载。修复:失败路径中重置 oldLevel = -1; oldTierHash = 0;,或在提前 return 条件中加入 !LevelLoadManager.checkRegistered(pos)
  • WormholeStabilizerHandler.java 行为变更 — 旧 createLoadChunkData(1, pos, false, level)isNeedRandomTick=false,不运算随机刻)改为 createSimpleLoadChunkData(1, pos, serverLevel)(默认 flags=0 → 强加载区块按原版默认运算随机刻)。虫洞稳定器的 3x3 加载区现在会跑随机刻,与旧行为相反,请确认是否有意。
  • 每 tick 扫描开销OverseerBlockEntity.tick() 每 tick 全量扫描 4 层金字塔(3×3+5×5+7×7+9×9 = 164 个方块),每个方块还要做最多 4 次 tag 判断(isTierComplete + getBlockTier + getBlockSourceFlags),约 1000+ 次 tag 查找/监督者/tick,且提前 return 发生在扫描之后,无法避开。建议按方块更新事件(neighbor update/block event)触发重扫,或每 N tick 节流。
  • en_us.json tooltip 混入全角逗号"(max 4 layers, 9x9 range), different base blocks..." 英文文本里混入中文全角逗号" ,",en_ud 同步带入。应改为英文标点。
  • 19 处文件缺末尾换行No newline at end of file),含 12 个新 Java 文件,checkstyle 可能报错。

💡 建议

  • 跨监督者/混合金字塔的 flag 冲突语义ChunkFeatureManagershouldSkipRandomTick/shouldAllowFireSpread 等对同一区块多来源取 OR:随机刻"跳过"优先于"默认运算"(皇家钢与浮霜重叠时整块停随机刻),火焰/刷新"允许"优先。建议在文档或注释中明确该优先级,确认是否符合预期。
  • OVERSEER_BASE 旧 tag 已成死代码 — 新逻辑只读 tier 0-3 tag,ModBlockTags.OVERSEER_BASE 仅剩 ModBlocks 注册时打标和旧 overseer_base.json 数据文件,无任何读取方,建议清理或标注兼容保留。
  • MobCategoryMixinMobMixin 双路径 — 两者都改 getDespawnDistance(前者靠 ThreadLocal、后者靠实体实际位置)。前者仅在 spawnForChunk 窗口内有效且依赖已失效的 ThreadLocal,建议统一走 MobMixin 风格或明确分工。
  • NaturalSpawnerMixin RETURN 注入不处理异常路径spawnForChunk 抛异常时 CURRENT_SPAWNING_CHUNK 会泄漏到后续调用,建议 try/finally 或确认 spawnForChunk 自身兜底。
  • LoadChunkDatalevel/tier 字段实际只用于展示与 hash — 区块集完全由 offsetMappings 决定,tier 只参与 hashTierMappings。建议加注释说明,避免后续维护者误以为 tier 参与加载半径计算。

🟢 看起来不错

  • 引用计数强加载LevelLoadManager.forceChunk)正确覆盖多监督者共享区块的叠加/释放,count<=1 边界处理安全。
  • 删除完整性 — 在 PR 树全量 grep 确认 LoadChuckData/RandomChuckTickLoadManager 无残留引用,WormholeStabilizerHandler、事件监听均已迁移到新 API。
  • 生命周期LevelEventListener.onLevelUnloadremoveAll(清 LOAD_DATA_MAP+CHUNK_REF_COUNT),notifyServerStarted 冲刷延迟任务,reload 中 discard→apply→removeIf 顺序正确。
  • SourceFlags 与 PR 描述逐项吻合:皇家钢=DEFAULT(随机刻开)、浮霜=NO_RANDOM_TICK、下界合金/余烬=FIRE、多相=MULTIPHASE、超限=TRANSCENDIUM;tier-0 方块(铁/金/钻/绿宝)通过 blockTier >= 1 门控不产生映射、只提升显示等级,符合"提级不加载"。
  • en_ud 镜像正确("9x9"→"6x6" 是标准翻转),/overseer 命令与 CommandLang 键值、en_us/en_ud 同步一致。
  • 新 mixin 全部登记进 anvilcraft.mixins.jsonpermissions() API 用法与 base 分支现有命令一致。

📋 声称验证表

声称 状态 证据
resolved #1582(监督者加强建议) 1底座=1区块、4级上限、更多底座、分级效果、/overseer 命令均在 diff 中实现
一个底座方块对应一个区块 BlockOffsetMapping(dx,dz) 直接作为区块偏移进入 createLoadChunkData
最高 4 级 TIER_RADIUS = {1,2,3,4} + MAX_LEVEL = 4
更多方块可作为底座 overseer_base_tier_0..3 四个新 tag
铁/金/绿宝/钻石提级不加载 tier-0 块 blockTier>=1 门控被排除出 mappings
皇家钢=强加载+随机刻 DEFAULT flags(0),随机刻不跳过
浮霜=强加载不随机刻 FROST = NO_RANDOM_TICK
下界合金/余烬=+随机刻+火焰蔓延 FIRE = ALLOW_FIRE_SPREAD
多相=不随机刻+火焰蔓延 MULTIPHASE = NO_RANDOM_TICK|ALLOW_FIRE_SPREAD
超限=自然刷新+刷怪笼刷新 ⚠️ 实现齐全但整条自然刷新链路因 CURRENT_SPAWNING_DIMENSION 从未赋值而失效(见 🔴1)
/overseer 显示等级、加载区块数、总数 OverseerCommand + getOverseerChunkCount + getAllOverseerForcedChunkCount

结论: REQUEST_CHANGES — 架构重构方向清晰、删除与生命周期处理干净,但超限合金块核心卖点"生物自然刷新"当前完全不可用(ThreadLocal 未赋值导致 spawn 候选链路全断),且修复不能只靠补一个 set()(getSpawnCandidateChunks 的调用时机在 spawnForChunk 之前),需要调整维度上下文的传递方式后再合入。

🧪 测试建议

被测目标 推荐测试场景 优先级
超限合金块自然刷新链路 玩家远离 128+ 格(2048 区块内)时超限金字塔区块仍生成生物;修复 ThreadLocal 后验证 getSpawnCandidateChunks 包含目标区块 🔴
ServerLevelRandomTickMixin 浮霜/多相/超限区块随机刻停用且方块实体 tick 不受影响;皇家钢区块随机刻正常 🟡
引用计数 forceChunk 两个监督者共享区块,先后拆除,确认最后一个释放时区块才真正卸载 🟡
监督者结构失效/恢复 拆装 MID/TOP 监督者方块后,金字塔配置不变时加载是否恢复(当前会失败) 🔴
虫洞稳定器 3x3 加载区随机刻行为变更确认(现为开启) 🟡

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

两个关键问题均已确认。审查完成,输出审查意见。

代码审查摘要 — PR #4354(synchronize: Powerful overseer)

操作: synchronize
范围: 35 个文件(25 Java,19 新增,2 删除)/ ~1950 行 diff
主题: 监督者(Overseer)区块加载系统重构 — 底座方块按层(tier)划分、等级上限提升至 4、按底座材质区分加载特性(随机刻/火焰蔓延/生物刷新)、新增 /overseer 命令

已通过 refs/pull/4354/head 拉取 PR 分支交叉验证(残留引用、ThreadLocal 赋值点、标签读取方)。

🔴 关键问题

  1. CURRENT_SPAWNING_DIMENSION ThreadLocal 从未被赋值 → 3 个 mixin 注入是死代码(超限合金"自然刷新"功能不完整)
    ChunkFeatureManager 声明了 CURRENT_SPAWNING_DIMENSION,但整个 PR 分支上只有 3 处读取DistanceManagerMixin ×2、MobCategoryMixin ×1),没有任何 set 调用NaturalSpawnerMixin 只 set/remove CURRENT_SPAWNING_CHUNK)。读取时值为 nullCHUNK_SOURCES.get(null) 恒为 null → 以下注入永远不生效:

    • DistanceManagerMixin.hasPlayersNearby(FALSE→DEFAULT 覆盖)→ 玩家远离时"无玩家在附近"判定无法被改写,超限合金区块的自然刷新可能被 vanilla 的玩家距离门控跳过;
    • DistanceManagerMixin.getSpawnCandidateChunks(扩展候选区块)→ 超限合金区块不进 SpawnState 统计 → 该区块内已存在的生物不计入全局/分类 mob cap,可能超量刷怪
    • MobCategoryMixin.getDespawnDistance → SpawnState 统计半径扩展失效。

    目前只有 ChunkMapMixin.playerIsCloseEnoughForSpawningMobMixin.checkDespawn(自行从实体位置取 chunk)真正生效。修复:在 NaturalSpawnerMixin.spawnForChunk 的 HEAD 注入里一并 CURRENT_SPAWNING_DIMENSION.set(level.dimension())(与 CURRENT_SPAWNING_CHUNK 成对 set/remove)。

  2. OverseerBlockEntity.tick 失败路径不重置状态 → 结构修复后永不重新注册区块加载
    checkOverseerBlocks() 为 false 时只 unregisterreturn不清空 oldLevel/oldTierHash。复现场景:监督者正常工作(oldLevel=2,已注册)→ 敲掉中间一格(MID)→ tick 注销加载、但 oldLevel 仍为 2 → 补回 MID → scanPyramidBase 结果与缓存完全一致 → !levelChanged && !tiersChanged → 提前 return → 区块加载永远不会恢复,直到底座结构再变一次。修复:失败路径上 this.oldLevel = -1; this.oldTierHash = 0;(或注销后统一重置)。

⚠️ 警告

  1. WormholeStabilizerHandler 行为静默改变:随机刻从关闭变为开启
    createLoadChuckData(1, pos, false, ...)isNeedRandomTick=false)→ 新 createSimpleLoadChunkData(1, pos, serverLevel)(flags=0 → 默认运算随机刻)。虫洞稳定器的 3×3 加载区现在会跑随机刻(作物生长、方块随机行为),影响无关功能且带来额外性能开销。若为有意统一,请在 PR 描述中说明。

  2. en_us.json tooltip 混入全角逗号
    "(max 4 layers, 9x9 range), different base blocks provide different effects" — 英文文本中出现 CJK 全角 (且已原样镜像进 en_ud.json)。应改为半角 , 后重新 runData。

  3. 多相物质块的"启用火焰蔓延"实际不生效
    火焰扩散由火焰方块的随机刻驱动,而 MULTIPHASE = NO_RANDOM_TICK | ALLOW_FIRE_SPREAD 且随机刻跳过策略是"任一源跳过即跳过"(ChunkFeatureManager.shouldSkipRandomTick any-wins)→ 该区块根本不执行随机刻 → canSpreadFireAround 永远不会被调用到。下界合金/余烬(无 NO_RANDOM_TICK)的火焰蔓延是有效的,但多相物质的组合自相矛盾。

  4. OverseerUtil.placedOverseers 静态泄漏
    (a) 监督者被破坏(方块被替换)时,onUnloadOverseershouldTrack 检查 getBlockState() 已不是 OverseerBlock → 记录不删除,幽灵条目永久残留(/overseer list 持续报 "Invalid overseer");(b) 静态 Map 在服务器停止/世界切换时从不 clear(ChunkFeatureManager.clear() 也无调用者),单人模式切换世界后旧世界的条目会污染新世界同名维度。建议:unload 时以 hasLevel/自身 BE 状态判断而非方块状态,并在 ServerStopping 时清理。

  5. 策略叠加:同一区块被多个监督者覆盖时特性互相矛盾
    "跳过随机刻"是 any-wins(frost/多相/超限任一覆盖即整区块跳过随机刻),会压制同区块皇家钢/下界合金的"运算随机刻";火焰蔓延与自然刷新则是 allow-wins。三个特性采用不同的聚合语义,且与"皇家钢=原版默认加载"的宣称在重叠场景下冲突。建议至少写入文档或在 LoadChunkData 注释中明确聚合规则。

💡 建议

  1. "一个底座方块对应一个区块"的映射语义与描述不符scanPyramidBase 把方块的方块偏移 (dx,dz) 直接当区块偏移用(centerChunk.x() + m.chunkOffsetX()),即底座方块在 (dx,dz) 偏移处 → 加载中心区块偏移 (dx,dz) 的区块,而非该方块实际所在的区块。完整金字塔下包含底座方块的区块都在映射范围内(±1 ≤ ±level),功能无缺口,但在区块边界处(监督者位于区块边缘)加载区域会与底座实际位置错位,且同样金字塔在区块内不同位置会产生不同加载集。若意图是"每个底座方块加载它所在区块",应改用 ChunkPos.containing(tierCenter.offset(dx, 0, dz));若是"层级投影"设计,建议在代码注释中说明。
  2. oververse_base tag 已成为死代码:全库已无任何读取方(BE 只读 tier tags),但 ModBlocks 仍在给皇家钢/浮霜/余烬等打该 tag,生成的 overseer_base.json 也残留,建议清理。
  3. scanPyramidBase 每 tick 全量扫描(4 级时 3×3+5×5+7×7+9×9 ≈ 164 次 getBlockState + tag 匹配),即使无变化也执行。建议缓存并在 NeighborChanged/底座变化时失效。
  4. LOAD_DATA_MAP 仍以 BlockPos 为键:不同维度同坐标的两个监督者会互相挤掉注册(第二个 register 被 containsKey 静默忽略)。既然本次已引入维度键(CHUNK_REF_COUNTChunkFeatureManager),建议数据表也按维度分键。
  5. 等级与加载量跳变:纯铁/金/钻石/绿宝石底座(tier-0)时 4 级监督者只加载自身 1 个区块;等级 1→2 时加载量从 1 跳到 25 区块。功能与"铁块只提等级不加载"一致,但建议在 tooltip 中说明加载量与底座材质相关。
  6. 新 mixin 全部面向 vanilla 内部方法(BaseSpawner.isNearPlayerDistanceManager.hasPlayersNearby/getSpawnCandidateChunksChunkMap.playerIsCloseEnoughForSpawningServerLevel.tickChunk 等),签名不符会在启动时崩溃而非编译期暴露,建议合并前跑一次服务端启动冒烟测试。
  7. /overseer list 必须显式传维度参数,建议缺省时用发送者所在维度。

🟢 看起来不错

  • 引用计数强加载CHUNK_REF_COUNT + forceChunk)修复了旧实现"两个加载源共享区块、移除一个即卸载"的隐患;apply/discardapplied 标志防重入,reload 先 discard 后 apply 顺序正确,跨源计数平衡。
  • 类型迁移干净:LoadChuckDataLoadChunkDataRandomChuckTickLoadManager 删除后全分支零残留引用ServerLifecycleEventListener 中旧 tick 调用同步移除。
  • Tier/系列 tag 体系清晰(royal/frost/ember 系列 tag + 4 级 base tier tag),getBlockSourceFlags 与 PR 特性表一一对应(皇家钢=默认、浮霜=禁随机刻、下界合金/余烬=火焰、多相=禁随机刻+火焰、超限=自然刷新+刷怪笼)。
  • ServerLevelRandomTickMixin 的取消+重入(tickChunk(chunk, 0))带 isReentering 守卫,正确保留方块刻/流体刻/实体刻而只跳过随机刻。
  • /overseer list 输出等级、单监督者加载区块数、维度去重总数,与 PR 宣称一致;命令与 tooltip 文案 en_us/en_ud 均已重新生成。
  • 无硬编码密钥、无 TODO/FIXME 残留。

📋 声称验证表

声称 状态 对应实现
一个底座方块对应一个区块 ✅(近似) offsetMappings → centerChunk + (dx,dz),中心区块恒加载
监督者最高 4 级 MAX_LEVEL=4TIER_RADIUS={1,2,3,4}
更多方块可作为底座 4 个 tier tag 覆盖铁/金/钻/绿宝石/皇家钢/浮霜/下界合金/余烬/多相/超限
铁/金/绿宝石/钻石提升等级但不加载 blockTier >= 1 才生成 mapping,tier-0 仅作结构
皇家钢=强加载+随机刻 DEFAULT flags
浮霜=强加载、无随机刻 FROST = NO_RANDOM_TICK
下界合金+余烬=强加载+随机刻+火焰蔓延 FIRE = ALLOW_FIRE_SPREAD(随机刻为默认开启)
多相物质=强加载、无随机刻、火焰蔓延 ⚠️ MULTIPHASE 标志齐全,但无随机刻则火焰不会扩散,火焰蔓延实际无效
超限合金=强加载+自然刷新+刷怪笼刷新 ⚠️ TRANSCENDIUM 标志齐全,但 CURRENT_SPAWNING_DIMENSION 未赋值 → 3 处 mixin 死代码,功能不完整(见 🔴#1
/overseer 显示等级/加载数/总数 OverseerCommand + getOverseerChunkCount + getAllOverseerForcedChunkCount

结论: REQUEST_CHANGES — 架构重构方向正确、迁移干净,但存在两个功能性问题需修复:① 自然刷新特性因未赋值的 ThreadLocal 而半失效(3 处 mixin 死代码);② 监督者结构破坏后按原样修复时区块加载不会重新注册。

🧪 测试建议

被测目标 推荐测试场景 优先级
OverseerBlockEntity.tick 敲掉 MID 格 → 补回同结构 → 断言 chunk 重新强加载(复现 🔴#2);纯 tier-0 底座 4 级 → 仅 1 区块 🔴
LevelLoadManager.forceChunk 两个监督者共享同一区块 → 移除其一 → 区块仍加载;全部移除 → 卸载 🔴
ChunkFeatureManager 不同维度同坐标注册互不干扰;同区块多源 skip/allow 聚合语义 🟡
ServerLevelRandomTickMixin 浮霜区块:随机刻被跳过但方块刻/流体刻/实体刻仍执行 🟡
WormholeStabilizerHandler 虫洞加载区随机刻行为变化确认(有意 or 回归) 🟡
LoadChunkData.createLoadChunkData 混合材质底座的 flag 并集、中心区块恒加载、偏移映射边界(监督者位于区块边缘) 🟡

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/chunk-loading-review-patterns.md in skill 'minecraft-mod-pr-review' (1 replacement). · Patched SKILL.md in skill 'minecraft-mod-pr-review' (1 replacement).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants