本篇教程由作者设定未经允许禁止转载。

介绍:

AnotherCommonBugFix(ACBF)通过加载.minecraft/fixResource目录中的修复包的方式修复部分Mod中的bug。


修复包的格式:

只有.zip文件并且文件内包含/transform.cfg时才会被识别为修复包


transform.cfg文件格式:

transform.cfg支持4种修复方式,格式分别为:

    被替换的类的全限定名 替换用的文件的路径

        e.g.    forestry.core.inventory.ItemInventory classResource/ItemInventory.class

    被转换的类的全限定名 实现转换操作的js文件的路径

        e.g.    forestry.core.inventory.ItemInventory jsTransform/ItemInventoryTransform.js

    被转换的类的全限定名 实现转换操作的类的全限定名

        e.g.    forestry.core.inventory.ItemInventory com.transform.ItemInventoryTransform

    实现转换操作的类的全限定名

        e.g.    com.transform.ForestryTransform

此外,transform.cfg还支持注释和打印日志,注释以#开头,在修复配置上方最近的一行注释会被带到配置文件中,输出日志以info:开头。


替换Class的修复法:

此方法的配置格式为:

    被替换的类的全限定名 替换用的文件的路径

这里用林业信件bug举例,首先下载林业mod及其源码,已知林业信件bug的刷取方法是

    准备两个信件,在打开第一封信件的同时切换到第二封

查看相关代码可知,林业通过nbt中的"UID"项区分不同的信封

制作ACBF修复包-第1张图片制作ACBF修复包-第2张图片

但是通过blockdata指令查看信封nbt发现,"UID"项是空字符串

制作ACBF修复包-第3张图片

然后找到信件保存相关代码

制作ACBF修复包-第4张图片

发现"UID"相在其他地方都是以String格式保存和读取的,只有在生成和比较的时候是以int格式进行的,这就导致生成的int格式的"UID"项在保存时变成了空字符串,在比较时由于"UID"相已经变成String格式,按照int格式读取只会返回0所以造成了这个bug。

修复方式很简单,只要把生成和比较"UID"的地方也改成String格式即可

制作ACBF修复包-第5张图片制作ACBF修复包-第6张图片

然后按照源码中的README.md给出的指令编译mod,将编译完的mod中修改过的Class提取出来,制作修复包

制作ACBF修复包-第7张图片制作ACBF修复包-第8张图片

此方法是最原始的修复方法,ACBF最初就支持的方法,但是此方法兼容性差,编译修复Class时需要依赖被修复mod,被修复mod的版本变动很容易就使修复包失效,未来版本可能会废弃此方法,不推荐使用。


通过js转换Class的修复法:

此方法的配置格式为:

    被转换的类的全限定名 实现转换操作的js文件的路径

这里用匠魂的工具装配台bug举例,首先下载匠魂mod及其源码,已知匠魂bug的刷取方法是

    两个玩家同时打开装配台界面,其中一个玩家改名,另一个玩家取出工具即可获得两份工具

分析刷取方法可知,这是一个和改名有关的bug,定位到改名相关代码

制作ACBF修复包-第9张图片

由源码可知,玩家A改名后修改了自己客户端的ContainerToolStation中的工具名并发包到服务端,服务端接收到数据包以后修改了玩家A在服务端的ContainerToolStation中的工具名并通过发包修改了所有打开此装配台Gui的其他玩家的客户端的ContainerToolStation中的工具名,但是却没有修改其他玩家在服务端的ContainerToolStation中的工具名,这就导致其他玩家取出物品以后服务端错误判断为此次操作无效,导致不消耗原材料,获得了两份工具。

修复方法也很简单,只要让服务器接到玩家发送的修改工具名的包的时候把其他玩家在服务端的ContainerToolStation中的工具名也改了就好了,改完后的代码如图

制作ACBF修复包-第10张图片

只要在发送给玩家数据包的同时把玩家在服务端的ContainerToolStation中的工具名也改了就好了,所以需要通过js的转换把这条方法调用添加到发包方法调用的后面,具体代码如图

制作ACBF修复包-第11张图片基本思路是找到方法对应的MethodNode节点,在MethodNode中找到发包方法调用对应的字节码节点,然后在这个节点之后插入((ContainerToolStation) player.openContainer).setToolName(text);对应的字节码。

