LunarCN mod development - earthsworth/LunarClient-CN GitHub Wiki

If something wrong, please create a issue to help us improve this page

The example mod -> LunarMod-Example

Notify to developers

This is not a Java tutorial or a Mixin tutorial, you need to understand basic knowledge about Java and Mixin

Features will deprecate

  1. Use LunarGradle to decompile Minecraft, not Weave-Gradle (LunarGradle is underDevelop)
  2. Use LunarLauncherLib to launch LunarClient, not LunarClient Launcher

Contents will add in this tutorial

  1. Access Writer (need finish LunarGradle develop)

About LunarGradle

I will publish LunarGradle to Gradle Plugin Portal

Source: here

Setup workspace

  1. Create a project using Gradle build tool (dsl: groovy)
  2. Download latest LunarCN Loader API jar into libs folder
  3. Add implementation fileTree(dir: "libs", include: ["*.jar"]) in block dependencies
  4. Add plugin repo in settings.gradle
  5. Add id 'com.github.weave-mc.weave-gradle' version 'bcf6ab0279' in block plugins
  6. Add Minecraft block
  7. Sync project

Download LunarCN Loader online

Anytimes repo.lunarcn.top will down, if muskf forget renew it

Today is 2023/10/22, muskf ran away :(

Plugins repo

pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
        maven {
            name = 'JitPack'
            url = 'https://jitpack.io'
        }
    }
}

Minecraft Block

minecraft {
    version "1.8.9"
}

Mod config file

src/main/resources/lunarcn.mod.json

  1. Code a EntryPoint -> here
  2. Code Mixins -> here
  3. Code Hooks -> here
{
  "mixinConfigs": [
    "example.mixins.json" // Mixin configs
  ],
  "entrypoints": [
    "com.example.ModEntry" // Entry points
  ],
  "hookPackage": [
    "com.example.hooks" // Hook Class
  ]
}

Add Entry point for your addon

ModEntry.java

This class may statement in lunarcn.mod.json

package org.example;

import org.cubewhy.lunarcn.loader.api.ModInitializer;


public class ModEntry implements ModInitializer {
    @Override
    public void onCrash(@NotNull CrashReport crashReport) {
        System.out.println("OOPS, game crashed, LOG = " + crashReport.getFile().getPath());
    }

    @Override
    public void onInit() {
        System.out.println("Lunar init!");
    }

    @Override
    public void onPreInit() {
        System.out.println("Init mod!");
    }

    @Override
    public void onStart() {
        System.out.println("Game started!");
    }

    @Override
    public void onStop() {
        System.out.println("See you again");
    }
}

Add Mixins for your addon

This is a example, here isn't a Mixin tutorial

If you want use Mixin in your addon, you need setup Mixin

  1. Mixin config
  2. Mixin classes

Mixin config

You need add a mixin config in your project and statement it in Mod config file

src/main/resources/example.mixins.json

{
  "required": true,
  "minVersion": "0.8.5",
  "package": "org.example.mixins", // Mixin package
  "compatibilityLevel": "JAVA_8",
  "mixins": [
    "MixinMinecraft" // Mixins you have
  ]
}

Mixin classes

examples only

Make sure add className into mixins config

MixinMinecraft.java

package org.example.mixins;

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

@Mixin(Minecraft.class)
public class MixinMinecraft {
    @Inject(method = "startGame", at = @At("HEAD"))
    public void startGameHead(CallbackInfo ci) {
        System.out.println("Starting game!");
    }

    @Inject(method = "startGame", at = @At("RETURN"))
    public void startGameReturn(CallbackInfo ci) {
        System.out.println("Init finished!");
    }
}

Setup Mixin

Add codes into build.gradle

repositories {
    maven { url = "https://repo.spongepowered.org/repository/maven-public/" }
}

dependencies {
    compileOnly 'org.spongepowered:mixin:0.8.5'
}

And rebuild your project

Add Hooks for your addon

Make sure add this className into Mod config

HookMinecraft.java

package org.example.hooks;

import org.cubewhy.lunarcn.loader.api.Hook;
import org.jetbrains.annotations.NotNull;
import org.cubewhy.lunarcn.loader.api.SubscribeHook;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;

@SubscribeHook // 1.3+ uses subscribeHook to register a hook
public class HookMinecraft extends Hook {

    public HookMinecraft() {
        super("net/minecraft/client/Minecraft"); // The target class
    }

    @Override
    public void transform(@NotNull ClassNode classNode, @NotNull AssemblerConfig assemblerConfig) {
        classNode.methods.stream()
                .filter(m -> m.name.equals("startGame")) // find target method
                .findFirst().orElseThrow()
                .instructions.insert(
                        new MethodInsnNode(
                                Opcodes.INVOKESTATIC,
                                Type.getInternalName(HookMinecraft.class), // where the method onStartGame is
                                "onStartGame", // invoke onStartGame
                                "()V"
                        )
                );
    }

    public static void onStartGame() {
        System.out.println("Minecraft Hook!");
    }
}

Run your addon

  1. Launch LunarCN and close it
  2. Put your plugin into ~/.cubewhy/lunarcn/mods folder
  3. Run ~/.cubewhy/lunarcn/launch.bat
  4. Then you can see the log in the console