提示

上期模组开发教程:ModuleManager与配置文件及简易功能实现

AccessTransformer访问转换器的使用

简绍

AccessorTransformer 被简称为 AT,它能够修改指定字段或方法的访问修饰符以及是否被 final 修饰,它与 mixin 不同的是它可以访问被 protected 修饰符所修饰的变量或函数。

使用

首先需要先在 build.gradle 文件的 minecraft 项中添加以下内容,并在模组资源目录的 META-INF 目录中新建文件 accesstransformer.cfg:

minecraft {

    [...]

    accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
}

最后重新构建项目即可。

注意:当模组在非开发环境中加载时,Forge 只会加载模组文件中的 META-INF/accesstransformer.cfg AT配置文件。

在AT的配置文件中使用的是 srgName,我们可以使用 mixin 的特性来获取 srgName,例如位于 Minecraft 类中的 timer 变量,该变量用于控制游戏每秒执行的操作数量。

package cn.ksmcbrigade.em.mixin;

import net.minecraft.client.Minecraft;
import net.minecraft.client.Timer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(Minecraft.class)
public interface MinecraftMixin {
    @Accessor("timer")
    Timer getTimer();
}
{
  "required": true,
  "minVersion": "0.8",
  "package": "cn.ksmcbrigade.em.mixin",
  "compatibilityLevel": "JAVA_8",
  "refmap": "em.refmap.json",
  "mixins": [
  ],
  "client": [
    "MinecraftMixin"
  ],
  "injectors": {
    "defaultRequire": 1
  }
}

这里使用 Accessor 来注解以获取指定变量,若要执行指定函数需要使用 Invoker 来注解。

然后运行 build 任务来导出模组文件,最后通过解压软件打开模组文件中的 modid.refmap.json 即可查看到指定变量的srgName:

{
  "mappings": {
    "cn/ksmcbrigade/em/mixin/MinecraftMixin": {
      "timer": "f_90991_:Lnet/minecraft/client/Timer;"
    }
  },
  "data": {
    "searge": {
      "cn/ksmcbrigade/em/mixin/MinecraftMixin": {
        "timer": "f_90991_:Lnet/minecraft/client/Timer;"
      }
    }
  }
}

其中的 f_90991_ 即为 srgName,然后将需要被转换成的访问修饰符和类路径和 srgName 填入 AT 配置文件即可。

public-f net.minecraft.client.Minecraft f_90991_

其中的 -f 的意思为去掉 final 修饰符,因为 timer 变量是被 final 所修饰的,若要加上 final 修饰符,将 -f 改为 +f 即可。

提示:该方法对于函数依旧可以使用。

简易功能实现

Timer

该功能可以修改游戏的运算速度,首先在 modules 目录中创建 Timer 类,并继承 Module,然后重写 enabled 和 disabled 函数:

import cn.ksmcbrigade.em.Module;

import java.awt.event.KeyEvent;

public class Timer extends Module {
    public Timer() {
        super("Timer", KeyEvent.VK_Y); //key v
    }

    @Override
    public void enabled() throws Exception {
        super.enabled();
    }

    @Override
    public void disabled() throws Exception {
        super.disabled();
    }
}

然后再在 enabled 函数和 disabled 函数中修改 timer 变量:

@Override
public void enabled() throws Exception {
    Minecraft.getInstance().timer = new net.minecraft.client.Timer(50,0L); //set 50 (2.5)
}

@Override
public void disabled() throws Exception {
    Minecraft.getInstance().timer = new net.minecraft.client.Timer(20,0L);  //set 20
}

Timer 的构建函数的第二个参数默认填 0L 即可,这里的 50 为 2.5 倍数,20 为默认倍数。

Pegasus

简绍

该功能可以使在马上的玩家飞行,无视是否已穿戴马鞍和是否已被驯服。

灵感来源:BoatFly,AirJump。

实现

