16 KiB
公共收包线程归属 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将受支持的服务端游戏包交给真正主线程独占处理,修复把手松开路径,同时保留阶段之外的维度并行。
Architecture: 每服务器有界 FIFO 接收显式主线程工作;服务器外层 tick 建立全维度安全阶段,批量调用原处理器。阶段状态阻止递归收口和新增维度写入,旧组装入口复用同一执行权。生命周期清理与通用包失效分开处理。
Tech Stack: Java 21、Minecraft 1.21.1、NeoForge、Mixin/MixinExtras、现有 Gradle 隔离服务器回归。
Spec: docs/superpowers/specs/2026-10-06-payload-ownership-design.md
执行状态(2026-10-06): Task 1 已提交 691e949;Task 2–5 的公共入口、阶段、适配与诊断代码已实施并有回归证据。Task 4 的完整原生资源异常清理矩阵、Task 5 的双服务器压力测试、Task 6 的完整性能/客户端矩阵未全部验收,因此不勾选混合这些要求的条目。具体通过项和剩余项以 docs/payload-ownership-1.1.17-validation.md 为准,本轮只交付候选包。
Global Constraints
- 第一版新公共入口默认全维度隔离,不凭“玩家当前维度”或包内坐标猜测整个处理器的访问范围。
- 初始批次上限为 64 项或 2 ms 的处理时间,达到任一条件后不启动下一项;不强行中断已开始的处理器。
- 服务器队列上限 4096 项、单连接上限 256 项。
- 记录上限 128 个 ID,超出归入 other,避免恶意 ID 撑大统计。
- 登录、握手、配置阶段、客户端接收和纯网络线程处理不被无条件改派。
- 超时用于诊断,不是允许并发执行的理由。
- 不自动上传远端或部署正式服务器。测试仅操作仓库隔离世界或显式创建的存档副本。
- 不改变原处理器权限、距离、强加载规则;不放松 Level 的线程守卫。
- ServerWarashi 是后续独立修复,需要单独的采样设计及失败测试;本计划不宣称解决该崩溃。
- C2ME、后台光照查询及既有停服超时不纳入修复声明。
Review Focus
- 主线程正在服务等待中的 Sable 回调时再次 enqueue:不得等待该回调所属 worker,任务 2、3 验证。
- 前一个包传送/替换玩家后,同连接后续旧包不得操作新世界:任务 3、4 验证。
- 嵌套调用导致新世界/循环出现:不得在独占期间启动另一个写入者,任务 2 验证。
- 队列满载时恰好收到松手:拒绝要显式完成 future,断线清理不能遗留约束,任务 1、4 验证。
- 停服与阶段 finally、开关/菜单暂停同时发生:不得恢复已停止或仍应暂停的循环,任务 2、5 验证。
执行与证据约定
当前工作树含尚未提交的 1.1.16 归属修复,不能用新 worktree 的裸 HEAD 替代这些依赖。执行开始按 worktree 技能检查隔离方式;保留并核对所有现有变更,禁止 reset 或覆盖。不把原有未验证内容顺手算作本次通过。
以下路径相对仓库根。每个任务先写断言、记录失败,再改生产代码;每任务结束显式暂存该任务文件并提交(git 使用本仓库 safe.directory)。混合文件先核对 diff,不能误纳入用户后续改动。
命令统一使用 Java 21、--no-daemon --no-configuration-cache --console=plain。新的 -PpayloadRegression 夹具与 ownershipRegression、interactionRegression 互斥;运行结束必须有全部指定 PASS 标记且没有 unexpected exception,不能仅依赖进程退出码。证据放 build/reports/payload/。
文件职责
core/compat/PayloadWorkQueue.java:不依赖 Minecraft 的 FIFO、容量、取消及批次预算。core/compat/MainThreadPhaseCoordinator.java:服务器阶段状态与独占租约。core/compat/PayloadMainThreadDispatch.java:实际玩家上下文、future、生命周期与 drain。core/compat/PayloadDiagnostics.java:有界计数及耗时快照。mixin/ServerPayloadContextMixin.java:两个 enqueueWork 重载,保留原有分支及 guard。mixin/MinecraftServerPayloadMixin.java:外层 tick 的 drain,不扩展到通用 pollTask。- 已有 ServerManager、LevelTickLoop、Sable 桥、组装兼容、停止入口:接入阶段生命周期。
tests/PayloadWorkQueueRegression.java:纯 Java 队列测试。tests/payload/PayloadRegression.java:实际 NeoForge 上下文与多维度集成。tests/payload/SimulatedHandleRegression.java:可选真实模组处理器及约束清理测试。
Task 1: 可测试的有界队列
Files: 创建 src/main/java/dev/modzuozhi/core/compat/PayloadWorkQueue.java、tests/PayloadWorkQueueRegression.java;修改 build.gradle。
Interfaces: PayloadWorkQueue<K>(LongSupplier nanoTime);<T> CompletableFuture<T> submit(K connection, Supplier<T> action);int drainBatch();void close(Throwable reason);int size()。拒绝返回异常 future;执行前已取消的任务不产生副作用。锁只保护队列及计数,绝不包住 action 或 completion 回调。
- 写
fifoAndExactlyOnce、capacityAndCancellation、batchCountAndTime、closeAndReentrantCompletion断言:同连接顺序 0..255;第 257 项拒绝;16 个连接各 256 项填满后第 4097 项拒绝;取消使容量回收;批次最多 64;假时钟到 2_000_000 ns 后不再启动下一项;慢 action 不被中断;close 后旧 future 全结束且不能再接收。 - 添加
runPayloadWorkQueueRegression到 check;先运行并保存失败证据,不能以无测试发现作为 RED。 - 实现上述接口;future 完成回调可重新 submit/close 而无锁死;Supplier 异常只影响本项。
- 运行
./gradlew.bat runPayloadWorkQueueRegression,输出PAYLOAD QUEUE PASS;并行提交/取消压力测试核对计数为零。 - 审阅并提交本任务文件。
Task 2: 安全阶段与循环生命周期
Files: 创建 src/main/java/dev/modzuozhi/core/compat/MainThreadPhaseCoordinator.java、tests/payload/PayloadRegression.java;修改 core/dimthread/ServerManager.java、core/dimthread/LevelTickLoop.java、core/compat/SableMainThreadBridge.java(均位于 src/main/java/dev/modzuozhi/),修改 build.gradle。
Interfaces: coordinator 的 void run(MinecraftServer server, String reason, Runnable action)、boolean owns(MinecraftServer server)、boolean blocksWorkerStart(MinecraftServer server)、void stopping(MinecraftServer server)、void clear(MinecraftServer server);Sable 桥新增 boolean inHandoff(MinecraftServer server)。ServerManager.runMainThreadPhase 委托 coordinator,保持现有 CC 调用不变。
- 写
exclusivePhase、nestedPhase、handoffReentryRejected、newLoopDuringPhase、stopDuringPhase:处理期间全部世界 owner 为真主线程、worker 写入计数不增加;等待可完成 Sable 回调;独占内嵌套只建一次屏障;handoff 中建全局屏障立即明确失败;新 loop 在阶段退出前不能开跑;预先暂停、菜单暂停、停止状态不被 finally 恢复。 ./gradlew.bat runServer -PpayloadRegression得到预期断言失败,保存日志;夹具另用超时终止测试而非让生产代码越过屏障。- 实现 IDLE/DRAINING/EXCLUSIVE/RESTORING/STOPPING 状态、租约与入口检查。循环注册、scheduleIfNeeded、resume 共享阶段启动门禁;新 loop 参与退出处理。等待不持锁,继续 pump 必要回调;嵌套 drain 不递归。循环已经在途时等待其子任务及线程字段全部归还。
- 运行夹具输出
PAYLOAD PHASE PASS;复跑 ownershipRegression,确认 CC 预置暂停及 Sable 回调仍通过。 - 审阅并提交本任务文件。
Task 3: NeoForge 公共入口与 future 语义
Files: 创建 src/main/java/dev/modzuozhi/core/compat/PayloadMainThreadDispatch.java、src/main/java/dev/modzuozhi/mixin/ServerPayloadContextMixin.java、src/main/java/dev/modzuozhi/mixin/MinecraftServerPayloadMixin.java;修改 tests/payload/PayloadRegression.java、src/main/resources/dimensionalripper.mixins.json。
Interfaces: dispatch 的 <T> CompletableFuture<T> submit(ServerPlayer player, ResourceLocation payloadId, Supplier<T> action)、boolean owns(MinecraftServer server)、void drain(MinecraftServer server)、void stop(MinecraftServer server)。Runnable 适配 Supplier。任务保存实际 connection、player、接收时 ServerLevel 身份及时间;各服务器实例独立。
- 用真实 ServerPayloadContext 的 Runnable/Supplier 路径写
mainPayloadSeesBlockEntity、futureContract、phaseEscapeAndStalePlayer、configurationPassthrough。断言并发维度下能取得原 BE、true main、至多一次;Supplier 返回 42;异常、取消和 guard 的对外结果与未修改 NeoForge 一致,内部真实失败仍可见;权限拒绝未发生世界变更;配置阶段不调用 player();网络计算原线程不变。 - 运行 payloadRegression 记录原入口 wrong-owner/null 的失败;记录尚未覆盖的真实 Veil 路径,不冒充实测。
- 两个 enqueueWork 仅拦截游戏玩家的显式主线程工作。网络/worker 投递不等待;独占内嵌套立即完成;普通主线程外层进入安全阶段时先遵守同连接已排队顺序;DRAINING 中普通投递只入队;同步 handoff 重入按任务 2 契约明确失败。原 handler、上下文与 NetworkRegistry.guard 保留,不双重执行。
- 在 MinecraftServer.tickServer HEAD 的外层主线程 drain 一批;不在 pollTask 或 worker 回调内 drain。执行前验证连接、player 身份及世界;失效 future 明确结束。无队列不建屏障。
- 加
workerWaitCycleDetected测试:worker 等待队列 future 时测试必须识别无法收口且取消夹具,不误报自动兼容。运行集成输出PAYLOAD ENTRY PASS,覆盖没有并行 loop 的原版情况。 - 审阅并提交本任务文件。
Task 4: 原模组交互与清理路径
Files: 修改 src/main/java/dev/modzuozhi/core/compat/SableAssemblyDispatch.java、src/main/java/dev/modzuozhi/mixin/CreateConfigurationPacketMixin.java、src/main/java/dev/modzuozhi/mixin/SimulatedAssemblePacketMixin.java;创建 tests/payload/SimulatedHandleRegression.java;修改 build.gradle。若真实路径证明需要专用适配,仅新增 src/main/java/dev/modzuozhi/mixin/JadeRequestDispatchMixin.java 与 src/main/java/dev/modzuozhi/mixin/SimulatedHandleLifecycleMixin.java,并登记可选 mixin。
Interfaces: 保持 SableAssemblyDispatch.dispatch(ServerPlayer,Runnable),改为复用公共阶段/投递,禁止再送进已暂停的维度队列。具体可选模组方法描述符从本地实际 jar 审计后锁定,不能猜注入目标。
- 先记录真实
UpdatePlayerUsingHandlePacket.handle的抓取、距离、松手入口、BE 身份、HandleConstraint 移除结果,建立 RED。核对 HandleBlockEntity.checkPlayers 及断线/死亡/卸载清理,不仅测试模拟 Runnable。 - 审计 Veil 注册和 Jade 二次 server.execute;建立逃逸测试。只有已观察的逃逸点使用定点包装,不改写全部 MinecraftServer.execute。旧 Create/组装入口在公共独占内直接调用原 handler;阶段外走相同队列。
- 测试顺序与副作用:抓取一次、更新距离一次、松手一次,最后 constraint 不存在;两个玩家不会互相释放;转速配置、组装拆解保留原权限和距离行为,错误坐标不强加载。
- 覆盖断线、换维度、死亡、把手拆除、sublevel 卸载和单连接 256 满载后松手被拒绝;若原清理不足,可信生命周期适配在旧世界安全阶段释放资源,不重放过期客户端包。过载断开连接调用原 disconnect/cleanup,日志限频。
- 真实运行输出
SIMULATED HANDLE PASS、CREATE CONFIG PASS、JADE REQUEST PASS。原模组依赖或原生物理无法启动时,记录未通过项并暂停“把手已修复”的交付结论,不能用假夹具替代。 - 审阅并提交已验证的适配文件;无必要不创建专用 mixin。
Task 5: 停止、可观测性与限频
Files: 创建 src/main/java/dev/modzuozhi/core/compat/PayloadDiagnostics.java;修改 src/main/java/dev/modzuozhi/core/compat/PayloadMainThreadDispatch.java、src/main/java/dev/modzuozhi/mixin/MinecraftServerDimMixin.java、src/main/java/dev/modzuozhi/core/compat/ThreadOwnershipDiagnostics.java、src/main/java/dev/modzuozhi/command/MasterCommand.java、tests/payload/PayloadRegression.java。
Interfaces: PayloadDiagnostics.snapshot(MinecraftServer server): String,只读且不新建循环;dispatch.stop 拒绝接收、终结待执行 future,阶段退出不恢复停止中的 loop;clear 在清理结束移除服务器引用。
- 写
boundedDiagnostics、stopRejectsAndReleases、twoServersIsolated:1000 个不同 ID 最多 128 个具名项加 other;1000 次失败不输出1000份堆栈;停止后无遗留 future/连接/世界引用;某一服务器 stop 不影响另一服务器。 - 运行得到 RED。实现每服务器计数、队列最老等待、最近 32 事件、等待/执行耗时、批次数;异常全局 30 秒限频,快照无包正文。正常/异常停止入口在既有循环停止与保存之前关闭新队列;不要把旧 30 秒停服风险说成已解决。
- status 和 crash report 附快照,区分等待 worker、handler 慢和 Sable handoff;测试所有失败 future 最终完成且 handler 从不重复执行。
- 运行 payloadRegression 输出
PAYLOAD LIFECYCLE PASS、PAYLOAD DIAGNOSTICS PASS,审阅并提交。
Task 6: 兼容验收与候选包
Files: 修改 README.md、gradle.properties(候选版本 1.1.17);证据 build/reports/payload/。
Interfaces: 交付明确列出“源码/字节码审计”“夹具通过”“真实模组通过”的覆盖表,不将它们合并为兼容保证。
- 分别运行
./gradlew.bat check、runServer -PpayloadRegression、runServer -PownershipRegression -PccTestJar=<已验证本地CC路径>、runServer -PinteractionRegression。逐项确认纯 Java、CC、区块、水流、实体路由和新 PASS 标记,不把预期故障夹具当成功事故。 - 无 Create/Veil/Jade/Simulated 可选模组启动通过;真实模组组合另跑,不自动复制到用户 mods 目录。
- 对比无交互、普通交互、密集抓握、多人查询及慢维度:主线程 TPS、维度完成 TPS、队列延迟、阶段耗时。无交互新增屏障数必须为零;报告慢维度代价,不以 Carpet MSPT 独立判定。
- 正常构建
./gradlew.bat clean build,不带任何夹具属性。检查 jar 无 tests/Regression/测试模组类、元数据 1.1.17,记录 SHA256。 - 按 requesting-code-review 技能审查完整差异,修复阻塞问题并重跑受影响测试。README 清楚标注 ServerWarashi 仍未由本计划修复以及真实航空学验收状态;提交本任务。
- 只交付本地候选 jar 和覆盖/风险说明;如真实物理未通过,交付测试候选而非宣称全部修好。不推远端、不部署。
自审结果
设计第 4 节对应任务 1–3;旧适配、Veil/Jade 与生命周期对应任务 4–5;有界诊断与性能/打包对应任务 5–6。所有五个 Review Focus 已映射到断言。ServerWarashi 明确作为独立后续工作,不以当前方案替代采样栈修复。