How Minecraft Mods Works (For dummies).

At first Minecraft modding can be a little bit overwhelming especially for beginner. So in this article I will try to explain how modding works by creating a simple mod with you !

How does modding work, and what are mod platforms?

So if you are unfamiliar with how modding works on MC, let’s just say it’s a vast topic. But in a nutshell there is 2 main way to modify the game :

  • By directly modifying the Minecraft code using for example the Minecraft Coder Pack which help you decompile the Minecraft source code. It’s not really recommended to mod this way because you’ll not be able to add multiple mods. So besides really specific use cases such as Optifine or pvp clients don’t mod this way !
  • Or by using a mod loader. Mod loaders are as their name suggest software that allow you to use multiple mods at once. The main two mod loaders are Forge and Fabric. These two different one are unique in their own way, Forge is more suitable for mod that add content to the game such as new items, new mobs, or even new technology. When with Fabric you’ll most of the time encounter client-side mods that don’t affect gameplay directly, but for example performance, little enhancement, etc…

In our case we’ll be using Fabric as I just want to make a little mod 😉

Requirement

Hold on for a second ! Before starting coding we need some things setup, here is a list of what you will need :

  • Minecraft installed with the fabric mod loader. Here is a great tutorial !
  • A proper IDE that can compile some java code, I personally use IntelliJ for MC modding.
  • Some basics with Java. (I hate this programming language 🤮️, but I have to deal with it for MC)
  • And finally a mod template such as the fabric-example-mod

Setting up the project

So now that you have downloaded everything and open your project in your favorite IDE you’ll need to launch the gradle installation thingy. As it’s different for each IDE, I would suggest you read this tutorial to setup everything for your IDE. Once gradle has finished his installation try running your project, with IntelliJ IDEA try restarting your IDE and then you should see a ▶️ button, click on it ! If everything is right Minecraft should load, and you should see the label Fabric (Modded) next to your MC version. Congratulations, you’ve built your first mod 🥳️.

In this tutorial we’ll not see how to change your mod ID or maven group but if you want to I’m sure you can find how online.

Let’s start modding !

Now the fun part 🤨️. To check if our mod works properly let’s add a simple feature to the game: an event that send you into the sky each time you right click a block. For that we need to detect when the player click on a block. Here is the my ExampleModClient inside src/client/java/com/example that does exactly that

Java
package com.example;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.util.ActionResult;

public class ExampleModClient implements ClientModInitializer {

	@Override
	public void onInitializeClient() {
	
		UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
		
			System.out.println("Block clicked !");
			return ActionResult.PASS;
			
		});
		
	}
}

Now when you run the project again you should see the message Block clicked ! in your IDE console whenever you click on a block. Great ! but how does this works ?!

  • First UseBlockCallback.EVENT.register() register the event that is triggered when the player right click a block
  • This event take for argument (player, world, hand, hitResult)
  • Then we say to fabric what code we want to execute in our case System.out.println(“Block clicked !”) which print our message to the console
  • And finally we say to the game how we want to finish the event with ActionResult.PASS in our case this let the action continue but we could also have put ActionResult.Fail to cancel the action.

Ok now let’s launch our player to the stars 🚀️. We can do that by setting the player velocity using this code :

Java
package com.example;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.util.ActionResult;

public class ExampleModClient implements ClientModInitializer {

	@Override
	public void onInitializeClient() {
	
		UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
		  
			System.out.println("Block clicked !");
			player.setVelocity(0, 4, 0);
			return ActionResult.PASS;
			
		});
		
	}
}

Now relaunch your game and fly up into the sky 🤩️

The Mixin hell

Did you find the previous part fun ? I hope so because now we’ll be diving into a more complex part : Mixins 😤️. What we previously saw can help you with some basic modding but you’ll quickly encounter cases where you need to actually modify the game itself. And we do that by injecting code using our mixins. But as an example is better that 1000 words let’s say I want to increase my brightness to see better in dark caves. Here is the code that I would write :

Java
package com.example;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.SimpleOption;
import net.minecraft.util.ActionResult;

public class ExampleModClient implements ClientModInitializer {

	@Override
	public void onInitializeClient() {

		UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
		
			final SimpleOption<Double> gamma = MinecraftClient.getInstance().options.getGamma();
			gamma.setValue(10.0);
			
			return ActionResult.PASS;
		});
	}
}

However, when I test this code, I encounter this error 😭️. Why?

Bash
[11:42:55] [Render thread/ERROR] (Minecraft) Illegal option value 10.0 for translation{key='options.gamma', args=[]}

This is because I set the gamma to a value that exceed his limit of 1.0 let’s see how the Minecraft code works. Here is the snippet of the code for setValue()

Java
    T value;
    
    public void setValue(T value) {
        T object = this.callbacks.validate(value).orElseGet(() -> {
            LOGGER.error("Illegal option value " + value + " for " + this.text);
            return this.defaultValue;
        });
        if (!MinecraftClient.getInstance().isRunning()) {
            this.value = object;
        } else {
            if (!Objects.equals(this.value, object)) {
                this.value = object;
                this.changeCallback.accept(this.value);
            }

        }
    }

So as we can see in this function there are multiple checks such as a test to determine if the value is valid. So what we want to do is to create a new mixin file in mixin.client I’m gonna call it IllegalValueMixin.java, here is the content of it :

IllegalValueMixin.java
package com.example.mixin.client;

import net.minecraft.client.option.SimpleOption;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(SimpleOption.class)
public class IllegalValueMixin<T> {
    @Shadow
    T value;

    @Inject(method = "setValue", at = @At("HEAD"), cancellable = true)
    private void setIllegalValue(T value, CallbackInfo info) {
        this.value = value;
        info.cancel();
    }
}

And we also need to declare it to fabric inside the client/ressources/modid.client.mixins.json like that

modid.client.mixins.json
{
	"required": true,
	"package": "com.example.mixin.client",
	"compatibilityLevel": "JAVA_17",
	"client": [
		"ExampleClientMixin",
		"IllegalValueMixin"
	],
	"injectors": {
		"defaultRequire": 1
	}
}

Launch your game and enjoy your code working perfectly 🥳️. But what are these magic tricks ? Well let’s analyze our IllegalValueMixin.java

  • First @Mixin(SimpleOption.class) is used to declare that we want to modify the class SimpleOption which contains our method SetValue
  • Then @Shadow allows us to modify a property of the class file in our case T Value;
  • And now the interesting part; @Inject(method = “setValue”, at = @At(“HEAD”), cancellable = true) this allows us to inject our code into the method “SetValue” and make it cancellable.
  • And finally our code to inject, that sets the value and cancels the function to avoid the valid value test.

Final Words

I really hope you liked this introduction to modding with Fabric ! I’m not a professional of any kind so feel free to comment if I made any mistake ;D
Don’t forget to check out the fabric mc wiki as it’s a great reference
And finally all the code is available on my gitea.

Have a great Day 😊️
– Ayabusa

1 thought on “How Minecraft Mods Works (For dummies).”

Leave a Comment

Your email address will not be published. Required fields are marked *