Skip to content

Fix storage port server-side scanning crash 修复服务端扫描仓储端口时崩溃 - #5140

Merged
PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:fix/storage-port-server-scan-5137
Oct 3, 2026
Merged

PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:fix/storage-port-server-scan-5137

Conversation

@WhereisFff

Copy link
Copy Markdown
Contributor

fix(storage-port): 修复服务端扫描仓储端口时崩溃 (#5137)

仅在客户端拾取仓储端口时读取 Ctrl 按键并复制缓存、标记或流体数据,避免服务端扫描加载客户端类。

修复 Anvil-Dev#5137
@Gugle2308

Copy link
Copy Markdown
Collaborator

已完成核对(PR 状态:opened,未合并;diff 24 行,2 文件 +2/-0;已通过 mclo.gs 原始日志与 1.21.1 mappings 交叉取证)。


代码审查摘要 — PR #5140

操作: opened(开放中,未合并)
范围: 2 个文件(均为 Java 逻辑代码,无生成资源)/ +2 −0

变更本质

在 StoragePortBlock#getCloneItemStack(base 第 81 行)与 StorageFluidPortBlock#getCloneItemStack(base 第 68 行)的 Ctrl 判断链最前面插入 && realLevel.isClientSide,使 Screen.hasControlDown() 在服务端被短路,永不触发客户端类加载。

崩溃链路复核(issue #5137 / mclo.gs/8zamqcq,实测日志)

Thaumcraft(tc4port) ThaumometerObservationSystem.onPlayerTick:33
  → ScanManager.findTarget:85
  → nf IBlockStateExtension.getCloneItemStack:242      ← 第三方扫描模组走 NeoForge 扩展方法
  → anvilcraft StoragePortBlock.getCloneItemStack:81   ← 崩溃点
  → RuntimeDistCleaner: Attempted to load class
     net/minecraft/client/gui/screens/Screen for invalid dist DEDICATED_SERVER
  ← Player.tick:332 / ServerPlayer.doTick:556(服务端 tick 中抛异常 → 踢人 → 重连即复现)

触发者不是原版中键拾取(客户端路径),而是第三方模组在服务端 PlayerTick 里遍历扫描方块状态。PR 精确命中了该调用链。

🟢 修复评估(正确且完整)

维度 结论 证据
短路顺序 ✅ isClientSide 位于 Screen.hasControlDown() 之前,服务端分支永不解析 Screen
项目一致性 ✅ 已有同款先例:StorageBlockEntity.applyPickStorageId() 用 if (!level.isClientSide() || !Screen.hasControlDown()) return;。本 PR 是该约定的补漏,未引入新写法
覆盖完整性 ✅ 全仓核对所有 override getCloneItemStack 的文件,只有这两个文件 import net.minecraft.client.*;CrateBlock/LargeCrateBlock/ShulkerContainerBlock/HyperdimensionStorageStationBlock 走已守卫的 applyPickStorageId,PipeBlock 无客户端类引用,AbstractStoragePortBlock 及其第三子类 StoragePortConsolidatorBlock 也干净 —— 仓储端口系列的服务端扫描路径已无同类残留
功能回归 ✅ 无 核对 mappings:1.21.1 没有 ServerboundPickItemFromBlockPacket(拾取栈由客户端计算),创造模式经 ServerboundSetCreativeModeSlotPacket 携带组件同步 ⇒ 客户端算出的带数据栈仍能到达服务端,「Ctrl 拾取带缓存/标记/流体」在联机中照常生效
附带收益 ✅ 新条件把 isClientSide 放在 realLevel.getBlockEntity(pos) 之前,服务端不再于第三方每 tick 扫描热路径中访问方块实体(Level#getBlockEntity 可能触发同步区块访问),顺带缓解 issue 中提到的「mspt 暴涨」

⚠️ 建议(非阻塞)

  1. 注释缺 dist 约束:两处注释只写了「保留…数据」,没说明为什么必须限定客户端。建议补一句「仅客户端;服务端禁止加载 Screen,见 [Bug] 玩家在服务器中手持神秘时代(重置)的魔导透镜时对准储存端口即被无限踢出 #5137」,防止后续重构把守卫当冗余删掉——本次 bug 正是这类误删的后果。
  2. 模式已重复 3 处 → 建议抽共用判定,如 StorageBlockEntity.isPickDataRequested(LevelReader)(内部 level.isClientSide() && Screen.hasControlDown()),两个端口 + applyPickStorageId 复用,从结构上杜绝再写出裸 Screen.hasControlDown()。
  3. 同类风险长期防护:建议为「非 client/ 包不得直接引用 net.minecraft.client.*」加一条轻量 CI/grep 门禁,或在 AGENTS.md 固化 dist 守卫约定。顺带核查过的近邻(SpacetimeSupercomputerBlock 用 @OnlyIn(CLIENT)、PlacementIntervalsBlockItem / HeliostatsBlockEntity / MobAmberBlock / ResentfulAmberBlock 均在客户端判据之后才引用客户端类)目前均安全,无需本 PR 处理。

🧪 测试建议

被测目标 场景 优先级
两个 getCloneItemStack 专用服务端放置带缓存/标记/流体的仓储端口,用扫描类模组(Thaumometer 类)每 tick 扫该方块 → 不得抛 RuntimeDistCleaner、不得踢人、不得 mspt 飙升 🔴
同上 客户端创造模式 Ctrl+中键拾取 → 仍附带缓存/标记/流体数据 🔴
同上 客户端普通中键拾取(无 Ctrl)→ 不带数据(行为与修复前一致) 🟡

📋 声称验证表

PR 声称 状态 证据
仅在客户端拾取时读取 Ctrl 并复制数据 ✅ 两处新增 isClientSide 短路,服务端直接跳过整块分支
避免服务端扫描时加载客户端类 ✅ 与崩溃栈 RuntimeDistCleaner / IBlockStateExtension.getCloneItemStack 完全对应
fixed #5137 ✅ issue 指定崩溃点 StoragePortBlock.java:81,正是本次修改的条件所在行

结论:APPROVE — 修复正确、最小、覆盖完整,可合并;仅建议补注释与抽公共判定。

标题核对:「Fix storage port server-side scanning crash 修复服务端扫描仓储端口时崩溃」已是「英文 + 中文」规范格式且描述准确,无需修改(另外 gh auth status 显示 token 已失效,即使需要也无法自动改标题,本次未执行 gh pr edit)。

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched SKILL.md in skill 'client-server-side-separation-review' (1 replacement).

@WhereisFff
WhereisFff marked this pull request as ready for review October 2, 2026 16:06
Copilot AI balanced review requested due to automatic review settings October 2, 2026 16:06

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

❌ Non-retryable error (HTTP 400): HTTP 400: You have insufficient credits to make this request. Please purchase more credits to continue using the service.

@Gugle2308

Copy link
Copy Markdown
Collaborator

HTTP 400: You have insufficient credits to make this request. Please purchase more credits to continue using the service.

@Gugle2308

Copy link
Copy Markdown
Collaborator

代码审查摘要 — PR #5140

操作: ready_for_review(PR 状态:Open;关联 issue #5137 仍为 Open)
范围: 2 个文件(2 Java,+2 行 / -0 行,24 行 diff);无新增/删除文件,无 ghost 文件,无 EOF 缺失

文件 变更
block/StoragePortBlock.java getCloneItemStack 条件链新增 && realLevel.isClientSide(head:80)
block/StorageFluidPortBlock.java 同上(head:67)

✅ 崩溃机理核对(与 issue #5137 描述一致)

  • 触发点确认为 StoragePortBlock#getCloneItemStack 中无 dist 判断地调用 net.minecraft.client.gui.screens.Screen.hasControlDown()(base:81)。
  • 服务端 RuntimeDistCleaner 在专用服务端拒绝加载 Screen → 抛 RuntimeException,因发生在 player tick 内被包装成 ReportedException: Ticking player → 玩家被踢出(issue 中的「无限踢出」)。
  • 调用方为第三方 mod(ThaumCraft 4R 移植版符文扫描仪)在玩家 tick 内对瞄準方块调用 getCloneItemStack;单人不复现是因为 dist=CLIENT。这是一个「公共代码引用客户端类」的通用缺陷,不是该第三方 mod 的 bug,修复方向正确。

✅ 修复正确性与覆盖度验证

  1. 短路顺序正确:守卫置于 getBlockEntity(pos) 与 Screen.hasControlDown() 之前,服务端在 isClientSide==false 处直接短路,Screen 的符号引用永不解析 → 无 NoClassDefFoundError/RuntimeDistCleaner 异常。✅
  2. 客户端行为零变化:客户端 Level 与集成服务端的客户端侧 isClientSide==true,Ctrl 中键复制缓存/标记/流体路径完全保留;单人世界不受影响。✅
  3. 同类位点已穷尽:对 base 分支全仓扫描全部 16 个 getCloneItemStack 覆写文件,只有这两个文件 import net.minecraft.client.*(git grep -ln "^import net.minecraft.client" rvbase1216 -- <getCloneItemStack 覆写文件>)。其余仓储类方块(ShulkerContainerBlock、HyperdimensionStorageStationBlock)经 StorageBlockEntity.applyPickStorageId 走,而该方法本身已有 dist 守卫(if (!level.isClientSide() || !Screen.hasControlDown()) return;)。→ 拾取路径上已无遗留同类风险。✅
  4. 与项目既有惯例一致:同一文件的 getTicker 已用字段式 level.isClientSide,新增写法风格统一、可编译(Level.isClientSide 字段在本分支存在)。✅
  5. 流体端口一并修复合理:issue 只报了 StoragePortBlock,但 StorageFluidPortBlock:68 是完全相同的模式,属于正确的预防性同修,不是范围蔓延。✅

🟢 与 dev/26.1/1.6 的交叉验证(重要正向证据)

26.1 线(dev/26.1/1.6)中同两个方块已经用更好的方案解决了同一问题:改用原版 getCloneItemStack(..., boolean includeData, ...) 参数(block/logistics/storage/StoragePortBlock.java:67-82、StorageFluidPortBlock.java:54-71),完全不引用客户端类,服务端同样安全。1.21.6 的 NeoForge 签名是 getCloneItemStack(BlockState, HitResult, LevelReader, BlockPos, Player),没有 includeData(全分支 git grep includeData 无命中),因此本分支用 isClientSide 守卫是当前 API 下的合理等价手段。该 PR 的效果与 26.1 线对齐。✅

💡 建议(非阻塞)

  • 给守卫加一句 dist 注释:现有注释只解释「拾取时保留数据」,未说明 isClientSide 是为了避免服务端加载客户端类。后续维护者若调整条件顺序(把 Screen.hasControlDown() 前移)会立刻复现 [Bug] 玩家在服务器中手持神秘时代(重置)的魔导透镜时对准储存端口即被无限踢出 #5137,建议补一行注释固化该约束。
  • 两处方块重复同一守卫:可考虑像 StorageBlockEntity.applyPickStorageId 那样抽一个 AbstractStoragePortBlock 的静态 helper(内部做 dist 判断 + saveToDrop),减少第二处遗漏的可能。可选,不阻塞。
  • 行为差异备忘:服务端第三方调用方现在拿到的是无数据的裸物品(此前是抛异常)。26.1 线在服务端调用方传 includeData=true 时仍会附带数据,两分支对非原版调用方语义略有差异——属于设计取舍,记录即可。

🧪 测试建议

被测目标 场景 优先级
StoragePortBlock#getCloneItemStack 专用服务端上模拟第三方 mod 在 player tick 内直接调用 → 应返回 new ItemStack(this) 且无异常(#5137 回归验证) 🔴
StorageFluidPortBlock#getCloneItemStack 同上 🔴
两方块 Ctrl 中键拾取 专用服务端 + 单人世界分别验证缓存/标记/流体仍写入物品与 tooltip 显示正常 🟡
集成服务端(单人) 确认 isClientSide 守卫不会误伤客户端拾取路径 🟡

📋 声称验证表

PR 声称 状态 证据
仅在客户端拾取时读取 Ctrl 键并复制缓存/标记/流体 ✅ head 源码 isClientSide 短路 + 客户端行为不变
避免服务端扫描加载客户端类 ✅ 守卫先于 Screen.hasControlDown(),客户端类符号服务端不解析
fixed #5137 ✅ issue #5137 仍 Open,崩溃行 base:81 已被守卫覆盖,合并后应自动关闭

结论: APPROVE(comment 级) — 变更极小、机理核对无误、同类位点已穷尽、与 26.1 线方案对齐,未发现需要修复的问题,可合并。

标题:现有标题 Fix storage port server-side scanning crash 修复服务端扫描仓储端口时崩溃 准确且已中英双语,无需修改。本次 gh auth status 显示 /opt/data/home/.config/gh/hosts.yml 中 token 已失效,故未执行 gh pr edit(未做任何凭据导出操作)。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/public-api-invoked-from-server-by-third-party-mods.md in skill 'client-server-side-separation-review' (1 replacement). · Patched references/common-side-client-class-linkage.md in skill 'client-server-side-separation-review' (1 replacement). · Patched SKILL.md in skill 'client-server-side-separation-review' (1 replacement).

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

Copy link
Copy Markdown
Collaborator

PR #5140 的 webhook 操作类型为 closed,按审查流程约束第 1 条(closed/labeled → 停止),本次不进行审查、不发布任何评论。

如需要该修复的审查意见,请在 PR 重新打开(reopened / ready_for_review)后再次触发。

WhereisFff added a commit to WhereisFff/AnvilCraft that referenced this pull request Oct 3, 2026
- 优先使用原版 includeData 标志控制数据复制,避免服务端扫描读取客户端按键状态
- 保留 26.1 服务端中键克隆时的缓存、标记与流体数据

(cherry picked from commit d7c2f74)
PigeonNian added a commit that referenced this pull request Oct 3, 2026
…修复到26.1 (#5143)

* The text regarding celestial death has been corrected, and some progress has been made. 修正了天体死亡文本,增加了一些进度 (#5129)

* feat(advancement): 添加天体进化挑战成就及触发器

- 新增 CelestialEvolutionTrigger 用于天体质量进化判定
- 在触发 util 中添加 celestialEvolution 方法,触发成就判定
- 在加速器逻辑中调用触发方法进行成就检测
- 添加无残留(no_remnant)挑战成就,要求64质量铁砧的天体进化至终局
- 将成就文本翻译添加至英文语言文件和特殊字符文件
- 在成就数据中注册无残留成就及其触发条件

* feat(advancement): 添加行星毁灭成就及触发器支持

- 新增PlanetDetonationTrigger用于行星毁灭事件的判定
- 在ExcavatorHandler中增加触发行星毁灭触发器的逻辑
- 在AdvancementLineHelper中集成planetDetonation方法方便创建该成就
- 在AnvilCraftAdvancement中添加efficiency成就分支,绑定行星毁灭触发器
- 更新语言文件,添加行星毁灭成就的标题与描述
- 注册PlanetDetonationTrigger触发器并完善其触发实现
- 新增efficiency成就JSON配置文件,定义触发条件及显示信息

* feat(advancement): 新增因行星坍缩死亡的成就触发器

- 添加了 PlanetaryCollapseDeathTrigger 触发器类及其数据结构
- 在 ModCriterionTriggers 中注册 planetary_collapse_death 触发器
- 在 AdvancementLineHelper 中新增 planetaryCollapseDeath 方法便于调用
- 在 AnvilCraftAdvancement 中添加 Cannon Fodder 成就,绑定行星坍缩死亡触发器
- 在语言文件中添加 Cannon Fodder 成就标题与描述的多语言支持
- 在 OverworldLikeEventListener 中监听行星坍缩死亡事件并触发相应成就
- 在 TriggerUtil 中实现 planetaryCollapseDeath 方法来触发成就判定
- 新增 Cannon Fodder 成就的 JSON 配置文件,包含父级与图标等信息
- 修改效率成就添加隐藏属性,改善游戏内成就展示体验

* feat(advancement): 新增黑洞和中子星终局星体成就

- TriggerUtil新增支持根据StellarTerminal.Kind触发星体演化成就
- CelestialEvolutionTrigger增加对终局类型的判定和对应触发方法
- AdvancementLineHelper新增celestialEvolution重载方法支持终局类型参数
- AnvilCraftAdvancement中增加黑洞和中子星分支成就定义和保存
- AdvancementLang添加黑洞与中子星成就名称及描述多语言支持
- 语言文件(en_us, en_ud)增加对应成就的描述和标题
- 新增黑洞和中子星成就JSON数据文件,定义触发条件和显示信息

* feat(damage): 新增天体接触伤害类型及独立标签支持

- 添加黑洞接触、恒星接触及行星接触三种新伤害类型
- 天体接触伤害采用独立类型以实现专属死亡提示
- 更新伤害标签,支持新伤害类型绕过护甲、抗性、击退等效果
- 修改重力控制器中伤害来源,替换为对应新天体接触伤害类型
- 保持星体及行星接触伤害原版燃烧和摔落效果表现不变
- 增加对应的语言文本显示新伤害的死亡描述信息
- 修正玩家攻击混入对行星接触伤害的判定,保持原摔落伤害一致性
- 新增相关资源数据文件,完善伤害类型和标签配置

* feat(advancement): 添加“夜晚来访”星接触死亡进度条件

- 新增 StarContactDeathTrigger 实现星接触死亡触发器
- 注册并初始化星接触死亡触发条件 ModCriterionTriggers.STAR_CONTACT_DEATH
- AdvancementLineHelper 新增 starContactDeath 方法支持该触发器
- AnvilCraftAdvancement 新增“come_at_night”任务,使用星接触死亡触发器
- AdvancementLang 中添加“come_at_night”进度名称及描述文本
- OverworldLikeEventListener 监听星接触死亡事件触发进度
- TriggerUtil 新增 starContactDeath 方法简化触发调用

* feat(locale): 添加“come_at_night”成就的中英文描述

- 新增“advancements.anvilcraft.come_at_night”标题的本地化文本
- 新增“advancements.anvilcraft.come_at_night”描述的本地化文本
- 中文描述内容为“被星光笼罩”
- 英文标题内容为“Showdown at Night”倒转样式

* feat(advancement): 添加“come_at_night”隐藏成就

- 创建新的隐藏成就“come_at_night”
- 设置成就触发器为“star_contact_death”
- 指定成就图标为“celestial_forging_anvil”
- 定义成就的父级为“the_start”
- 启用成就的遥测事件发送

---------

Co-authored-by: Pigeon_Nian <adove2005@163.com>

* fix(celestial): 修复演化音效衰减与锻造界面恢复 (#5131) (#5132)

- 将演化音乐归入唱片音量分类,并以单声道音源实现 64 格原生距离衰减。
- 保持音效与字幕的听距一致。
- 通过实时同步入口恢复锻造状态,避免重开界面时丢失正在锻造的进度。

* fix(building): 修复幻灵铁砧虚影撤销时重复返还物品 (#5134)

- 撤销无掉落幻灵铁砧虚影时不再结算基础物品
- 保留实体铁砧与其他下落方块的资源结算

fixed #5133

* fix(item-splitter): 修复物品分配器掉落物超出堆叠上限 (#5136)

按物品实际堆叠上限拆分每个目标位置的掉落物,保留均分份额、余数、零动量和拾取延迟。

修复 #5135

* fix(button): 修复大红按钮遮挡边缘交互与按压动画 (#5141)

- 使用实际命中位置进行服务端射线校验,修复按钮可见但底座中心被遮挡时无法按下的问题。
- 按住按钮后等待服务端确认再播放手部动画,并忽略已释放或已切换目标的过期确认。
- fixed #5138

* fix(storage-port): 修复服务端扫描仓储端口时崩溃 (#5140)

- 优先使用原版 includeData 标志控制数据复制,避免服务端扫描读取客户端按键状态
- 保留 26.1 服务端中键克隆时的缓存、标记与流体数据

(cherry picked from commit d7c2f74)

---------

Co-authored-by: PigeonNian <78527064+PigeonNian@users.noreply.github.com>
Co-authored-by: Pigeon_Nian <adove2005@163.com>
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.

[Bug] 玩家在服务器中手持神秘时代(重置)的魔导透镜时对准储存端口即被无限踢出

4 participants