docs: scope random tick and server event ownership fixes
This commit is contained in:
1 parent
763c015103
commit
6cef4e20ad
1 file changed
+94
@@ -0,0 +1,94 @@
|
|||||||
|
# 随机刻与服务器 tick 回调归属修复
|
||||||
|
|
||||||
|
日期:2026-10-13(Asia/Shanghai)
|
||||||
|
|
||||||
|
状态:用户已选择先修复随机刻/邻居通知链和服务器全局 tick 回调。本文为待确认的书面设计;尚未实施代码,也未生成新 jar。
|
||||||
|
|
||||||
|
基线:`763c015`,DimensionalRipper 1.1.17,工作分支 `codex/payload-ownership`。
|
||||||
|
|
||||||
|
## 1. 目标与范围
|
||||||
|
|
||||||
|
修复日志中两条已经定位的错误路径,同时保留其他现有并行能力:
|
||||||
|
|
||||||
|
1. 同一世界的不同区块并行随机刻,经同步邻居通知进入 DriveByWire 共享信号网络,触发 `WireNetworkManager.setSource:240` 的 `HashMap.computeIfAbsent` CME。
|
||||||
|
2. Lootr 在主线程 `ServerTickEvent.Post` 扫描/转换容器,而维度 worker 仍持有世界归属,导致方块实体读取被守卫拒绝。
|
||||||
|
|
||||||
|
用户选择的是以上两项,不包括坐服童子采样修复。不修改用户正式服务器、启动器 mods、原存档,不自动推送远端。
|
||||||
|
|
||||||
|
验收目标是正确执行而非仅不抛异常:随机刻、信号、箱子转换没有丢失或重复;等待期间 Sable 交接能结束;安全阶段不能错误恢复暂停或停止中的循环。
|
||||||
|
|
||||||
|
## 2. 已核对的证据
|
||||||
|
|
||||||
|
- 用户 2026-10-13 日志加载 DimensionalRipper 1.1.17、NeoForge 21.1.233、DriveByWire 0.3.0、Lootr 1.21.1-1.11.37.120。
|
||||||
|
- 三次 CME 分别在 11:02:10、11:02:17、11:02:23;事件总线和 ChunkEnv 各打印一份,不把六份堆栈计作六次独立异常。
|
||||||
|
- 当前 `ServerChunkCacheMixin` 将 `level.tickChunk` 收集成分片,使用按区块锁后调用自定义 `IChunkEnvTick`。这些锁不保护同一世界的 DriveByWire 网络管理器。
|
||||||
|
- 本机同版本 DriveByWire 字节码显示 `sourceValues` 是普通 HashMap,`setSource` 的计算函数只新建内部 HashMap。并发写入是强证据支持的原因;尚未捕获用户环境中另一条写线程,不能宣称已有完整双线程现场复现。
|
||||||
|
- 本机 Lootr 的 `HandleTick.onServerTick(ServerTickEvent.Post)` 直接调用 `BlockEntityTicker.onServerTick(server)`,遍历不同维度的 ticker;不经过公共收包入口。
|
||||||
|
- 本地 NeoForge 21.1.244 的 `MinecraftServer.tickServer` 在 `tickChildren` 前调用 `fireServerTickPre`,在方法末尾调用 `fireServerTickPost`。用户 21.1.233 必须另外核对注入及运行兼容,不能仅凭版本范围宣称已测试。
|
||||||
|
|
||||||
|
## 3. 选择的方案
|
||||||
|
|
||||||
|
不选仅替换 DriveByWire 集合:嵌套集合及“读取—更新—通知”仍是复合操作,且无法覆盖其他邻居监听器。
|
||||||
|
|
||||||
|
不选关闭所有并行:这是应急措施,会扩大性能回退范围。
|
||||||
|
|
||||||
|
采用两项边界调整,共用已有世界执行权协调器。第一项减少维度内不安全并行;第二项让明确的主线程事件边界与维度写入互斥。它不是任意模组线程安全的通用保证。
|
||||||
|
|
||||||
|
### 3.1 区块环境 tick 恢复所属维度内串行
|
||||||
|
|
||||||
|
- 当前 `tickChunks` 中每次 `level.tickChunk(chunk, randomTickSpeed)` 恢复直接调用原方法,不再投递区块环境子任务或分片。
|
||||||
|
- 调用在该世界合法 owner 上执行:维度并行启用时是所属维度 worker,原版路径则为服务器主线程。不是把每个区块同步投递到主线程。
|
||||||
|
- 沿用原 tickChunks 选中的区块、顺序和随机刻速度;同一已选区块只执行一次。由原方法及其模组注入处理随机源、降水、闪电、方块和流体随机刻,不继续绕过原方法手工复刻。
|
||||||
|
- 方块更新与同步邻居通知保留在同一调用链,不独立排队、不合并、不延后一拍,不调整 `randomTickSpeed`。
|
||||||
|
- 不允许 `fine on` 或 `fine sharded on` 重新开启旧区块环境并行。第一版不保留“默认关闭但可误开启”的不安全开关;现有实体/方块实体等其他并行开关保持原功能。
|
||||||
|
- 既有实体/方块实体子任务必须先按原屏障归还执行权,不能与本阶段重叠;实施测试需验证,不以线程名称替代归属检查。
|
||||||
|
- 修改 README/status 的区块环境并行说明,不能继续宣称此阶段仍分片并行。历史累计指标如保留,必须明确不是当前并行工作量。
|
||||||
|
|
||||||
|
范围限制:这一改动消除已知的同世界并行随机刻来源,不代表所有实体、方块实体、异步模组或跨世界共享状态已安全。若真实 DriveByWire 回归暴露其他写入来源,先报告证据并重新确认范围,不能仅压住 CME。
|
||||||
|
|
||||||
|
### 3.2 服务器全局 Pre/Post 回调取得执行权
|
||||||
|
|
||||||
|
- 只包装 `MinecraftServer.tickServer` 对 `EventHooks.fireServerTickPre` 与 `fireServerTickPost` 的两个原调用点,保留同一 server、BooleanSupplier、原位置及原事件总线顺序,原调用恰好执行一次。
|
||||||
|
- 复用 `MainThreadPhaseCoordinator.run`:禁止维度续排,等待全部在途及退出中循环完成;期间继续处理使 Sable 交接结束所需的主线程工作;随后进入真实主线程独占阶段执行原事件回调。
|
||||||
|
- 不包住整个 tickServer,不把两个事件之间全部主线程工作都纳入长期独占,也不移动事件的顺序。
|
||||||
|
- 不修改全局 `EventBus.post`、通用 `MinecraftServer.execute` 或 `pollTask`;不把 `LevelTickEvent.Pre/Post` 搬到主线程。因而坐服童子的维度事件采样异常仍可能出现。
|
||||||
|
- 不能只看总开关判断“无并行”;存在尚未归还执行权的循环时仍须等待。没有并行循环的原版路径应保留直接原调用,不创建或启动新循环。
|
||||||
|
- 独占内 CC 或 payload 嵌套使用既有租约,不递归建立屏障。禁止从正在被维度 worker 同步等待的 Sable 回调创建全局阶段,避免主线程与该 worker 互等。
|
||||||
|
- 等待不能持有第三方模组锁;超时只做限频诊断,不能授权与 worker 并发写世界。
|
||||||
|
- 异常保持原对象和传播语义,不吞异常、不重试整个事件、不重放已执行监听器。finally 正确释放本次租约,但不能解除菜单暂停或复活停止状态;阶段内新建循环仍受约束。
|
||||||
|
|
||||||
|
全局事件每次进入都需要安全条件,不能根据猜测“这 tick Lootr 没任务”跳过保护;也不为单个模组过滤事件监听器。
|
||||||
|
|
||||||
|
## 4. 性能与可观测性
|
||||||
|
|
||||||
|
- 第一项取消区块环境细粒度并行;第二项在服务器每 tick 的 Pre/Post 两个边界收口,主线程可能等待最慢维度。因此不再承诺“一个慢维度绝不影响主线程和其他维度”。两次安全阶段之外仍允许原有维度及其他阶段并行。
|
||||||
|
- 不承诺固定开销比例、20 TPS 或性能提升。先保证正确性,再测量收益;如果安全回退代价不可接受,应基于实测重新设计,而不是放松互斥。
|
||||||
|
- Pre 与 Post 分开记录次数、最近等待/执行时间、累计或最大耗时;使用有界数据,不按监听器或每 tick 打堆栈。
|
||||||
|
- 本地原版实现先记录部分 tick 耗时再执行 Post,故 Carpet/原版 MSPT 可能未完整涵盖新增 Post 等待。必须同时查看主线程实际 TPS、维度完成 TPS 和 Pre/Post 独立耗时,不能用低 MSPT 证明不卡。
|
||||||
|
- 不把本次扩展成通用采样器或故障保护重写。
|
||||||
|
|
||||||
|
## 5. 必须验证的项目
|
||||||
|
|
||||||
|
先建立能失败的回归,再改变生产行为:
|
||||||
|
|
||||||
|
1. 两个以上区块的真实随机刻及邻居监听器,在旧版本记录并行重叠,新版本无同世界重叠且不运行在细粒度子任务上。核对原 `tickChunk` 注入仍被调用、选中区块至多一次、邻居通知同步完成。
|
||||||
|
2. 实际 DriveByWire 0.3.0 对多个位置的信号更新测试,核对最终信号值及连接状态,不能仅测一个自建 HashMap 或等待偶然 CME。
|
||||||
|
3. 注册真实 ServerTickEvent.Pre/Post 测试监听器;旧版本在 worker 持有世界时不能合法读取,新版本看到原 BE、真正主线程、全部循环不在途。核对事件顺序和执行次数。
|
||||||
|
4. 真实 Lootr 扫描/转换的测试容器,核对预期转换结果及内容/战利品表,而非仅注册假监听器。检查未加载位置不会被额外强加载。
|
||||||
|
5. 等待中的 Sable 回调、嵌套 CC/payload、异常、菜单暂停、动态/退出中的循环、停服状态均不引入死锁、重复回调或错误恢复。
|
||||||
|
6. 无 DriveByWire/Lootr/Sable 时可启动;分别验证细粒度 on/off、分片 on/off 都不会重开区块环境并行。重复单人世界进入/退出,检查静态诊断引用释放。
|
||||||
|
7. 重跑已有队列、屏障、CC、payload、实际航空学把手、Create/Jade、水流、区块与实体路由回归。
|
||||||
|
8. 比较空载、随机刻密集、Lootr 转换负载及一个人为慢维度:报告实际 TPS、阶段等待/执行耗时及随机刻进度,不只看 Carpet MSPT。
|
||||||
|
9. Java 21 干净构建;校验 jar 元数据、无测试夹具及可选依赖打包,记录 SHA256。运行日志必须具备 PASS 标记且没有非预期服务器异常,不能只看 Gradle 退出码。
|
||||||
|
|
||||||
|
真实模组无法运行或完整测试矩阵未通过时,明确保留未验收项目,只能交付对应范围的测试候选,不能宣称两项已全面解决。坐服童子测试应独立,不能让其已知异常混淆这两项因果验证。
|
||||||
|
|
||||||
|
## 6. 不在本次范围与交付流程
|
||||||
|
|
||||||
|
不修 ServerWarashi 采样栈,不宣称修复任意异步世界访问、其他实体/方块实体并行、跨维度共享集合、C2ME、既有停服超时或所有主线程世界访问。保护两个 tick 事件不等于保护所有主线程指令、函数和第三方后台任务。
|
||||||
|
|
||||||
|
用户确认本文后再编写具体实施计划及失败测试拆分,确认计划和执行方式后实施。交付本地候选 jar、测试证据、回退代价及未解决项;不部署或推送。
|
||||||
|
|
||||||
|
## 自审
|
||||||
|
|
||||||
|
设计与“先修 1 和 2”的范围一致。随机刻选择维度 owner 串行而非 worker 同步请求全局阶段,避免自等待;全局事件仅保护两个明确入口。性能描述明确承认两次全局等待破坏完全维度隔离;验收区分假监听器与真实模组。未将坐服童子或其他已有问题纳入修复承诺。
|
||||||
Reference in new issue
Block a user