本篇教程的视频

本篇教程的源代码

GitHub地址:TutorialMod-1.20.1-Forge-Start

前言

和前面的教程一样,我们就来一个简短的前言

本系列教程和后面即将开行的NeoForge教程一样,不再像Fabric,通过讲解源代码来做教程,
而是回到那种它们的文档怎么写的,那么我们就怎么写的状态

当然,我会穿插关于源代码的部分,有些地方还是会用上的

ForgeNeoForge的话,并不太适合通过源代码来进行模组开发教程

一方面,它们有自己独立的注册系统,并不像Fabric那样可以直接用原版的注册系统,Fabric的运作逻辑和它们两个并不一样

另一个方面,根据MinecraftEULA协议,是不能公开他们的源代码的,因为ForgeNeoForge现在采用了Mojang的官方映射,
我通过源代码来做教程有风险,虽然教程可能是个特殊的领域,但小心为妙,万一哪一天我被制裁了

不过,既然都说到这里了,我们就来聊聊这三端的运作逻辑

Fabric,最轻量级,因为它本身是一个Mixin,通过Mixin来将模组注入到游戏中

Mixin的话,最早由Sponge项目提出的,在Fabric中广泛运用,Mixin是一种更轻量、模块化、易维护的修改方式

我们现在在Fabric中用的Mixin,也依旧是Sponge

Mixin对游戏的破坏很小,所以游戏运行较为稳定

Fabric缺点就在于它没有像ForgeNeoForge那样那么多的API
很多模块需要借助第三方API,或者自己手动去写

所以可能会造成大型模组首选ForgeNeoForge这样的印象,不过,你愿意折腾,Fabric也是完全可行的

Forge的话,它的运作依赖于它的Patch系统,patch,那么翻译过来就是补丁,它是从字节码的层面来将模组加载进游戏的

patchMixin相比,它更复杂,而且无法做到像Mixin那样模块化、细致化的修改,更难维护

它是有可能破坏游戏某些代码,所以它的稳定性相比较于Fabric就没那么好

虽然Forge在后面引入了Mixin来代替Patch系统的一部分,但主要还是依赖于Patch系统

NeoForge,从Forge中分离出来,是Forge的重构版,因为原先的Patch系统过于老旧难以维护
NeoForge模块化了原来的Patch系统,虽然仍然延用Patch,但模块化的设计也更易于开发和维护,
同样也使用Mixin,更加简洁现代化

它的稳定性优于原本的Forge,不过因为附带很多其他内容,所以体积较大,启动时间较长(但还是比Forge快)

另外呢,还是那句老话,模组开发,通其一,便可通其二,你学会一个端的,另外两端也可以直接上了

再把基础内容过一遍,上手很快的

准备工作

那么从本期教程开始,我们正式的开始,这个1.20.1 Forge模组的开发教程

Forge的教程就止步于1.20.1,在1.21,我们还将开行1.21.1 NeoForge长线教程计划

在高版本上的话,应该是NeoForge用得更多

那么第一期教程的话,和以往一样,我们就来进行开发环境的配置

JDK17

那么我们的Minecraft的话,它是拿Java去写的

而我们要进行开发的话,就得装一个JDK

这个JDK的全称的话就是Java Development Kit(开发工具包),简称JDK

大家学Java的时候的话,应该也都是装过这个东西的

不管是8也好,还是17也好,还是21,应该都是装过这个东西的

1.20.1它这个版本,它应该是拿Java 17去写的,所以说我们安装的版本的话,应该要装的是这个JDK17

当然你装JDK21其实也没有问题,它这个版本的话,主要你得看那个模板文件里面,它用的那个Gradle的版本

关于它的下载的话,我这里提供Oracle官网上的Java下载

链接:Oracle

链接里点进去,再往下翻,就能翻到JDK17的下载
Oracle官网

它分三个端,三个端的不同的安装程序,下载你自己对应系统

