Jan 2, 20264 mins read

guide to minecraft mappings

understanding mojang mappings for minecraft cheat development

if you’re getting into minecraft cheat development, you’ve probably heard about mappings but aren’t sure what they actually are or how to use them. this guide breaks down everything you need to know about minecraft mappings, specifically focusing on mojang’s official mappings.

the problem: obfuscation

mojang historically shipped minecraft with obfuscated code using proguard. instead of readable class and method names, everything is replaced with meaningless identifiers:

public class gfj {
    private static gfj A;
    private float c;
    
    public void d() {
        this.c -= 0.08f;
    }
}

this makes reverse engineering harder, but it also makes development nearly impossible. how do you know what gfj is? what does field A represent? this is where mappings come in.

important note: as of version 1.21.11 and beyond, mojang now releases deobfuscated builds alongside obfuscated ones. read more about this change here. however, for versions before 1.21.11, you’ll still need to work with obfuscated code and mappings.

what are mappings?

mappings are translation files that tell you what the obfuscated names actually mean. they’re essentially a dictionary:

net.minecraft.client.Minecraft -> gfj
    instance -> A
    level -> r
    player -> s

this tells you that the class gfj is really Minecraft, field A is instance, and so on.

critical understanding: mappings don’t modify minecraft’s runtime. the jvm is still running obfuscated code. mappings are just a reference sheet for developers to understand what’s what.

getting mappings (version 1.14.4 - 1.21.11)

mojang provides official mappings for minecraft versions 1.14.4 through 1.21.11. here’s how to get them:

step 1: access the version manifest

go to mojang’s version manifest at https://piston-meta.mojang.com/mc/game/version_manifest_v2.json and find your target version. for example, version 1.21.11:

{
  "id": "1.21.11",
  "type": "release",
  "url": "https://piston-meta.mojang.com/v1/packages/30bb79802dcf36de95322ef6a055960c88131d2b/1.21.11.json"
}

step 2: get version-specific metadata

open the url from step 1 in your browser and search for client_mappings:

"client_mappings": {
  "sha1": "031a68bebf55d824f66d6573d8c752f0e1bf232a",
  "size": 11779287,
  "url": "https://piston-data.mojang.com/v1/objects/031a68bebf55d824f66d6573d8c752f0e1bf232a/client.txt"
}

step 3: access the mappings

open the client.txt url in your browser. this is your mapping file in proguard format (proguard is the obfuscator minecraft uses).

community mappings for older versions

for versions before 1.14.4, you’ll need community-created mappings like mcp (minecraft coder pack), available at http://www.modcoderpack.com/.

using mappings: practical example

let’s say you want to get the minecraft instance via jni. first, search the mappings file for net.minecraft.client.Minecraft:

net.minecraft.client.Minecraft -> gfj:
    net.minecraft.client.Minecraft instance -> A
    net.minecraft.client.multiplayer.ClientLevel level -> r
    net.minecraft.client.player.LocalPlayer player -> s

now you know:

  • Minecraft class → obfuscated to gfj
  • instance field → obfuscated to A
  • level field → obfuscated to r
  • player field → obfuscated to s

the jni code

here’s how you’d access the minecraft instance using these mappings:

// find the minecraft class using its OBFUSCATED name
jclass minecraftClass = env->FindClass("gfj");

// get the static 'instance' field using its OBFUSCATED name 'A'
jfieldID instanceField = env->GetStaticFieldID(
    minecraftClass,
    "A",           // obfuscated name from mappings
    "Lgfj;"        // type signature
);

// retrieve the instance
jobject minecraftInstance = env->GetStaticObjectField(minecraftClass, instanceField);

why you use obfuscated names

when minecraft is running, the jvm only knows about gfj and A. it has no concept of Minecraft or instance. those readable names only exist in the mappings file.

think of it like this:

  • what you want to access: Minecraft.instance
  • what actually exists in memory: gfj.A
  • what mappings do: tell you that Minecraft = gfj and instance = A

you can’t write env->FindClass("Minecraft") because no class named Minecraft exists in the running jvm. you must use the obfuscated name gfj.

understanding the java side

the minecraft class uses a singleton pattern. before obfuscation:

public class Minecraft {
    private static Minecraft instance;
    
    public static Minecraft getInstance() {
        return instance;
    }
}

after obfuscation (what’s actually running):

public class gfj {
    private static gfj A;
    
    public static gfj a() {
        return A;
    }
}

when you inject a dll and use jni, you’re interacting with the obfuscated version. mappings just help you understand what you’re looking at.

jni breakdown

here’s what each jni call does:

  1. FindClass(“gfj”) - searches loaded classes for gfj
  2. GetStaticFieldID(…) - gets a reference to field A in class gfj
  3. GetStaticObjectField(…) - reads the value of that field

that’s it. mappings told you what to search for, but the actual interaction is with obfuscated names.

wrapping up

mappings are essential for minecraft development because they let you understand obfuscated code. remember:

  • mappings are reference files, not runtime modifiers
  • always use obfuscated names in your jni code
  • mojang mappings work for 1.14.4+, use community mappings for older versions
  • the jvm has no knowledge of deobfuscated names at runtime
← back to writing