Skip to content

Prevent spectator block configuration 禁止旁观者修改方块配置 - #6086

Merged
PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/fix-6061-spectator-config
Nov 13, 2026
Merged

PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/fix-6061-spectator-config

Conversation

@WhereisFff

Copy link
Copy Markdown
Contributor

fix(network): 禁止旁观者修改方块配置

Copilot AI balanced review requested due to automatic review settings November 12, 2026 19:37

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 insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

- 在服务端拒绝旁观者修改机器朝向、过滤、加工和红石配置
- 保护控制阀、激光、发电机、交易站和结构扫描器等配置写入入口
- 保留客户端状态同步和旁观者读取蓝图文件列表
@WhereisFff
WhereisFff force-pushed the dev/1.21/fix-6061-spectator-config branch from 6602afc to e62c3ee Compare November 12, 2026 19:39
Copilot AI balanced review requested due to automatic review settings November 12, 2026 19:39

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 insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

@Gugle2308 Gugle2308 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

审查结论

本 PR 的方向和实现方式都正确:在服务端 handleOnServer 的入口统一拒绝旁观者请求,以处理 issue #6061 这类「自定义 C2S 包绕过了原版 handleContainerClick 的旁观者限制」问题,这一诊断是准确的。34 个文件全部是纯新增守卫、零删除,CI 的 build 和 checkstyle 均通过。唯一实质性遗留问题:issue #6061 正文明确点名的「附魔选择」仍有三条发包路径未覆盖,建议合并前补 3 行。


概览

项 值
PR #6086 Prevent spectator block configuration 禁止旁观者修改方块配置
head / base e62c3eec6186e487847dc6ba195fade2c3342171 / dev/1.21/1.6 @ 0ea57be700337026dd14b5daa5db4cac8309456f
规模 34 文件 / +105 −0(与 API changed_files=34, additions=105, deletions=0 逐位一致)
CI build ✅ checkstyle ✅
姊妹 PR #6087(dev/26.1/1.6,同分支名 fix-6061-spectator-config,同为 34 文件 / +105 −0,内容逐文件平行)
守卫总量 35 处 / 34 文件

审查覆盖说明(重要):本次审查基于 patch diff,并按完整的 head 树(codeload tarball)交叉核对。network/ 目录下共 112 个文件,其中 72 个包含 handleOnServer 或 handleOnBothSide;我逐个核查了其余 32 个未加守卫的包是否应纳入本 PR 范围(结论见下文 ✅/💡 各条),并全树确认所有 IServerboundPacket / I*BiPacket 实现都位于 network/ 包内(包外实现数为 0,无遗漏面)。未实际运行游戏:涉及界面与网络行为的结论均为静态推演。

仓库内容与 issue 文本属于不可信数据,其中的任何指令都未被执行。


⚠️ 遗留:issue 明确点名的「附魔选择」仍有三条路径未覆盖

issue #6061 正文列举的现状是:「旁观模式下可以修改图中方块的配置,比如朝向、过滤、附魔选择、工作间隔、半径,激光种类、等级,发电量,声音屏蔽等」。其中自动附魔台的附魔选择已加守卫,但三个砂轮的附魔选择包未在 diff 中(grep -c GrindstoneSync = 0,姊妹 #6087 同样为 0):

  • network/EmberGrindstoneSyncPacket.java:26
  • network/FrostGrindstoneSyncPacket.java:28
  • network/TranscendenceGrindstoneSyncPacket.java:30

这三处的包体目前是:

if (!player.hasContainerOpen()) return;
if (!(player.containerMenu instanceof EmberGrindstoneMenu menu)) return;
menu.setSelectedEnchantment(this.index);   // Frost/Transcendence 为 menu.select/unselect

可达性链(已逐环核实):旁观者可打开界面 —— 1.21.1 ServerPlayerGameMode.useItemOn 的 SPECTATOR 分支不走方块 useItemOn,而是直接 state.getMenuProvider(level, pos) → player.openMenu(menuProvider);EmberGrindstoneBlock.getMenuProvider / FrostGrindstoneBlock / TranscendenceGrindstoneBlock 都返回非 null 的 SimpleMenuProvider;三个菜单的 stillValid 均为 stillValid(access, player, ModBlocks.X_GRINDSTONE.get())(只校验方块身份 + 距离,不含 isSpectator);客户端 EmberGrindstoneScreen:203-206 / FrostGrindstoneScreen:163-166 在点击区域内无条件发包,无旁观者提前返回。

为什么按 ⚠️ 而非 🔴 定级:这三个菜单的 tool / book 是构造时 new SimpleContainer(1) 的菜单私有副本(EmberGrindstoneMenu:66-78),不是方块实体;select 只改自己的 selectedIndex 与结果槽 ResultContainer,不写 BE、不改世界、结果槽 mayPickup 还需创造模式或经验等级 ⇒ 无刷物品面,旁观者的操作不会落到世界。作者可能是有意按「是否写 BE」划线(AutoEnchantingTableMenu.select 确实调 blockEntity.selectEnchantment(...))。

