提示

若您并没有拥有 Minecraft 账号,请自行退出,否则在完成之后你可能将会违反 Minecraft EULA,该教程仅适用于拥有 Minecraft 账号的人。

反编译

首先下载用于反编译 Minecraft 1.12.2 的工具,MCP,若打不开链接可使用 DevSidecar

下载完成之后将下载好的文件解压到单独一个目录。

然后使用任意 Minecraft 启动器切换到官方启动器目录并使用 正版登录/微软登录 启动一次原版 Minecraft 1.12.2 并退出。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第1张图片退出之后,在打开的目录中打开 powershell 并输入:

./decompile.bat

输入后等待反编译完成即可,反编译大概需要 10-20 分钟左右,反编译时间受电脑配置影响。

最后反编译完成将会显示大概如下内容:

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第2张图片

导入项目

这里以 IntelliJ IDEA 为例,进入主界面后点击 打开项目 然后选择打开的目录中的 eclipse\Client 目录。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第3张图片然后打开项目结构导入模块,首先点到模块界面点击导入模块并选择打开的目录的 eclipse\Client\.project 文件点击确定,然后一直点下一步和创建,最后点击否,然后点击添加内容根,内容根选择打开的目录的 src\minecraft 目录,语言级别选择 JDK 8。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第4张图片提示:若导入模块后已有其他内容根,请全部删除。

最后点击应用并关闭即可。

运行

首先点击 IDEA 右上角的当前文件并选择编辑配置,随后点击添加应用程序,选择 JDK 17,主类选择 Start,然后添加 VM 选项,VM选项填写以下内容:

-Djava.library.path=打开的目录\jars\versions\1.12.2\1.12.2-natives

提示:natives 目录名称与启动器有关,此教程示例为 PCL2 启动器。

工作目录选择打开的目录的 jars 目录,程序实参填写 --username Dev (可选)。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第5张图片

最后保存并运行测试即可。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第6张图片

打包

首先打开项目结构并点到工件界面添加 JAR,选择空,然后将 'Client' 编译输出 添加进去,最后应用关闭。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第7张图片最后构建工件即可看到打包好的文件,若需运行,可将构建好的文件覆盖原版文件运行,但请注意,若启动器有文件校验,需要提前关闭启动器的文件校验功能。

修改游戏窗口标题及简易功能实现

修改游戏窗口标题

首先打开类文件 net.minecraft.client.Minecraft,随后找到函数 createDisplay ,然后将 Minecraft 1.12.2 改为你想要的名称即可。

截图超出上传的文件大小范围就不上传了

简易功能实现-Spider

功能名称解释

使你能够像蜘蛛一样随意在墙上攀爬。

实现

首先新建一个软件包,并新建一个类,然后创建两个函数,以及一个布尔值函数。

package cn.ksmcbrigade; //packet

public class Client {

    public static boolean enabled = false;

    public static void tick(){ //event

    }

    public static void keyInput(int key){  //event
        
    }
}

然后在 net.minecraft.client.Minecraft 类中的 runTick 函数中的任意位置插入引用 tick 函数的代码以注册客户端 Tick 事件:

public void runTick() throws IOException
{
    Client.tick(); // add
    if (this.rightClickDelayTimer > 0)
    {
        --this.rightClickDelayTimer;
    }

    this.mcProfiler.startSection("gui");

    if (!this.isGamePaused)
    {
        this.ingameGUI.updateTick();
    }

    this.mcProfiler.endSection();
    this.entityRenderer.getMouseOver(1.0F);
    this.field_193035_aW.func_193297_a(this.world, this.objectMouseOver);
    this.mcProfiler.startSection("gameMode");

    if (!this.isGamePaused && this.world != null)
    {
        this.playerController.updateController();
    }

    this.mcProfiler.endStartSection("textures");
    [...]
  }

然后再在 runTickKeyboard 函数的任意位置插入 keyInput 函数以注册按键检测事件。

private void runTickKeyboard() throws IOException
{
    while (Keyboard.next())
    {
        int i = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();

        if (this.debugCrashKeyPressTime > 0L)
        {
            if (getSystemTime() - this.debugCrashKeyPressTime >= 6000L)
            {
                throw new ReportedException(new CrashReport("Manually triggered debug crash", new Throwable()));
            }

            if (!Keyboard.isKeyDown(46) || !Keyboard.isKeyDown(61))
            {
                this.debugCrashKeyPressTime = -1L;
            }
        }
        else if (Keyboard.isKeyDown(46) && Keyboard.isKeyDown(61))
        {
            this.actionKeyF3 = true;
            this.debugCrashKeyPressTime = getSystemTime();
        }

        this.dispatchKeypresses();

        if (this.currentScreen != null)
        {
            this.currentScreen.handleKeyboardInput();
        }

        boolean flag = Keyboard.getEventKeyState();

        if (flag)
        {

            Client.keyInput(i); //add

            if (i == 62 && this.entityRenderer != null)
            {
                this.entityRenderer.switchUseShader();
            }

            boolean flag1 = false;

            if (this.currentScreen == null)
            {
                if (i == 1)
                {
                    this.displayInGameMenu();
                }

                flag1 = Keyboard.isKeyDown(61) && this.processKeyF3(i);
                this.actionKeyF3 |= flag1;

                if (i == 59)
                {
                    this.gameSettings.hideGUI = !this.gameSettings.hideGUI;
                }
            }
            [...]

然后在 keyInput 函数中判断你想要设置的键位是否启用,比如 V 键,若启用则将布尔值函数设置为 true:

public static void keyInput(int key){  //event
    if(Keyboard.KEY_V==key){ //if key
        enabled=!enabled;  //set enabled
    }
}

然后再在 tick 函数中判断是否启用,启用则判断玩家是否是水平碰撞状态,若是,则判断玩家 Y 运动值是否小于 0.2,若小于,则设置为 0.2:

public static void tick(){  //event
    if(enabled){  //if enabled
        EntityPlayerSP player = Minecraft.getMinecraft().player;  //get player
        if(player!=null && player.isCollidedHorizontally){  //if != null and collided horizontally
            if(player.motionY<0.2){  //if < 0.2
                player.motionY = 0.2;  //set 0.2
            }
        }
    }
}

最后运行测试即可。

1.12.2客户端的反编译与运行打包及修改游戏标题和简易功能实现-第8张图片

Client.java 完整代码

package cn.ksmcbrigade;  //packet

import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import org.lwjgl.input.Keyboard;

public class Client {

    public static boolean enabled = false;

    public static void tick(){  //event
        if(enabled){  //if enabled
            EntityPlayerSP player = Minecraft.getMinecraft().player;  //get player
            if(player!=null && player.isCollidedHorizontally){  //if != null and collided horizontally
                if(player.motionY<0.2){  //if < 0.2
                    player.motionY = 0.2;  //set 0.2
                }
            }
        }
    }

    public static void keyInput(int key){  //event
        if(Keyboard.KEY_V==key){ //if key
            enabled=!enabled;  //set enabled
        }
    }
}