feat: protect mod payloads with main-thread ownership in 1.1.17
This commit is contained in:
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); }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user