Table of Contents

リフレクション

通常の Java 開発と比較して、Minecraft は(難読化された)公式のコード、中間コード、およびマップされた(yarn または mojmap)名の間で再マップされているため、リフレクションおよびメソッドハンドル用の特定のフィールドまたはメソッドを見つけるには、特別な注意が必要です。 Class.forName のように、クラスがリテラル文字列名で見つかった場合も、特別な注意が必要です。(MinecraftClient.class のような定数参照はリマッパーによって処理されます)

再マッピング

See also: 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)
);

Records

1.18 以降、バニラ Minecraft は records をコードで使用しています。 ただし、 Minecraft は Proguard によって処理され、Minecraft クラスからレコード情報が削除されるため、これらのレコードクラスは、ソースでレコードとして逆コンパイルされているにもかかわらず、実行時にこのような動作をします:

recordClass.isRecord() == false
recordClass.getRecordComponents() == null

その結果、リフレクションを持つバニラクラスのレコードコンポーネントを見つけることができません。

isRecord() および getRecordComponents() については、JDK 17 API ドキュメント(英語)を参照してください。

さらに、proguard は、(メソッドからではなく)レコードクラスから(一般的な情報を示す)シグネチャも削除します。 Yarn のマッピングは、これらのクラスのシグネチャマッピングを定義します。 これは、recordClass.getTypeParameters() などの一部のリフレクションメソッドへの呼び出しでのリフレクション結果に影響し、レコードに存在しない型パラメーターを参照するメソッドでgetGenericReturnType() を呼び出すと、MalformedParameterizedTypeException が発生する場合があります。