Files
DimensionalRipper/README.md
T
Disy 32b11aee87 fix: improve threaded world compatibility and chunk reads in 1.1.15
Add regression tests and per-dimension progress diagnostics. Document the unresolved C2ME random-initialization redirect conflict.
2026-10-03 21:24:31 +08:00

134 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dimensional Ripper
Dimensional Ripper 是一个基于 **NeoForge 1.21.1** 的多线程维度并行优化模组。它通过独立维度 tick 循环与细粒度并行调度,充分利用多核 CPU 提升服务端性能,同时保持稳定的 20 TPS。
## 优化范围(均为服务端运算,不影响客户端画面/渲染)
### 🗺️ 维度
- **维度并行 tick**:主世界/下界/末地/自定义维度各一个独立循环线程同时跑,一个维度卡顿不拖累其它维度。
- **保存独立接管**:存档保存流程单独处理,防止存盘时整服冻结。
### 👾 实体(优化重点)
- **实体 tick 并行**:村民、动物、怪物、掉落物等原版生物按 ID 分几十片多线程同时 tick(ShardGate 分片掉拍:单片慢只拖自己,不阻塞全局屏障)。
- **存储结构并发化**:实体查找表、区块内实体表、section 容器全部换成无锁并发集合,消除读锁排队。
- **村民 AI 特化**:大脑(Brain)、最近实体传感器、寻路、找床/找工作点、抢职位竞争、八卦消息全部做了多线程安全加固。
- **碰撞优化**:实体间碰撞默认关闭(`nocollide`,省去 O(n²) 开销),碰撞查询本身并行执行。
- **跨维度传送**:换维度处理重定向到正确线程,避免跨线程状态错乱。
- 压测数据:约 1.9 万村民场景下 TPS 从 9 提升到 20(16 核)。
### 🧱 区块 / 世界
- **区块环境并行**:每个区块的随机 tick(植物生长、水流、岩浆、红石脉冲)按区块坐标分片并行跑。
- **调度并发化**:区块加载/卸载、DistanceManager ticket 管理、计划刻调度(LevelTicks/BinaryHeap)全部线程安全。
- **爆炸**:统一串行执行,防止并发写坏区块数据。
### ⚙️ 方块实体(机器)
- **机器 tick 并行**:熔炉、箱子、信标等原版方块实体的 tick 按位置分片并行。
- **POI 并发化**:村民找床/找工作点查询不再排队等锁。
- **方块更新/掉落/游戏事件**:收集器、掉落物捕捉、游戏事件分发重定向到正确线程。
- **保守串行清单(黑名单)**(有意为之,非未实现):漏斗/投掷器/发射器/雕刻书架/合成器/蜂巢(跨容器 5×5 访问、共享 Level 级随机源,并行会数据竞争,见 [SerDesFilter.BLACKLIST](https://github.com/xxdd001/DimensionalRipper/blob/main/src/main/java/dev/modzuozhi/core/filter/SerDesFilter.java)),黑名单内置不可命令修改;第三方模组 TE 默认串行,可用白名单指令放行(见下)。
### 🎒 物品
- 物品本身无独立并行优化(合成、堆叠仍走原版逻辑);掉落物作为实体参与并行 tick,方块掉落物在正确线程处理。
## 其它特性
- **异步传送门传送**:支持原版下界/末地传送门与模组维度(如暮色森林、以太)传送门的异步预加载与渐进生成,避免传送到未加载区块导致的卡顿、冻结与虚空传送。
- **区块预取**:根据玩家位置与朝向预加载区块,加快跑图时的区块加载速度。
- **故障降级**:内置看门狗与故障熔断机制,异常时安全降级回原版串行 tick,保障服务器长期稳定运行。
## 环境要求
- Minecraft **1.21.1**
- NeoForge **21.1.x**
- JDK **21**
## ⚠️ 兼容性说明
**已知未修复问题(1.1.15)**:与 C2ME 的 `c2me_fixes_worldgen_threading_issues` 同时加载时,双方对 `Level` 构造器内随机源初始化的 `@Redirect` 冲突,可能导致启动失败。日志已确认 C2ME 优先级 1100 的重定向覆盖本模组优先级 1000 的 `modzuozhi_threadSafeRandom`,继而触发注入检查失败。当前源码尚未包含此兼容修复,不能视为可直接部署的稳定版。
1.1.15 针对 Sable 碰撞查询期间反复创建实心占位区块的问题:
- 删除 `ModZuozhiSolidChunk`。并行子任务的 `getChunk(..., required=false)` 在区块缺失时返回 `null`,不再伪造“已加载”结果、不分配区块段,也不触发加载/生成。已有区块仍返回当前活动对象。
- `required=true` 的缺失 FULL 区块与缺失其它状态统一快速失败,并关闭后续细粒度并行;不会等待正处于屏障的维度线程,也不在部分执行后重跑同一个实体。
- 实体预计移动包围盒(外扩 1 格)涉及未加载区块,或扫描跨度超过 8 个区块时,在实体批提交前交所属维度线程串行执行;已加载区域仍保留并行。此预检不是对任意模组远距离查询的保证,遗漏的必须加载请求仍由上一条保护。
- 每个超时阶段只输出一次详细线程栈,限服务器/本模组非空闲线程;后续保留简短等待信息。超时不会强行放走仍在操作世界的任务。
隔离 NeoForge 21.1.244 / Java 21 运行测试:两条实际子任务共执行 60 万次缺失查询、40 万次已加载查询,确认返回值/对象身份、区块数量不增加且并行未降级;验证缺失 FULL/EMPTY 必须加载请求快速失败,加载区域实体并行、超大移动范围实体串行且不重复执行。主线程交接、水流计划刻和维度完成进度测试同时通过。测试中的一次 FaultGuard ERROR 是必须加载失败用例主动触发并在测试中复位,不代表正常运行需要复位。运行方式:`gradlew runServer -PinteractionRegression`,仅用于隔离测试世界。
本次未运行用户完整服务端整合包(尤其其额外的 Sable Chunk Guard 1.0.0);以上不是完整飞行器/区块发送验收,也不宣称此前 DriveByWire 并发异常已修复。请在存档副本测试,不直接用于正式服。
1.1.14 增加每维度进度监控:`/dimensionalripper status` 列出所有已创建维度循环的实际完成 TPS(约 10 秒滚动窗口,100ms 分桶)、工作 MSPT 均值/最近值、当前 Tick 持续时间、距最近成功完成时间及失败 Tick 总数。TPS 不再由工作耗时推算,也不钳制到 20;没有新完成时约 10 秒内降至 0,当前 Tick 或无完成时间超过 1 秒单独提示,提示不等于断定死锁。暂停/停止显示状态和 `—` TPS;恢复后的窗口包含暂停时间,约 10 秒后更新完毕。无采样不显示 20。统计随维度循环销毁,不跨存档复用。
工作 MSPT 含玩家包处理、主线程交接等待和世界工作,排除末尾区块队列轮询;完成记录在整个循环归还世界所有权后写入。世界 Tick 抛异常不计成功完成;细粒度内部自行捕获的错误仍应结合故障状态判断。这些指标描述执行进度,不能证明水流、组装等游戏逻辑正确,也不修改 Carpet 的 HUD。
1.1.13 为水流问题的**诊断版,不宣称修复停流**。`flowstatus` 改为优先检查准星所指的 8 格内方块/水源,报告实体加载状态、LevelTicks 完整执行条件、最近调度时间、该区块队首计划刻的位置/剩余时间和调度索引。此前 1.1.12 仅显示玩家区块状态及全维度计数,不能据此判断同两项流体任务一直被卡住。本版不绕过实体加载门槛、不重排计划刻、不改写用户存档。
1.1.12 候选修复:Simulated 的 AssemblePacket(组装/拆解)和 Create 的 BlockEntityConfigurationPacket(包含转速 ValueSettingsPacket)先进入所属维度安全点,再借用服务器主线程执行原处理器。避免维度线程接管期间,主线程 getBlockEntity 返回空值而请求静默失效;保留原处理器的距离和权限检查,排队后换维度/离线则丢弃请求。其余维度继续运行,不全局关闭并行。
隔离运行测试 `InteractionFlowRegression` 验证了真实 ServerLevel 中的独占主线程方块实体读取,以及放水后的计划刻扩散。测试不加载 Create/Simulated,不能代替完整整合包的实际拉杆、转速和拆解验收;报告的整合包水流停滞尚未复现、未宣称修复。可站在问题水源附近间隔数秒执行两次 `/dimensionalripper flowstatus`,获取世界时间、区块更新条件和计划刻数量;诊断只应用户请求输出,不持续刷日志。
1.1.11 修正:加载界面的暂停状态不再停止维度循环。暂停交接移到服务器线程,在原版暂停保存之前等待在途维度 tick 完成,并继续处理 Sable 回调;已暂停且完全空闲的维度将区块处理权交还服务器主线程。调度入口与暂停共用同步保护和代次校验,防止取消后的旧任务在恢复时重复启动。
验证:Lithium 0.15.3 + Sable 2.0.5 临时服务端完成 10 次暂停状态下的真实 ServerPlayer 构造、同步生成新区块、恢复循环,以及 10,000 次运行中区块请求,细粒度保持开启。纯 Java 测试覆盖加载/登录暂停条件及在途/空闲所有权。服务端测试不加载客户端暂停 Mixin,因此不能替代完整 StarLight 单机进世界、ESC 暂停及飞行器验证。临时测试停机时仍记录 Sable 等待被中断的异常,不能将本测试描述为全程无错误。
1.1.10 修正:1.1.9 在 `getChunk` 入口检查 `mainThread` 后才进入原版方法;维度 worker 可在两次检查之间接管,导致真正服务器线程仍落入原版阻塞 `join`,同时 Sable 排队等服务器线程。现在维度循环启动后,外部服务器区块请求一律通过 `tell` 排队,等待期间处理服务器回调;只有明确暂停了维度 worker 的同步 Sable/玩家交接阶段才允许直接读取。维度异常也不再强行清空仍有区块请求的线程队列。
本次在 Lithium 0.15.3 + Sable 2.0.5 的临时服务端测试了 10,000 次 server-post-tick 同步区块请求,未卡死且细粒度保持启用,保存退出完成。测试夹具保留于 `tests/integration/ChunkHandoffRegression.java`,不进入发布 jar;完整 StarLight 客户端整合包尚未验证。
1.1.9 修正:
- 子任务屏障改为按完整提交阶段计数,修复“先完成一批、随后又提交任务”时一次性 latch 提前放行的问题。超时和中断均不再允许在任务未完成时继续下一阶段。
- 10 秒是诊断阈值,不是正常加载预算;超时后关闭后续细粒度阶段,但保留当前维度所有权并记录完整线程栈、锁持有者、阶段和池状态。真正卡死的任务不会被此机制强行解锁,仍需根据诊断定位。
- 实体查询先在锁内收集结果,再在锁外执行回调,避免回调移动/删除实体导致读锁升级死锁。
- 子任务读取非 FULL 区块也走已就绪路径;必须同步生成时明确报错并关闭后续细粒度并行,避免等待正在等待自己的维度线程。
- **Lithium 环境下村民改在所属维度线程执行**:实测 65 村民并行查询 POI 可写坏 Lithium 的 `ListeningLong2ObjectOpenHashMap`。这是定向限制,Sable 物理主线程桥接、其它生物、方块实体和区块环境的并行开关不因此关闭。
- Lithium 的实体分类缓存添加同一实例上的读写同步,并返回快照,修复碰撞查询绕过 section 锁后与实体移动并发修改分类集合的问题。
等待逻辑回归测试:`tests/TaskBarrierRegression.java`(提交间隙、超时、中断、可见性、2 万次并发完成)和 `tests/ManagedFutureAwaitRegression.java`。这些测试与小规模 Lithium + Sable 临时世界测试不替代完整整合包及真实飞行器验证;旧日志缺少子任务栈,无法唯一确定旧 10 秒超时的具体阻塞点。
1.1.8 修正:真实服务器主线程同步等待区块 Future 时,使用服务器事件循环的 managedBlock 继续处理回调,避免主线程等待区块、维度线程等待物理/玩家主线程回调的循环等待。区块任务仍由原区块执行器处理。独立等待关系回归测试位于 `tests/ManagedFutureAwaitRegression.java`;它验证回调归属、完成、异常和取消,不替代整合包内的进服及飞行器测试。
1.1.7 修正:加载 StarLight Extra 时,玩家连接 Tick(包括玩家 Tick 事件和断开清理)同步交回服务器主线程,对应维度等待完成后继续。区块反序列化恢复使用原有区块执行器,避免后台反序列化同时写入 Lithium 的 POI 存储。其它维度及细粒度并行开关不因此关闭。这是针对已观察到异常的修补,编译或空服启动成功不代表完整整合包、玩家交互和飞行器压力测试已通过。
> **Create: Aeronautics / Sable**:1.1.5 起内置主线程桥接。Sable 的物理子世界 Tick 同步交回真正的服务器主线程,完成后维度 worker 继续运行。已开始的物理阶段必须结束后才能归还所有权,不能通过超时强行中断。可通过 `/dimensionalripper status` 确认桥接及 Lithium 村民限制。
>
> **Lithium**:1.1.6 起会优先保留 Lithium 的实体移动碰撞注入,避免双方争抢同一调用点导致启动崩溃。本模组的实体互推关闭仍会生效;移动碰撞查询交由 Lithium 优化。
>
> 其它多线程优化模组或跨维度交互类模组仍不建议与本模组同时使用;尚未建立专门兼容路径的组合可能发生冲突。
## 指令
| 指令 | 说明 |
|------|------|
| `/dimensionalripper status` | 查看当前状态、TPS、各开关与掉拍分片统计 |
| `/dimensionalripper fine on\|off` | 开关维度内细粒度并行(实体/方块实体/区块环境) |
| `/dimensionalripper fine sharded on\|off` | 开关分片并行(实体/TE/区块环境,ShardGate,默认开启) |
| `/dimensionalripper nocollide on\|off` | 开关实体碰撞优化(默认开启) |
| `/dimensionalripper tewhitelist <注册名>` | **白名单**:将指定第三方模组方块实体(TE)加入并行白名单放行(如 `stresskit:heavy_furnace`) |
| `/dimensionalripper teunwhitelist <注册名>` | 从并行白名单移除该 TE(恢复默认强制串行;重启服务器也会自动清空白名单) |
> **白名单说明**:第三方模组 TE 默认串行处理(保守策略),加入白名单后才会进入并行分片管线。黑名单(漏斗/发射器等原版保守串行 TE)为内置硬编码,**不支持命令添加/移除**。
模组**默认启用**多线程与分片并行,开箱即用,无需额外配置。
## 构建
```bash
./gradlew build
```
构建产物位于 `build/libs/` 目录。
## 设计说明
本模组的设计思想借鉴了 Folia、MCMT、Chlorophyll 等项目的多线程调度方案,但全部代码基于 NeoForge API 独立实现,不包含任何 GPL 代码。
## 许可证
[MIT License](LICENSE) © 2026 xxdd001
---
> **AI 生成声明**:本 README 说明文档由 AI 辅助生成,使用的 AI 为 DeepSeek-V4-Flash。