上一篇我们讲了如何设置开发环境并导入Forge 1.20.1模板项目。这一篇我们将配置模组的基本信息(如名称、版本号等),并在构建脚本中进行基础的配置。此处,我们暂且将模组命名为Forge Tutorial。
1. 模组主类
1.1 @Mod
注解、模组ID
模组主类是Forge识别和加载模组的入口。打开源码的ExampleMod类,可以看到类声明上方有注解:
@Mod(ExampleMod.MODID)
@Mod
注解表示该类为一个模组的主类。一个模组必须有且仅有一个主类。
参数ExampleMod.MODID
(类内有定义)为该模组的ID。模组ID是一个字符串,用于在所有加载的模组中识别特定模组,在所有模组中必须唯一。这意味着不能将ID设置得过于简单,以免与其他模组撞车。例如模组Alex’s Mobs,ID为"alexsmobs"
,这是一个较不容易重名的ID;而"am"
就是一个糟糕的ID,极易重名。模组ID只能包含小写字母、数字和下划线。这里我们将其改为"forge_tutorial"
。
另外,我们将主类名称和路径Shift+F6更改为net.sodiumzh.tutorial.ForgeTutorial
以匹配模组名称。
Tip:不建议在@Mod
注解中直接用引号内容定义模组ID字符串,而应当用一个定义在主类中的静态常量代替(此处为ForgeTutorial.MODID
)。即:不要使用@Mod("forge_tutorial")
。在模组的其他任何地方需要引用模组ID时,也建议引用该常量而非直接输入引号内容,以免出现难以排查的字符串错误。其他需要经常被引用的字符串(注册表键等)也建议定义常量而非在每个使用处输入引号内容。
1.2 主类的构造方法
主类的构造方法是Forge加载一个模组时最初运行的方法,用于执行一些将本模组内容(注册表项等)最终注册到Forge中的操作。
模板项目的默认主类中有大量的示例,这些内容我们以后会提到,现在暂时将除了MODID
和构造方法之外的所有内容都注释掉。目前没有需要注册的内容,因此暂且将构造方法中的所有内容也注释掉,仅保留一行:
MinecraftForge.EVENT_BUS.register(this); // 将本模组注册到Forge事件总线中
2. Gradle构建脚本
Gradle构建脚本用于配置将模组源码打包为jar文件的方法。Gradle构建脚本配置往往是最劝退新modder的地方。我已经见过不止一个想入坑MC模组制作的人卡在Gradle脚本配置上并最终无奈放弃。由于我对Gradle也不是很熟悉,下面仅介绍必要的修改。
Gradle配置脚本为build.gradle
,位于项目根目录下。Forge 1.20.1相比1.19大幅度简化了Gradle的修改,将绝大多数需要修改的参数移动到了gradle.properties
中,而build.gradle
引用这些参数。然而,添加项目依赖(第4节会讲)时仍然需要修改build.gradle
。
2.1 模组参数修改、gradle.properties
gradle.properties
用于为构建脚本中定义的参数赋值。
2.1.1 模组信息
翻到文件最下方Mod Properties。
涉及运行必须修改的是mod_id
,改成刚才设置的模组ID(forge_tutorial
)。
mod_group_id
:改成模组所有类的公共路径。例如如果所有的java文件都位于com.sodiumzh.tutorial
或其子包下,则改成com.sodiumzh.tutorial
。
其他条目是用于在模组界面显示的,视情况修改。
2.1.2 映射
此处的映射(mapping)指从Minecraft混淆源码到人类可读源码的映射。(下2段如果看不懂可以跳过)
未修改的Minecraft源代码是被混淆(obfuscate)过的,其类名、方法名、变量名等为无意义字符串(如aabd
,暂且称为“混淆名”)。在Minecraft Forge游戏中加载的类名、方法名、变量名已经从混淆名映射为编号,例如方法名是像m_12345_
这样的。这种编号名称叫做SRG名。混淆名和SRG名之间的映射为自动进行,通常不需要考虑。
由于SRG名的可读性仍然差,我们还需要一个映射来将SRG名进一步“翻译”为人类可读名称,这一步也是自动进行的。这种可读名称叫做MCP名。我们写模组的时候需要打交道的绝大多数都是MCP名,只有少数需要考虑SRG名的情况。
以上过程是不需要配置的。如果我们打开项目依赖中的forge源码(位于Gradle: net.minecraftforge:forge:1.20.1-...
)的类,会发现虽然类、方法和变量名已经可读,但方法参数等仍然为编号,这会对我们阅读原版代码造成一定困难。我们需要修改的映射即用于处理该情况。
Parchment是一个非官方映射,将方法参数名修改为了人类可读名称,并添加了一些注释。不同MC版本对应的Parchment版本可以在这里查到:https://parchmentmc.org/docs/getting-started
以下导入Parchment:
找到gradle.properties
的mapping_channel
和mapping_version
,改成:
mapping_channel=parchment
mapping_version=2023.09.03-1.20.1
在build.gradle
最上方的plugins
块内加一行:
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
在settings.gradle
(位于项目根目录)的pluginManagement
块中的repositories
块内加一行:
maven { url = 'https://maven.parchmentmc.org' }
gradle.properties
中的其他参数暂时不需要修改。
3. mods.toml
mods.toml
用于向Forge提供该mod的信息。不同于build.gradle
,mods.toml
的内容是会保留到运行时的。即在将源码编译打包为.jar
包后,mods.toml
仍然保留。游戏启动时Forge时会读取所有模组包中的mods.toml
文件以收集模组信息。而build.gradle
仅影响编译打包的过程,打包成.jar
后build.gradle
不再保留。
打开src/main/resources/META-INF/mods.toml
,可以看到其中的值为参数引用。这些引用也指向gradle.properties
。目前不需要修改。
4. 增加项目依赖
项目依赖是在模组编辑和测试过程中需要依赖的外部包,包括其他模组。
加入项目依赖后,这些外部包的代码可以被调用,测试时也会加入测试游戏的模组列表,但不会打包进.jar
包(除非特殊配置,此处不考虑)。因此如果在代码中使用了这些外部包(类、方法、数据等),需要考虑这些外部包不存在时的情况(此处不展开说)。
4.1 加入JEI依赖
JEI(Just Enough Items)是一个非常常用的显示道具和配方的模组,Forge已经将其预设在了build.gradle
中,位于dependencies
块中,但是是注释掉的。直接把注释符号删掉即可导入JEI依赖。
JEI依赖的代码中引入了两个参数:mc_version
和jei_version
。mc_version
就是MC版本,直接改成minecraft_version
即可;jei_version
是JEI的版本,此前没有定义,需要到gradle.properties
中定义。在gradle.properties
中加一行:
jei_version=15.20.0.106
此外我们需要定义JEI所在的远程仓库(repository)。在repositories
块中加入:
maven {
name = "Jared Maven"
url = "https://maven.blamejared.com/"
}
maven {
name = "ModMaven"
url = "https://modmaven.dev"
}
这样在构建时,程序会在这两个仓库中查找JEI。
4.2 加入其他模组依赖
以下我们以JER(Just Enough Resources)为例引入一个在CurseForge上能下载到的模组依赖。JER是一个常用的用于显示道具掉落来源的实用模组。
首先在repositories
中加入Curse Maven(可以理解为CurseForge上能下载到的mc模组所在的仓库)。加入以下代码:
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
随后在CurseForge上找到需要引入的包(可以在mcmod.cn查找并跳转,不赘述)。例如,JER Forge 1.20.1的其中一个版本位于:https://www.curseforge.com/minecraft/mc-mods/just-enough-resources-jer/files/5057220
进入该网页,找到File Details -> Curse Maven Snippet(点右侧小三角展开)-> Forge,复制其下的代码到build.gradle
的dependencies
块中:
implementation fg.deobf("curse.maven:just-enough-resources-jer-240630:5057220")
执行Gradle genIntellijRuns
任务,随后刷新项目(见上一章第2节,会花一些时间下载依赖,建议挂代理)。调试运行runClient
,可以发现JEI和JER已经出现在模组列表中了。