这里我们以Windows为例

那么它还分一些不同的安装方式,有三个东西

其中一个是zip,这是压缩文件,它是已经完全安装好的Java,你可以下载解压之后,放到你自己的Java目录中

另外两个exemsi,这两个的话都是安装程序,可以自己选择下载安装

安装的话,默认是安装到C盘的,其实也不用改安装路径,IDEA会默认到C盘找你的Java

当然你也可以选择安装到其他位置,只要你自己找得到就行

不过,在Oracle上下载JDK17的话,现在是要注册登录的,所以说你下载的话,得先注册一个Oracle的账户

嫌麻烦的话,你也可以去百度搜索一下,JDK17的下载,有很多其他的网站

IDEA

IDEA的全称是IntelliJ IDEA,是JetBrains公司的一款Java IDE

那么现在我们就用它作为我们模组开发的编辑器,其他编辑器当然也可以,只是我个人建议使用IDEA,有些操作会方便一点

那么下载这里我提供官方的下载链接

链接:IDEA下载

那么到下载界面,先别捉急下载,开头的那个IntelliJ IDEA Ultimate,什么30天免费试用,咱不要

那玩意是专业版,其实就对模组开发而言,社区版足矣,我们用不着专业版的一些功能

然后往下翻,找到IntelliJ IDEA Community,下载社区版就行,社区版是完全免费的

IDEA下载

IDEA的安装默认也在C盘,这个的话,你就改到其他的位置好了,如果你C盘够大,也可以装C盘里

那么,在安装中,会有一个更新path变量的选项,这个选项的话,勾选了的话,需要重启电脑才能生效

PS:以上两个部分直接引自【1.20.1 Fabric 长线教程计划】的第一期,因为差不多,拿过来就能用了

模板文件

那么接下来,我们就要开始正式开发了,首先我们得先获得一个模板文件

这里我们前往Forge的官网,去下载一个MDK,这个是模组开发工具包
Forge官网

在左侧栏中找到1.20.1,我们能看到它推荐版本是1.20.1-47.4.0,我们用它就行

点击Mdk,跳转到下载页面(应该是要使用法的),等页面右上角出现SKIP,点击一下,就开始下载了
10

然后我们就获得了一个压缩包,然后将它解压到一个地方(记得创建一个文件夹),然后将整个文件夹用IDEA打开

初始化项目

Gradle构建

进入IDEA之后,它会自动开始Gradle的构建,当然,这个过程不出意外的话还是要魔法

同理,出现BUILD SUCCESSFUL之后,就说明构建成功了,当然可能会出现一些警告,不用管他

Gradle使用的Java版本

构建完成之后,我们可以到build.gradle文件中,当前项目使用的Java版本

1
2
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17)

可以看到,它默认使用的是Java 17,这个是没问题的

项目结构

接下来我们来看看项目结构里的设置是否正确

项目结构的快捷键是Shift+Alt+Ctrl+S,也找到设置中的项目结构打开,在项目界面,找到SDK,确认一下使用的是JDK17

在后面一个模块中,也可以设置使用的SDK,这里我们选择17

当然,其实用21也是没关系的,只要你的项目不出问题,JDK21也是可以选择的

项目结构

Gradle JVM

虽然说我们看到build.gradle里,它默认使用的是17,但是,有时候IDEA会自己给你选择其他的版本,我们也来检查一下

设置 -> 构建、执行、部署 -> 构建工具 -> Gradle,检查Gradle JVM是否为JDK17

同样的,21如果不出问题,也可以用

Gradle JVM

IDEA一些小设置

IDEA 2024.3版本之后,我们就不再需要安装中文语言包这个插件了,因为这个时候中文已经是IDEA内置语言

