TechnocoderZola2021-12-07T00:00:00+00:00https://blog.techno.fish/atom.xmlRunning x86-64 Docker Images on ARM2021-12-07T00:00:00+00:002021-12-07T00:00:00+00:00https://blog.techno.fish/running-x86-64-docker-images-on-arm/<p>Need to run Intel-based Docker images on a Linux ARM machine? Run this command:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#eb6772;">docker</span><span style="color:#abb2bf;"> run</span><span style="color:#eb6772;"> --rm --privileged</span><span style="color:#abb2bf;"> aptman/qus</span><span style="color:#eb6772;"> -s</span><span style="color:#adb7c9;"> --</span><span style="color:#abb2bf;"> -p x86_64
</span></code></pre>
<p>That's it! Try running your Docker image. Keep reading to learn how it works and what you need to be aware of.</p>
<h2 id="how-it-works">How it works</h2>
<p>When you run any executable, it gets sent to the kernel so that it can check its architecture. You might have seen this error message before:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">exec user process caused "exec format error"
</span></code></pre>
<p>This means the architecture didn't match up with what's supported for your system. The Linux kernel has a feature called <a href="https://en.wikipedia.org/wiki/Binfmt_misc"><code>binfmt_misc</code></a> which allows us to define our own handlers for arbitrary executable formats. We use this feature to load <code>x86-64</code> executables using QEMU.</p>
<p><a href="https://www.qemu.org">QEMU</a> is a suite of machine emulators for a huge variety of system architectures. In this case, we don't actually emulate a system with a kernel but instead, we use QEMU's <a href="https://www.qemu.org/docs/master/user/main.html">user space emulator</a>. This means it has much less overhead than emulating a complete system. Privileged actions like writing to files or sending network requests are delegated directly to the Linux kernel.</p>
<p>The <a href="https://github.com/dbhi/qus">qus</a> Docker image packages this whole setup so that all you need to do is run it as a container. It contains the required QEMU binaries and also automatically registers them into <code>binfmt_misc</code>. As it requires direct access to the Linux kernel, we use the <code>--privileged</code> Docker flag. You can learn more about it on its project page.</p>
<h2 id="caveats">Caveats</h2>
<h3 id="performance">Performance</h3>
<p>As we're emulating (not virtualizing!) the <code>x86-64</code> architecture, the performance penalty is going to be significant. That is, it could be up to an order of magnitude slower for some workloads. I've found that it works fine for a small web server.</p>
<h3 id="reboots">Reboots</h3>
<p>The Linux kernel resets any registered <code>binfmt_misc</code> handlers on system reboots. To persist these handlers across reboots, you can use <code>crontab</code> to run a script containing the Docker command at startup:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#eb6772;">$</span><span style="color:#abb2bf;"> crontab</span><span style="color:#eb6772;"> -e
</span></code></pre><pre style="background-color:#2b303b;">
<code><span style="font-style:italic;color:#5f697a;"># ...
</span><span style="color:#eb6772;">@reboot</span><span style="color:#abb2bf;"> sh $</span><span style="color:#eb6772;">HOME</span><span style="color:#abb2bf;">/script.sh
</span></code></pre><h2 id="further-resources">Further resources</h2>
<ul>
<li><code>qus</code> homepage: <a href="https://dbhi.github.io/qus/">https://dbhi.github.io/qus/</a></li>
<li><code>multiarch/qemu-user-static</code> - a similar tool for <code>x86-64</code> hosts: <a href="https://github.com/multiarch/qemu-user-static">https://github.com/multiarch/qemu-user-static</a></li>
</ul>
Minecraft 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>
Intercepting Vanilla Minecraft Network Packets2021-06-06T00:00:00+00:002021-06-06T00:00:00+00:00https://blog.techno.fish/intercepting-vanilla-minecraft-network-packets/<p>In this tutorial we will discuss how to intercept vanilla Minecraft network packets.</p>
<h2 id="approach">Approach</h2>
<p>While Forge allows us to register and handle <a href="https://mcforge.readthedocs.io/en/latest/networking/">custom network packets</a>, it does not provide a way to intercept or modify vanilla Minecraft packets. Fortunately, Forge does provide a way for us to access Minecraft's Netty pipeline directly.</p>
<p><a href="https://netty.io">Netty</a> is a library that Minecraft uses to handle its networking. The core component of Netty is the <a href="https://netty.io/4.1/api/io/netty/channel/ChannelPipeline.html"><code>ChannelPipeline</code></a>. The pipeline consists of a sequence of packet handlers that are each invoked until a packet is handled successfully. We will insert our own handler into this pipeline.</p>
<h2 id="implementing-a-packet-handler">Implementing a packet handler</h2>
<p>For this tutorial, we will be extending the <a href="https://netty.io/4.0/api/io/netty/channel/SimpleChannelInboundHandler.html"><code>SimpleChannelInboundHandler</code></a> as we will only be handling inbound packets. If you wish to capture outbound packets as well, you can implement a <a href="https://netty.io/4.1/api/io/netty/channel/ChannelDuplexHandler.html"><code>ChannelDuplexHandler</code></a>. We will also assume that we are implementing a handler for a client.</p>
<p>Our class will start off 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;">io.netty.channel.</span><span style="color:#f0c678;">SimpleChannelInboundHandler</span><span style="color:#abb2bf;">;
</span><span style="color:#cd74e8;">import </span><span style="color:#abb2bf;">net.minecraft.network.</span><span style="color:#f0c678;">Packet</span><span style="color:#abb2bf;">;
</span><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">CustomHandler </span><span style="color:#cd74e8;">extends </span><span style="color:#9acc76;">SimpleChannelInboundHandler</span><span style="color:#adb7c9;"><</span><span style="color:#f0c678;">Packet</span><span style="color:#adb7c9;">> {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#adb7c9;">}
</span></code></pre>
<p>Let's add the method that will actually handle packets:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">Override
</span><span style="color:#cd74e8;">protected</span><span style="color:#abb2bf;"> void </span><span style="color:#eb6772;">channelRead0</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">ChannelHandlerContext</span><span style="color:#abb2bf;"> ctx, </span><span style="color:#f0c678;">Packet</span><span style="color:#abb2bf;"> msg) throws </span><span style="color:#f0c678;">Exception </span><span style="color:#abb2bf;">{
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
</span></code></pre>
<p>The <code>Packet</code> class is subclassed by all vanilla Minecraft packets. To intercept a specific packet, first find the packet you want from the <a href="https://wiki.vg/Protocol">protocol specification</a> and then check if the packet is of the correct type using the <code>instanceof</code> operator. We will use the <strong>ScoreboardObjective</strong> packet as an example:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">if </span><span style="color:#abb2bf;">(msg </span><span style="color:#adb7c9;">instanceof </span><span style="color:#f0c678;">S3BPacketScoreboardObjective</span><span style="color:#abb2bf;">) {
</span><span style="color:#f0c678;">System</span><span style="color:#abb2bf;">.out.</span><span style="color:#eb6772;">println</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"Scoreboard objective changed!"</span><span style="color:#abb2bf;">);
}
</span></code></pre>
<p>Finally, we need to pass the packet on to further handlers in the pipeline as we are only inspecting it:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">ctx.</span><span style="color:#eb6772;">fireChannelRead</span><span style="color:#abb2bf;">(msg);
</span></code></pre><h2 id="registering-our-packet-handler">Registering our packet handler</h2>
<p>Before we add the code for registering our handler, let's first override the default constructor of our class:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">public </span><span style="color:#eb6772;">CustomHandler</span><span style="color:#abb2bf;">() {
</span><span style="color:#eb6772;">super</span><span style="color:#abb2bf;">(</span><span style="color:#db9d63;">false</span><span style="color:#abb2bf;">);
}
</span></code></pre>
<p>This is necessary as, by default, the <code>SimpleChannelInboundHandler</code> automatically removes packets from memory after they are handled by our method. This is obviously undesirable as handlers later in the pipeline would no longer be able to read the packet.</p>
<p>We will also need to add an annotation to our class:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">ChannelHandler</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">Sharable
</span><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">CustomHandler </span><span style="color:#cd74e8;">extends </span><span style="color:#abb2bf;">...
</span></code></pre>
<p>Packet handlers can only be added once to a pipeline. This annotation ensures that if our handler is already registered, it will reuse the existing one instead of throwing an exception. This also means that <strong>your class must not rely on any unsynchronized variables</strong>. Furthermore, Minecraft's networking is on a separate thread so to access objects such as the game world you must use scheduled tasks. If your class does not access any state outside of the handler method then that's fine.</p>
<p>Finally, let's add our handler to the pipeline using Minecraft Forge:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">SubscribeEvent
</span><span style="color:#cd74e8;">public</span><span style="color:#abb2bf;"> void </span><span style="color:#eb6772;">connect</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">FMLNetworkEvent</span><span style="color:#abb2bf;">.</span><span style="color:#f0c678;">ClientConnectedToServerEvent</span><span style="color:#abb2bf;"> event) {
</span><span style="color:#f0c678;">ChannelPipeline</span><span style="color:#abb2bf;"> pipeline </span><span style="color:#adb7c9;">=</span><span style="color:#abb2bf;"> event.manager.</span><span style="color:#eb6772;">channel</span><span style="color:#abb2bf;">().</span><span style="color:#eb6772;">pipeline</span><span style="color:#abb2bf;">();
pipeline.</span><span style="color:#eb6772;">addBefore</span><span style="color:#abb2bf;">(</span><span style="color:#9acc76;">"packet_handler"</span><span style="color:#abb2bf;">, </span><span style="color:#eb6772;">this</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">getClass</span><span style="color:#abb2bf;">().</span><span style="color:#eb6772;">getName</span><span style="color:#abb2bf;">(), </span><span style="color:#eb6772;">this</span><span style="color:#abb2bf;">);
}
</span></code></pre>
<p>As evidenced by the event name, this method will be invoked whenever the client connects to a server. The first argument to the <code>addBefore</code> method is the name of the handler we want to precede. Here, <code>"packet_handler"</code> refers to Minecraft's packet handler. We cannot append our handler onto the end of the pipeline as Minecraft's handler does not pass on packets it receives. If you'd like to intercept Forge packets as well, you can use <code>"fml:packet_handler"</code> instead.</p>
<p>The second argument is a unique name for our handler. Any descriptive string will do so I chose the name of our packet handler class. The last argument is an instance of our handler.</p>
<p>Make sure to register our class with Minecraft Forge:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">@</span><span style="color:#eb6772;">Mod</span><span style="color:#abb2bf;">(...)
</span><span style="color:#cd74e8;">public class </span><span style="color:#abb2bf;">... {
@</span><span style="color:#eb6772;">EventHandler
</span><span style="color:#cd74e8;">public</span><span style="color:#abb2bf;"> void </span><span style="color:#eb6772;">init</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">FMLInitializationEvent</span><span style="color:#abb2bf;"> event) {
</span><span style="color:#f0c678;">MinecraftForge</span><span style="color:#abb2bf;">.</span><span style="color:#db9d63;">EVENT_BUS</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">register</span><span style="color:#abb2bf;">(</span><span style="color:#cd74e8;">new </span><span style="color:#f0c678;">CustomHandler</span><span style="color:#abb2bf;">());
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
}
</span></code></pre>
<p>Now you can test the mod.</p>
<h2 id="modifying-packet-fields">Modifying packet fields</h2>
<p>Often, a packet's fields will be private or exposed only through a getter method. This makes it impossible to modify packet data. To get around this, we can use <a href="https://www.oracle.com/technical-resources/articles/java/javareflection.html">reflection</a>.</p>
<p>Let's take the <strong>ScoreboardObjective</strong> packet as an example. We want to modify any scoreboard objective names so that they display as "Foo". Let's take a look at the class's member fields:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">public class </span><span style="color:#f0c678;">S3BPacketScoreboardObjective </span><span style="color:#cd74e8;">implements </span><span style="color:#abb2bf;">...
{
</span><span style="color:#cd74e8;">private </span><span style="color:#f0c678;">String</span><span style="color:#abb2bf;"> objectiveName;
</span><span style="color:#cd74e8;">private </span><span style="color:#f0c678;">String</span><span style="color:#abb2bf;"> objectiveValue;
</span><span style="color:#cd74e8;">private </span><span style="color:#f0c678;">IScoreboardObjectiveCriteria</span><span style="color:#abb2bf;">.</span><span style="color:#f0c678;">EnumRenderType</span><span style="color:#abb2bf;"> type;
</span><span style="color:#cd74e8;">private int</span><span style="color:#abb2bf;"> field_149342_c;
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
</span></code></pre>
<p>By examining Minecraft's protocol specification we can see that we need to modify the <code>objectiveValue</code> field. We will use the <code>ReflectionHelper</code> class from Forge:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#f0c678;">S3BPacketScoreboardObjective</span><span style="color:#abb2bf;"> packet </span><span style="color:#adb7c9;">= </span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">S3BPacketScoreboardObjective</span><span style="color:#abb2bf;">) msg;
</span><span style="color:#f0c678;">ReflectionHelper</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">setPrivateValue</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">S3BPacketScoreboardObjective</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">class</span><span style="color:#abb2bf;">, packet, </span><span style="color:#9acc76;">"Foo"</span><span style="color:#abb2bf;">, </span><span style="color:#db9d63;">1</span><span style="color:#abb2bf;">);
</span></code></pre>
<p>The first argument takes the class definition of the object we want to modify. The second argument is the object itself. The third argument is the value we want to set the field to. Finally, the last argument is the <em>index</em> of the field we want to modify. Note that the index may change across Minecraft versions.</p>
<h2 id="handling-exceptions">Handling exceptions</h2>
<p>Your packet handler must handle exceptions or else they may not be logged correctly and can cause the game to crash. The easiest way to do this is to catch any exceptions in the method itself:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#cd74e8;">try </span><span style="color:#abb2bf;">{
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">} </span><span style="color:#cd74e8;">catch </span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">Exception </span><span style="color:#eb6772;">e</span><span style="color:#abb2bf;">) {
</span><span style="font-style:italic;color:#5f697a;">// ...
</span><span style="color:#abb2bf;">}
ctx.</span><span style="color:#eb6772;">fireChannelRead</span><span style="color:#abb2bf;">(msg);
</span></code></pre>
<p>Alternatively, if you're confident that your code will not throw any unhandled exceptions and you just want a complete stack trace in the event of an unexpected error you can override the <code>exceptionCaught</code> method:</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:#abb2bf;"> void </span><span style="color:#eb6772;">exceptionCaught</span><span style="color:#abb2bf;">(</span><span style="color:#f0c678;">ChannelHandlerContext</span><span style="color:#abb2bf;"> ctx, </span><span style="color:#f0c678;">Throwable</span><span style="color:#abb2bf;"> cause) throws </span><span style="color:#f0c678;">Exception </span><span style="color:#abb2bf;">{
cause.</span><span style="color:#eb6772;">printStackTrace</span><span style="color:#abb2bf;">();
</span><span style="color:#eb6772;">super</span><span style="color:#abb2bf;">.</span><span style="color:#eb6772;">exceptionCaught</span><span style="color:#abb2bf;">(ctx, cause);
}
</span></code></pre>Minecraft 1.8 Forge Modding Quick Start2021-06-06T00:00:00+00:002021-06-06T00:00:00+00:00https://blog.techno.fish/minecraft-18-forge-quick-start/<p>This guide will get you started with Forge modding on Minecraft 1.8.</p>
<h2 id="setup">Setup</h2>
<p>Go to the <a href="https://files.minecraftforge.net/net/minecraftforge/forge/index_1.8.9.html">1.8.9 Minecraft Forge download page</a> and select the "Mdk" (Mod Development Kit) option. Extract the contents of the downloaded archive into a folder. This folder will serve as the workspace for our mod so feel free to rename it. </p>
<p>Open <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a> and select "Open". Then, select the folder containing our extracted contents. This folder should contain a <code>build.gradle</code> file. Click "OK". Wait for the initial build to complete.</p>
<div class="flash flash-warning">
<b>Warning: </b>You will see this message in the build dialog: </p>
<blockquote>
<p>This mapping 'stable_20' was designed for MC 1.8.8! Use at your own peril.</p>
</blockquote>
<p>This refers to the deobfuscation mappings that translate the symbols of Minecraft's decompiled bytecode into sensible names. If you will be extensively modifying Minecraft's source code directly, you may wish to download the Mod Development Kit for 1.8.8 instead.
</div>
<p>Open <code>ExampleMod.java</code> (inside <strong>src > main > java > com.example.examplemod</strong>). A message at the top of the editor will appear asking you to select the module JDK. Click it and select <code>1.8</code>. If you haven't already, you should install the 1.8 Java Development Kit for your platform before performing this step. Alternatively, you can select it by opening your Project Structure (under the File menu) and choosing it from the Project SDK drop-down.</p>
<p>You will notice that IntelliJ does not detect any of Forge's packages. To fix this, click on Gradle on the right sidebar and expand <code>forgegradle</code>. Then, run <code>setupDecompWorkspace</code>. </p>
<div class="flash flash-warning">
<b>Warning: </b>You may see this error message appear:
<blockquote>
<p>Out of memory, GC overhead limit exceeded</p>
</blockquote>
<p>To fix this, create a new file named <code>gradle.properties</code> next to <code>build.gradle</code> and add this line to it:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">org.gradle.jvmargs=-Xmx4096M
</span></code></pre>
<p>This will increase the amount of memory allocated to Gradle. Run <code>setupDecompWorkspace</code> again.</p>
</div>
<p>After this, run <code>setupDevWorkspace</code>. Finally, click the refresh icon at the top left of the Gradle panel and then Forge's packages and symbols will be recognised.</p>
<h2 id="testing">Testing</h2>
<p>Open the Gradle panel and under <code>forgegradle</code> run <code>runClient</code>. This will open Minecraft with your mod automatically loaded. Any messages printed to standard output or log messages (including errors) will be shown in the Run dialog.</p>
<p>To set a specific username for the client player, add this snippet to your <code>build.gradle</code> file:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">runClient {
args "--username", "<username>"
}
</span></code></pre>
<p>while replacing <code><username></code> with your preferred username.</p>
<p>To authenticate with a specific account, use this snippet instead:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">runClient {
args "--username", "<username>", "--password", "<password>"
}
</span></code></pre><h2 id="exporting">Exporting</h2>
<p>Open the Gradle panel and under <code>build</code>, run <code>build</code>. This will generate a <code>jar</code> file inside of the <strong>build > libs</strong> folder in the form of <code>[mod-id]-[version].jar</code>. You can install your mod by dragging it into the <code>mods</code> folder of your client.</p>
<h2 id="customisation">Customisation</h2>
<p>To change your mod identifier we will need to edit several files.</p>
<p>First, open <code>ExampleMod.java</code> again and change the string assigned to the static variable <code>MODID</code>. You will also need to rename the last component of your package path (<code>com.example.examplemod</code>). It is recommended you rename the entire package path according to the <a href="https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html">Java naming guidelines</a>. </p>
<p>Next, open <code>mcmod.info</code> (inside <strong>src > main > resources</strong>) and change the <code>modid</code> field. You may edit other fields to your discretion.</p>
<p>Finally, open <code>build.gradle</code> and change the <code>group</code> variable to your new package path. You should also change <code>archivesBaseName</code> to your new mod identifier. This will alter the name of the exported <code>jar</code> file.</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://mcforge.readthedocs.io/en/latest/">Official Forge Documentation</a> - be aware that parts of it are not applicable to version 1.8</li>
</ul>