人工生成字节码过程如下:

  1. 首先需要获取局部变量player,进行局部变量栈分析,0号位和1号位分别是this引用和方法参数,2号位和3号位是container和server,由于有foreach语法,所以4号位存放着一个隐藏变量:server.playerEntities的迭代器,所以player在5号位,所以第一条字节码就是ALOAD 5。

  2. 然后要获取player的字段:openContainer,由于混淆的原因,这里不能直接获取openContainer,openContainer在运行时的真正名字是field_71070_bA,混淆表可以在C:\Users\<用户名>\.gradle\caches\minecraft\de\oceanlabs\mcp\mcp_snapshot\<版本>\1.12.2\srgs\mcp-srg.srg中找到,所以第二条字节码就是GETFIELD net/minecraft/entity/player/EntityPlayer field_71070_bA Lnet/minecraft/inventory/Container;

  3. 然后进行强制类型转换CHECKCAST slimeknights/tconstruct/tools/common/inventory/ContainerToolStation

  4. 然后获取text字段ALOAD 0    GETFIELD slimeknights/tconstruct/tools/common/network/ToolStationTextPacket text Ljava/lang/String;

  5. 然后调用setToolName方法INVOKEVIRTUAL slimeknights/tconstruct/tools/common/inventory/ContainerToolStation setToolName (Ljava/lang/String;)V

然后制作修复包

制作ACBF修复包-第12张图片

此方法由于类加载机制,只能使用ASM Tree API,无法使用ASM Core API,而且缺乏静态类型检查,但是此方法不依赖被修复的mod,版本兼容性好,无需编译,适合没有安装Java开发环境的人低门槛制作修复包。


通过Java代码转换Class的修复法:

此方法的配置格式为:

    被转换的类的全限定名 实现转换操作的类的全限定名

这里用热力膨胀储物箱bug举例,首先下载热力膨胀mod及其源码,已知储物箱bug的刷取方法是

    副手拿要刷的物品,主手拿工业扳手右键储物箱

根据刷的办法,定位到相关代码

制作ACBF修复包-第13张图片根据源码可知,获取物品时会获取正在使用的手中的物品,但是当物品放入储物箱成功以后删除手中物品时,只会删除主手物品,估计是从低版本升上来的时候忘记改了,解决方法很简单,把player.inventory.setInventorySlotContents(player.inventory.currentItem, ret);改成player.setHeldItem(hand, ret);即可。

制作ACBF修复包-第14张图片操作步骤和之前类似,先定位,再人工生成字节码,这次用ASM Core API举例,定位到方法调用字节码以后将调用所需的3个参数从栈中弹出,然后将调用修改后方法所需的参数压入栈中,再插入方法调用字节码,最后直接返回。

需要注意的是方法签名必须是byte[] transform(byte[])

此方法相比于js修复法,支持了ASM Core API,并且有静态类型检查,但是需要编译,建议安装了Java开发的人通过这种方法制作修复包。


通过带注解的Java代码转换Class的修复法:

此方法的配置格式为:

    实现转换操作的类的全限定名

要转换的类的全限定名通过方法上的注解给出,注解的全限定名是com.anotherera.core.Transform,要用此方法制作修复包必须添加ACBF的API,不限定转换方法的名字。

制作ACBF修复包-第15张图片

此方法把要转换的类的全限定名从transform.cfg中转移到了方法注解上,使一个类中可以有多个转换方法,适合涉及多个Class的bug的修复。


定位相关代码的一般方法:

这里用林业信件bug举例

首先在游戏中找到bug物品

制作ACBF修复包-第16张图片然后再源码中搜索物品id

制作ACBF修复包-第17张图片然后就可以快速找到相关代码了


远程调试方法及VS Code调试配置:

想要准确定位bug位置最好的办法还是调试,但是有些mod由于依赖过多等原因不方便在开发环境中直接调试,这时候就可以用远程调试。

在客户端设置中的JVM参数中加入

    -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000

即可

制作ACBF修复包-第18张图片

然后找个能远程调试的软件,这里以VS Code为例,按下图配置VS Code制作ACBF修复包-第19张图片

配置完成后启动游戏,点击开始调试即可开始远程调试


部分资源下载地址:

AnotherCommonBugFix(https://www.mcmod.cn/download/1697.html)

VS Code(https://code.visualstudio.com/)

Eclipse(https://www.eclipse.org/downloads/packages/)

JD-GUI(https://jd-gui.apponic.com/)

Luyten(https://github.com/deathmarine/Luyten/releases)