Xiao-no-love
c24b35640b
feat(faultguard): 单维度 tick 工作耗时连续超 300ms 自动关细粒度
...
- FaultGuard 新增 onTickSlow(double workMs):连续 3 次 > SLOW_TICK_MS(300ms)
关闭细粒度并行(保留维度并行),复用 fineDegraded 通道;恢复靠手动
/dimensionalripper fine on(setFine 内 FaultGuard.reset 会清计数)。
- LevelTickLoop.runIfCurrent finally:成功完成后上报本次维度纯工作耗时
(workNanos[0],不含收口等待)到 FaultGuard.onTickSlow。
- reset() 增加 slowTicks 清零。
背景:MSPT 爆高时细粒度并行放大单 tick 内调度/锁竞争开销,先关它把 tick
压回可控范围,避免拖成硬超时/假死。关闭安全:子任务在独立池内,不影响
维度 tick 收口,保留维度所有权。
2026-10-13 21:51:59 +08:00
Xiao-no-love
1702d11d8f
fix(phase): payload 相位等待期间维度线程跳过 playerPacketQueue.drain,断 getChunk 死锁链
...
死锁现场(threaddump):主线程在 MainThreadPhaseCoordinator.run 的 managedBlock 自旋,
等所有维度 isTicking()==false;overworld 唯一在 tick,栈:
drainChunkTasks:277 -> PlayerPacketQueue.drain -> ServerboundMovePlayerPacket.handle
-> Entity.move -> BlockGetter.clip(碰撞射线) -> getBlockState -> getChunk(未加载)
-> ServerChunkCache.getChunk:159 -> mainThreadProcessor.managedBlock park 等区块生成。
两方互等:主线程等维度停;维度等区块 future;future 推进需主线程 poll,而主线程在等维度停。
修复:drainChunkTasks 调 playerPacketQueue.drain() 前检查
MainThreadPhaseCoordinator.blocksWorkerStart(server)——主线程正在相位等待时跳过本轮移动包处理,
让维度尽快收口、主线程相位尽快通过;包不丢(留队列),下一拍主线程不再等时再处理。
本次也回退 ChunkPrefetcher 深度/扇面(实测加深反而下降)。
2026-10-13 21:15:14 +08:00
Xiao-no-love
1ab215dc72
perf(prefetch): ChunkPrefetcher 深度 6→10 + 前方扇面 ±1,缓解冲刺进新区块卡顿
...
观察:沿已生成区块边缘扩展极快,冲进大片未生成区则卡(1300mspt)。
根因:维度线程 tick 中 getChunk 未加载区块 → mainThreadProcessor.managedBlock park
等生成;冲刺速度快时预取前沿(view+6)不够,生成跟不上 → park 久 → tick 卡 →
主线程 payload 全局独占相位跟着等。
改动:
- PREFETCH_DEPTH 6→10(前方多预取 4 格,给生成管线更多提前量)。
- 新增 PREFETCH_FAN_HALF=1:除正前方外,垂直轴左右各预取 1 列,覆盖拐弯/斜向移动。
- 沿已生成边缘无额外开销:hasChunk 命中即跳过,不重复下 ticket。
- 纯增益,可回退(ENABLED 开关 + 常量改回)。
2026-10-13 21:05:24 +08:00
Xiao-no-love
a7afed5311
fix(deadlock): 回退 runDistanceManagerUpdates 代调 + 邻居更新锁改 subtask-only
...
实测死锁(threaddump):
- Server thread 持有 CollectingNeighborUpdater 实例锁(@136e9665),在 FastLeafDecay
(fireServerTickPost) → getBlockState → getChunk(未加载) → managedBlock → pollTask 里
跑出了 Sable 物理任务(Rapier3D.step 卡住),锁长期不释放。
- overworld 维度线程 tickChunks → SpreadingSnowyDirtBlock.randomTick → setBlock →
updateNeighborsAt → CollectingNeighborUpdater.updateNeighborsAtExceptFromFacing 等同一把锁。
- 主线程又在 custom payloads 相位等 overworld 停 → 三方互锁。
修复1:回退上一版(8f169b8 )在相位等待里代调的 runDistanceManagerUpdates——
它内部 runAllUpdates 持 DistanceManagerLock,与维度线程争锁;且其 getChunk 路径会阻塞。
保留 pollTask()(pumpchunk 版行为,无此死锁)。
修复2:CollectingNeighborUpdaterMixin 的实例锁改为仅 FineGrainScheduler.inSubTask() 时生效——
只有并发子任务之间才真正并发访问同一邻居栈;维度 worker / 主线程各自是当时唯一 ticker,
不应被该锁拖入"持锁阻塞"。这也避开主线程事件(FastLeafDecay)与维度线程争锁。
2026-10-13 20:25:19 +08:00
Xiao-no-love
8f169b8a5e
fix(phase): 相位等待期间主线程代维度 runDistanceManagerUpdates 推进区块生成
...
上一版(fd38301)只代维度 pollTask(),但 pollTask 仅 poll mainThreadProcessor 队列,
不推进区块生成。threaddump 实测:overworld #238 仍 TIMED_WAITING 在
ServerChunkCache.getChunk:159 -> mainThreadProcessor.managedBlock(future::isDone),
future 永不完成(推进它的 runDistanceManagerUpdates 只在维度线程的 ServerChunkCache.tick
每拍调用一次,而该维度线程这一拍就卡在 getChunk 上,到不了下一拍)。
修复:ServerChunkCacheAccessor 新增 @Invoker runDistanceManagerUpdates;
MainThreadPhaseCoordinator 相位等待内,主线程代各维度调
modzuozhi_runDistanceManagerUpdates()(→ runAllUpdates + runGenerationTasks + promoteChunkMap)
再 pollTask()。这是原版主线程 tick 该做的事,主线程补做安全(runAllUpdates 自带
DistanceManagerLock,持有时间短)。
2026-10-13 20:12:44 +08:00
Xiao-no-love
fd38301937
fix(phase): 相位等待期间主线程无条件代维度 poll chunkSource,解 getChunk 死锁
...
死锁现场(threaddump 抓到):
- Server thread RUNNABLE 卡在 MainThreadPhaseCoordinator.run:54 的 managedBlock 自旋,
等所有维度 loop isTicking()==false。
- dimensionalripper_server_overworld TIMED_WAITING on String,栈:
LevelTickLoop.drainChunkTasks -> PlayerPacketQueue.drain -> 处理 ServerboundMovePlayerPacket
-> Entity.move 碰撞检测 -> lithium ChunkAwareBlockCollisionSweeper -> Level.getChunk
-> ServerChunkCache.getChunk:159 -> mainThreadProcessor.managedBlock(future::isDone) park。
即维度线程在 tick 中请求未加载区块,park 等区块生成 future。
根因:DR 把维度 tick 丢独立线程后,主线程在 managedBlock 等待期间需代维度推进区块。
原版 MinecraftServer.pollTaskInternal 的「代各维度 chunkSource.pollTask」被
if (isSprinting() || haveTime()) 挡住——卡顿累积后 haveTime()==false,主线程停止代 poll
→ 维度 park 的区块 future 永不完成 → 维度永不停 → 与主线程等待互锁,单 tick 从 40s 涨到 200s。
修复:MainThreadPhaseCoordinator.run 的 managedBlock 条件内,无条件遍历所有维度
loop.getLevel().getChunkSource().pollTask(),与原版 pollTaskInternal 语义一致但去掉 haveTime 门槛。
单维度异常不影响相位收口。
2026-10-13 19:58:30 +08:00
Xiao-no-love
01a966fffc
diag(threadownership): 慢相位等待超2s时 dump 主线程+DR线程栈,抓死锁现场
...
背景:18:44 测试出现 tick 死锁——主线程在 custom payloads 相位的 managedBlock 里等
overworld 维度 loop 停(isTicking),而 overworld 卡在 connections 阶段的
SableMainThreadBridge.runForLevel(server.execute排队 + completion.get 等主线程),
tick 永不返回,单 tick 从 40s 单调涨到 203s。
ModernFix watchdog 的 Thread Dump 不含 Server thread/DR 线程,现场缺失。
本改动:ThreadOwnershipDiagnostics.slowPhase 等待 >2s 时 dumpKeyThreads(),
用 ThreadMXBean 输出 Server thread + 所有 dimensionalripper_* 线程的完整栈,
含 lockName/lockOwner/lockInfo/blocked/waited/ownedSynchronizers。
时间窗口 30s 限流,避免刷屏。纯诊断,不改执行逻辑。
2026-10-13 19:44:20 +08:00
Xiao-no-love
74cc5505b0
fix(mixin): 子任务线程上 updateNeighbourForOutputSignal 第二格邻居访问加加载守卫
...
根因:Level.updateNeighbourForOutputSignal 遍历6方向,第一格邻居有 hasChunkAt 守卫,
但若第一格是红石导体,会再往外取第二格(blockpos.relative(direction))并直接 getBlockState
——没有 hasChunkAt 守卫。原版主线程会按需同步生成区块;DR 细粒度子任务线程上则触发
ServerChunkCacheMixin.modzuozhi_subtaskSafeGetChunk 的 required=true 未命中分支,
抛 IllegalStateException + FaultGuard 降级。异常被 LevelChunk$BoundTickingBlockEntity.tick
崩溃保护包成 CrashReport("Ticking block entity"),同时细粒度被关 → 跑图卡顿。
实测:18:10:03 trial_spawner(-6765,-15,1376) tick 时 setChanged → updateNeighbourForOutputSignal
→ 第二格 getBlockState(chunk=-423,85 未加载) → 崩。
修复:新增 LevelNeighbourSignalGuardMixin,@WrapOperation 拦第二格 getBlockState(ordinal=1),
子任务线程 + !hasChunkAt 时返回 VOID_AIR(getWeakChanges=false,不传播红石信号),
与 getBlockState 越界分支语义一致。主线程/维度 worker 行为不变。
参考:Folia 对 updateNeighbourForOutputSignal 加加载守卫的同类修复思路。
2026-10-13 18:28:28 +08:00
Xiao-no-love
92ba852fc5
feat(payload): per-id 诊断明细,日志/命令输出 top-15 payload 来源
...
- PayloadDiagnostics.snapshot() 追加 topIds():按 received 降序取前15个 payload id
- 格式 id(r收/c完/f失/x消/e期/rej拒),供定位哪些 mod 频繁触发 enqueueWork
- 触发路径自动覆盖:/dimensionalripper status 命令 + slowPhase 慢相位日志
- 本改动纯诊断,不改任何执行时序,无性能影响(仅在 snapshot 调用时排序)
2026-10-13 18:05:55 +08:00
Xiao-no-love
a3b0655072
Revert "perf(payload): scope payload ownership to affected dimensions only"
...
This reverts commit 490be29f57 .
2026-10-13 17:55:33 +08:00
Xiao-no-love
be4b368a35
Revert "perf(payload): fast-path idle dimensions + scoped ownership"
...
This reverts commit d231930c6b .
2026-10-13 17:55:33 +08:00
Xiao-no-love
d231930c6b
perf(payload): fast-path idle dimensions + scoped ownership
...
补充上一版按维度精细化独占:
- MainThreadPhaseCoordinator.runScoped 加快速路径:涉及维度当前都没在 tick 时,
直接执行 action,跳过 hold/cancel/schedule 与 managedBlock 往返。
多玩家时绝大多数 payload 落在维度空闲窗口,此路径消除其交接开销。
2026-10-13 17:45:24 +08:00
Xiao-no-love
490be29f57
perf(payload): scope payload ownership to affected dimensions only
...
问题:多玩家服务器上,每个 mod payload(NeoForge ServerPayloadContext.enqueueWork)
处理都走 MainThreadPhaseCoordinator 的全局独占——挂起并等待【全部】维度 tick 停下。
跑图时维度 tick 是长任务,主线程 managedBlock 自旋等待达数秒(日志 oldestMs=5917),
全服掉到 10 TPS。而 payload 实际只碰它所属玩家的那个维度。
修法:
- PayloadWorkQueue.Entry 增 dimension 字段(submit 时取 player.serverLevel)
- 新增 pendingDimensions() / drainBatchByDimension():按维度分组处理
- MainThreadPhaseCoordinator.runScoped():只挂起/等待指定维度,不设全局 EXCLUSIVE
(其它维度 blocksWorkerStart 仍 false,照常自排程)
- PayloadMainThreadDispatch.drain 改走 runScoped(涉及维度),只暂停受影响维度
效果:多玩家时玩家 A(overworld) 的 payload 只停 overworld,nether/end 等继续 tick。
注:这是 payload 路径的优化,未改变维度 tick 本身不可中断的性质。
2026-10-13 17:41:52 +08:00
Xiao-no-love
d76d6593bd
feat(compat): expose shared DistanceManager lock for cross-mod ticket iteration
...
将 DistanceManagerMixin 的私有 static LOCK 抽到公开的
dev.modzuozhi.core.compat.DistanceManagerLock.LOCK,供 ServerWarashi
的 TicketScanService 通过反射共用,实现 ticket 表「读-写互斥」。
背景:DR 维度线程在 tick 中并发增删 DistanceManager.tickets
(Long2ObjectOpenHashMap),warashi 主线程遍历同表做采样归属快照时,
fastutil fail-fast 迭代器内部 wrapped 被置 null -> nextEntry NPE 崩服。
DR 原有锁保护所有写路径,但读路径(warashi 遍历)不在锁内。
- 新增 DistanceManagerLock(公开 ReentrantLock)
- DistanceManagerMixin 的 LOCK 指向该共享锁
2026-10-13 17:02:08 +08:00
Xiao-no-love
72936288d9
fix(finegrain): default FineGrainScheduler.enabled to false
...
规避 DR × Lithium WorldBorder 并发死锁:
Lithium 的 WorldBorderPositionListenerMulti 用非线程安全 WeakHashMap
假设单线程访问 WorldBorder;DR 细粒度并行(fine=on)让多个 subtask
并发调 WorldBorder.addListener -> WeakHashMap.put 死循环 ->
subtask 持 ChunkLock 卡死 -> barrier 永挂 -> overworld 维度线程卡
Barrier.await 攥着 level-tick 所有权 -> 主线程 payload DRAINING 积压
-> 假死。
治标方案:默认关闭细粒度并行,需要时 /dimensionalripper fine on 手动开。
同步修正类 javadoc 中"默认开启"的描述。
2026-10-13 16:30:05 +08:00
Xiao-no-love
e358289931
fix(compat): load warashi tracker via context classloader
...
上一版用 ModContainer.getClassLoader() 编译失败(该 API 不存在)。
改为:线程上下文 classloader + Class.forName 默认加载 双重兜底,
均失败则桥降级 no-op。不依赖任何具体 loader API。
2026-10-13 15:33:53 +08:00
Xiao-no-love
cc0cbe1679
feat(compat): bridge dimension tick scopes to ServerWarashi
...
- 新增 WarashiScopeBridge:软依赖检测 serverwarashi,MethodHandle 反射调
PerfWorkTracker.enterScope/exitScope;未加载则全 no-op,零开销
- LevelTickLoop.runIfCurrent:维度 tick 体进入/退出本维度采样作用域
(axisId = 维度 ResourceLocation),让 warashi 采样端能区分各维度轴
- 检测模式沿用 DimensionalRipperMixinPlugin 的 FMLLoader.getModFileById
配合 warashi 的 Scope 化,多维度并发 tick 时各维度采样状态隔离,消除
static depth 竞态导致的越界崩溃。
2026-10-13 15:26:08 +08:00
Xiao-no-love
0ac8eea34d
fix: skip Level random-source redirect when C2ME is present
...
C2ME's fixes-worldgen-threading-issues module redirects the same
Level.<init> RandomSource.create() injection point with mixinPriority=1100,
which silently skips our redirect (priority 1000) and trips defaultRequire=1,
crashing startup.
Split the random-source @Redirect out of LevelMixin into a new LevelRandomMixin
so it can be skipped independently, and gate it in DimensionalRipperMixinPlugin
on C2ME presence. C2ME's CheckedThreadLocalRandom checks ownership via
() -> Level.thread; our swapThreadsAndRun replaces Level.thread together with
the worker thread, so the two remain consistent and C2ME's random source stays
safe under dimension parallelism.
LevelMixin keeps its getBlockEntity thread-guard masquerade unconditionally.
2026-10-13 13:30:57 +08:00
Disy
6cef4e20ad
docs: scope random tick and server event ownership fixes
2026-10-13 11:30:12 +08:00
Disy
763c015103
feat: protect mod payloads with main-thread ownership in 1.1.17
2026-10-06 17:36:00 +08:00
Disy
691e949691
feat: add bounded cancellable payload work queue
2026-10-06 16:44:11 +08:00
Disy
2619f0936d
docs: specify shared payload ownership handoff
2026-10-06 16:28:40 +08:00
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
xxdd001
74cae0b581
build: add v1.1.4 sources jar
2026-09-14 20:14:09 +08:00
xxdd001
629f9e50d8
build: keep v1.1.4 artifact in libs/
2026-09-14 20:13:14 +08:00
xxdd001
00bbb8005e
build: upload dimensionalripper-1.1.4.jar artifact
2026-09-14 20:11:01 +08:00
xxdd001
98e39a0265
fix: reconcileConnections CME under high player churn; bump v1.1.4
2026-09-14 20:09:18 +08:00
modzuozhi
563787c5d4
Bump version to 1.1.3 and publish build artifacts
2026-09-12 20:27:22 +08:00
modzuozhi
7990d70255
README: make SerDesFilter.BLACKLIST reference a clickable link to source
2026-09-12 20:21:12 +08:00
modzuozhi
350920eaed
Add teunwhitelist command to remove modded TE from parallel whitelist
...
- New /dimensionalripper teunwhitelist <regname> removes a modded block
entity from the parallel whitelist, restoring its default forced-serial
processing; reports whether it was actually present
- SerDesFilter.unwhitelistModded now returns boolean (removal success)
- README: document whitelist/teunwhitelist commands and clarify the
built-in blacklist is hardcoded, not command-editable
2026-09-12 20:13:28 +08:00
modzuozhi
44e6fa2537
README: clarify conservative-serial TE list (hopper/dropper/dispenser/beehive etc.) to match implementation
2026-09-12 01:19:31 +08:00
modzuozhi
75ed486fec
Update README: rewrite feature list as optimization scope (dimensions/entities/chunks/BE/items, all server-side)
2026-09-12 00:50:56 +08:00
modzuozhi
849ce7171c
Add built artifacts: dimensionalripper-1.1.2.jar and sources jar
...
Bump mod_version to 1.1.2. Includes ESC-pause freeze fix, TE whitelist command, shard drop counters and POI lock optimization.
2026-09-12 00:47:23 +08:00
modzuozhi
801deb701a
Fix singleplayer disconnect on ESC pause: freeze dimension loops
...
- New client mixin MixinMinecraftPauseDetector: on each Minecraft.runTick, detect integrated-server pause and freeze/unfreeze all dimension loops via DimThreadCore.setPaused, so no worker writes LevelChunk while the main thread is saving/pausing. Fixes reproducible disconnect (packet decode IndexOutOfBounds on ClientboundLevelChunkWithLight) ~0.5s after opening the pause menu.
- LevelTickLoop: add pause()/resume() (cancel next tick, stop self-rescheduling, re-schedule on resume) plus a paused guard at run() entry. Resilient to RejectedExecutionException when the master pool is already shut down.
- ServerManager.setPaused: pause all loops, then wait up to 5s for the in-flight tick to fully settle before returning.
- Keep existing work: TE whitelist command (/dimensionalripper tewhitelist), shard drop counters (entity/TE/chunk-env), POI lock optimization.
2026-09-12 00:44:23 +08:00
modzuozhi
3b9320d1e0
Update README: document sharding command and new optimizations
2026-09-09 22:22:43 +08:00
modzuozhi
3c4710490c
Add entity sharding (ShardGate) and POI lock optimization
...
- ShardGate: shard entity ticking into per-shard CAS gates so slow shards
skip a tick instead of blocking the whole barrier
- EntityTickParallel: collect entities by stable id into shards, submit via
ShardGate, count missed shards
- FineGrainScheduler: add submitShard path with gate release and barrier
counting
- SectionStorageMixin: replace global exclusive synchronized map with
ConcurrentLong2ObjectMap to remove the POI read lock bottleneck
(~19k villagers: TPS 9 -> 20)
- MasterCommand: add 'fine sharded on|off' toggle and status display
- Add environment setup/launch scripts
2026-09-09 22:13:02 +08:00
xxdd001
1607a87213
Add built artifacts: dimensionalripper-1.1.1.jar and sources jar (includes save-hang fix)
2026-09-07 19:24:09 +08:00
xxdd001
3dc6f4141f
Fix intermittent 'Saving world' freeze on shutdown
...
killAllLoops() only cancelled the next scheduled tick without waiting for
in-flight dimension ticks to finish. The main thread then ran saveAllChunks
concurrently with worker threads (level.tick -> processUnloads) mutating the
non-thread-safe ChunkMap.toDrop, corrupting the fastutil map and hanging the
save forever.
Add ServerManager.stopLoopsAndWaitIdle() that kills all loops and spins until
each loop isTicking()==false (30s watchdog), and call it first in
MinecraftServerDimMixin.stopServer before killAllLoops(), so dimension ticks
fully finish before the main thread starts saving.
2026-09-07 19:17:54 +08:00
xxdd001
da3246878b
Add built artifacts: dimensionalripper-1.0.0.jar and sources jar
2026-08-27 18:32:26 +08:00
xxdd001
675b74205d
Fix chunk entity map concurrency: use concurrent Int2ObjectMap for ChunkMap.entityMap
2026-08-26 23:42:58 +08:00
xxdd001
837a6497e4
Add incompatibility warning for multithreading/cross-dimension mods
2026-08-26 21:19:56 +08:00
xxdd001
ce6532b644
Unify license to MIT
2026-08-26 21:16:34 +08:00
xxdd001
d14a892217
Add AI-generated disclosure to README
2026-08-26 21:13:13 +08:00
xxdd001
d4f1914c24
Add README with features and usage
2026-08-26 21:06:33 +08:00
xxdd001
453e715f3b
Initial commit: Dimensional Ripper mod source (NeoForge 1.21.1)
2026-08-26 21:04:16 +08:00
xxdd001
df32c72cc9
Initial commit
2026-08-26 21:02:33 +08:00