package dev.modzuozhi; import com.mojang.authlib.GameProfile; import dev.modzuozhi.core.compat.SableAssemblyDispatch; import dev.modzuozhi.core.compat.SableMainThreadBridge; import dev.modzuozhi.core.dimthread.DimThreadCore; import dev.modzuozhi.core.dimthread.FineGrainScheduler; import dev.modzuozhi.core.dimthread.FaultGuard; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ClientInformation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.chunk.status.ChunkStatus; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.event.tick.ServerTickEvent; import java.util.UUID; /** Opt-in runtime fixture: no real user world or network connection. */ @EventBusSubscriber(modid = "dimensionalripper") public final class InteractionFlowRegression { private static boolean started, completed; private static long startTime; private static final BlockPos SOURCE = new BlockPos(8, 100, 8); @SubscribeEvent public static void tick(ServerTickEvent.Post event) { var server = event.getServer(); var level = server.overworld(); if (server.getTickCount() < 15) return; if (!started) { started = true; DimThreadCore.MANAGER.setPaused(server, true); level.setChunkForced(0, 0, true); level.getChunk(0, 0); checkMissingChunkProbes(server); for (int x = 4; x <= 12; x++) for (int z = 4; z <= 12; z++) { level.setBlockAndUpdate(new BlockPos(x, 99, z), Blocks.STONE.defaultBlockState()); level.setBlockAndUpdate(new BlockPos(x, 100, z), Blocks.AIR.defaultBlockState()); } level.setBlockAndUpdate(SOURCE.above(3), Blocks.CHEST.defaultBlockState()); var player = new ServerPlayer(server, level, new GameProfile(UUID.randomUUID(), "Regression"), ClientInformation.createDefault()) { @Override public boolean hasDisconnected() { return false; } }; startTime = level.getGameTime(); DimThreadCore.MANAGER.setPaused(server, false); SableAssemblyDispatch.dispatch(player, () -> { if (!server.isSameThread() || !SableMainThreadBridge.ownsHandoff(level)) throw new AssertionError("Interaction did not obtain exclusive server ownership"); if (level.getBlockEntity(SOURCE.above(3)) == null) throw new AssertionError("Main-thread block entity lookup silently failed"); level.setBlockAndUpdate(SOURCE, Blocks.WATER.defaultBlockState()); completed = true; }); } if (server.getTickCount() == 180) { DimThreadCore.MANAGER.setPaused(server, true); if (!completed) throw new AssertionError("Interaction was not dispatched"); var progress = DimThreadCore.MANAGER.getLoop(level).progress(System.nanoTime()); if (!(progress.tps() > 0) || Double.isNaN(progress.averageWorkMs()) || Double.isNaN(progress.sinceCompletedMs()) || !Double.isNaN(progress.runningMs())) throw new AssertionError("Invalid drained-loop progress: " + progress); ModZuozhi.LOGGER.info("TICK PROGRESS TEST: {}", progress); if (level.getGameTime() <= startTime) throw new AssertionError("World clock stopped"); String diagnostic = ((dev.modzuozhi.core.compat.ScheduledTickDiagnostics) level.getFluidTicks()) .dimensionalripper$describeTicks(SOURCE, level.getGameTime()); if (diagnostic.contains(Long.toString(Long.MIN_VALUE))) throw new AssertionError("Missing tick instrumentation"); ModZuozhi.LOGGER.info("FLOW DIAGNOSTIC TEST: {}", diagnostic); if (!level.getFluidState(SOURCE.east()).is(Fluids.FLOWING_WATER)) throw new AssertionError("Scheduled water tick did not spread: time=" + level.getGameTime() + " pending=" + level.getFluidTicks().count()); ModZuozhi.LOGGER.info("INTERACTION REGRESSION PASS: exclusive main-thread block entity lookup and scheduled water spread"); server.halt(false); } } /** Run the actual transformed getChunk from two real scheduler subtasks. */ private static void checkMissingChunkProbes(net.minecraft.server.MinecraftServer server) { var level = server.overworld(); var cache = level.getChunkSource(); var loaded = level.getChunk(0, 0); int loadedCount = cache.getLoadedChunksCount(); var failure = new AtomicReference(); var completed = new AtomicInteger(); var barrier = FineGrainScheduler.beginBarrier("missing-chunk-regression"); long began = System.nanoTime(); for (int task = 0; task < 2; task++) { barrier.submit(server, () -> { try { if (!FineGrainScheduler.inSubTask()) throw new AssertionError("Not a subtask"); for (int i = 0; i < 100_000; i++) { // Alternate coordinates: no fake positive or negative cache entries. int x = 100_000 + (i & 1); if (cache.getChunk(x, -100_000, ChunkStatus.FULL, false) != null) throw new AssertionError("Missing FULL probe returned a fabricated chunk"); if (cache.getChunk(x, -100_000, ChunkStatus.EMPTY, false) != null) throw new AssertionError("Missing partial-status probe returned a chunk"); if (cache.getChunkNow(x, -100_000) != null) throw new AssertionError("Missing getChunkNow returned a chunk"); if (cache.getChunk(0, 0, ChunkStatus.FULL, false) != loaded || cache.getChunk(0, 0, ChunkStatus.FULL, true) != loaded) throw new AssertionError("Loaded chunk identity lost"); } completed.incrementAndGet(); } catch (Throwable error) { failure.compareAndSet(null, error); } }); } barrier.await(); barrier.drain(); if (failure.get() != null) throw new AssertionError("Missing chunk regression", failure.get()); if (completed.get() != 2 || !FineGrainScheduler.isEnabled()) throw new AssertionError("Probes did not complete with parallelism enabled"); if (cache.getLoadedChunksCount() != loadedCount) throw new AssertionError("Optional probe loaded chunks"); var rabbit = new net.minecraft.world.entity.animal.Rabbit(net.minecraft.world.entity.EntityType.RABBIT, level); rabbit.setPos(8, 101, 8); var routed = new AtomicInteger(); dev.modzuozhi.core.dimthread.EntityTickParallel.begin(); level.guardEntityTick(entity -> { if (!FineGrainScheduler.inSubTask()) throw new AssertionError("Loaded interior lost parallelism"); routed.incrementAndGet(); }, rabbit); if (routed.get() != 0) throw new AssertionError("Interior entity was not batched"); dev.modzuozhi.core.dimthread.EntityTickParallel.end(); if (routed.get() != 1) throw new AssertionError("Interior entity did not run exactly once"); rabbit.setDeltaMovement(10_000, 0, 0); dev.modzuozhi.core.dimthread.EntityTickParallel.begin(); level.guardEntityTick(entity -> { if (FineGrainScheduler.inSubTask()) throw new AssertionError("Unbounded collision scan on subtask"); routed.incrementAndGet(); }, rabbit); if (routed.get() != 2) throw new AssertionError("Wide movement did not stay on owner"); dev.modzuozhi.core.dimthread.EntityTickParallel.end(); if (routed.get() != 2) throw new AssertionError("Owner entity tick duplicated"); ModZuozhi.LOGGER.info("ENTITY ROUTING REGRESSION PASS: loaded interior parallel, wide movement on owner, exactly once"); ModZuozhi.LOGGER.info("MISSING CHUNK REGRESSION PASS: 600000 absent reads + 400000 loaded reads, {} ms, fine enabled", (System.nanoTime() - began) / 1_000_000); // A required miss must fail instead of allocating a fake chunk or blocking forever. var requiredBarrier = FineGrainScheduler.beginBarrier("required-chunk-regression"); requiredBarrier.submit(server, () -> { try { for (var status : new ChunkStatus[]{ChunkStatus.FULL, ChunkStatus.EMPTY}) { try { cache.getChunk(100_000, -100_000, status, true); throw new AssertionError("Required missing chunk was fabricated"); } catch (IllegalStateException expected) { if (!expected.getMessage().startsWith("Parallel task requested synchronous chunk generation:")) throw expected; } } } catch (Throwable error) { failure.compareAndSet(null, error); } }); requiredBarrier.await(); requiredBarrier.drain(); if (failure.get() != null) throw new AssertionError("Required chunk regression", failure.get()); if (FineGrainScheduler.isEnabled() || !FaultGuard.isFineDegraded() || cache.getLoadedChunksCount() != loadedCount) throw new AssertionError("Required miss did not fail safely"); FaultGuard.reset(); FineGrainScheduler.setEnabled(true); ModZuozhi.LOGGER.info("REQUIRED CHUNK REGRESSION PASS: FULL/EMPTY fail fast, no generation; test-only fault reset"); } }