[MMS]《Fabric进阶开发指南》其一::访问加宽器

Fabric CN Wiki地址:fabricmc.net/wiki/zh_cn:tutorial:accesswideners
本教程使用 Yarn 映射,集成开发环境为 IntelliJ IDEA。

概述
关于访问加宽器是什么,Wiki 已经给出了通俗易懂的解释,并且对于它的使用时机也提供了一些建议,在这里我就用自己的话简单转述一下——
访问加宽器(Access Wideners)可以访问并修改已经存在或是已经编译好的类、方法、字段(即变量和常量)的访问权限,它弥补了 Mixin 无法修改或访问私有类以及 final 定义的字段的缺点。
关于访问加宽器的使用时机,Wiki 上也给出了建议:注入代码时应优先使用 Mixin,在遇到无法访问或修改的私有对象时才可使用加宽器。
为了更直观地方便理解,我在这里举一个例子——
你编写了一个名为 ExampleMenuScreen 的类,并让其继承 net.minecraft.client.gui.screen 包内的 Screen 类,你想在游戏运行时动态修改 Screen 内 drawableChild 的渲染顺序,最简单的方法就是对 Screen 类内的 drawables 进行重排序,但是当你看到 private final List<Drawable> drawables = Lists.newArrayList() 这条语句时你就已经知道,ExampleMenuScreen 类作为 Screen 类的子类是没有权限访问 drawables 这个字段的,final 修饰符也在说明它更无从修改。(这里排除自己定义一个新的 List<Drawable> 并实现 render() 方法)遇到这种类似情况时访问加宽器就可以将 drawables 变为 public 并删去 final 修饰符使其可以访问并修改。
相信很多 modders 看到 Wiki 也是一头雾水,接下来我将会按照操作顺序逐步讲解访问加宽器。
需求
Fabric-loader 0.8.0 或更高
Loom 0.2.7 或更高
开始开发!
1.新建 Access Widener 文件
访问加宽器的本体是一个后缀名为 .accesswidener 的文件,这个文件里定义了我们想要更改的类、方法或字段,该文件必须位于项目中的 /resources/ 文件夹中,与 fabric.mod.json 位于同一层级。
右键项目中的 /resources/ 文件夹,在【新建】侧拉菜单中单机【文件】,如图1.1。

键盘键入进行命名。文件名应为 modid.accesswidener,其中 modid 应为你的模组 ID(一说也称命名空间),如图1.2。

此时你的 /resources/ 文件夹根目录应该会有你新建的 .accesswidener 文件,此时它应该是空的,因为我们并没有向其中输入任何内容。
2.编写第一行
双击打开我们刚刚新建的 .accesswidener 文件,在文件首行键入:accessWidener v1 named,如图2.1。任何空白字符都会作为 .accesswidener 文件的分隔符,推荐制表符 Tab键。其中,named 可以换成 intermediary ,它们的区别是 named 为 Yarn 命名,即模组混淆命名空间(可以说是模组ID),intermediary 为中间命名,一般不常用。

3.将 .accesswidener 文件添加进项目编译
打开 build.gradle 文件,依据自己的 loom 版本在文件末尾添加代码块:
loom 0.9 或更高版本:
loom {
accessWidenerPath = file("src/main/resources/modid.accesswidener")
}
loom 0.8 或更低版本:
loom {
accessWidener = file("src/main/resources/modid.accesswidener")
}
,如图3.1。其中,file() 方法内的变量为你的 .accesswidener 文件的路径。

接下来打开 fabric.mod.json,在大括号内添加代码块:
"accessWidener": "modid.accesswidener"
,如图3.2。其中,"accessWidener" 为 JSON KEY,"visualnovellib.accesswidener" 即为 JSON VALUE,将它更改为你自己的 .accesswidener 文件名。

完成以上两步后你需要重新构建 Gradle 项目。点击 build.gradle 文件右上角的 旋 转 大 象 按钮(如图3.3),或是单击菜单栏中的【构建】,在下拉选单中单击【重新构建项目】(如图3.4),等待项目构建完成后再进行下一步。


