前言

我重生了,上一世我没能坚持学习 Fabric Modding 导致未能幸存,这一世我要拿回属于我的一切😤

一直以来想学着做一个 mod 供自己玩,于是在此记录学习过程,鞭策自己学下去。

平台:Windows11
Java 版本:Java 21
Minecraft 版本:1.20.1
Git 版本:git version 2.50.1.windows.1
IDE:IntelliJ IDEA Community Edition 2025.1.3(以下将简称为 IDEA)

Fabric 模板项目

推荐使用 Fabric 官方提供的模板项目开始学习。你可以点击进行跳转:

image-20250723174244140

Minecraft 版本为 1.20.1 并且在高级选项中,将 Data Generation 勾选上,取消勾选 Split client and common sourcesData Generation 能让我们可以构建合成配方等资源,取消勾选拆分客户端和服务端代码是因为,初学者基本搞不清哪部分是服务器端代码哪部分是客户端代码,之后我们将手动进行拆解。

下载下来将压缩包解压,将目录中的 LICENSE.github 删除(前者为文件,后者为文件夹)。接着我们用 IDEA 将其作为工程项目打开。

项目配置

在使用 IDEA 将项目打开之后,优先检查以下设置:

  1. 项目结构:将 SDK 设置为受支持的版本,现在 Fabric 模板项目部分依赖已经要求 SDK 为 21 所以我们选择为 21
    image-20250723180039128
  2. IDEA 设置:将 Gradle 用户主目录从 C 盘更改到其他你喜欢的路径,并检查 Gradle 使用的 JVM 版本是否为 SDK 21
    image-20250723180235470

都确认无误之后,别忘记点击应用并确定更改。

在打开项目,配置设置的这段时间,IDEA 会自动触发 gradle 下载依赖,构建速度取决于你的网络链接状态。构建完成时,你将会在日志输出界面看到 BUILD SUCCESSFUL 的字样:
image-20250723180551406

若你的 SDK 为 java 17 或其他版本,你可能会在构建的时候遇到下面的报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
A problem occurred configuring root project 'tutorial-mod-template-1.20.1'.
> Could not resolve all artifacts for configuration 'classpath'.
   > Could not resolve net.fabricmc:fabric-loom:1.11-SNAPSHOT.
     Required by:
         root project : > fabric-loom:fabric-loom.gradle.plugin:1.11-SNAPSHOT:20250708.081131-4
      > Dependency requires at least JVM runtime version 21. This build uses a Java 17 JVM.

* Try:
> Run this build using a Java 21 or newer JVM.
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

该报错的原因是 fabric-loom 插件版本内部依赖了 Java 21+ 的功能或库,你可以在 build.gradlegradle.properties 中找到相关的 fabric-loom 版本说明:

1
2
3
4
5
6
7
8
9
10
11
// build.gradle
plugins {
	id 'fabric-loom' version "${loom_version}"
	id 'maven-publish'
}

//gradle.properties
minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10
loader_version=0.16.14
loom_version=1.11-SNAPSHOT

为什么要使用该版本?Fabric 文档是这么写的…

The recommended loom version is 1.11-SNAPSHOT. This is usually defined near the top of your build.gradle file.
推荐的 loom 版本是 1.11-SNAPSHOT。这通常定义在您的 build.gradle 文件顶部附近。

既然使用 Java21 了,相对应的也把 build.gradle 里面的相关配置项也修改为对应的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
java {
     withSourcesJar()

-    sourceCompatibility = JavaVersion.VERSION_17
-    targetCompatibility = JavaVersion.VERSION_17
+    sourceCompatibility = JavaVersion.VERSION_21
+    targetCompatibility = JavaVersion.VERSION_21
 }

 tasks.withType(JavaCompile).configureEach {
-    it.options.release = 17
+    it.options.release = 21
 }

好了。接着,在项目视图的选项中,将平展相关选项取消勾选,这将使得 IDEA 不再将 com.frozen.tutorialmod 下的 mixin 文件夹显示为 com.frozen.tutorialmod.mixin 格式,方便我们掌握目录层级情况。
image-20250723181055120

Mod ID

mod 的 id 是该 mod 的唯一标识,且该 id 只能为数字 0-9、小写字母 a-z、下划线_和短横线 - 组成的字符串