外观与行为->系统设置->语言和区域,我们可以更改语言设置(当然需要重新启动

语言设置

那么另外的插件方面,Minecraft Development可以装一下,像Translation这样的翻译插件也可以装一个

模组基本配置

模组主类

接下来我们来看看模组主类,模板文件默认的名字是ExampleMod

这个类会在模组加载中调用,用来加载模组

然后在这个类中,我们可以看到一些示例,比如注册物品方块物品栏等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
// Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
// Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);

// Creates a new Block with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.STONE)));
// Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM = ITEMS.register("example_block", () -> new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties()));

// Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2
public static final RegistryObject<Item> EXAMPLE_ITEM = ITEMS.register("example_item", () -> new Item(new Item.Properties().food(new FoodProperties.Builder()
.alwaysEat().nutrition(1).saturationMod(2f).build())));

// Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab
public static final RegistryObject<CreativeModeTab> EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder()
.withTabsBefore(CreativeModeTabs.COMBAT)
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
.displayItems((parameters, output) -> {
output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event
}).build());

这些也都是我们后面会讲到的,在这里的话就全删了,我们后面会单开类去注册各个内容的

删掉以后,在构造函数中出现的报错也删掉,addCreative方法中报错的也删掉

另外的一些LOGGER之类的,也可以删掉,那些是日志输出的

但注意不要把MODID下的那个LOGGER字段删了

MODID

ExampleMod类中,我们可以看到MODID字段,这个字段是模组的唯一标识符

在我们模组开发中,这个东西是最重要的东西,它是我们的命名空间

它默认是examplemod,我们得改改,改成我们自己的,这里我做教程的话,我就使用tutorial_mod

这个MODID可以重构成MOD_ID,看起来更好看一点

1
public static final String MOD_ID = "tutorial_mod";

敲重点!!!这个ID在自己定义的时候,只能用小写字母和下划线!不能有其他字符!否则在加载模组时会崩溃!

Fabric那边还能用短横线,但在ForgeNeoForge这里就不行

主类的名字我们也可以进行更改,这里我改成TutorialMod

注意使用IDEA的重命名功能来进行,快捷键是Shift+F6,不然会报错

文件夹结构

文件夹结构也可以进行更改,因为它默认的是com.example.examplemod

这里我给它改成com.besson.tutorial

mods.toml

src/main/resources/META-INF文件夹下,有一个mods.toml文件,这个文件是模组的配置文件

这个文件下的大部分东西是在gradle.properties文件中定义的,
我们待会直接看gradle.properties中的参数即可

gradle.properties

接下来我们就来看看这个文件,这个是整个项目,或者说模组的配置文件

模组的名称mod id版本等等信息都是在这个文件中定义的,所以这也是一个很重要的文件

minecraft_version

1
2
3
4
5
6
7
# The Minecraft version must agree with the Forge version to get a valid artifact
minecraft_version=1.20.1

# The Minecraft version range can use any release version of Minecraft as bounds.
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
# as they do not follow standard versioning conventions.
minecraft_version_range=[1.20.1,1.21)

显然易见,这个是定义Minecraft版本的,这里我们使用1.20.1版本

forge_version

1
2
3
4
5
6
# The Forge version must agree with the Minecraft version to get a valid artifact
forge_version=47.4.0
# The Forge version range can use any version of Forge as bounds or match the loader version range
forge_version_range=[47,)
# The loader version range can only use the major version of Forge/FML as bounds
loader_version_range=[47,)

这个是定义Forge版本的,这里我们使用47.4.0版本

mod_id

我们在模组主类中改了MOD ID,所以在这里我们也要改

1
2
3
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}
# Must match the String constant located in the main mod class annotated with @Mod.
mod_id=tutorial_mod

mod_name

这个是模组在模组列表中显示的名字

1
2
# The human-readable display name for the mod.
mod_name=Tutorial Mod

这里你写啥字符都行

mod_license

这个是模组使用的许可证,像一些开源许可证,比如说MITGPL3.0等等

1
2
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT

这里更改之后,在项目文件下还有一个LICENSE.txt文件,这个文件就是许可证文件

