LaunchWrapper
Last updated
Was this helpful?
Last updated
Was this helpful?
1.6,Minecraft更换了游戏目录的结构,使得同一文件夹中支持多个版本共存。
为了配合这一变化,启动器中提供了从alpha、beta的大部分发布版本及1.0.0-1.5.2的正式版本,而这些版本注定是不支持新启动器与新目录结构的,用于修改老版本游戏兼容性的应运而生。
由于LaunchWrapper主要由Forge团队制作,其代码大量参考了FML Relauncher,LaunchWrapper也被用于FML在1.6-1.12.2的加载,从此摆脱了修改核心文件的安装方式。
LaunchWrapper不支持Java9及以上的版本。
类是LaunchWrapper的主类,在这个类中创建了LaunchClassLoader
的对象,初始化了ITweaker
(下称Tweaker)的列表:
在Launch
的构造器中,创建了LaunchClassLoader
的对象,LaunchClassLoader
是URLClassLoader
的子类,在创建过程中传入了当前使用的URLClassLoader
使用的URLs,并更改了当前线程的ClassLoader
,此后的类加载均会通过LaunchClassLoader
进行
由于从Java9开始,不再默认使用URLClassLoader
而是AppClassLoader
,在Java9及以上的版本这一行代码会发生ClassCastException
导致游戏,直到ModLauncher才修复了这一问题
在launch
方法中,首先从传入的参数中读取了Tweaker的类名
经过一番排除重复的操作后,便使用反射来实例化传入的Tweaker,并加入列表
遍历tweakers
列表调用接口ITweaker
的acceptOptions
将启动参数传递入Tweaker,以及injectIntoClassLoader
会传入使用的LaunchClassLoader
上面有一个非常细节的设计,遍历的过程中从tweakers
中移除已遍历的Tweaker,加入allTweakers
中,再配合上一个检测tweakClassNames
是否已空的循环,这样可以方便的让Tweaker在上述过程中动态加入新的Tweaker,而不需要在启动参数中指定所有的Tweaker
在初始化完所有的Tweaker后,从Tweaker中重新获得启动参数,需要注意的是,这个启动参数是所有Tweaker参数的简单合并,如果有重复参数可能会导致JOpt Simple无法按照Minecraft的要求读取参数而崩溃
最后调用第一个Tweaker的getLaunchTarget
方法获得目标启动类,并调用这个类的main方法
先把目光聚焦于loadClass
这个方法中,这个方法传入一个class名,需要返回Class
的实例,简而言之就是一个用于加载类的方法,在这个方法中需要完成两大任务——加载类、修改类
首先,classLoaderExceptions
列表记录了应当从原先的ClassLoader
中加载的类,transformerExceptions
记录了不应该被修改的类
对于一个可以被修改的class,会调用runTransformers
方法来对其进行修改,并返回Class
的对象
在runTransformers
方法中,依次调用Transformer对类进行修改,并返回修改后的类
以上我们可以得知两个关键信息——ITweaker
以及IClassTransformer
。
acceptOptions
,接收Minecraft启动参数,args
是LaunchWrapper尚未读取的参数,gameDir
是游戏路径,assetsDir
是assets
文件夹的路径,profile
是版本名称
injectIntoClassLoader
,参数classLoader
为将要用于加载Minecraft的LaunchClassLoader
对象,在这个方法中,可使用registerTransformer
方法进行注册IClassTransformer
等操作LaunchClassLoader
的行为
getLaunchTarget
,返回需要启动的主类,一般为net.minecraft.client.main.Main
,以第一个Tweaker的返回值为准
getLaunchArguments
,返回Minecraft参数,请注意需要返回Minecraft所需的所有参数
一个简单的ITweaker
实例如下:
IClassTransformer
是LaunchWrapper提供的接口,需要实现以下方法:
transform
,接收name
、transformedName
与basicClass
三个参数,分别是原类名、反混淆类名和class文件的二进制byte
数组,需要返回修改后的class文件的byte
数组
需要特别注意的是:
name
原类名和basicClass
class文件,在游戏运行时为notch混淆
transformedName
只有当IClassNameTransformer
存在时才会与name
不同,具体反混淆方式取决于IClassNameTransformer
的实现,例如FML会将其反混淆成mcp名称
basicClass
可能已被其他IClassTransformer
修改过
切记无论如何都要返回一个有效的byte
数组,否则会导致ClassNotFoundException
、NoClassDefFoundError
等导致的崩溃
一个没有对class进行任何修改的IClassTransformer
实现如下:
复制版本json到另一个版本文件夹中,并修改对应的名称
在libraries
中加入LaunchWrapper与自己编写的Tweaker,例如:
修改mainClass
为net.minecraft.launchwrapper.Launch
在arguments
中加入--tweakClass com.example.ExampleTweaker
FML与LiteLoader的1.6-1.12.2均支持加载Tweaker,只需要在Manifest中写入以下内容:
放入.minecraft/mods
文件夹即可
一般需要自带映射表或动态映射来进行版本兼容,不能保证运行时一定存在IClassNameTransformer
使用ModLoader和库文件挂载难以使用同一个Tweaker,处理参数的方式不同
参数中多个tweakClass
极有可能出错,Tweaker重复处理或都不处理启动参数均会导致Minecraft无法正常启动
库文件挂载较为麻烦,最终仍然需要依赖ModLoader
是LaunchWrapper加载Minecraft所使用ClassLoader
,在这个类中完成了类加载与transform
:
接口需要实现以下方法: