42 lines
2.1 KiB
Java
42 lines
2.1 KiB
Java
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); }
|
|
}
|
|
}
|