Repository navigation
Conversation
第一阶段卫星实体此前 playerControllable=false,无法进入。本次开放接管能力。 判定链:possess 的前提是 InputHandler.java:783 的 unit.isAI() && unit.team == player.team() && !unit.dead && unit.playerControllable() 而 isAI() 的实现是 controller instanceof AIController(UnitComp.java:494)。 三处改动: 1. OrbitSatelliteController 改为继承 AIController(原先只 implement UnitController, isAI() 恒假、ctrl 点不进来),覆写 updateUnit() 且不调 super——父类会跑 updateVisuals/updateTargeting/updateMovement,那是作战单位的索敌与移动,卫星的运动 完全由轨迹函数决定。 2. controller 字段显式指定为轨迹控制器工厂。不能只设 aiController:UnitType.java:281 的 默认工厂在 playerControllable=true 且队伍为玩家时直接返回 new CommandAI(), aiController 只在该三元的另一分支被引用——卫星会因此失去唯一的运动驱动源而静止。 3. 覆盖 UnitType.update(Unit):possess 会把 controller 换成 Player 对象本身 (UnitComp.java:950 的 isPlayer() = controller instanceof Player),此后 UnitComp.java:839-841 调的是 Player.updateUnit(),轨迹控制器停跑;该钩子由 UnitComp.java:654 无条件每帧调用、与 controller 无关,用它补位。 与 updateUnit 路径由 unit.getPlayer() != null 互斥,不会双倍累加相位; !net.client() 保证相位只在服务端累加一次,客机仍靠单位同步取位置。 接管期间 applyMotion 每帧覆写位置并清零 vel,玩家 WASD 输入被立刻抹掉——能进去看、 沿轨道飞,但轨迹不可手改。 版本号 a0.13.0.0 -> a0.13.0.1(上游 PR 例行 bump)。
fork/main 上的 possess 是早期实现,存在两处缺陷: 1) 只设了 aiController,而 UnitType.java:281 的默认 controller 工厂在 playerControllable=true 且队伍为玩家时直接返回 new CommandAI(),aiController 分支根本不会被走到——卫星失去唯一 的运动驱动源,读档与刚发射的都静止。 2) 缺少接管期间的补位钩子:possess 会把 controller 换成 Player 对象本身 (UnitComp.java:950 的 isPlayer()),此后 UnitComp.java:839-841 调 Player.updateUnit(), 轨迹控制器停跑,卫星原地冻结。 本次把这两处对齐到已提交上游的 PR SiliconMod#71(feat/possess-satellite): - OrbitSatelliteController 直接取用 PR 分支版本(两分支该文件此前逐字节相同): 继承 AIController、updateUnit 覆写不调 super、运动逻辑提取为静态 applyMotion。 - SatelliteUnits:显式指定 controller;覆盖 UnitType.update(Unit) 在被接管时补位 (与 updateUnit 路径由 unit.getPlayer() != null 互斥,不双倍累加相位;!net.client() 保证相位只在服务端累加一次)。 验证:gradlew clean jar 通过。
There was a problem hiding this comment.
自动审查结果(Robin Review / workflow_run 二段式)
本 PR 让卫星可被玩家 possess(OrbitSatelliteController 继承 AIController + 显式指定 controller + UnitType.update 补位驱动),整体思路可行,unit.getPlayer() != null 的双驱动互斥判据成立,注释与行号引用也较充分。主要问题有两处:applyMotion 在同一 tick 内先于 Player.updateUnit 执行,导致「清零速度使输入失效」的前提在接管帧不成立;以及版本号只递增了次位,与「新增游戏功能」不符。另有两处低优先级问题:文档对接管期间控制器归属的描述自相矛盾、接管者客户端的视觉滞后未处理。
共 4 条发现,0 条已发为行内评论。
维度锚点是旧版信号系统的遗留方块,早已无功能(BuildVisibility.hidden,隐藏于建造菜单)。 它此前作为「兼容存根」保留,唯一作用是维持原注册位置、并消费旧版存档写入的自定义 String (不消费会导致同 chunk 后续建筑的反序列化错位)。 本次彻底移除: - 删除 src/silicon/world/blocks/signal/DimensionAnchor.java(含 StubBuild 读写对称实现) - Blocks.java:移除 import、字段声明、注册块 - bundle.properties / bundle_zh_CN.properties:移除 4 行条目 - .github/code-reviewer.md:移除对应审查要点与「关键类」列表条目 影响(已知并接受):包含该方块的旧存档加载后,该位置的方块会回退为 contentMap[0](一个原版 方块)。此后注册的方块 content id 各自前移一位——存档按内容名映射,正常存档不受影响;仅在 多人两端 mod 版本不一致时可能出现 id 错位。 本改动保持第一阶段范围,未引入任何第二阶段内容(构建产物确认无 AsatInterceptor)。 验证:gradlew clean jar 通过;无残留引用。
追加提交:移除废弃方块「维度锚点」新增提交 维度锚点是旧版信号系统的遗留方块,早已无功能( 本次彻底移除:
影响(已知并接受):包含该方块的旧存档加载后,该位置的方块会回退为 验证: 对应 release 的 |
|
/robin |
🏹 Robin✅ Review done. I flagged 8 things worth a look. Push fixes whenever you like, then comment |
There was a problem hiding this comment.
🏹 Robin
Heads up: this is a point-in-time review. Push fixes freely, then comment
/robinwhenever you want another pass.
🚨 2 High |
Summary
本 PR 让卫星可被玩家 Ctrl 接管:把 playerControllable 改为 true、显式指定 controller、让 OrbitSatelliteController 继承 AIController 以通过 possess 的 isAI() 判定,并在 UnitType.update(Unit) 里用 !net.client() && unit.getPlayer() != null 作为接管期的补位驱动——思路清晰、权威端守卫正确、注释详尽,对 UnitType.java:281 默认工厂与 UnitComp 调用点的溯源很扎实。但存在两个阻断性问题:一是删除了 DimensionAnchor 存根(旧存档按名解析失败会回退成 contentMap[0] 的原版方块、且 chunk 字节错位),二是用 controller 替换 aiController 后未保留 aiController,引擎在「取消接管 / 玩家退出」时会用 type.aiController 还原 controller,导致卫星永久失去轨迹驱动(或空指针);此外版本号只递增了次位,与内容变更不匹配。
📄 Findings Not Posted Inline
🚨 1 (src/silicon/world/blocks/signal/DimensionAnchor.java) — 整个兼容存根被删除(同时 Blocks.dimensionAnchor 字段、Blocks.load() 中的实例化与两个 bundle 的 key 一并移除)。被删文件自身的 Javadoc 明确记录了不能删的原因:Mindustry 存档头按内容名解析旧 ID,名字解析不到时 ContentLoader.getByID 会返回 contentMap[0],旧存档里该位置的方块会变成一个原版方块(比留存根更糟);并且 StubBuild.write/read 存在的唯一目的是消费旧版在 base 数据后写入的那个 String,若缺失,同一 chunk 中后续建筑会因字节错位而消失/错乱甚至读档失败。这是旧存档的真实数据损坏,属于高危。
恢复该存根方块(可改名为
LegacyBlockStub等,但必须以同一个内容名silicon-dimension-anchor注册,且保留BuildVisibility.hidden与消费旧 String 的StubBuild.read/write)。若确定要彻底移除,必须先提供名称映射/迁移方案(例如在读档前置阶段把旧名改写到新内容),否则禁止合并。
Robin — the Robin Hood of code review. Free for every PR.
| // 读档与刚发射的都静止不动。 | ||
| // 显式指定后 controller 与 playerControllable 解耦:无论是否被玩家接管,卫星拿到的 | ||
| // 始终是这个控制器,轨迹连续(possess 期间也在走)。 | ||
| controller = u -> new OrbitSatelliteController(orbit); |
There was a problem hiding this comment.
Robin — 🚨 HIGH · confidence: medium
用 controller = u -> new OrbitSatelliteController(orbit) 顶替了原来的 aiController = ...,但引擎的取消接管/玩家断开路径(Call.unitControl 的服务端分支处理 pre 单元)会用 pre.type.aiController.create() 还原之前的 controller。现在 aiController 已不再赋值(只剩引擎默认值),于是玩家按第二次 Ctrl 释放卫星、或玩家退出/死亡后,卫星拿到的 controller 将不是 OrbitSatelliteController——最坏情况是对 null 的 aiController.create() 空指针导致服务器崩溃;即使不崩,也会退化成通用 AI,卫星从此静止。而新增的兜底驱动条件是 unit.getPlayer() != null,此时已不成立,两条驱动路径同时失效,卫星被永久冻结(并且可能响应玩家右键指令,违反「玩家不可操控」不变量)。本 PR 的核心卖点正是「接管往返不中断」,这个路径完全没被覆盖。
Recommendation: 保留 aiController 与 controller 双写,让引擎的还原路径拿到的仍是轨迹控制器:aiController = () -> new OrbitSatelliteController(orbit); controller = u -> new OrbitSatelliteController(orbit);。另外建议在 update(Unit) 里加一次性自愈:当 unit.getPlayer() == null && !(unit.getController() instanceof OrbitSatelliteController) 时把 controller 重新指向轨迹控制器,避免任何未预料的还原路径静默冻结卫星。
aiController = () -> new OrbitSatelliteController(orbit);
controller = u -> new OrbitSatelliteController(orbit);
|
|
||
| #the mod version | ||
| version: a0.13.0.0 | ||
| version: a0.13.0.1 |
There was a problem hiding this comment.
Robin —
playerControllable false→true、控制器继承关系变更、驱动路径新增)并删除了「维度锚点」方块。按规范,新增游戏功能/行为修改应递增小(Patch)位,新增/删除方块应递增中(Minor)位,因此 version至少应为 a0.13.1.0(按删除方块计则为 a0.14.0.0);当前为 a0.13.0.1,仅递增了次(Sub)位,与 README 新增的 a0.13.0.1「卫星接管」功能条目自相矛盾。
Recommendation: 将 version 调整为 a0.13.1.0(若认可维度锚点删除为内容变更则 a0.14.0.0),并同步修正 minGameVersion/README 顶部标记,保持 README 更新日志标题与 mod.hjson 一致。
version: a0.13.1.0
| * ① **未被接管**:本控制器每帧走这里。引擎只在服务端/单机调用 controller.updateUnit() | ||
| * (UnitComp.java:838-841 的 {@code !net.client()} 守卫),客机靠单位同步 + 插值取位置。 | ||
| * <p> | ||
| * ② **被玩家接管**:本控制器**依然在跑** —— 卫星的 controller 是显式指定的 |
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: high
applyMotion 的 Javadoc 第 ② 条断言「被玩家接管:本控制器依然在跑……possess 前后是同一个控制器,轨迹自然连续」,与本 PR 自己在 SatelliteUnits.java:157-159 的说明直接矛盾——possess 会把 controller 换成 Player 对象本身,updateUnit() 已被 Player.updateUnit() 顶替,接管期真正驱动轨迹的是 UnitType.update(Unit) 里那条兜底路径,不是本控制器。读者会据此误以为接管期无需兜底。同段上方第 16-17 行「读档后经 UnitType.aiController 工厂重建」也已过时:工厂已改为 controller。
Recommendation: 把 ② 改写为:接管期 controller 已被替换为 Player,本控制器不再被驱动,轨迹由 UnitType.update 中的兜底调用驱动(两者由 unit.getPlayer() != null 互斥);同时把第 16-17 行的 UnitType.aiController 改为 UnitType.controller。
*② **被玩家接管**:controller 被替换为 Player,本控制器不再被驱动;轨迹由 SatelliteUnits 中
* UnitType.update 的兜底调用驱动(判据 getPlayer() 互斥),相位不会双倍累加。
| // 不加此旗标卫星会被编入 flying 物理层,与飞行单位互相推挤 | ||
| killable = true; // 保留 scripted 击落能力 | ||
| playerControllable = false; | ||
| playerControllable = true; // 允许玩家按 Ctrl 接管(原版 possess);接管前后都是 |
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: high
行内注释「接管前后都是 controller 里那个轨迹控制器在驱动,运动不中断」不准确:接管期间 controller 是 Player,轨迹实际由下方 update(Unit) 覆写驱动。第 33-37 行的类级说明同样写着「接管前后用的都是同一个 controller……轨迹不中断」。这类错误注释会误导后续维护者以为可以安全移除兜底驱动。
Recommendation: 统一改写为「接管期间 controller 被替换为 Player,由下方 UnitType.update 覆写兜底驱动;未接管时由 controller 驱动,两条路径由 unit.getPlayer() != null 互斥」。
| @Override | ||
| public void unit(Unit unit) { | ||
| this.unit = unit; | ||
| public void updateUnit() { |
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: medium
删除了自有的 unit 字段与 unit()/unit(Unit) 覆写,改为依赖 AIController父类维护的 unit 字段。任何未调用 controller.unit(this) 的创建/还原路径都会让 unit 为 null,而 applyMotion 的 if (u == null) return; 会让卫星静默静止、不打任何日志——正是本 PR 想消灭的症状,且调试成本极高。
Recommendation: 在 updateUnit() 里对 null 做显式告警而不是静默返回(如首次命中时 SiliconLog.warn 一条并继续尝试从 Vars.content/名册恢复),或保留一层自持有的引用作为兜底,避免「静止但无迹可寻」。
| // 判据 unit.getPlayer()(UnitComp.java:955):只有真被接管时才补位——未接管时 | ||
| // controller 仍是 OrbitSatelliteController,updateUnit 已在驱动,这里再跑会双倍累加相位。 | ||
| // !net.client() 保证相位只在服务端累加一次,客机仍旧靠单位同步取位置(与未接管时同一模型)。 | ||
| if(!Vars.net.client() && unit.getPlayer() != null){ |
There was a problem hiding this comment.
Robin — 💡 SUGGESTION
接管/释放这条往返路径是本 PR 最容易出问题的环节(见高危项 1),而目前没有任何可观测手段能发现「controller 已被还原但没人驱动」的状态。
Recommendation: 在接管开始/结束(unit.getPlayer() 由 null→非 null、非 null→null 的跃迁)时各打一条 debug 日志(含 unit.id 与当前 controller 类名),成本极低但能让线上问题立刻可定位。
| } | ||
|
|
||
| @Override | ||
| public void update(Unit unit){ |
There was a problem hiding this comment.
Robin — 💡 SUGGESTION
update(Unit) 覆写的注释只依赖 UnitComp.java:654 的行号来论证「无条件每帧调用」,而 UnitType 提供了 updateFirst 这一顺序开关,覆写与 controller.updateUnit() 的先后顺序会影响玩家输入是否会被本帧清零。
Recommendation: 在注释中补一句对该顺序的说明(或显式设置 updateFirst),确保「applyMotion 必定晚于玩家输入生效」这一前提被显式固定,而不是依赖引擎默认顺序。
响应 PR 上的 Robin 自动审查(8 条),本次处理其中 3 条: 1. OrbitSatelliteController 的 Javadoc ②「被玩家接管:本控制器依然在跑」是错的。 controller 在接管期已被替换为 Player 本身(PlayerComp.java:328 的 unit.controller(this)), 本控制器不再被驱动;轨迹实际由 SatelliteUnits 覆写的 UnitType.update(Unit) 兜底 (UnitComp.java:665,早于同方法 L860 的 controller.updateUnit()),两条路径由 unit.getPlayer() != null 互斥。改写该段并补上释放路径 (PlayerComp.java:319 resetController() → UnitType.createController())。 2. 同文件类注释第 16 行的「读档后经 UnitType.aiController 工厂重建」已过时—— 工厂早已改为 controller 字段(显式指定,见 SatelliteUnits)。 3. SatelliteUnits 的类注释与行内注释同样写着「接管前后都是同一个 controller 驱动」, 与实际矛盾(会误导维护者以为可以安全移除 update 兜底),一并改写为准确描述。 另按审查建议(第 5 条):applyMotion 在 unit 为 null 时不再静默返回,改为节流打一条 silicon 日志——「卫星静止且无任何日志」是最难定位的症状,留个取证点。 未采纳的两条: - 第 1 条(HIGH,称 controller 顶替 aiController 会导致释放后 NPE/永久冻结)经查为误报: 释放路径 PlayerComp.java:319 → UnitComp.resetController() → UnitType.createController() → controller.get(unit),全链路读 controller 字段;aiController 在引擎里仅 UnitType.java:281 定义与 :283 三元内被引用,而该三元已被显式 controller 覆盖。已在 PR 评论中附证据澄清。 - 第 2 条(版本号应递增 Patch 位)依据不足:仓库历史里 a0.12.4.2「新增模组内消息系统」 只递增了第 4 位;其引用的规范文件 AGENTS.md 不在仓库中。 验证:gradlew clean jar 通过。
对 Robin 审查(8 条)的回应提交 🔴 第 1 条(HIGH):经查为误报审查认为:用 该还原路径在引擎里不存在。 释放接管的实际链路是: // PlayerComp.java:317-328
if(this.unit != null){
this.unit.resetController(); // ← 释放旧单位
}
this.unit = unit;
if(unit != null){
unit.controller(this); // ← 接管时 controller = Player 本身(此处审查判断正确)
}
// UnitComp.java:466
public void resetController(){
controller(type.createController(self()));
}
// UnitType.java:550-551
public UnitController createController(Unit unit){
return controller.get(unit); // ← 读的是 controller 字段
}全链路读的是 我搜遍了引擎源码, 唯一的读取点是 因此:不会 NPE、不会退化成通用 AI、不会永久冻结。建议的「 「接管括往返不中断」这一卖点确实成立:接管期由
|
| # | 结论 |
|---|---|
| 1 | 误报(附带完整引擎调用链证据) |
| 2 | 依据不足(规范文件不存在,历史实践相反) |
| 3 | 已修正 ✅ |
| 4 | 已修正 ✅ |
| 5 | 已采纳 ✅ |
| 6 | 部分采纳 |
| 7 | 前提不成立,但已把真实的顺序差异写进注释 |
提交:374e8e0
There was a problem hiding this comment.
自动审查结果(Robin Review / workflow_run 二段式)
本次审查主要为卫星接管功能修正(OrbitSatelliteController 继承 AIController、显式 controller、被接管时由 UnitType.update 补位驱动轨迹),以及文档清理。最大的风险是直接删除了维度锚点(DimensionAnchor)存根类——该做法违反了「旧存档名解析失败会回退到 contentMap[0],不得直接删除」的存档兼容性铁律,极可能导致旧存档读取错位。
共 3 条发现,0 条已发为行内评论。
|
/robin |
🏹 Robin✅ Review done. I flagged 1 thing worth a look. Push fixes whenever you like, then comment |
There was a problem hiding this comment.
🏹 Robin
Heads up: this is a point-in-time review. Push fixes freely, then comment
/robinwhenever you want another pass.
🚨 1 High
Summary
本次 diff 主要完成了卫星单位的玩家接管支持(从 UnitController 改为 AIController 并新增兜底驱动路径)、移除了已废弃的 DimensionAnchor 方块及相关资源,并将版本号提升至 a0.13.0.1。整体设计思路清晰,特别是对 possess 流程与轨迹驱动的互斥路径处理做了详细注释。但删除 DimensionAnchor 存根方块存在严重的旧存档兼容性风险。
📄 Findings Not Posted Inline
🚨 1 (src/silicon/world/blocks/signal/DimensionAnchor.java) — 删除了 DimensionAnchor 存根类。该类注释明确指出:旧存档会按内容名解析该方块,若名字找不到会回退到 contentMap[0](更糟);且旧版 write() 在 base 数据后写入了一个 String(send:xxx/receive:xxx),存根必须定义 StubBuild 来消费该 String 并保持读写对称,否则同一 chunk 后续建筑的反序列化会错位,导致旧存档读档失败或建筑错乱。
不要删除该存根。至少保留 DimensionAnchor 类及其 StubBuild(buildVisibility=hidden、update=false、不注册到建造菜单),确保旧存档的自定义序列化数据被正确消费。或者提供等效的存档向前兼容方案后再移除。
Robin — the Robin Hood of code review. Free for every PR.
关于「删除 DimensionAnchor 存根」——确认是有意取舍Robin 这条判断正确,而且指出了一个我此前的说明不足以覆盖的后果。先把它讲完整,再说结论。 后果比我最初写的更严重我在提交 被删掉的类自己的注释写得很清楚(逐字核对过): * 旧版 DimensionAnchorBuild.write() 会在 base 数据后写入一个 String("send:xxx" / "receive:xxx")。
* 如果存根不读取该 String,残留字节会导致同一 chunk 中后续建筑的反序列化错位(建筑消失/错乱)。
* 因此必须定义 stubBuild 来消费旧格式数据。完整链条是: 影响面不是「那一个方块变成别的」,而是「它所在 chunk 之后的建筑」。Robin 的引用准确,这条 HIGH 成立。 结论:维持移除经维护者确认,本 PR 维持移除 DimensionAnchor,不做恢复。理由:
即:这是一次有意的存档兼容断点,而非疏忽。 给使用者的提示如果你持有较早期版本(含维度锚点方块)的存档,请先不要直接升级到本版本:
升级后才发现的存档,建议回到旧版本处理,不要在本版本上反复读写 —— 反序列化错位一旦写回磁盘就不可逆。 其他 6 条的回应见上一条评论(#issuecomment-5968693017):第 1 条 HIGH( |
|
/robin |
🏹 Robin✅ Review done. I flagged 9 things worth a look. Push fixes whenever you like, then comment |
There was a problem hiding this comment.
🏹 Robin
Heads up: this is a point-in-time review. Push fixes freely, then comment
/robinwhenever you want another pass.
🚨 1 High |
Summary
本次 diff 主要是卫星「可被玩家接管」功能实现(OrbitSatelliteController 改为继承 AIController、显式指定 controller、接管期用 UnitType.update 兜底驱动轨迹)以及删除已废弃的 DimensionAnchor 存根。卫星接管的同步与互斥路径设计基本合理,但删除 DimensionAnchor 存根会直接破坏旧存档兼容性,属于严重问题。
📄 Findings Not Posted Inline
🚨 1 (src/silicon/world/blocks/signal/DimensionAnchor.java) — 完全删除 DimensionAnchor.java(已废弃存根)违反了代码注释和项目铁律:旧存档按内容名解析该方块,若找不到类名会回退到 contentMap[0](原版方块),且旧版 DimensionAnchorBuild.write() 会额外写入 String 配置。若不保留 StubBuild 消费这些旧序列化字节,同一 chunk 后续建筑的反序列化会错位,可能导致旧存档读取失败或建筑错乱。
保留 DimensionAnchor 类及 StubBuild(至少保留 buildType=StubBuild::new、read() 消费 read.str()、write() 写入空串以保持对称),并保持 buildVisibility.hidden。不要直接删除存根。
Robin — the Robin Hood of code review. Free for every PR.
| // 判据 unit.getPlayer()(UnitComp.java:955):只有真被接管时才补位——未接管时 | ||
| // controller 仍是 OrbitSatelliteController,updateUnit 已在驱动,这里再跑会双倍累加相位。 | ||
| // !net.client() 保证相位只在服务端累加一次,客机仍旧靠单位同步取位置(与未接管时同一模型)。 | ||
| if(!Vars.net.client() && unit.getPlayer() != null){ |
There was a problem hiding this comment.
Robin —
接管期补位用 !Vars.net.client() && unit.getPlayer() != null 调用 OrbitSatelliteController.applyMotion。注释已说明路径互斥(未接管时 controller 驱动,接管时 Player 驱动),但 unit.getPlayer() 仅在真被接管时为非空。需确认释放(resetController)后 controller 恢复为 OrbitSatelliteController 且不会在客机也触发(已用 !net.client 守卫)。另外 applyMotion 内对 unit==null 做节流告警是防御性好的做法。
Recommendation: 保留现有互斥逻辑;如后续加武器卫星,需确保补位路径只在权威端(server/headless)执行,且相位累加严格只发生一次。
if(!Vars.net.client() && unit.getPlayer() != null){ OrbitSatelliteController.applyMotion(unit, orbit); }
| * 位置是存档相位的纯函数;同时每帧清零速度——接管期间玩家输入(InputHandler 会对玩家单位 | ||
| * 调 moveAt 改 vel)会被立刻抹掉,所以看得见轨迹在走、但改不动它。 | ||
| */ | ||
| public static void applyMotion(Unit u, int orbit) { |
There was a problem hiding this comment.
Robin —
applyMotion 对 unit==null 节流告警(nullUnitWarn % 300)是合理的防御。但静态方法依赖 unit 状态(名册 recordOf(u.id))时,若 unit 未赋值给控制器就被调用,可能掩盖时序问题。建议在调用点确保 unit() 已就绪,且告警信息包含更多上下文(unit.id、是否已接管)。
Recommendation: 保留节流告警;必要时在 updateUnit() 中先校验 unit()!=null 再调用 applyMotion,避免静态路径误用。
if (u == null) { if ((nullUnitWarn++ % 300) == 0) SiliconLog.info("sat-motion: controller has no unit assigned (orbit=" + orbit + ")"); return; }
| block.silicon-dimension-anchor.name=Dimension Anchor (Deprecated) | ||
| block.silicon-dimension-anchor.description=Deprecated legacy block kept only for old save compatibility; has no function and can be deconstructed. | ||
|
|
||
| # —— 消息测试 —— |
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: medium
移除了「维度锚点(已废弃)」的 bundle 文案。若 DimensionAnchor 存根被恢复以维持存档兼容,相关 bundle 项(name/description)也应保留或保留最简文案,避免存档加载时 UI 显示空白(尤其是旧存档中残留方块在某些面板引用翻译时)。
Recommendation: 若决定恢复存根,恢复这两行 bundle 项;若最终不恢复,需确认旧存档中该方块不会在 UI 中直接展示翻译键。
| block.silicon-dimension-anchor.name=维度锚点(已废弃) | ||
| block.silicon-dimension-anchor.description=已废弃的旧版方块,仅保留以兼容旧存档;无任何功能,可拆除。 | ||
|
|
||
| # —— 消息测试 —— |
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: medium
同上:中文 bundle 中移除 DimensionAnchor 废弃文案。与存档兼容性决策保持一致。
Recommendation: 与英文 bundle 保持一致的处理方式。
| @@ -19,7 +19,6 @@ | |||
| import silicon.world.blocks.sandbox.PowerSource; | |||
| import silicon.world.blocks.satellite.SatelliteConsole; | |||
| import silicon.world.blocks.satellite.SatelliteLauncher; | |||
There was a problem hiding this comment.
Robin — 🔵 LOW · confidence: medium
已移除 dimensionAnchor 字段和 import。若恢复 DimensionAnchor 存根类,需要重新引入 import 和字段声明(以及 content 注册)。当前改动与删除存根强绑定。
Recommendation: 先恢复存根再清理引用,或在恢复存根时同步还原这些改动。
| @Override | ||
| public void unit(Unit unit) { | ||
| this.unit = unit; | ||
| public void updateUnit() { |
There was a problem hiding this comment.
Robin — 💡 SUGGESTION · confidence: high
updateUnit() 选择不调 super.updateUnit() 是正确的(卫星轨迹完全由函数决定)。建议在类注释中补充「不调用 super.updateUnit()」的理由摘要(已部分提及),便于后续维护者理解。
Recommendation: 在类注释「不调 super」段落再加一句:避免 AIController 的目标索敌/移动逻辑干扰纯轨迹运动。
| // 读档与刚发射的都静止不动。 | ||
| // 显式指定后 controller 与 playerControllable 解耦:无论是否被玩家接管,卫星拿到的 | ||
| // 始终是这个控制器,轨迹连续(possess 期间也在走)。 | ||
| controller = u -> new OrbitSatelliteController(orbit); |
There was a problem hiding this comment.
Robin — 💡 SUGGESTION · confidence: high
将 aiController 改为显式 controller = u -> new OrbitSatelliteController(orbit) 的注释非常清晰,点出了 UnitType 默认工厂在 playerControllable=true 时走 CommandAI 的陷阱。这个注释很有价值,建议保留。
Recommendation: 保留该段注释(已存在)不变。
|
|
||
| #the mod version | ||
| version: a0.13.0.0 | ||
| version: a0.13.0.1 |
There was a problem hiding this comment.
Robin — 💡 SUGGESTION · confidence: high
版本号从 a0.13.0.0 升至 a0.13.0.1:属于「新增游戏功能」(玩家可接管卫星)类变更,按版本号规则应递增小(Patch)位,当前递增次(Sub)位(0.0->0.1)。规则中「新增游戏功能、平衡调整、方块/物品的逻辑/数值/配方/行为修改 → 小(Patch)位 +1」。建议确认版本号语义:若视为「功能新增」应为 a0.13.1.0;若视为「卫星接管」是既有卫星特性的补充修正(接管流程打通),次位递增也勉强说得通。但需按项目既定惯例判断。
Recommendation: 复核版本号递增策略:若确认为新功能,建议改为 a0.13.1.0。
对 Robin 新一轮审查(8 条)的回应本轮 逐条结论
第 2 / 7 / 8 条:前提不成立这三条都以「若恢复 DimensionAnchor 存根」为前提。该前提已在上一轮明确否定:
因此 bundle 那两行([7][8])与 第 3 条:版本号,依据不在仓库内审查建议按规范把 而仓库的历史实践与之相反: 「新增模组内消息系统」这个先例与本 PR 的做法一致。若确有 第 5 条:建议自身矛盾建议是「告警信息包含更多上下文( public static void applyMotion(Unit u, int orbit) {
if (u == null) {
if ((nullUnitWarn++ % 300) == 0) {
SiliconLog.info("sat-motion: controller has no unit assigned (orbit=" + orbit + ")");
}
return;
}
至于「在 第 6 条:审查结论与本 PR 一致原文「保留现有互斥逻辑;如后续加武器卫星,需确保补位路径只在权威端执行,且相位累加严格只发生一次」—— 这正是当前实现( 本 PR 当前卡在哪:投票,不是代码
目前唯一那票是 PR 作者自身的 +1(投票机制会这样计)。门槛是 3 个不同投票人。 另外 本 PR 的改动范围很小(9 文件 / +92 −103),核心是把卫星的 如果还有需要澄清的地方,我这边随时可以补。 |
|
/robin |
🏹 RobinReason: Failed to get response from LLM: Error: 504 The model did not finish within 300s (REQUEST_TIMEOUT_MS). Free model routes drop sometimes — comment |
改动内容
第一阶段卫星实体此前
playerControllable = false,玩家无法进入。本次开放接管能力,并保证接管期间卫星继续沿轨道飞行、且轨迹不可被手动改变。关键点
possess 的前提是
isAI()。InputHandler.java:783的判定为:而
isAI()的实现是controller instanceof AIController(UnitComp.java:494)。原先OrbitSatelliteController只implements UnitController,isAI()恒假——只开playerControllable是进不去的。controller必须显式指定。UnitType.java:281的默认工厂是:aiController只在这个三元的第一个分支里被引用。playerControllable = true且队伍为玩家时走第二支,引擎直接给new CommandAI()——aiController根本不会被调用,卫星会失去唯一的运动驱动源而静止。所以只设aiController不够,必须显式指定controller。接管会把 controller 换成
Player本身。UnitComp.java:950的isPlayer()是controller instanceof Player。possess 之后UnitComp.java:839-841调的是Player.updateUnit(),OrbitSatelliteController.updateUnit()不再被调用。因此覆写UnitType.update(Unit)在被接管时补位驱动——该钩子由UnitComp.java:654无条件每帧调用、与 controller 无关。两条驱动路径由
unit.getPlayer() != null天然互斥(未接管时 controller 仍是轨迹控制器、在那里驱动),不会双倍累加相位;!net.client()保证相位只在服务端累加一次,客机仍靠单位同步取位置。玩家改不动轨迹。
applyMotion每帧覆写位置并清零vel,WASD 等输入被立刻抹掉——能进去看、沿轨道飞,但轨迹不可手改。改动文件
OrbitSatelliteController.javaAIController;updateUnit()覆写且不调 super(父类会跑updateVisuals/updateTargeting/updateMovement,卫星不需要);运动逻辑提取为静态applyMotionSatelliteUnits.javaplayerControllable = true;controller显式指定;覆写update(Unit)补位README.mdmod.hjsona0.13.0.0→a0.13.0.1验证
gradlew clean jar通过说明
本次只做"能进去 + 按轨迹飞",不含用玩家输入驱动卫星自由飞行(那需要把位置更新从控制器迁移到与 controller 无关的路径,涉及同步模型改动,另议)。