Technocoder - coremodZola2021-06-09T00:00:00+00:00https://blog.techno.fish/tags/coremod/atom.xmlMinecraft Forge Coremod Comprehensive Tutorial2021-06-09T00:00:00+00:002021-06-09T00:00:00+00:00https://blog.techno.fish/minecraft-forge-coremod-tutorial/<p>In this tutorial we will discuss what a coremod is and the details on developing your own coremod. </p>
<h2 id="what-is-a-coremod">What is a coremod?</h2>
<p>A mod modifies Minecraft in some way. Mods created with Minecraft Forge normally rely on Forge's API to modify Minecraft. This includes things like creating your own blocks, items, crafting recipes, and custom network packets. The vast majority of mods add new content to Minecraft in a way that Forge supports. But what if you want to do something that it doesn't?</p>
<p>If you're lucky, vanilla Minecraft itself might expose a way to achieve the behaviour you want. For example, you can <a href="https://blog.techno.fish/intercepting-vanilla-minecraft-network-packets/">intercept network packets by hooking into Minecraft's Netty pipeline</a>. Otherwise, you will need to modify Minecraft's bytecode directly. This is what a coremod does.</p>
<h2 id="outlining-a-class-transformer">Outlining a class transformer</h2>
<p>Let's start by writing the method that will actually modify Minecraft's bytecode. Our class will look like this:</p>
<pre style="background-color:#2b303b;">
<code><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#cd74e8;">import </span><span style="color:#abb2bf;">net.minecraft.launchwrapper.</span><span style="color:#f0c678;">IClassTransformer</span><span style="color:#abb2bf;">;
</span><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">CustomClassTransformer </span><span style="color:#cd74e8;">implements </span><span style="color:#9acc76;">IClassTransformer </span><span style="color:#adb7c9;">{
@</span><span style="color:#eb6772;">Override
</span><span style="color:#cd74e8;">public byte[] </span><span style="color:#5cb3fa;">transform</span><span style="color:#adb7c9;">(</span><span style="color:#f0c678;">String </span><span style="color:#eb6772;">name</span><span style="color:#adb7c9;">, </span><span style="color:#f0c678;">String </span><span style="color:#eb6772;">transformedName</span><span style="color:#adb7c9;">, </span><span style="color:#cd74e8;">byte[] </span><span style="color:#eb6772;">basicClass</span><span style="color:#adb7c9;">) {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#adb7c9;">}
}
</span></code></pre>
<p>You'll notice that the <code>IClassTransformer</code> interface is actually imported from Minecraft and not Forge. You can actually find this interface in Mojang's <a href="https://github.com/Mojang/LegacyLauncher">LegacyLauncher repository</a>. Presumably the Mojang developers use this to aid them with local development.</p>
<p>Our method is called for every class that's being loaded and takes three arguments. The first argument, <code>name</code>, is the raw <strong>qualified</strong> name of the current class being transformed. For example, in a deobfuscated environment (such as testing locally), this argument will look something like: <code>net.minecraft.client.gui.FontRenderer</code>. However, in an obfuscated environment it will look like: <code>avn</code>. </p>
<p>The second argument, <code>transformedName</code>, is similar to <code>name</code> but it will always be the deobfuscated, fully qualified name. We rely on this argument to check if we are transforming the right class.</p>
<p>Finally, the last argument, <code>basicClass</code>, contains the raw bytecode for the current class. We will be modifying and returning this argument.</p>
<h2 id="a-primer-on-visitors">A primer on visitors</h2>
<p>Internally, most compilers and interpreters <a href="https://en.wikipedia.org/wiki/Parsing">parse</a> and represent source code as an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract syntax tree</a>. Each node of a tree is an expression or statement and children of a node are subexpressions of that node. For example, take the mathematical expression: <code>1 + 1</code>. The root of the tree would be a node for the operator <code>+</code> and it would have two children, both of which would be the integer <code>1</code>.</p>
<p>We can extend this concept further and instead of converting source code, we can go in the other direction and turn bytecode (what Java source code is ultimately compiled to) into an abstract syntax tree. The resultant structure is less granular so we won't have expressions as nodes but we can still access individual methods and fields.</p>
<p>A common pattern with trees is to iterate over each node. This is called a <a href="https://en.wikipedia.org/wiki/Tree_traversal">tree traversal</a> or "walking the tree". From a software design perspective, we'd like to separate the logic for traversing the tree from the code that actually handles each node. This lets us avoid duplicating the logic for traversals and makes it easy to add new operations on the tree. This is achieved using the <a href="https://en.wikipedia.org/wiki/Visitor_pattern">visitor pattern</a>, where different operations are implemented with different visitors.</p>
<p>Let's examine the code for reading the class bytecode into a tree. We will be using the <a href="https://asm.ow2.io">ASM</a> library bundled with Forge:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#f0c678;">ClassNode</span><span style="color:#abb2bf;"> classNode </span><span style="color:#adb7c9;">= </span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">ClassNode</span><span style="color:#abb2bf;">();
</span><span style="color:#f0c678;">ClassReader</span><span style="color:#abb2bf;"> classReader </span><span style="color:#adb7c9;">= </span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">ClassReader</span><span style="color:#abb2bf;">(basicClass);
classReader.</span><span style="color:#eb6772;">accept</span><span style="color:#abb2bf;">(classNode, </span><span style="color:#db9d63;">0</span><span style="color:#abb2bf;">);
</span></code></pre><div class="flash flash-warning">
<b>Warning: </b>Ensure that you are importing from <code>org.objectweb.asm</code> and not some other package! Your IDE may import classes internal to your JDK.
</div>
<p>Here, confusingly, <a href="https://asm.ow2.io/javadoc/org/objectweb/asm/tree/ClassNode.html"><code>ClassNode</code></a> is being used as a visitor and <a href="https://asm.ow2.io/javadoc/org/objectweb/asm/ClassReader.html"><code>ClassReader</code></a> is treated as the data structure in this pattern. In fact, <code>ClassNode</code> represents both a tree and a visitor. We will explore this shortly. The method <code>classReader.accept(...)</code> reads the provided bytecode and invokes <code>visit</code> methods on <code>classNode</code> with the parsed class properties (such as methods or fields). The variable <code>classNode</code> takes these properties and adds them to itself as children.</p>
<p>To write this tree back as a sequence of bytes, we use the <a href="https://asm.ow2.io/javadoc/org/objectweb/asm/ClassWriter.html"><code>ClassWriter</code></a> class:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">int</span><span style="color:#abb2bf;"> flags </span><span style="color:#adb7c9;">= </span><span style="color:#f0c678;">ClassWriter</span><span style="color:#abb2bf;">.</span><span style="color:#db9d63;">COMPUTE_MAXS </span><span style="color:#adb7c9;">| </span><span style="color:#f0c678;">ClassWriter</span><span style="color:#abb2bf;">.</span><span style="color:#db9d63;">COMPUTE_FRAMES</span><span style="color:#abb2bf;">;
</span><span style="color:#f0c678;">ClassWriter</span><span style="color:#abb2bf;"> writer </span><span style="color:#adb7c9;">= </span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">ClassWriter</span><span style="color:#abb2bf;">(flags);
classNode.</span><span style="color:#eb6772;">accept</span><span style="color:#abb2bf;">(writer);
</span><span style="color:#cd74e8;">return</span><span style="color:#abb2bf;"> writer.</span><span style="color:#eb6772;">toByteArray</span><span style="color:#abb2bf;">();
</span></code></pre>
<p>Here, <code>ClassNode</code> is treated as a structure and <code>ClassWriter</code> as a visitor. Each time <code>writer</code> visits a node in the tree, it converts the node into the corresponding bytecode. The flags we pass into its constructor ensure that the class metadata is correctly recalculated.</p>
<h2 id="a-primer-on-java-bytecode">A primer on Java bytecode</h2>
<p>Now that we can inspect and modify the methods for a class, how do we correctly change its behaviour? To do that, we first need to understand how the <a href="https://en.wikipedia.org/wiki/Java_virtual_machine">JVM</a> (Java virtual machine) is designed.</p>
<p>Every method in Java, under the hood, consists of a sequence of instructions. These instructions tell the JVM what operations to perform: calling a method, adding two numbers; more generally, how your program should work. The "virtual" in "Java virtual machine" refers to the fact that it's quite literally emulating a processor or "machine" inside of your computer. </p>
<p>You may already be familiar with assembly instructions for other architectures such as <a href="https://en.wikipedia.org/wiki/X86">Intel (x86)</a> or <a href="https://en.wikipedia.org/wiki/MIPS_architecture">MIPS</a>. These architectures have their instructions operate on <em>registers</em>; effectively a limited set of variables that can only store values in binary. The JVM on the other hand is a <a href="https://en.wikipedia.org/wiki/Stack_machine">stack machine</a>; its instructions store and compute values on a stack.</p>
<p>For example, here's how you might compute <code>6 + 7</code> using Java bytecode:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">bipush 6
bipush 7
iadd
</span></code></pre>
<p>By referencing the <a href="https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings">JVM instruction listings</a> we can see that <code>bipush</code> pushes an integer smaller than 256 onto the stack. After executing the first two <code>bipush</code> instructions, the stack looks like this:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">7 <- Top of stack
6
</span></code></pre>
<p>The instruction <code>iadd</code> takes (pops off) the top two values and pushes the result of their addition:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">13 <- Top of stack
</span></code></pre>
<p>You can explore how Java source code transforms into bytecode with this website: <a href="https://javap.yawk.at">https://javap.yawk.at</a></p>
<div class="flash flash-warning">
<b>Warning: </b>Be aware that this stack (sometimes called the "operand stack") is <strong>different</strong> from the stack you're probably more familiar with: the call stack. The call stack stores stack frames and local variables in methods whereas this stack exclusively holds values to be used in calculations. They exist simultaneously and are separate.
</div>
<h2 id="tweaking-a-method">Tweaking a method</h2>
<p>Let's now change the behaviour of a method. For this example, we will change Minecraft's <code>FontRenderer</code> to replace the string "foo" in any rendered text with three asterisks. First we need to ensure that we are transforming the right class:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">Override
</span><span style="color:#cd74e8;">public byte[] </span><span style="color:#eb6772;">transform</span><span style="color:#abb2bf;">(...) {
</span><span style="color:#cd74e8;">if </span><span style="color:#abb2bf;">(</span><span style="color:#adb7c9;">!</span><span style="color:#abb2bf;">transformedName.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"net.minecraft.client.gui.FontRenderer"</span><span style="color:#abb2bf;">))
</span><span style="color:#cd74e8;">return</span><span style="color:#abb2bf;"> basicClass;
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
</span></code></pre><div class="flash flash-warning">
<b>Warning: </b>You must not import the class you want to transform. For example: <code>FontRenderer.class.getName()</code> is not allowed. Since the class has not been loaded yet, it will invoke your class transformer in an infinitely recursive loop.
</div>
<p>Then, after parsing the bytecode into a tree, we will loop over all the methods and find the one we want to modify:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">for </span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">MethodNode</span><span style="color:#abb2bf;"> method </span><span style="color:#adb7c9;">:</span><span style="color:#abb2bf;"> classNode.methods) {
</span><span style="color:#cd74e8;">if </span><span style="color:#abb2bf;">(method.name.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"renderString"</span><span style="color:#abb2bf;">)) {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
}
</span></code></pre><div class="flash flash-warning">
<b>Warning: </b>Comparing against the method name only works if there is only one method with that name. For methods that are overloaded, see the <a href="#handling-obfuscation"><strong>Handling obfuscation</strong> section</a>.
</div>
<p>By inspecting the definition of the <code>renderString</code> method in our IDE, we can see that the first argument is the string to be rendered. Let's write a static method inside our class transformer that will alter this argument:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">public static </span><span style="color:#f0c678;">String </span><span style="color:#eb6772;">alter</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">String</span><span style="color:#abb2bf;"> string) {
</span><span style="color:#cd74e8;">return</span><span style="color:#abb2bf;"> string.</span><span style="color:#eb6772;">replaceAll</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"foo"</span><span style="color:#abb2bf;">, </span><span style="color:#9acc76;">"***"</span><span style="color:#abb2bf;">);
}
</span></code></pre>
<p>This method must be marked as <code>public</code> so that it can be called from within <code>FontRenderer</code>. Next we will generate the payload for invoking this method. Let's first import the constants for each instruction's <a href="https://en.wikipedia.org/wiki/Opcode">opcode</a>:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">import static </span><span style="color:#abb2bf;">org.objectweb.asm.</span><span style="color:#f0c678;">Opcodes</span><span style="color:#abb2bf;">.</span><span style="color:#adb7c9;">*</span><span style="color:#abb2bf;">;
</span></code></pre>
<p>Our method for generating the payload will look like this:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">private static </span><span style="color:#f0c678;">InsnList </span><span style="color:#eb6772;">payload</span><span style="color:#abb2bf;">() {
</span><span style="color:#f0c678;">InsnList</span><span style="color:#abb2bf;"> payload </span><span style="color:#adb7c9;">= </span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">InsnList</span><span style="color:#abb2bf;">();
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#cd74e8;">return</span><span style="color:#abb2bf;"> payload;
}
</span></code></pre>
<p>To load the string as an argument to our <code>alter</code> method, we use the <code>ALOAD</code> instruction. This instruction takes an integer which represents the index of the variable we want to load. As the method <code>renderString</code> is not static, the first variable will be <code>this</code> (the <code>FontRenderer</code> instance). Arguments to the method come next and so the index of the string we want to modify is <code>1</code>:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">payload.</span><span style="color:#eb6772;">add</span><span style="color:#abb2bf;">(</span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">VarInsnNode</span><span style="color:#abb2bf;">(</span><span style="color:#db9d63;">ALOAD</span><span style="color:#abb2bf;">, </span><span style="color:#db9d63;">1</span><span style="color:#abb2bf;">));
</span></code></pre>
<p>Next we will retrieve the method we want to insert:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#f0c678;">Class</span><span style="color:#abb2bf;"><</span><span style="color:#f0c678;">CustomClassTransformer</span><span style="color:#abb2bf;">> target </span><span style="color:#adb7c9;">= </span><span style="color:#f0c678;">CustomClassTransformer</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">class</span><span style="color:#abb2bf;">;
</span><span style="color:#f0c678;">Method</span><span style="color:#abb2bf;"> other </span><span style="color:#adb7c9;">=</span><span style="color:#abb2bf;"> target.</span><span style="color:#eb6772;">getDeclaredMethod</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"alter"</span><span style="color:#abb2bf;">, </span><span style="color:#f0c678;">String</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">class</span><span style="color:#abb2bf;">);
</span></code></pre>
<p>...and add the instruction for invoking the method:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">payload.</span><span style="color:#eb6772;">add</span><span style="color:#abb2bf;">(</span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">MethodInsnNode</span><span style="color:#abb2bf;">(</span><span style="color:#db9d63;">INVOKESTATIC</span><span style="color:#abb2bf;">,
</span><span style="color:#f0c678;">Type</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">getInternalName</span><span style="color:#abb2bf;">(target), other.</span><span style="color:#eb6772;">getName</span><span style="color:#abb2bf;">(),
</span><span style="color:#f0c678;">Type</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">getMethodDescriptor</span><span style="color:#abb2bf;">(other), </span><span style="color:#db9d63;">false</span><span style="color:#abb2bf;">));
</span></code></pre>
<p>The method <code>getMethodDescriptor</code> returns an encoded version of a method's descriptor (<a href="https://stackoverflow.com/questions/7526483/what-is-the-difference-between-descriptor-and-signature">effectively its signature and return type</a>). This allows the JVM to differentiate between methods with the same name but with different parameter types (that is, overloaded methods).</p>
<p>Finally, we will add the instruction for replacing the argument with our returned string:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">payload.</span><span style="color:#eb6772;">add</span><span style="color:#abb2bf;">(</span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">VarInsnNode</span><span style="color:#abb2bf;">(</span><span style="color:#db9d63;">ASTORE</span><span style="color:#abb2bf;">, </span><span style="color:#db9d63;">1</span><span style="color:#abb2bf;">));
</span></code></pre>
<p>Now we just need to insert the payload at the start of the <code>renderString</code> method:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">if </span><span style="color:#abb2bf;">(method.name.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(...)) {
method.instructions.</span><span style="color:#eb6772;">insert</span><span style="color:#abb2bf;">(</span><span style="color:#eb6772;">payload</span><span style="color:#abb2bf;">());
}
</span></code></pre><h2 id="registering-our-class-transformer">Registering our class transformer</h2>
<p>Now that we have our class transformer, we can register it with Forge. We need to create a class implementing <code>IFMLLoadingPlugin</code>:</p>
<pre style="background-color:#2b303b;">
<code><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#cd74e8;">import </span><span style="color:#abb2bf;">net.minecraftforge.fml.relauncher.</span><span style="color:#f0c678;">IFMLLoadingPlugin</span><span style="color:#abb2bf;">;
</span><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">CustomPlugin </span><span style="color:#cd74e8;">implements </span><span style="color:#9acc76;">IFMLLoadingPlugin </span><span style="color:#adb7c9;">{
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#adb7c9;">}
</span></code></pre>
<p>For this tutorial, we will only implementing the <code>getASMTransformerClass</code> method. All the other required methods can return either nothing or <code>null</code>. This method returns an array of fully qualified names of our class transformers:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">Override
</span><span style="color:#cd74e8;">public </span><span style="color:#f0c678;">String</span><span style="color:#cd74e8;">[] </span><span style="color:#eb6772;">getASMTransformerClass</span><span style="color:#abb2bf;">() {
</span><span style="color:#cd74e8;">return new </span><span style="color:#f0c678;">String</span><span style="color:#abb2bf;">[]{</span><span style="color:#f0c678;">CustomClassTransformer</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">class</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">getName</span><span style="color:#abb2bf;">()};
}
</span></code></pre>
<p>We will also add two annotations on our class:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">IFMLLoadingPlugin</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">MCVersion</span><span style="color:#abb2bf;">(</span><span style="color:#adb7c9;"><</span><span style="color:#abb2bf;">version</span><span style="color:#adb7c9;">></span><span style="color:#abb2bf;">)
@</span><span style="color:#eb6772;">IFMLLoadingPlugin</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">TransformerExclusions</span><span style="color:#abb2bf;">(</span><span style="color:#adb7c9;"><</span><span style="color:#abb2bf;">group</span><span style="color:#adb7c9;">></span><span style="color:#abb2bf;">)
</span><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">CustomPlugin </span><span style="color:#abb2bf;">...
</span></code></pre>
<p>The <code>MCVersion</code> annotation tells Forge what version of Minecraft this coremod was made for. This is important as coremods break easily across different Minecraft versions. Replace <code><version></code> with a version string (such as: <code>"1.8.9"</code>). </p>
<p>The <code>TransformerExclusions</code> annotation tells Forge what packages should be excluded from being transformed by your class transformers. Replace <code><group></code> with your mod's package path (by default, this will be: <code>"com.example.examplemod"</code>). This ensures transformers will not attempt to recursively transform themselves.</p>
<p>Finally, we need to modify our artifact's manifest so that Forge knows that our mod is a coremod. Add this snippet to the <code>build.gradle</code> file:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">jar {
manifest {
attributes </span><span style="color:#9acc76;">"FMLCorePlugin"</span><span style="color:#abb2bf;">: </span><span style="color:#9acc76;">"<group>.CustomPlugin"
</span><span style="color:#abb2bf;">}
}
</span></code></pre>
<p>As with the <code>TransformerExclusions</code> annotation, replace <code><group></code> with your mod's package path. You can now test your coremod locally.</p>
<h2 id="making-the-mod-appear">Making the mod appear</h2>
<p>By default, coremods do not appear in the mods list. To make your coremod appear, you must add an attribute to your manifest:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">jar {
manifest {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;"> attributes </span><span style="color:#9acc76;">"FMLCorePluginContainsFMLMod"</span><span style="color:#abb2bf;">: </span><span style="color:#9acc76;">"true"
</span><span style="color:#abb2bf;">}
}
</span></code></pre>
<p>Ensure that you have a class annotated with <code>Mod</code> and a <code>mcmod.info</code> file (these should be present by default). This attribute tells Forge to load your mod class as well so you are free to do anything a normal mod can (such as registering event handlers or adding new items). For more information, see my <a href="https://blog.techno.fish/minecraft-18-forge-quick-start/">1.8 quick start guide</a>.</p>
<h2 id="handling-obfuscation">Handling obfuscation</h2>
<p>While class names are automatically deobfuscated for us, method names are not. This means our coremod will not work on obfuscated clients. To fix this, we will compare against both deobfuscated and obfuscated method names. </p>
<p>To find obfuscated method names we will use the mappings from <a href="http://www.modcoderpack.com">MCP</a> (Mod Coder Pack). Download the pack specific to your target Minecraft version and extract it. The decompiled sources you see in your IDE are generated from MCP's mappings and so they're compatible with each other.</p>
<p>Inside of your extracted folder will be a <strong>conf</strong> folder. This folder contains all the mappings for Minecraft's symbols (such as field and method names). Let's open the <strong>methods.csv</strong> file in a text editor.</p>
<p>Each line of the <strong>methods.csv</strong> file is in the format: <strong>unique identifier</strong>, <strong>deobfuscated name</strong>, <strong>side</strong>, <strong>description</strong> (excluding the first line which is the header). For this tutorial, we are only interested in the first two columns. Use your editor to find the line containing your target method name (in this case: <code>renderString</code>):</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">func_180455_b,renderString,0,"Render single line string by setting GL color, current (posX,posY), and calling renderStringAtPos()"
</span></code></pre>
<p>Some methods may have the same name so make sure that the description matches with the documentation in your IDE for that method.</p>
<p>Our method's unique identifier is: <code>func_180455_b</code>. This is <strong>not</strong> the obfuscated method name. This is a unique identifier that MCP assigns to make it easier for people to contribute to the mappings. To find the obfuscated name, open the <strong>joined.srg</strong> file (<em>not</em> <strong>joined.exc</strong>) in the same folder and find the line containing your method's unique identifier:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">MD: avn/b (Ljava/lang/String;FFIZ)I net/minecraft/client/gui/FontRenderer/func_180455_b (Ljava/lang/String;FFIZ)I
</span></code></pre>
<p>The first part of the line, <code>MD</code>, denotes that this mapping is for a method. Every method mapping is in the format:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;"><obfuscated class>/<obfuscated name> <descriptor> <class>/<name> <descriptor>
</span></code></pre>
<p>This means that our target method's obfuscated name is <code>b</code> and it's in the class <code>avn</code>. However, there are many other methods with the same name and in the same obfuscated class. This a result of the obfuscation process. For example:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">avn/b ()Z net/minecraft/client/gui/FontRenderer/func_78260_a ()Z
</span></code></pre>
<p>The only difference between these two obfuscated methods is their descriptor. We compare against both method names and descriptors to ensure that we are modifying the correct method:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">boolean</span><span style="color:#abb2bf;"> name </span><span style="color:#adb7c9;">=</span><span style="color:#abb2bf;"> method.name.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"renderString"</span><span style="color:#abb2bf;">) </span><span style="color:#adb7c9;">||</span><span style="color:#abb2bf;"> method.name.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"b"</span><span style="color:#abb2bf;">);
</span><span style="color:#cd74e8;">boolean</span><span style="color:#abb2bf;"> descriptor </span><span style="color:#adb7c9;">=</span><span style="color:#abb2bf;"> method.desc.</span><span style="color:#eb6772;">equals</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"(Ljava/lang/String;FFIZ)I"</span><span style="color:#abb2bf;">);
</span><span style="color:#cd74e8;">if </span><span style="color:#abb2bf;">(name </span><span style="color:#adb7c9;">&&</span><span style="color:#abb2bf;"> descriptor) {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
</span></code></pre>
<p>After adding this, your mod can now be distributed.</p>