fix: skip Level random-source redirect when C2ME is present
C2ME's fixes-worldgen-threading-issues module redirects the same Level.<init> RandomSource.create() injection point with mixinPriority=1100, which silently skips our redirect (priority 1000) and trips defaultRequire=1, crashing startup. Split the random-source @Redirect out of LevelMixin into a new LevelRandomMixin so it can be skipped independently, and gate it in DimensionalRipperMixinPlugin on C2ME presence. C2ME's CheckedThreadLocalRandom checks ownership via () -> Level.thread; our swapThreadsAndRun replaces Level.thread together with the worker thread, so the two remain consistent and C2ME's random source stays safe under dimension parallelism. LevelMixin keeps its getBlockEntity thread-guard masquerade unconditionally.
This commit is contained in:
1 parent
6cef4e20ad
commit
0ac8eea34d
5 files changed
+56
-22
No files matched your search
+5
-5
@@ -46,8 +46,8 @@ mod_description=Multi-threaded dimension parallel optimization: independent per-
|
||||
|
||||
# Proxy settings for VPN / system proxy
|
||||
# 说明:VPN 开启(系统代理模式)时取消下面 4 行注释;直连可访问时保持注释状态
|
||||
#systemProp.http.proxyHost=127.0.0.1
|
||||
#systemProp.http.proxyPort=57777
|
||||
#systemProp.https.proxyHost=127.0.0.1
|
||||
#systemProp.https.proxyPort=57777
|
||||
#systemProp.http.nonProxyHosts=localhost|127.*|192.168.*|10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.20.*|172.21.*|172.22.*|172.23.*|172.24.*|172.25.*|172.26.*|172.27.*|172.28.*|172.29.*|172.30.*|172.31.*
|
||||
systemProp.http.proxyHost=127.0.0.1
|
||||
systemProp.http.proxyPort=7897
|
||||
systemProp.https.proxyHost=127.0.0.1
|
||||
systemProp.https.proxyPort=7897
|
||||
systemProp.http.nonProxyHosts=localhost|127.*|192.168.*|10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.20.*|172.21.*|172.22.*|172.23.*|172.24.*|172.25.*|172.26.*|172.27.*|172.28.*|172.29.*|172.30.*|172.31.*
|
||||
@@ -11,20 +11,30 @@ import java.util.Set;
|
||||
/** Selects compatibility-safe mixins before Minecraft target classes are transformed. */
|
||||
public final class DimensionalRipperMixinPlugin implements IMixinConfigPlugin {
|
||||
private boolean lithiumLoaded;
|
||||
private boolean c2meLoaded;
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) {
|
||||
try {
|
||||
var loadingMods = FMLLoader.getLoadingModList();
|
||||
this.lithiumLoaded = loadingMods != null && loadingMods.getModFileById("lithium") != null;
|
||||
this.c2meLoaded = loadingMods != null && loadingMods.getModFileById("c2me") != null;
|
||||
} catch (Throwable ignored) {
|
||||
// If loader discovery is unavailable, preserve the standalone behavior.
|
||||
this.lithiumLoaded = false;
|
||||
this.c2meLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
// C2ME's fixes-worldgen-threading-issues module redirects the same Level.<init> random
|
||||
// source injection point with a higher priority (1100), which would silently skip ours and
|
||||
// trip defaultRequire=1. Its CheckedThreadLocalRandom is compatible with our thread swapping,
|
||||
// so let it own the injection point whenever C2ME is present.
|
||||
if (mixinClassName.endsWith(".LevelRandomMixin")) {
|
||||
return !this.c2meLoaded;
|
||||
}
|
||||
if (mixinClassName.endsWith(".LithiumEntityGroupsMixin")) {
|
||||
return this.lithiumLoaded;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package dev.modzuozhi.mixin;
|
||||
|
||||
import dev.modzuozhi.core.dimthread.DimThreadCore;
|
||||
import dev.modzuozhi.core.dimthread.FineGrainScheduler;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -27,22 +26,6 @@ public abstract class LevelMixin {
|
||||
@Shadow
|
||||
public Thread thread;
|
||||
|
||||
/**
|
||||
* 将 {@code Level.random}(及 {@code randValue})初始化为线程安全随机源。
|
||||
* <p>
|
||||
* 原版 {@code Level.random = RandomSource.create()} 得到 {@code LegacyRandomSource},
|
||||
* 其内部通过 {@code ThreadingDetector} 检测跨线程访问并抛异常。维度并行/细粒度并行下,
|
||||
* 实体 AI(村民、群游鱼等)与服务器主线程会并发访问 {@code getRandom()},导致
|
||||
* "Accessing LegacyRandomSource from multiple threads"。这里重定向构造期间的
|
||||
* {@code RandomSource.create()} 为 {@code createThreadSafe()}({@code ThreadSafeLegacyRandomSource},
|
||||
* 基于 AtomicLong CAS,无线程检测),使并发访问安全。
|
||||
*/
|
||||
@Redirect(method = "<init>",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/RandomSource;create()Lnet/minecraft/util/RandomSource;"))
|
||||
private static RandomSource modzuozhi_threadSafeRandom() {
|
||||
return RandomSource.createThreadSafe();
|
||||
}
|
||||
|
||||
@Redirect(method = "getBlockEntity",
|
||||
at = @At(value = "INVOKE", target = "Ljava/lang/Thread;currentThread()Ljava/lang/Thread;"))
|
||||
private Thread modzuozhi_recallForDimthreads() {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package dev.modzuozhi.mixin;
|
||||
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
/**
|
||||
* 将 {@code Level.random}(及 {@code randValue})初始化为线程安全随机源。
|
||||
* <p>
|
||||
* 原版 {@code Level.random = RandomSource.create()} 得到 {@code LegacyRandomSource},
|
||||
* 其内部通过 {@code ThreadingDetector} 检测跨线程访问并抛异常。维度并行/细粒度并行下,
|
||||
* 实体 AI(村民、群游鱼等)与服务器主线程会并发访问 {@code getRandom()},导致
|
||||
* "Accessing LegacyRandomSource from multiple threads"。这里重定向构造期间的
|
||||
* {@code RandomSource.create()} 为 {@code createThreadSafe()}({@code ThreadSafeLegacyRandomSource},
|
||||
* 基于 AtomicLong CAS,无线程检测),使并发访问安全。
|
||||
* <p>
|
||||
* <b>与 C2ME 的兼容</b>:C2ME 的 {@code fixes-worldgen-threading-issues} 模块对同一个
|
||||
* {@code Level.<init>} 注入点做 {@code @Redirect}(返回其 {@code CheckedThreadLocalRandom}),
|
||||
* 且 C2ME 的 {@code mixinPriority=1100} 高于本模组默认的 1000。若两边同时应用,本模组的
|
||||
* 重定向会被静默跳过,而 {@code defaultRequire=1} 会导致注入检查失败、启动崩溃。
|
||||
* <p>
|
||||
* 因此本 mixin 被拆为独立类,由 {@code DimensionalRipperMixinPlugin} 在检测到 C2ME 加载时
|
||||
* 整体跳过(返回 false)。C2ME 的 {@code CheckedThreadLocalRandom} 以 {@code () -> this.thread}
|
||||
* 做归属校验,而本模组的 {@code swapThreadsAndRun} 会把 {@code Level.thread} 与当前 worker
|
||||
* 一并替换,两者方向一致,故 C2ME 的随机源在维度并行下同样安全 —— 让出此注入点不影响
|
||||
* 本模组的并发正确性,也保留了独立的 {@code createThreadSafe()} 行为(C2ME 不在时)。
|
||||
* <p>
|
||||
* 注意:{@code LevelMixin} 中的 {@code getBlockEntity} 线程守卫伪装与本类无关,永远应用。
|
||||
*/
|
||||
@Mixin(Level.class)
|
||||
public abstract class LevelRandomMixin {
|
||||
|
||||
@Redirect(method = "<init>",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/RandomSource;create()Lnet/minecraft/util/RandomSource;"))
|
||||
private static RandomSource modzuozhi_threadSafeRandom() {
|
||||
return RandomSource.createThreadSafe();
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
"LevelExplodeMixin",
|
||||
"LevelGetChunkMixin",
|
||||
"LevelMixin",
|
||||
"LevelRandomMixin",
|
||||
"LevelTicksMixin",
|
||||
"LivingEntityGetInBlockStateMixin",
|
||||
"MinecraftServerDimMixin",
|
||||
|
||||
Reference in new issue
Block a user