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 里被调用,且在那里完全没必要」。

已知问题

  1. 删除全部 RETURN 指令(:96):若上游 RenderTilePile 的 <init> 里有多个 RETURN(理论上不该有),全部被移除。实际靠 :105 补回一个,但这是脆弱的假设。
  2. <clinit> 整体删除的脆弱性:注释已声明前提是「除字段初始化外无其它逻辑」。上游一旦在 <clinit> 加静态初始化块,这些代码静默丢失,且没有任何警告。
  3. 字段名 toLowerCase() 拼接(:81):"Ingot".toLowerCase() → ingotRender 字段名。这个大小写映射靠 staticRemovers(:27)与 ASM 常量 type.toLowerCase() 隐式对齐,两处必须同步:staticRemovers 写的是全小写,拼接出的也是全小写。上游若用驼峰 ingotRender 则恰好一致,但这个约定没有断言保护。
  4. ClassWriter(0) 不重算 maxs/frames:本类当前操作确实不改变栈深,但这是隐式依赖。若后续补丁加入需要栈平衡的指令,会产出校验失败的 class。
  5. 日志前缀 [AngelicaCompat](:53)——移植痕迹,同 ImmersiveEngineeringTransformer。

相关条目