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」策略不一致,这是本文件间的真实差异。
相关条目
- AngelicaClientTweaker - 黑板 tweak 追加与 8 个收窄开关的发布
- FML Transformers - 被移除 / 替换的 transformer 实现
- CompatConfig -
fixXaeros不存在;Xaeros 的移除是无条件的 - AngelicaModulesConfig -
transformerCompat8 个收窄开关