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 加加载守卫的同类修复思路。
This commit is contained in:
Xiao-no-love committed 2026-10-13 18:28:28 +08:00
1 parent 92ba852fc5
commit 74cc5505b0
2 files changed
+46

No files matched your search

@@ -0,0 +1,45 @@
package dev.modzuozhi.mixin;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import dev.modzuozhi.core.dimthread.FineGrainScheduler;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
/**
* 修复细粒度子任务线程上 {@code Level.updateNeighbourForOutputSignal} 触发未加载区块同步生成导致崩溃。
* <p>
* 原版该方法遍历 6 个方向:第一格邻居用 {@code hasChunkAt} 守卫,但<strong>若第一格是红石导体,
* 会再往外取第二格</strong>({@code blockpos.relative(direction)})并直接 {@code getBlockState}——<strong>没有 hasChunkAt 守卫</strong>。
* 原版主线程上这个 {@code getBlockState} 会按需同步生成/加载区块;但在 DR 细粒度子任务线程上,
* {@code ServerChunkCacheMixin.modzuozhi_subtaskSafeGetChunk} 检测到 required=true 未命中,
* 会抛 {@code IllegalStateException}(并触发 {@link dev.modzuozhi.core.dimthread.FaultGuard} 降级)。
* 该异常被 {@code LevelChunk$BoundTickingBlockEntity.tick} 的崩溃保护包成 CrashReport,
* 表现为"Ticking block entity"崩溃 + 细粒度被关 → 跑图卡顿。
* <p>
* 修复:拦第二格的 {@code getBlockState}(ordinal=1)。目标区块未加载时返回 {@code VOID_AIR}
* (与 {@code getBlockState} 越界分支语义一致:{@code VOID_AIR.getWeakChanges} 为 false,
* 不会触发 {@code neighborChanged},等价"该方向无红石信号传播"),避免子任务线程请求同步生成。
* <p>
* 仅在子任务线程生效;主线程/维度 worker 行为完全不变。
*/
@Mixin(Level.class)
public abstract class LevelNeighbourSignalGuardMixin {
@WrapOperation(method = "updateNeighbourForOutputSignal",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/world/level/Level;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;",
ordinal = 1))
private BlockState modzuozhi_guardSecondNeighbour(Level level, BlockPos pos, Operation<BlockState> original) {
if (FineGrainScheduler.inSubTask() && !level.hasChunkAt(pos)) {
// 未加载的第二格邻居:不请求同步生成,返回 VOID_AIR(getWeakChanges=false,不传播信号)。
return Blocks.VOID_AIR.defaultBlockState();
}
return original.call(level, pos);
}
}
@@ -38,6 +38,7 @@
"LevelGetChunkMixin",
"LevelMixin",
"LevelRandomMixin",
"LevelNeighbourSignalGuardMixin",
"LevelTicksMixin",
"LivingEntityGetInBlockStateMixin",
"MinecraftServerDimMixin",