Files
DimensionalRipper/README.md
T

160 lines
20 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。
## 1.1.17:公共收包线程归属候选修复
服务端游戏阶段经 NeoForge `ServerPayloadContext.enqueueWork` 明确提交到主线程的工作,现在统一进入有界队列,并在维度更新归还执行权后,由真正的服务器主线程调用原处理器。Create/航空学旧组装入口复用同一安全阶段;采用这条公共入口的模组不需要主动适配。登录、配置阶段、客户端及纯网络计算不改派,也不代表任意模组的任意线程访问已自动安全。
队列上限为服务器 4096 项、单连接 256 项,每批最多 64 项或 2 ms(不强行中断已经开始的处理器)。保留观察到的入队顺序;执行前取消的工作不再修改世界,换世界/换玩家/停服后的旧工作明确失效。超载通过原断线流程处理;业务处理器自己抛出的拒绝异常不会被误判为超载踢人。新循环继承菜单暂停,仍在退出的循环也必须归还执行权。等待期间继续服务 Sable 主线程交接,拒绝会产生锁反转的同步重入。
真实本地模组回归:Sable 物理子世界内创建了真实把手约束,复现原路径松手后约束仍有效;经新入口处理同一松手包后约束失效。Create 调速包及 Jade 查询通过,原距离拒绝仍有效。另有队列、取消、暂停、停止和真实 CC 回归。详细环境、日志标记及未覆盖项见 [1.1.17 验证报告](docs/payload-ownership-1.1.17-validation.md)。这些是隔离服务器测试,不等于用户完整整合包、客户端飞行或多人压力验收。
阶段之外维度并行仍保留;有交互时会等待最慢维度收口,不承诺零开销。没有待处理交互时不建立收包屏障。`/dimensionalripper status` 与归属崩溃快照包含队列积压、最老等待、最近批次等待/执行时间;仅凭 Carpet 的低 MSPT 不能判断所有维度正常。记录最多 128 个具名包 ID 加 other、最近 32 条结果,失败警告全局 30 秒限频,不记录包正文。
**仍为测试候选,不要直接替换正式服务器:ServerWarashi(坐服童子)启动分析崩溃、C2ME 冲突、任意后台世界访问及既有停服超时不在这次修复范围内。** 工作线程提交后同步等待公共队列 future 仍不受支持;不能把它宣传为所有模组免适配。
## 1.1.16:CC 调度线程归属候选修复
针对 CC:Tweaked 1.118.0 `TickScheduler` 主线程读取正在被维度线程更新的世界、导致显示器身份检查得到 `null` 的问题:有待处理 token 时,先暂停维度续排并等待在途更新归还所有权,在服务器主线程执行原调度检查,最后恢复原先运行的循环。等待期间继续处理主线程回调,允许 Sable 交接完成;不绕过身份检查,不吞掉真实异常,不强加载未加载区块。CC 的全局延迟队列表对应的区块回调也做串行保护。未安装 CC 时不启用此补丁。
这不是零开销修复:CC 有任务时,安全阶段会等待当前最慢的维度收口,可能暂时影响主线程 TPS;没有任务时不建立屏障,阶段结束后维度与细粒度并行继续运行。不能保证所有维度在这一兼容阶段完全互不影响。`/dimensionalripper status` 增加最近一次交接等待/执行耗时。
线程诊断记录维度、Level/ChunkSource 所属线程、调用线程、位置、阶段和 Sable 交接状态。每维度最多保留 16 条最近事件;入口归属不匹配警告每维度最多 30 秒一次,抑制期间累计计数,慢安全阶段全局最多 30 秒一次。入口快照不等于原子世界快照,也不据此单独判定死锁。崩溃报告新增 `DimensionalRipper Thread Ownership`。
隔离测试:`gradlew runServer -PownershipRegression -PccTestJar=<本机CC的jar绝对路径>`。使用 `runs/server` 下的专用测试世界(运行前确认 `level-name=interaction-regression`、`server-ip=127.0.0.1`、`server-port=25579`),不要指向正式存档。测试源码和 CC jar 不加入常规发布包。已在修复前用真实 CC 显示器复现 `Expected MonitorBlockEntity ... got null`;修复后运行任务会检查日志中的 `OWNERSHIP REGRESSION PASS`、`OWNERSHIP EDGE PASS`、`OWNERSHIP CALLBACK PASS` 和 `OWNERSHIP RESUME PASS`。Minecraft 崩溃有时仍以零退出码结束,不能只检查 Java 退出码。
覆盖:真实显示器身份检查、实际主线程交接桥的回调执行、嵌套/异常后的暂停恢复、拆除与真实身份不匹配、未加载 token、80 个延迟 token 的双维度并发 ticket/unload 回调,以及崩溃诊断。1001 次入口归属不匹配在测试日志中仅输出 1 条警告。另用 1 TPS 的空闲预算复现暂停后仍等待整秒的问题,修正为世界工作完成后立即退出已请求暂停的空闲轮询;保留在途工作的收尾。一次本机测试的 CC 等待/执行为约 7.4/0.12 ms,仅是隔离样本,不是整合包性能承诺。测试调用实际交接桥,但没有加载 Sable 原生物理引擎。
本修复限定于 CC 调度路径,不是对所有模组跨线程访问的通用放行。仍需在完整整合包的存档副本验证,尤其 CC 显示器随 Sable 结构移动的情况;不要据此认为所有航空学、区块发送、停服问题均已解决。
## 优化范围(均为服务端运算,不影响客户端画面/渲染)
### 🗺️ 维度
- **维度并行 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 / 1.1.16)**:与 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。