Late Tweakers(延迟注入的 ITweaker)

基本信息

loading/fml/tweakers/ 共 5 个文件(4 个 ITweaker + 1 个 package-info.java):

类 行数 实现 注册方式
AngelicaLateTweaker 72 ITweaker 黑板 tweak 列表(RFB 未加载时)
CeleritasLateTweaker 35 ITweaker 黑板 tweak 列表(RFB 未加载时)
IncompatibleModsDisablerTweaker 138 ITweaker 声明式(gradle coremod 列表)
XaerosTransformerDisablerTweaker 47 ITweaker 声明式(gradle coremod 列表)

四个类的 getLaunchTarget() 都返回 null,getLaunchArguments() 都返回 new String[0],acceptOptions() 都是 no-op。

为什么不用 getASMTransformerClass

AngelicaClientTweaker :164-165 的注释解释了:

// If we were to register them normally in getASMTransformerClass(),
// they would be sorted at index 0 which we do not want.

它们在 injectIntoClassLoader / getLaunchArguments 里反射改写 LaunchClassLoader.transformers 列表,从而拿到正确的排序位置。

AngelicaLateTweaker:收窄 transformer 排除 + 换掉重定向器

:30-32 的方法注释:「Reduces overly broad transformer exclusions that prevent AngelicaRedirector from doing its job.」

两个动作:

阶段 行 动作
injectIntoClassLoader :26-28 narrowTransformerExclusions(classLoader)
getLaunchArguments :55-71 移除 EarlyRedirectorTransformer,注册完整版 AngelicaRedirectorTransformer

narrowTransformerExclusions(:34-46)

反射取 LaunchClassLoader.transformerExceptions(Set<String>),然后:

TransformerNarrower.narrow(exceptions, Launch.blackboard, "angelica", "Angelica", EcosystemNarrowRules.ALL);

第三个参数 "angelica" 是黑板 key,第四个 "Angelica" 是显示名,第五个 EcosystemNarrowRules.ALL 表示对全部规则生效。异常时只打 FMLRelaunchLog.warning,不抛出(:43-45)。

这与 AngelicaClientTweaker.injectData :148-155 发布的 8 个 narrowTransformerConfig 开关直接对应 —— 开关关掉的 mod 会被排除在收窄之外,即保持原有的宽泛排除。

getLaunchArguments 的替换逻辑(:55-71)

transformers.removeIf(t -> t.getClass().getName().equals(EARLY_REDIRECTOR_CLASS));
...
Launch.classLoader.registerTransformer(
    "com.gtnewhorizons.angelica.loading.fml.transformers.AngelicaRedirectorTransformer");

EARLY_REDIRECTOR_CLASS 常量定义在 :18。:56 的注释说明意图:「Remove the scoped early redirector (no longer needed) and register the full one in its proper position: after mixins」。异常同样只 warning(:63-65)。

IncompatibleModsDisablerTweaker:强制禁用 OptiFine / Fastcraft / BetterFPS

:24 有一个硬编码常量:

private static final boolean DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS = true;

它没有配置开关,也没有系统属性 —— 恒为 true。

:33-36 命中后先打日志「Disabling Optifine, Fastcraft, BetterFPS, and other incompatible mods (if present)」再调 disableIncompatibleMods(classLoader)。

:52-60 的做法是反射取 LaunchClassLoader.transformers 字段,然后倒序遍历(for (int idx = xformers.size() - 1; idx >= 0; idx--))按类名前缀剔除:

匹配前缀 目标
optifine OptiFine
fastcraft Fastcraft
me.guichaguri.betterfps BetterFPS

disableIncompatibleMods 分三个独立的 try 块(:52-137),每一块失败都 throw new RuntimeException(e) —— 与 AngelicaLateTweaker 的「只 warning」策略相反。

第 1 块(:52-65):移除 IClassTransformer

反射取 LaunchClassLoader.transformers 字段,倒序遍历按类名前缀剔除:

匹配前缀 目标
optifine OptiFine
fastcraft Fastcraft
me.guichaguri.betterfps BetterFPS

每移除一个打一条 Removing transformer <name>。倒序遍历是为了在移除元素时不破坏索引。

第 2 块(:66-96):移除 ModContainer 与 coremod

反射取 cpw.mods.fml.common.Loader.injectedContainers(List<String>),倒序剔除 optifine / fastcraft 前缀,日志为 Removing mod container <name>。

随后处理 CoreModManager 的两个静态字段 reparsedCoremods 与 loadedCoremods(:79-80):

if (coreMod.startsWith("optimizationsandtweaks")) {
    // Fool the CoreModManager into not checking for a mod container again later
    loadedCoremods.add(reparsedCoremods.remove(idx));
}

优化和调整(Optimizations and Tweaks) 是 BetterFPS 的前置依赖。源码注释 :89 说明了技巧:把它从 reparsedCoremods 移到 loadedCoremods,而不是直接删掉 —— 这样 CoreModManager 后续就不会再去检查它对应的 mod 容器。

第 3 块(:97-136):移除 mixin 配置

清理三处 mixin 状态:

目标 黑板 key 匹配方式 行
默认 mixin 配置列表 mixin.configs.default ArrayList<String>,name.contains(...) :98-107
队列中的 mixin 配置 mixin.configs.queue Set<Config>,先收集到 toRemove 再 removeAll :108-118
Mixin 容器 mixin.platform → MixinPlatformManager.containers 读 IContainerHandle.getAttribute("MixinConfigs") :119-131

三者都匹配 optimizationsandtweaks(用 contains 而非 startsWith,因为队列里存的是完整配置名)。第三处的容器用 entry.getKey().getAttribute("MixinConfigs") 判定,日志为 Removing mixin container <...>。

注意 :129 在 for (Map.Entry<...> : containers.entrySet()) 循环体内直接 containers.remove(entry.getKey()),属于边遍历边删除 HashMap。这在实践中通常不会抛 ConcurrentModificationException(删除的元素已被迭代器持有,删除后不再访问),但仍是本文件里一个值得留意的写法。

XaerosTransformerDisablerTweaker:单目标精准移除

比前一个简单得多 —— 只匹配一个 transformer,:26-32:

for (int idx = transformers.size() - 1; idx >= 0; idx--) {
    final String name = transformers.get(idx).getClass().getName();
    if (name.startsWith("xaero.common.core.transformer.GuiIngameForgeTransformer")) {
        transformers.remove(idx);
        FMLRelaunchLog.info("[Angelica/XaerosTransformerDisablerTweaker] Removed transformer " + name);
    }
}

移除 xaero.common.core.transformer.GuiIngameForgeTransformer —— Xaeros Minimap 的 HUD transformer。与 Angelica 的 HUDCaching 冲突:两者都写 HUD,会重复渲染。

注意此处匹配后立即 remove 但不 break,与 IncompatibleModsDisablerTweaker 同样用倒序索引。异常时 throw new RuntimeException(e)(:33-35)—— 与 AngelicaLateTweaker 的「只 warning」策略不一致,这是本文件间的真实差异。

相关条目