首先在 modules 目录中创建 Pegasus 类,并继承 Module,然后重写 uodate 函数:

package cn.ksmcbrigade.em.modules;

import cn.ksmcbrigade.em.Module;

import java.awt.event.KeyEvent;

public class Pegasus extends Module {
    public Pegasus() {
        super("Pegasus", KeyEvent.VK_I); //key i
    }

    @Override
    public void update() throws Exception {
        super.update();
    }
}

然后再在 update 函数中判断玩家是否有坐骑和坐骑是否可以被转换为 AbstractHorse 类以及空格键是否被按下,若都符合则设置坐骑的马鞍存在状态和是否正在跳跃为 true,疾跑计数器 为 7 ,然后使坐骑跳跃:

@Override
public void update() throws Exception {
    Minecraft MC = Minecraft.getInstance();
    Player player = MC.player;
    if(player==null){
        return;
    }
    if(player.getVehicle()==null){
        return;
    }
    if(!MC.options.keyJump.isDown()){
        return;
    }
    Entity vehicle = player.getVehicle();
    if(vehicle instanceof AbstractHorse horse){
        horse.setFlag(4,true);
        horse.sprintCounter = 7;
        horse.setIsJumping(true);
        horse.jumpFromGround();
    }
}

其中的 setFlag 函数和 jumpFromGround 函数需要用到访问转换器,他们分别位于 AbstractHorse 和 LivingEntity 类中。

public net.minecraft.world.entity.LivingEntity m_6135_()V
public net.minecraft.world.entity.animal.horse.AbstractHorse m_30597_(IZ)V

注册功能

然后在 ModuleManager.modulesClass 类中注册即可:

public static Module Timer = new Timer();  //key y
public static Module Pegasus = new Pegasus(); //key i

最后运行测试即可。

1.18模组开发之访问转换器的使用和简易功能实现-第1张图片

完整代码

accesstransformer.cfg

public-f net.minecraft.client.Minecraft f_90991_
public net.minecraft.world.entity.LivingEntity m_6135_()V
public net.minecraft.world.entity.animal.horse.AbstractHorse m_30597_(IZ)V

Timer.java

package cn.ksmcbrigade.em.modules;

import cn.ksmcbrigade.em.Module;
import net.minecraft.client.Minecraft;

import java.awt.event.KeyEvent;

public class Timer extends Module {
    public Timer() {
        super("Timer", KeyEvent.VK_Y);
    }

    @Override
    public void enabled() throws Exception {
        Minecraft.getInstance().timer = new net.minecraft.client.Timer(50,0L);
    }

    @Override
    public void disabled() throws Exception {
        Minecraft.getInstance().timer = new net.minecraft.client.Timer(20,0L);
    }
}

Pegasus.java

package cn.ksmcbrigade.em.modules;

import cn.ksmcbrigade.em.Module;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraft.world.entity.player.Player;

import java.awt.event.KeyEvent;

public class Pegasus extends Module {
    public Pegasus() {
        super("Pegasus", KeyEvent.VK_I);
    }

    @Override
    public void update() throws Exception {
        Minecraft MC = Minecraft.getInstance();
        Player player = MC.player;
        if(player==null){
            return;
        }
        if(player.getVehicle()==null){
            return;
        }
        if(!MC.options.keyJump.isDown()){
            return;
        }
        Entity vehicle = player.getVehicle();
        if(vehicle instanceof AbstractHorse horse){
            horse.setFlag(4,true);
            horse.sprintCounter = 7;
            horse.setIsJumping(true);
            horse.jumpFromGround();
        }
    }
}

ModuleManager.modulesClass

static class modulesClass {
    public static Module NoFall = new NoFall("NoFall", KeyEvent.VK_N);
    public static Module XYZ = new XYZ("XYZ", KeyEvent.VK_B);
    public static Module Timer = new Timer();  //key y
    public static Module Pegasus = new Pegasus(); //key i
}