feat: protect mod payloads with main-thread ownership in 1.1.17

This commit is contained in:
Disy committed 2026-10-06 17:36:00 +08:00
1 parent 691e949691
commit 763c015103
29 files changed
+1552 -53

No files matched your search

@@ -0,0 +1,41 @@
package dev.modzuozhi.mixin;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import dev.modzuozhi.core.dimthread.DimThreadCore;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.LevelChunk;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import org.spongepowered.asm.mixin.*;
import java.util.Queue;
/** CC remains optional. Keep its identity check intact, but run it with exclusive ownership. */
@Pseudo
@Mixin(targets = "dan200.computercraft.shared.util.TickScheduler", remap = false)
public abstract class ComputerCraftTickSchedulerMixin {
@Shadow @Final private static Queue<?> toTick;
@Unique private static final Object dimensionalripper$schedulerLock = new Object();
@WrapMethod(method = "tick()V")
private static void dimensionalripper$safeTick(Operation<Void> original) {
// No tokens -> no world access, no global barrier. New tokens can wait until next tick.
if (toTick.isEmpty()) return;
var server = ServerLifecycleHooks.getCurrentServer();
if (server == null) throw new IllegalStateException("CC tick without a running server");
DimThreadCore.MANAGER.runMainThreadPhase(server, "CC TickScheduler", () -> {
synchronized (dimensionalripper$schedulerLock) { original.call(); }
});
}
// CC's delayed map is global across dimensions. These callbacks only manipulate tokens;
// neither loads chunks nor waits on the main thread. Never take this lock before draining.
@WrapMethod(method = "onChunkTicketChanged")
private static void dimensionalripper$ticket(ServerLevel level, long pos, int oldTicket, int newTicket, Operation<Void> original) {
synchronized (dimensionalripper$schedulerLock) { original.call(level, pos, oldTicket, newTicket); }
}
@WrapMethod(method = "onChunkUnload")
private static void dimensionalripper$unload(LevelChunk chunk, Operation<Void> original) {
synchronized (dimensionalripper$schedulerLock) { original.call(chunk); }
}
}