====== 反射 ======
与常规的 Java 开发相比,由于 Minecraft 在官方(混淆)、中间名和映射的(yarn 或者 mojmap)名称之间重映射,因此,通过反射寻找特定的字段或者方法需要格外小心。如果类是由原始的字符串名称找到的,例如 [[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#forName(java.lang.String)|Class.forName]],这些类也需要格外小心。(常量引用,如 MinecraftClient.class,由重映射器处理。)
===== 重映射 =====
//参见:[[mappings]]//
反射查找方法或字段时,建议通过中间名引用,因为在不同的 Minecraft 版本之间,中间名往往是保持恒定的(借助匹配),反射的代码可以在新版本的 Minecraft 中运行而无需发布新的模组版本。
例子:
// 我们将使用解析器将中间名转换为运行时名称
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
Class> cls;
// 将中间类名转化为运行时类名的例子
cls = Class.forName(resolver.mapClassName("intermediary", "net.minecraft.class_2960"));
// 你也可以只适用类的常量引用,重映射器会处理:
cls = Identifier.class;
// 为 Identifier.getNamespace 创建一个方法处理:
MethodHandles.Lookup lookup = MethodHandles.publicLookup(); // public 方法
MethodHandle namespaceGetter = lookup.findVirtual(cls,
resolver.mapMethodName(
"intermediary",
// 反映射的例子,简单输出 "net.minecraft.class_2960"
resolver.unmapClassName("intermediary", cls.getName()),
"method_12836",
"()Ljava/lang/String;"
),
MethodType.methodType(String.class)
);
===== 记录 =====
自从 1.18,原版 Minecraft 在代码中开始使用 [[https://docs.oracle.com/en/java/javase/16/language/records.html|记录]]。但是,由于 Minecraft 是使用 Proguard 处理的,会从 Minecraft 类中移除记录信息,因此这些记录类会在运行时有如下的行为,尽管在代码中反编译成了记录:
recordClass.isRecord() == false
recordClass.getRecordComponents() == null
结果就是,您无法通过反射找到原版类的记录组件。
参见 [[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#isRecord()|isRecord()]] 和 [[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getRecordComponents()|getRecordComponents()]] 的 JDK 17 API 文档。
而且,proguard 会移除从记录中移除表示通用信息的签名(但不会从方法中移除)。Yarn 映射为这些类定义签名映射。这会影响对一些反射方法的调用的反射结果,例如 [[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getTypeParameters()|recordClass.getTypeParameters()]],在引用了没有类型参数的记录的方法中调用 [[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Method.html#getGenericReturnType()|getGenericReturnType()]] 可能会造成 MalformedParameterizedTypeException。