GenericCompatTransformer
基本信息
| 属性 | 值 |
|---|---|
| 类 | com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer |
| 路径 | src/main/java/com/gtnewhorizons/angelica/loading/fml/transformers/GenericCompatTransformer.java |
| 行数 | 91 |
| 类型 | public class ... implements IClassTransformer(:22) |
| 驱动的子 transformer | 4 个 compat/transformers/generic/ 静态工具 |
它是什么
数据驱动的通用兼容 transformer。它自己不写任何字节码规则,而是把 5 个 ICompatHandler 提供的数据在构造期合并成 4 张查表 + 1 个目标类集合,然后在类加载时按表分派。
这是本 mod 唯一使用 fastutil 集合(Object2ObjectOpenHashMap / Object2BooleanOpenHashMap / ObjectOpenHashSet)的地方。
5 个字段(:24-28)
| 字段 | 类型 | 来源方法 |
|---|---|---|
fieldLevelTessellator |
Map<String, List<String>> |
getFieldLevelTessellator() |
tileEntityNullGuard |
Map<String, List<String>> |
getTileEntityNullGuard() |
threadSafeIBSRH |
Map<String, Boolean> |
getThreadSafeISBRHAnnotations() |
hudCachingEarlyReturn |
Map<String, List<String>> |
getHUDCachingEarlyReturn() |
targetedClasses |
Set<String> |
上述 4 表 key 的并集(buildTargetClassSet() :48-53) |
构造期(:30-35)
for (ICompatHandler handler : CompatHandlers.getHandlers()) { registerHandler(handler); }
buildTargetClassSet();
registerHandler(:37-45)对 4 个方法各做一次 != null 守卫后 putAll。由于 5 个 handler 的目标类名互不重叠,putAll 不存在互相覆盖。
注意 CompatHandlers.getHandlers() 的结果已被静态缓存(见 CompatHandlers :31-33),且此时 coremod 阶段 CompatConfig 已装载,所以表内容是确定的。
挂在哪个阶段
LaunchWrapper 类加载期,且是 CompatHandlers.getTransformers() :56-58 在 handler 非空时唯一追加一次的类:
if (!handlers.isEmpty()) {
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer");
}
它在 getTransformers() 的末尾追加,即排在 2 个 specific/ transformer 之后。RFB 路径不提供本类 —— 这是 5 个 handler 全部失效的根因。
transform 的分派(:55-88)
| 行 | 动作 |
|---|---|
:57 |
basicClass == null → 返回 null |
:59 |
不在 targetedClasses → 原样返回(绝大多数类走这条) |
:61-62 |
ClassReader → ClassNode |
:64-74 |
依次尝试 4 个子 transformer,每张表独立判断,命中就调 |
:76-78 |
MixinClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES) 写出 |
:79 |
dump |
一个类可同时命中多张表:例如 Stacks on Stacks 的 RenderTilePile 同时在 tileEntityNullGuard 与 threadSafeIBSRH 里,会被两个子 transformer 依次处理。
已知问题
- 即使一个子 transformer 都没实际改动,仍会重写字节码(
:76-79):4 张表命中判断基于「类名在表里」,而子 transformer 内部可能因为字节码模式不匹配而什么都不做(如 TileEntityNullGuardTransformer 的三层邻接匹配)。此时该类仍被MixinClassWriter重新序列化并 dump,产生一次无意义的字节码往返。COMPUTE_FRAMES还会在早期类加载时有TypeNotPresentException风险。 COMPUTE_FRAMES的早期风险:本类与其他 transformer 不同,用了COMPUTE_MAXS | COMPUTE_FRAMES。帧重算需要解析类的公共父类/接口,coremod 阶段可能尚不可用。- 数据在构造期冻结:
CompatConfig若在构造后被修改(理论上不会,因为 coremod 早于preInit),表不会更新。 - 日志缺失:本类不打印任何日志(对比 StacksOnStacksTransformer
:53)。用户无法确认哪些类被处理了,只能查 AngelicaClassDump 的产物。 threadSafeIBSRH拼写错误:字段名是threadSafeIBSRH(大写 ISBRH),其余 3 张表用全小写或驼峰Tessellator/Entity/Hud。:80的调用点写的是threadSafeIBSRH.get(...)——一致,但命名不统一。
相关条目
- ICompatHandler - 5 个 default 方法的契约
- CompatHandlers - 数据来源与
GenericCompatTransformer的唯一追加点 - FieldLevelTessellatorTransformer / TileEntityNullGuardTransformer / ThreadSafeISBRHAnnotationTransformer / HUDCachingEarlyReturnTransformer - 4 个被驱动的子 transformer
- AngelicaRfbPlugin - RFB 路径为何拿不到本类
- AngelicaClassDump -
:79的落盘调用 - CompatConfig - 决定 5 个 handler 中哪几个进入构造