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.
This commit is contained in:
Disy committed 2026-10-03 21:24:31 +08:00
1 parent 74cae0b581
commit 32b11aee87
42 files changed
+1506 -381

No files matched your search

@@ -0,0 +1,171 @@
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<Throwable>();
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");
}
}