4.正式编写 .accesswidener 文件
打开 .accesswidener 文件,就可以按照 Wiki 上的教程进行编写了。Wiki 上的教程非常详细,请移步那里去看,我在这里也不多说了,只将截图贴过来并讲解一些地方。
首先, .accesswidener 文件是支持注释的,按照 Wiki 页面上所说,如图4.1。

其次,对于类名包名的分隔格式 Wiki 也做了说明,如图4.2。这里要注意一点内部类的分隔方式的特殊性。

然后,访问加宽器支持类、方法和字段三种类型的权限修改,如图4.3。这里要注意几点——
第一,访问权是指你想要修改的结果,分别为 extendable(可继承)、accessible(可访问) 和 mutable(可修改),这三种访问权分别是什么意思请见图4.4。
第二,访问权虽各司其职,但可叠加。例如我们在概述举的例子中,drawables是一个 private final 字段,如果我们给其赋予 accessible 访问权,虽然 private 变成了 public,但是 final 修饰符仍然存在,我们依然无法修改 drawables,此时我们可以在 .accesswidener 文件中再定义一行,将 mutable 访问权赋予给 drawables 字段。
第三,<className> 即类名,它需要填写完整的包路径,并需严格遵守以上的分隔格式,例如 Screen 类位于 net.minecraft.client.gui.screen 包下,那么 <className> 应为 net/minecraft/client/gui/screen/Screen 。如果我们在 .accesswidener 文件首行使用了 intermediary 关键字而非 named 关键字,那么 <className> 应为中间名称,即 net/minecraft/class_3637 。
第四,<methodDesc> 即方法描述符并非如字面意思可以随意填写,很多人只看到了“描述”二字而并没有看到“符”字便以为这是可以随意填写描述的参数导致项目报错。其实这里的描述符是指Java中的描述符,在这里就是参数 <methodName> 的类的描述符。例如在 NamespaceResourceManager 类中的 validate() 方法为 Identifier 类型,那么 <methodDesc> 应为 (Lnet/minecraft/util/Identifier;)V ,注意括号、分号和大写字母 L 和 V,这是和下面的字段描述符所不同的地方,并且你应该会很熟悉这个格式,没错,这是在编写 Mixin 时所使用的类似的格式。
第五,<fieldDesc> 即字段描述符,同上第四,例如在 Screen 类中的 drawables 字段类型为 List<Drawable> ,那么 <fieldDesc> 应为 Ljava/util/List; ,尖括号内的 Drawable 为 List 的泛型,不用管他,要注意分号和大写字母 L。


5.重生成Minecraft源代码以应用访问加宽器
在编写完 .accesswidener 文件后我们还需使它生效。单击最右侧边栏的【Gradle】选项卡,并在 modid/Tasks/fabric 中双击【genSources】,重生成访问加宽后的Minecraft源代码,在该 Gradle 任务跑成功后你应该能够在反编译的源码中看到你在 .accesswidener 文件中定义的类、方法或字段已经访问加宽了,接下来你就可以对其为所欲为访问修改了。

一个 .accesswidener 文件的例子
这里有一个 .accesswidener 文件的编写例子可供你参考(取自Lazurite Dev Wiki:lazurite.gitbook.io/lazurite-dev-wiki/modding-knowledge/access-wideners)——
1. accessWidener v1 named
2. # Classes
3. accessible class net/minecraft/client/render/model/WeightedBakedModel$Entry
4. extendable class net/minecraft/util/profiler/ProfilerTiming
5. # Methods
6. extendable method net/minecraft/resource/NamespaceResourceManager validate (Lnet/minecraft/util/Identifier;)V
7. accessible method net/minecraft/resource/NamespaceResourceManager validate (Lnet/minecraft/util/Identifier;)V
8. # Fields
9. accessible field net/minecraft/client/render/model/WeightedBakedModel models Ljava/util/List;
10. mutable field net/minecraft/resource/ResourceType directory Ljava/lang/String;
11. mutable field net/minecraft/resource/DefaultResourcePack LOGGER Lorg/apache/logging/log4j/Logger;
结束
以上就是本期访问加宽器(Access Widener)的所有内容了,自己去试试吧,再会!

.V2x5aHV2.
.Minecraft Modding Style.