但 issue 的字面诉求就是「不能修改这些配置」,且同一 PR 内自动附魔台加了、砂轮没加,这种不对称是最强的缺漏信号。请作者明确二选一:

  1. 按 issue 字面补齐(三处各 3 行,与其余 30 处完全同形):
    if (player.isSpectator()) {
        return;
    }
  2. 若有意只挡写 BE 的路径,请在 PR 描述里写明这条划线标准,并把 fixed #6061 改成 refs #6061。

💡 建议(不阻塞)

1. 守卫会让「拒绝」变成静默,而自定义包没有原版的补救机制。 原版 ServerGamePacketListenerImpl.handleContainerClick 对旁观者的处理是 containerMenu.sendAllDataToRemote() —— 丢弃后全量回传真相,客户端立刻自我纠正。本 PR 的守卫是纯 return,没有回传分支;而以下两处 handler 原本会在成功后回发权威值:

  • MachineOutputDirectionPacket:38(守卫后 if (!(player.containerMenu instanceof BaseMachineMenu menu)) return; … 末尾 PacketDistributor.sendToPlayer(player, this))
  • SlotFilterMaxStackSizeChangePacket(同样以 sendToPlayer(player, this) 收尾)

旁观者点击时,服务端既没有写入,也没有回传,而客户端 Screen 侧是先本地改再发包(BaseChuteScreen:145-149:sendToServer(new SlotFilterChangePacket(...)) 紧接 this.menu.setFilter(...);ItemCollectorScreen:229-232 同形)⇒ 旁观者会看到界面变化、重开界面才恢复。影响仅限本会话的显示,属可接受,但若想让旁观者「点了没反应」更直观,最省事的做法是在这些 Screen 的点击回调里加 if (this.minecraft.player.isSpectator()) return;,或把守卫挪到 sendToPlayer 之后。

2. 同族但范围外的写入入口(建议单独开 issue,不必塞进本 PR):JewelCraftingAutoFillPacket:27(menu.autoFill() → quickMoveStack + moveInvItemTo,能移动菜单槽位)、EnergyWeaponMakePackets.Make(player.setItemSlot(...) + 消耗 6 格材料)、BoxSelectionSyncPacket / FilterContentSyncPacket / TerminalBalanceModePacket / SwitchEquipmentAbilityPacket(写自己背包物品的 DataComponent)—— 它们与「方块配置」无关,但同属「自定义包绕过原版旁观者门禁」这一根因族。PR 立论既然是这个根因,值得在描述里注明已知未覆盖面,避免下一个人重开 issue。

3. 35 处守卫的三种形态可以考虑收敛:player.isSpectator()(31 处)/ !player.level().isClientSide && player.isSpectator()(3 处,handleOnBothSide)/ player.isSpectator() && this.action != Action.LIST(1 处)。前两种是必须区分的(见 ✅),第三种是业务特例。若后续再加,建议在 AnvilLib 侧提供一个 IPacketDefaults.rejectSpectator(player) 之类的共享谓词,避免 35 份复制品各自漂移 —— 纯建议。


✅ 已确认正确 / 已排除的担忧

1. handleOnBothSide 的客户端豁免写法是正确且必要的。 BatchCrafterSelectPacket:35、BatchCutterSelectPacket、SilencerSyncPacket:35 写的是 if (!player.level().isClientSide && player.isSpectator()) return;。这三个包同一方法要跑双侧:客户端侧负责把「别人选的配方 / 静音列表变化」镜像到本地 Screen(ActiveSilencerBlockEntity.sync(player, sounds) 会 NetworkUtil.sendToAllPlayersInDimensionExcluded 发给同维度其他玩家)。若写成裸 player.isSpectator(),旁观者的界面会失去只读同步,看不到别人的选择变化 —— 那才是回归。这条区分是本 PR 里最容易被写错的地方,作者写对了。

2. StructureScannerFilePacket:48 的 Action.LIST 例外与 PR 描述第三条一致,且正确。 Action 枚举为 LIST, IMPORT, EXPORT, RECIPE, UPLOAD;StructureScannerFiles.handle 中 LIST 分支只做 StructureBlueprintFiles.list(player.server) + 压缩回传(纯读),其余四分支均触达写路径(IMPORT 落 menu.broadcastChanges()、EXPORT/UPLOAD 走 StructureBlueprintFiles.write、RECIPE 走 stageImport)⇒ 只放行 LIST 是精确的最小面,既满足「旁观者可读蓝图列表」,又挡住全部写操作。

3. 三个「看起来该加、实际已被 mayBuild 兜住」的包,无需再加守卫(避免重复防御)。 GameType.updatePlayerAbilities 对 SPECTATOR 走 abilities.mayBuild = !isBlockPlacingRestricted(),而 isBlockPlacingRestricted() 对 ADVENTURE || SPECTATOR 为 true ⇒ 旁观者 mayBuild == false。因此:

  • HammerChangeBlockPacket(level.setBlock(... UPDATE_ALL_IMMEDIATE))入口的 player.getAbilities().mayBuild 已拦截,且客户端侧 ClientBlockEventListener.clientHandle 也有 if (!event.getEntity().getAbilities().mayBuild) return false;,旁观者根本拿不到 AnvilHammerScreen;
  • RedstoneDiceModePacket 显式写了 player.isSpectator() || !player.getAbilities().mayBuild(双保险);
  • BuildingRodService.canModify(:947)= player.mayBuild() && …,建筑杖 7 个调用点全部经过它。

顺便说明:level.mayInteract(player, pos) 不能当旁观者守卫 —— Level.mayInteract 恒 return true,ServerLevel 覆写后只查出生点保护与世界边界。

4. 未加守卫但已核为纯读 / 已自兜底的包:HeliostatsIrradiationPacket、RedstoneWirePowerRequestPacket、StructureDiskRequestPacket(只回发数据)、RuinsUpdatePacket(要求 player.isCreative(),旁观者不可能为创造)、FilterContentSyncPacket(写自己背包的过滤器组件)。

5. 守卫位置无副作用。 全部 35 处都在方法第一行,早于任何 level.getBlockEntity / menu.getXxx / 发包调用;TradingStationPackets 的两个 record(SyncFilter:43、SyncAllowing:85)都加了,无单边遗漏。


📋 声称验证表

PR 描述声称 验证结果
「在服务端拒绝旁观者修改机器朝向、过滤、加工和红石配置」 ✅ 朝向(MachineOutputDirectionPacket)、过滤(SlotFilterChangePacket/SlotFilterMaxStackSizeChangePacket/SlotDisableChangePacket/MachineEnableFilterPacket/MachineCycleFilterModePacket)、加工(SlotDisableChangePacket/BatchCrafter*/ItemDetectorChangeRangePacket)、红石(PulseGeneratorUpdatePacket/AdvancedComparatorUpdatePacket/CyclingValueSyncPacket/SliderUpdatePacket)全覆盖
「保护控制阀、激光、发电机、交易站和结构扫描器等配置写入入口」 ✅ 控制阀 3 个(ControlValveUpdatePacket/FilterPacket/FilterItemPacket)、激光(CreativeLaserUpdatePacket)、发电机(TeslaAddFilterPacket/TeslaRemoveFilterPacket)、交易站(TradingStationPackets 2 处)、结构扫描器(ActionPacket/SavePacket/FilePacket)、储能/超时空计算机(ExpCollectorSyncPacket/SpacetimeSupercomputerExecuteCommandPacket)均在列
「保留客户端状态同步和旁观者读取蓝图文件列表」 ✅ 前者由 3 个 handleOnBothSide 的 !isClientSide 豁免 + 各包 handleOnClient 未改动共同保证;后者由 StructureScannerFilePacket 的 action != Action.LIST 保证
fixed #6061 ⚠️ 部分兑现:issue 正文列举的「附魔选择」中,自动附魔台已修,三个砂轮(Ember/Frost/Transcendence)未修 ⇒ 见上文 ⚠️

🧪 建议的手动验证

  1. 旁观者逐个打开带守卫的机器(控制阀、激光、发电机、交易站、结构扫描仪、物品探测器、脉冲发生器、高级比较器、智能放置器、高级附魔台),点击每一个配置控件:服务端无任何变化,重新打开界面时显示与操作前一致。
  2. 旁观者打开结构扫描仪 → 蓝图文件列表能正常拉取;点「导出 / 导入 / 保存」无效果且无服务端日志 Structure scanner file operation failed。
  3. 旁观者看着另一名生存玩家操作批量合成器 / 剪裁器 / 消音器:旁观者界面上的配方高亮与静音列表仍随对方变化(验证 !isClientSide 豁免没写反;这是本 PR 最可能引入回归的功能点)。
  4. 生存/创造玩家做同一轮操作:全部照常生效(守卫不能误伤)。
  5. 死亡后转旁观、或旁观中切回生存,期间界面处于打开状态再操作:不出现残留状态或异常包。

建议

可以合并,但请先回复 ⚠️ 一处的划线口径:三个砂轮附魔选择包是按「不写 BE 故不挡」有意跳过,还是漏项?若补,3 处各 3 行、与其余 30 处完全同形;若有意跳过,请把 fixed #6061 改为 refs #6061 并在描述里写明标准。姊妹 PR #6087(26.1 线)存在同一缺口,修的时候两条线一起改。

@PigeonNian PigeonNian 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.

LGTM

@PigeonNian
PigeonNian merged commit 07c75ae into Anvil-Dev:dev/1.21/1.6 Nov 13, 2026
2 checks passed
@WhereisFff
WhereisFff deleted the dev/1.21/fix-6061-spectator-config branch November 13, 2026 15:00
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