java/com/frozen/tutorialmod/TutorialMod.java 中,检查是否已经将 Mod 的 id 进行引入:

1
2
3
4
5
6
7
8
9
public class TutorialMod implements ModInitializer {
	public static final String MOD_ID = "tutorial-mod"; // 关键语句
	public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

	@Override
	public void onInitialize() {
		LOGGER.info("Hello Fabric world!");
	}
}

此 ID 将在后面的开发过程中多次引用,所以这一步骤十分重要。

接着,在 resources/fabric.mod.json 文件中,查看 ID 是否正确,相关配置是否正确:
image-20250723191927192

dependssuggests 项,暂不用修改,等以后开发涉及到了视情况修改。

修改 mod 版本(可选)

我比较喜欢语义化版本控制。由于目前我们只是在配置、初始化阶段,所以在 gradle.properties 中,将 mod_version 进行修改:

1
2
3
4
5
  # Mod Properties
- mod_version=1.0.0
+ mod_version=0.0.1-1.20.1
  maven_group=com.frozen.tutorialmod
  archives_base_name=tutorial-mod

0.0.1 为例,这种命名的基本语法是:

名称 含义
MAJOR(主版本) 不兼容性变更时递增,比如移除 API、重构核心逻辑
MINOR(次版本) 添加了新功能,但向后兼容
PATCH(补丁版本) 修复了 bug,无新增功能,向后兼容

具体来说:

版本号 说明
1.2.3 主版本 1,添加了 2 个次要功能,修复了 3 个 bug
0.1.0 尚在早期开发阶段,有了基本功能
0.0.1 刚起步,甚至还不一定稳定
2.0.0 大版本更新,可能有重大 API 变更或破坏性更改

对于我的 0.0.1-1.20.1 版本号,-1.20.1 主要是为了声明所兼容的 Minecraft 版本。

由于我们修改了 gradle.properties 文件,这会影响到构建,所以需要重新加载一遍项目:

image-20250723195416897

这将花费一些时间。

Client 初始化

前面在模板项目生成的时候,我们取消了勾选拆分客户端和服务器端,这里尝试手动初始化客户端。

resources/fabric.mod.json 中,修改 entrypoints 节点内容:

1
2
3
4
5
6
7
8
9
10
11
 "entrypoints": {
   "main": [
     "com.frozen.tutorialmod.TutorialMod"
   ],
+  "client": [
+    "com.frozen.tutorialmod.TutorialModClient"
+  ],
   "fabric-datagen": [
     "com.frozen.tutorialmod.TutorialModDataGenerator"
   ]
 },

根据这项修改,我们就需要在 com.frozen.tutorialmod 文件夹下新建 Java 类 TutorialModClient,刚创建好该文件,代码内容为:

1
2
3
4
package com.frozen.tutorialmod;

public class TutorialModClient {
}

我们将 TutorialModClient 修改为实现 ClientModInitializer 接口的类:

1
2
3
4
5
6
7
8
9
10
package com.frozen.tutorialmod;

import net.fabricmc.api.ClientModInitializer;

public class TutorialModClient implements ClientModInitializer {
    @Override
    public void onInitializeClient() {
      
    }
}

onInitializeClient() 方法暂时不需要做任何事。

下载源码

Minecraft 的源码对我们的开发有着很高的参考价值,这将帮助我们理解各种写法。

检查 JAVA_HOME 版本

请确认自己的 JAVA_HOME 的值指向的是 Java21 而非其他版本。

Gradle 启动的时候 默认使用自己找到的 JDK。如果系统上现在有 多个 JDK 安装,Gradle 会优先使用以下路径之一来选择 JDK:

  1. 环境变量 JAVA_HOME优先级最高
  2. gradle.properties 中指定的 org.gradle.java.home
  3. 系统 PATH 中的 Java 版本(最后才使用)

对我来说,我的 JAVA_HOME 环境变量设为了 %JAVA_HOME21%,这将和我的 JAVA_HOME17JAVA_HOME21JAVA_HOMExx 格式的环境变量进行配合,一般来说,终端将会输出:

1
2
3
4
5
6
7
PS E:\Coding\Fabric\tutorial-mod-template-1.20.1> echo $env:JAVA_HOME
E:\Program Files\jdk-21.0.7
PS E:\Coding\Fabric\tutorial-mod-template-1.20.1> java -version
java version "21.0.7" 2025-04-15 LTS
Java(TM) SE Runtime Environment (build 21.0.7+8-LTS-245)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.7+8-LTS-245, mixed mode, sharing)
PS E:\Coding\Fabric\tutorial-mod-template-1.20.1>

这是正常的输出。

但如果,你的 JAVA_HOME 环境变量设置的是 %JAVA_HOME17%只是将 IDEA 中的相关配置为了 Java21 的话,那么可能在终端中执行 gradle 相关命令,例如:

1
.\gradlew -v

此时读取到的还是 17,而执行 java -version 却能正确输出为 java21 版本,这十分诡异对吧?

实际上,$env:JAVA_HOMEjava -version 读取的项是不一样的。这一点很容易搞混,尤其是初学者。具体来说:

  1. $env:JAVA_HOME:读取的是环境变量 JAVA_HOME 的值,如果是 %JAVA_HOME21% 那么就会得到 JAVA21
  2. java -version:读取的是环境变量 PATH 的值,java 命令会从 PATH 中按照先后顺序找到一个 java 有效目录,用以执行命令。

这与 IDEA 设置中设置的 Java21 是不相关的:
image-20250723205656287

如果你确实是上面的诡异情况,请在更改 JAVA_HOME 指向的值之后,重启 IDEA 再另起一个终端窗口。

下载源码

唤起终端窗口(默认唤起的是 PowerShell),执行下面的命令:

1
./gradlew genSources

下载成功将输出:

1
2
3
4
5
6
7
8
9
10
11
12
PS E:\Coding\Fabric\tutorial-mod-template-1.20.1> ./gradlew genSources
Starting a Gradle Daemon, 4 stopped Daemons could not be reused, use --status for details

> Configure project :
Fabric Loom: 1.11.4

> Task :genSourcesWithVineflower
Decompile cache stats: 0 hits, 4786 misses

BUILD SUCCESSFUL in 42s
1 actionable task: 1 executed
PS E:\Coding\Fabric\tutorial-mod-template-1.20.1>

如何查看源码

java/com/frozen/tutorialmod/mixin/ExampleMixin.java 文件中,将鼠标选中 MinecraftServer 并按下鼠标中键:

1
2
3
4
5
6
7
@Mixin(MinecraftServer.class)
public class ExampleMixin {
	@Inject(at = @At("HEAD"), method = "loadWorld")
	private void init(CallbackInfo info) {
		// This code is injected into the start of MinecraftServer.loadWorld()V
	}
}

这将跳转至我们刚才下载的 Minecraft 源码,如下图所示:
image-20250723213650992

注意,打开的应该是带注释的源码!如果你的 IDEA 版本和我不同,可能打开的是默认的 IDEA 帮你反编译之后的代码,你需要手动选择代码上方的蓝条的 Choose Sources 也就是选择源:
image-20250723214304170

如果选择源之后,代码上方的蓝条变成了红条,这也是正常的。

运行 Minecraft 测试

现在,我们就可以尝试运行一次 Minecraft 来测试我们的一切配置、代码是否正确了。
找到 Gradle 栏,选择 Tasks/fabric/runClient,如下图所示:
image-20250723214701481

不出意外出来 Minecraft 的窗口之后,我们创建一个存档,进入存档试试功能:
image-20250723214902547

只要在控制台中的日志没有什么 error 错误就行。

Git 版本控制

我十分推荐对项目进行版本控制,这里使用的是 GitHub。
注册账号就不讲了,自己搜索就行。由于我们已经在使用 IDEA,那么直接用 IDEA 的版本控制选项就行(旧版本可能叫 VCS):
image-20250723221011632

IDEA 将会弹出一个对话框,让你填相关信息:
image-20250723221203767

仓库私有与否,顾名思义,私有别人就看不到,如果你想和别人协作或者有别的考虑就可以公开。
接着进行第一次提交:
image-20250723221513229

确认文件无误就可以提交并推送了(如果只是提交,则是只提交到了本地仓库而 GitHub 是没有的)。此后,每次对相关文件的更改都将进行记录。