访问转换器
访问转换器(Access Transformer,简称 AT)允许扩大类、方法和字段的可见性,并修改其 final 标志。模组开发者借此可以访问和修改自己无法控制的类中原本不可访问的成员。
![访问转换器要点:开发时在 Gradle minecraft 块指定 AT 文件并刷新项目,发布 JAR 中固定为 META-INF/accesstransformer.cfg;可见性由 public 到 private 递减,+f 添加 final、-f 移除 final;原文方法描述符 (JJ)I 接收两个 long 并返回 int 数组。
原创说明图,依据 Forge 1.21.x 官方文档 绘制。
可在 Minecraft Forge GitHub 上查看规范文档。
添加 AT
向模组项目添加访问转换器,只需在 build.gradle 中添加一行:
// 这里也是指定映射版本的代码块
minecraft {
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
}
添加或修改访问转换器后,必须刷新 Gradle 项目,转换才会生效。开发期间 AT 文件可位于上述配置指定的任意位置;但在非开发环境加载时,Forge 只会在 JAR 文件中查找准确路径 META-INF/accesstransformer.cfg。
注释
从 # 到行尾的所有文本都视为注释,不会解析。
访问修饰符
访问修饰符指定目标转换后的新可见性,按可见性从高到低排列:
public:包内外所有类都可见。protected:仅包内类和子类可见。default:仅包内类可见。private:仅类内部可见。
可在上述修饰符后附加特殊修饰符 +f 或 -f,分别添加或移除 final。应用 final 会阻止继承、方法重写或字段修改。
警告:指令只修改直接引用的方法,不会转换其重写方法。应确保转换后方法不存在未转换且限制可见性的重写方法,否则 JVM 会抛出错误。
可以安全转换的方法示例包括 private 方法、final 方法(或 final 类中的方法)以及 static 方法。
目标和指令
重要:对 Minecraft 类使用访问转换器时,字段和方法必须使用 SRG 名称。
类
指定类的格式:
<访问修饰符> <完全限定类名>
内部类通过外部类的完全限定名与内部类名组合,以 $ 分隔。
字段
<访问修饰符> <完全限定类名> <字段名>
方法
指定方法需要特殊语法来表示参数和返回类型:
<访问修饰符> <完全限定类名> <方法名>(<参数类型>)<返回类型>
指定类型
这些也称为“描述符”;技术细节见 Java 虚拟机规范 SE 8 第 4.3.2 和 4.3.3 节。
| 描述符 | 类型或含义 |
|---|---|
B |
byte,有符号字节。 |
C |
char,无符号 16 位 UTF-16 代码单元。 |
D |
double,双精度浮点值。 |
F |
float,单精度浮点值。 |
I |
integer,32 位整数。 |
J |
long,64 位整数。 |
S |
short,有符号短整数。 |
Z |
boolean,true 或 false。 |
[ |
表示数组的一维;例如 [[S 表示 short[][]。 |
L<class name>; |
表示引用类型;例如 Ljava/lang/String; 表示 java.lang.String,注意使用斜杠而非点。 |
( |
表示方法描述符,括号中提供参数;无参数时留空。例如 <method>(I)Z 表示接收整数并返回布尔值的方法。 |
V |
表示方法不返回值,只能用于方法描述符末尾。例如 <method>()V 表示无参数、无返回值的方法。 |
原文将
char称为 Unicode 码点;Java 的char是 UTF-16 代码单元,单个码点可能需要一对代码单元。
示例
# 将 Crypt 中的 ByteArrayToKeyFunction 接口设为 public
public net.minecraft.util.Crypt$ByteArrayToKeyFunction
# 将 MinecraftServer 的 random 设为 protected 并移除 final
protected-f net.minecraft.server.MinecraftServer f_129758_ # random
# 将 Util 的 makeExecutor 方法设为 public;接收 String,返回 ExecutorService
public net.minecraft.Util m_137477_(Ljava/lang/String;)Ljava/util/concurrent/ExecutorService; # makeExecutor
# 将 UUIDUtil 的 leastMostToIntArray 方法设为 public;接收两个 long,返回 int[]
public net.minecraft.core.UUIDUtil m_235872_(JJ)[I # leastMostToIntArray