User Tools

Site Tools


Sidebar

← Go back to the homepage

Fabric Tutorials

Setup

Basics

These pages are essential must-reads when modding with Fabric, and modding Minecraft in general, if you are new to modding, it is recommended you read the following.

Items

Blocks and Block Entities

Data Generation

World Generation

Commands

These pages will guide you through Mojang's Brigadier library which allows you to create commands with complex arguments and actions.

Events

These pages will guide you through using the many events included in Fabric API, and how to create your own events for you or other mods to use.

Entities

Fluids

Mixins & ASM

These pages will guide you through the usage of SpongePowered's Mixin library, which is a highly complex topic. We recommend you read these pages thoroughly.

Miscellaneous

Yarn

Contribute to Fabric

tutorial:reflection

Table of Contents

Reflection

Compared to regular Java development, since Minecraft has been remapped between official (obfuscated), intermediary, and mapped (yarn or mojmap) names, finding particular fields or methods for reflection and method handles require special caution. If classes are found by literal string names, as in Class.forName, they need special attention as well. (Constant references like MinecraftClient.class are handled by remappers)

Remapping

See also: mappings

It is recommended to refer to methods and fields by their intermediary names when looking them up for reflection because intermediary names largely stay constant across Minecraft versions (thanks to matching efforts) and can allow your reflection code to potentially work in a new Minecraft version without having to release a new update.

An example:

// We will use the resolver to convert from intermediary to runtime names
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
 
Class<?> cls;
// An example of converting intermediary class name to runtime class name
cls = Class.forName(resolver.mapClassName("intermediary", "net.minecraft.class_2960"));
// Alternatively, you can just use the class constant reference, which remapper will handle:
cls = Identifier.class;
 
// Now we want to create a method handle to Identifier.getNamespace:
MethodHandles.Lookup lookup = MethodHandles.publicLookup(); // it is a public method
MethodHandle namespaceGetter = lookup.findVirtual(cls,
  resolver.mapMethodName(
    "intermediary",
    // An example of unmapping, simply yields "net.minecraft.class_2960"
    resolver.unmapClassName("intermediary", cls.getName()),
    "method_12836",
    "()Ljava/lang/String;"
  ),
  MethodType.methodType(String.class)
);

Records

Since 1.18, vanilla Minecraft starts using records in code. However, since Minecraft is processed by Proguard, which removes the record information from the Minecraft classes, these record classes will have such behaviors at runtime, despite being decompiled as records in source:

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

As a consequence, you cannot find the record components of vanilla classes with reflection.

See the JDK 17 API docs for isRecord() and getRecordComponents().

In addition, proguard also removes the signature (which indicates the generic information) from the record classes (but not from their methods). Yarn mappings defines signatures mappings for these classes. This affects reflection results on calls to some reflection methods, such as recordClass.getTypeParameters(), and calling getGenericReturnType() on methods that refer to the absent type parameters on records may cause MalformedParameterizedTypeException.

tutorial/reflection.txt · Last modified: 2021/11/18 22:49 (external edit)