StacksOnStacksTransformer
基本信息
| 属性 | 值 |
|---|---|
| 类 | ...loading.fml.compat.transformers.specific.StacksOnStacksTransformer |
| 路径 | src/main/java/com/gtnewhorizons/angelica/loading/fml/compat/transformers/specific/StacksOnStacksTransformer.java |
| 行数 | 166 |
| 类型 | public class ... implements IClassTransformer(:25) |
| 目标类 | RenderTilePile、ClientUtils(:26、:30) |
| 注册方 | StacksOnStacksCompatHandler.extraTransformers() :26-31 |
它是什么 / 挂在哪个阶段
真正的 IClassTransformer,166 行,是 compat/transformers/specific/ 里较大的一个。跑在 LaunchWrapper 类加载期,RFB 路径不提供。
目标类与分派(:32-49)
if (!transformedName.equals(RenderTilePile) && !transformedName.equals(ClientUtils)) return basicClass;
...
if (transformedName.equals(RenderTilePile)) { transformRenderTilePile(cn); }
else { transformClientUtils(cn); }
两个目标类各走一个完全独立的补丁。写出时用 ClassWriter(0)(:47)—— flag 为 0,即不重算 frames/maxs。这与 ImmersiveEngineeringTransformer 的 COMPUTE_FRAMES 相反。本类只做「删除静态初始化 + 把 static 访问改 GETFIELD」这类不改变栈深度的操作,因此不需要重算。
补丁 A:transformRenderTilePile(:86-142)—— 三个子操作
A1:删除 <clinit>(:130-131)
if (clinit != null) cn.methods.remove(clinit);
源码注释 :132-134 声明理由:「<clinit> 里除字段初始化外没有别的东西,没必要再往指令里钻」。该 <clinit> MethodNode 在 :113-115 遍历时先捕获、遍历结束后再删,避免在遍历中修改集合。
A2:在 <init> 里注入 3 个 PileRender 初始化(:88-112)
| 步骤 | 行 | 动作 |
|---|---|---|
| 摘掉早退 | :95-97 |
遍历 <init>,移除所有 RETURN |
定位 this 初始化 |
:96 |
记录最后一个 INVOKESPECIAL 节点 |
| 逆序插入 | :104-106 |
dustRender → gemRender → ingotRender,都在 objectInsn 前插入 |
:100-103 注释说明为何逆序:「这样 dustRender 最后出现,因为 RETURN 是作为它那份 InsnList 的一部分被加回去的」——RETURN 只在 dustRender 末尾(:105)。
buildPilerRenderInitializer(:60-84)为每个类型生成:ALOAD 0 → NEW PileRender<type> → DUP → INVOKESPECIAL <init> → PUTFIELD <type.toLowerCase()>Render。
A3:renderWorldBlock 里 static 访问改实例访问(:117-129)
对 staticRemovers(:27 = ["ingotRender","gemRender","dustRender"])中的每个字段名,把 GETSTATIC 替换为:
ALOAD 0
GETFIELD com/tierzero/stacksonstacks/client/render/RenderTilePile.<name> : LPileRender;
并清除 FieldNode.access 里的 ACC_STATIC(:136-140)。
补丁 B:transformClientUtils(:144-160)
把 pushMatrix 与 popMatrix 两个方法里的所有 INVOKESTATIC 指令删除,使其成为 no-op。
源码注释 :145-148:这些方法在 ISBRH 内部被大量调用,进而调用 GL11.glPushMatrix/glPopMatrix;经检查「这些只在 ISBRH 里被调用,且在那里完全没必要」。
已知问题
- 删除全部
RETURN指令(:96):若上游RenderTilePile的<init>里有多个RETURN(理论上不该有),全部被移除。实际靠:105补回一个,但这是脆弱的假设。 <clinit>整体删除的脆弱性:注释已声明前提是「除字段初始化外无其它逻辑」。上游一旦在<clinit>加静态初始化块,这些代码静默丢失,且没有任何警告。- 字段名
toLowerCase()拼接(:81):"Ingot".toLowerCase()→ingotRender字段名。这个大小写映射靠staticRemovers(:27)与 ASM 常量type.toLowerCase()隐式对齐,两处必须同步:staticRemovers写的是全小写,拼接出的也是全小写。上游若用驼峰ingotRender则恰好一致,但这个约定没有断言保护。 ClassWriter(0)不重算 maxs/frames:本类当前操作确实不改变栈深,但这是隐式依赖。若后续补丁加入需要栈平衡的指令,会产出校验失败的 class。- 日志前缀
[AngelicaCompat](:53)——移植痕迹,同 ImmersiveEngineeringTransformer。
相关条目
- StacksOnStacksCompatHandler - 注册方
- CompatHandlers -
extraTransformers()的消费逻辑 - FieldLevelTessellatorTransformer - 同一目标类
ClientUtils的另一处补丁 - AngelicaClassDump -
:54的落盘调用 - FML Transformers - 两套 transformer 体系的关系