1.6.1-1.12.2
Last updated
Was this helpful?
Last updated
Was this helpful?
1.6 修改了游戏文件的结构,单一.minecraft文件夹内可以同时使用多个游戏版本
1.6 引入
FML不再通过直接修改游戏的核心文件进行安装,改为先加入libraries,再通过传递参数--tweakClass
使用LaunchWrapper加载FML并对Minecraft进行binpatch
FML开始通过LaunchWrapper提供的IClassNameTransformer
进行运行时反混淆,将运行时的混淆方式从notch动态的反混淆成了srg
CoreMod不再通过FML提供的RelaunchClassLoader
对class进行动态修改,换用LaunchWrapper对应的LaunchClassLoader
取消了coremods
文件夹,CoreMod可以被直接放入mods
文件夹,CoreMod也可以直接包含普通Mod了
1.7 引入,Forge开发脱离了MCP工具
1.7 MCP对Minecraft的类进行分包,不再存放于net.minecraft.src
1.8 FML变更了包名,从cpw.mods.fml
改成了net.minecraftforge.fml
(对加载流程不感兴趣的话可以直接跳过这一部分,以下代码来自1.12.2FML)
Forge官方没有给出CoreMod的教程以及文档,我们需要通过探索FML如何加载CoreMod来得出制作的方法。
在中,FML由以下方式加载CoreMod:
discoverCoreMods
方法中,获取Mod文件列表,遍历每个Mod文件
读取Mod文件jar中的Manifest
通过Manifest中的FMLCorePlugin
属性存在与否来判断是否为CoreMod,如果不是则检测下一个Mod文件
将CoreMod加入ClassPath,并进行加载
loadCoreMod
方法中,取得IFMLLoadingPlugin
实例
将IFMLLoadingPlugin
装饰为FMLPluginWrapper
,加入列表
injectCoreModTweaks
方法中,FMLPluginWrapper
作为ITweaker
的子类直接加入LaunchWrapper的Tweaks列表中
FMLPluginWrapper
会被LaunchWrapper调用:
injectIntoClassLoader
方法中,首先调用getASMTransformerClass
获取IClassTransformer
对应的class名称,使用TransformerWrapper
进行装饰并向LaunchWrapper注册
LaunchWrapper会在每个类被加载进ClassLoader之前调用IClassTransformer
的transform
方法,通过这一方法,便可运行时动态修改其他class。
(以下代码适用于1.8-1.12.2,1.6.2-1.7.10需要变更FML包名,1.6.2-1.6.4需要变更Minecraft包名)
通过上文对FML CoreMod加载方式的分析,我们可以得到三个关键内容——Manifest、IFMLLoadingPlugin
与IClassTransformer
,以下逐个介绍。
Manifest有清单的意思,在这里指的是jar中的META-INF/MANIFEST.MF
文件,可以通过修改build.gradle
自动在打包时加入:
其中:
FMLCorePlugin
属性是IFMLLoadingPlugin
实现类的完整类名
FMLCorePluginContainsFMLMod
属性标记CoreMod的jar中是否还含有普通的Mod,如果为false,FML不会尝试寻找@Mod
注解并加载普通Mod
读者可能会注意到,jar
是会在build
等task打包时才会被执行,在runClient
之类的测试运行时并不会被读取到,如果需要在测试运行时使用CoreMod,还需要在build.gradle
中加入jvm参数的设置:
IFMLLoadingPlugin
是FML提供的一个接口,需要实现以下几个方法:
getASMTransformerClass
,返回IClassTransformer
的完整类名构成的数组,这是CoreMod的关键
getModContainerClass
,返回ModContainer
的实现类的完整类名,可空
getSetupClass
,返回IFMLCallhook
的实现类完整类名,可空
injectData
,可以获得mcLocation
、coremodList
与coremodLocation
,分别是Minecraft文件夹File
、CoreMod列表List
、当前CoreMod文件File
。这个方法与IFMLCallhook
中的injectData
主要区别为这个方法是在Minecraft启动后调用的,可以操作Minecraft的class
getAccessTransformerClass
,返回一个AccessTransformer
实现类完整类名,可空,一般可以直接使用FML提供的访问级转换器功能,无需自定义
同时,提供了以下几个注解来进行信息补充:
TransformerExclusions
,指定不被CoreMod修改的包名前缀数组,例如指定了com.example
后,com.example.something.Example
也不会被修改
MCVersion
,指定CoreMod适用的Minecraft版本
Name
,指定CoreMod的名称,如果不指定,会直接使用类名作为名字
DependsOn
,指定依赖
SortingIndex
,指定被调用的顺序
一个简单的IFMLLoadingPlugin
实现如下:
IClassTransformer
是LaunchWrapper提供的接口,相当于字节码修改器,需要实现以下方法:
transform
,接收name
、transformedName
与basicClass
三个参数,分别是原类名、mcp无混淆类名和class文件的二进制byte
数组,需要返回修改后的class文件的byte
数组
需要特别注意的是:
name
原类名和basicClass
class文件,在游戏运行时为notch混淆,开发环境测试时为mcp混淆
basicClass
可能已被其他CoreMod甚至Forge本身修改过
切记无论如何都要返回一个有效的byte
数组,否则会导致ClassNotFoundException
、NoClassDefFoundError
等导致的崩溃
一个没有对class进行任何修改的IClassTransformer
实现如下:
一个对net.minecraft.client.gui.GuiPlayerTabOverlay
的func_175249_a
(srg) renderPlayerlist
(mcp)方法进行修改的实例:
在LaunchWrapper中的ITweaker
调用行为与一致,会依据顺序逐个调用ITweaker
。
如果读者阅读过的相关内容,会发现制作方法大同小异,无非是使用LaunchWrapper相关的类进行对应。