mod_version

这个是模组的版本,我的建议是带上Minecraft的版本号,这样你自己进行多版本移植的时候也清楚

1
2
# The mod version. See https://semver.org/
mod_version=1.0.0-1.20.1

mod_group_id

这个就是文件夹结构,上面我们也改了,所以这里也要改

1
2
3
4
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
mod_group_id=com.besson.tutorial

如果这个mod_group_id不正确,模组加载器就找不到你的模组主类,也无法加载你的模组

mod_authors

这个是模组的作者

1
2
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
mod_authors=Besson

mod_description

这个是模组的描述,这里我们写一个简单的描述

注意换行不是直接回车,要使用\n转义

1
2
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
mod_description=This is a Tutorial Mod\nLet's Develop Minecraft Mods NOW!

映射

然后,我们回过头来看mapping_channelmapping_version这两个东西

这个是我们模组开发中使用的映射

什么是映射?我简单解释一下

Minecraft闭源的商业游戏,如果你拆过它的jar包就会发现,你尝试去找它的源码时,
你会发现一大堆奇怪的类名、方法名,这些都不是正常的类名、方法名,而是被混淆过的

映射的作用,就是将混淆过的类名、方法名还原成正常的类名、方法名

Mojang也公开了一部分映射,ForgeNeoForge也已经使用这些映射,而Fabric还有Yarn映射

Mojang的映射并不完整,使用我们在看源代码的时候,还是会看到很多filed_XXXmethod_XXX这样的类名、方法名,
它们并没有完全反编译过来,在开发中可能得理解好久才能反应过来,所以我们得改一下这里的映射

这里我们要使用羊皮纸映射,也就是Parchment,它完善了Mojang的映射,这个映射同样可以用在Fabric上

羊皮纸映射的版本可以在它提供的网站上找到:版本

更改映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# The mapping channel to use for mappings.
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
# Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin.
#
# | Channel | Version | |
# |-----------|----------------------|--------------------------------------------------------------------------------|
# | official | MCVersion | Official field/method names from Mojang mapping files |
# | parchment | YYYY.MM.DD-MCVersion | Open community-sourced parameter names and javadocs layered on top of official |
#
# You must be aware of the Mojang license when using the 'official' or 'parchment' mappings.
# See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
#
# Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge.
# Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started
mapping_channel=parchment

这里我们使用parchment

接下去改版本,版本格式照着上面的提示YYYY.MM.DD-MCVersion,这里我们使用2023.09.03-1.20.1版本

1
2
3
# The mapping version to query from the mapping channel.
# This must match the format required by the mapping channel.
mapping_version=2023.09.03-1.20.1

添加maven仓库

羊皮纸映射获取要设置其maven仓库,我们到settings.gradle文件中添加

1
2
3
repositories {
maven { url = 'https://maven.parchmentmc.org' } // Add this line
}

添加插件

羊皮纸映射需要使用一个Librarian插件,我们到build.gradle文件中添加

1
2
3
4
plugins {
// This should be below the net.minecraftforge.gradle plugin
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
}

重构项目

做完这一切之后,我们点击右上角冒出来的大象图标,同步更改Gradle设置,对项目进行重构

同样也是出现BUILD SUCCESSFUL,说明重构成功

运行genIntellijRuns

点击右侧Gradle(大象)的图标,找到Tasks/forgegradle runs下的genIntellijRuns,双击运行

这个是完成gradle配置,生成IntelliJ IDEA的配置文件

同样也是出现BUILD SUCCESSFUL,说明配置成功

启动!

那么最后一步了,同样也是在Tasks/forgegradle runs下有一个runClient,双击运行

你也可以找到上方运行菜单中的runClient,点击运行

这个就是启动游戏,那么当熟悉的音乐出来之后,你能够进入游戏,说明开发环境没有问题

那么恭喜你,正式踏入了我们1.20.1 Forge模组开发的大门!