reflection-remapper is a library to simplify making reflective calls, with specific support for remapped environments.
Releases are published to Maven Central.
Using snapshot builds
Snapshot builds are available on the Sonatype snapshots Maven repository:
https://s01.oss.sonatype.org/content/repositories/snapshots/
Consult your build tool's documentation for details on adding Maven repositories to your project.
Click to show build.gradle.kts
repositories { mavenCentral() } dependencies { implementation("xyz.jpenilla:reflection-remapper:0.1.1") }
Click to show build.gradle
repositories { mavenCentral() } dependencies { implementation 'xyz.jpenilla:reflection-remapper:0.1.1' }
Note: The following is a basic introduction; For further details, consult the reflection-remapper Javadocs.
ReflectionRemapper
is the interface provided by reflection-remapper for remapping class, method, and field names for reflection lookups.
Click to see Java code snippet
final Path mappingsFile = /* get path to mappings ... */; // ReflectionRemapper provides various static factory methods, in this example we use the one from a Path // Note that the standard ReflectionRemapper implementations store their mappings in memory, which can be multiple megabytes large in some cases. // This means it's best to use the remapper when your program starts and then dispose of any reference to it, so it can be garbage collected. final ReflectionRemapper reflectionRemapper = ReflectionRemapper.forMappings(mappingsFile, "fromNamespace", "toNamespace"); final String runtimeName = reflectionRemapper.remapClassName("net.minecraft.server.level.ServerPlayer"); final Class<?> serverPlayerClass = Class.forName(runtimeName); // Exception handling omitted for brevity final String runtimeFieldName = reflectionRemapper.remapFieldName(serverPlayerClass, "seenCredits"); // ...
A reflection proxy is a runtime generated implementation of a "reflection proxy interface", which uses cached MethodHandle
s to invoke the targets specified by
the interface definition. Reflection proxies can also make use of a ReflectionRemapper
on creation in order to allow them to work in a remapped (or not) environment.
The ReflectionRemapper
on its own, while powerful, tends to make already verbose reflection code even more verbose. When combined with reflection proxies, we
can have relatively clean reflection code while still properly remapping.
Click to see Java code snippet
public final class ServerLevel { private SleepStatus sleepStatus; private BlockPos findLightningTargetAround(BlockPos pos) { // implementation } }@Proxies(ServerLevel.class) // Can use Class or fully qualified class name (for inaccessible classes) private interface ServerLevelProxy { BlockPos findLightningTargetAround(ServerLevel instance, BlockPos pos); @FieldSetter("sleepStatus") void setSleepStatus(ServerLevel instance, SleepStatus value); }final ReflectionRemapper reflectionRemapper = /* get ReflectionRemapper ... */; final ClassLoader classLoader = /* get ClassLoader for loading proxy implementations, generally it needs to be able to see any reflection proxy interfaces you want to implement */; final ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper, classLoader); // ReflectionProxyFactory holds a ref to it's ReflectionRemapper // Generated proxy instances do not hold a ref to the ReflectionRemapper, and are fine to keep around. final ServerLevelProxy proxyInstance = reflectionProxyFactory.reflectionProxy(ServerLevelProxy.class); // ... final ServerLevel level = /* ... */; final BlockPos blockPos = /* ... */; final BlockPos target = proxyInstance.findLightningTargetAround(level, blockPos); proxyInstance.setSleepStatus(level, new SleepStatus());