====== Fabric Loom ======
Fabric Loom,或者简称为 Loom,是个 [[https://gradle.org/|Gradle]] 插件,用于 Fabric 生态系统的模组开发中。Loom 提供了在开发环境中安装 Minecraft 和模组的一系列工具,所以你可以针对 Minecraft 混淆及其发行版和版本之间的差异来将 Loom 与 Minecraft 链接起来。Loom 还提供了用于 Fabric 加载器的运行配置、mixin 编译处理,以及用于 Fabric Loader 的 jar-in-jar 系统的工具。
==== 常用任务 ====
* ''migrateMappings'':将当前源代码合并到指定的映射中。见 [[zh_cn:tutorial:migratemappings]]。
* ''remapJar'':产生包含 ''jar'' 任务的重映射的输出的 jar。同时也会将 jar-in-jar 包含在模组中。运行 ''build'' 时会调用。
* ''genSources'':使用默认的反编译器(CFR)反编译 Minecraft jar。
* ''downloadAssets'':下载配置的 Minecraft 版本的资源索引和对象,下载到用户缓存中。
* ''genEclipseRuns'':安装 Eclipse 运行配置并创建运行目录(如果还不存在)。
* ''vscode'':生成或覆盖 Visual Studio Code 的 ''launch.json'' 文件,启动配置在 ''.vscode'' 文件夹中,并创建运行目录,如果还不存在。
* ''ideaSyncTask'':生成(但是不会覆盖)Intellij IDEA 的启动配置,包括客户端和服务器。
* ''remapSourcesJar'':只有在 AbstractArchiveTask ''sourcesJar'' 存在时存在。将 ''sourcesJar'' 任务的输出重映射。
* ''runClient'':将 Fabric Loader 作为 Minecraft 客户端运行的 JavaExec 任务。
* ''runServer'':将 Fabric Loader 作为 Minecraft 专用服务器运行的 JavaExec 任务。
==== 依赖子项目 ====
设立依赖其他 loom 项目的多项目构建时,你应该在依赖其他项目时使用 ''namedElements'' 配置。默认情况下,项目的“输出”会映射到中间名。''namedElements'' 配置包含还未重映射的项目输出。
dependencies {
implementation project(path: ":name", configuration: "namedElements")
}
如果你在多项目构建中使用分离源集,你还需要为其他项目客户端源集添加依赖项。
dependencies {
clientImplementation project(":name").sourceSets.client.output
}
==== 分离客户端与常规代码 ====
多年来,服务器崩溃往往是因为安装在服务器上时意外调用客户端代码。最新的 loom 和 loader 版本提供了一个选项,要求将所有客户端代码移动到其自己的源集中。这样做是为了提供编译时的保证,以防止在服务器上调用仅限客户端的 Minecraft 代码或仅限客户端的 API。在客户端和服务器上都可以使用的单个 jar 文件仍然是从两个源集构建的。
以下 build.gradle 文件片段展示了如何为您的模组启用此功能。由于您的模组现在将拆分为两个源集,因此您将需要使用新的 DSL 来定义您的模组源集。这将会让 Fabric Loader 将您的模组类路径组合在一起,对于其他一些复杂的多项目设置也有用。
要分享客户端与服务器的代码,需要 Minecraft 1.18(建议 1.19)、Loader 0.14 和 Loom 1.0 以上的版本。
loom {
splitEnvironmentSourceSets()
mods {
modid {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
}
==== 多项目优化 ====
如果你的 Gradle 项目有多个子项目并使用相同的 Minecraft 版本,如 Fabric API,从 Loom 1.1 开始,你可以选择使用高级的优化。在 gradle.properties 中加入 fabric.loom.multiProjectOptimisation=true
从而减少构建时间和内存使用,因为会在重映射多个输出的 jar 时在项目之间共享 Tiny Remapper 实例。
==== 选项 ====
loom {
// 设置访问加宽的路径,参见 https://fabricmc.net/wiki/zh_cn:tutorial:accesswideners
accessWidenerPath = file("src/main/resources/modid.accesswidener")
// 添加额外的 log4j 配置文件。
log4jConfigs.from(file("log4j.xml"))
// 若启用,输出的存档会自动重映射。
remapArchives = true
// 若启用,*Elements 配置中的 -dev jars 会被替换为重映射的 jar。
setupRemappedVariants = true
// 若启用,传递性的访问加宽会从依赖中应用。
enableTransitiveAccessWideners = true
// 若启用,log4j 只会在运行时类路径中,强制使用 SLF4j。
runtimeOnlyLog4j = false
// 若启用,只有与服务器有关的特性和 jars 会被设置。
serverOnlyMinecraftJar()
// 若设置,minecraft jar 会分为常规和仅客户端。这只是实验性的。Fabric 加载器暂时还不支持这个功能。
splitMinecraftJar()
// 用于配置存在的或者新的运行配置
runs {
client {
// 添加 VM 变量
vmArgs "-Dexample=true"
// 添加 JVM 属性
property("example", "true")
// 添加 program 变量
programArg "--example"
// 需要运行的环境(或端),通常是 client 或 server。
environment = "client"
// 运行配置的完整名称,例如“Minecraft 客户端”。默认由基础名称决定。
configName = "Minecraft 客户端"
// 运行配置的默认主类。使用带有 fabric_installer.json 文件的模组加载器时,就会覆盖这个。
defaultMainClass = ""
// 此配置的运行目录,与根项目目录有关。
runDir = "run"
// 运行的源集,通常设为 sourceSets.test
source = sourceSets.main
// 若为 true,运行配置会为 IDE 生成。默认仅对根项目为 true。
ideConfigGenerated = true
// 配置以默认的客户端选项运行配置。
client()
// 配置以默认的服务器选项运行配置。
server()
}
// 为测试创建基本运行配置的示例
testClient {
// 从另一个运行配置复制设置。
inherit client
configName = "测试 Minecraft 客户端"
source = sourceSets.test
}
// 删除内置服务器配置的示例
remove server
}
// 配置所有运行配置以生成 ide 运行配置。对子项目很有用。
runConfigs.configureEach {
ideConfigGenerated = true
}
// 用于配置 mixin 选项,或应用到额外的源集。
mixin {
// 若禁用,会使用 tiny remapper 来重映射 Mixin 而非 AP。实验性。
useLegacyMixinAp = true
// 设置默认的 refmap 名称
defaultRefmapName = "example.refmap.json"
// 关于添加额外资源集的选项,参见 https://github.com/FabricMC/fabric-loom/blob/dev/0.11/src/main/java/net/fabricmc/loom/api/MixinExtensionAPI.java
}
// 配置或添加新的反编译器
decompilers {
// 配置默认的反编译器,cfr 或 fernflower
cfr {
// 将额外选项传递到编译器
options += [
key: "value"
]
// 设置分叉 JVM 时使用的内存量(以兆字节为单位)
memory = 4096
// 设置反编译器可以使用的最大线程数。
maxThreads = 8
}
}
interfaceInjection {
// 若启用,将会应用配置的注入的接口。
enableDependencyInterfaceInjection = true
}
// 将 Minecraft jar 和传入的依赖项拆分到 main(common)和仅限客户端的源集。
// 这可以在编译期间就确保访问仅限客户端的代码时的安全。
splitEnvironmentSourceSets()
// 这个 mods 语句块用于将由多个类路径的项组合在一起。
mods {
modid {
// 使用分离的资源时,你应该添加 main 和 common 源集
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
// 创建 modExampleImplementation 和重映射 mods 的相关配置。
createRemapConfigurations(sourceSets.example)
}
remapJar {
// 设置任务的输出 jar,同样对 remapSourcesJar 有效
inputFile = file("example.jar")
// 设置资源命名空间,同样对 remapSourcesJar 有效
sourceNamespace = "named"
// 设置目标命名空间,同样对 remapSourcesJar 有效
targetNamespace = "intermediary"
// 给重映射类路径添加额外的 jar,同样对 remapSourcesJar 有效
classpath.from file("classpath.jar")
// 将嵌套的模组 jar 添加到该任务,include 配置应该用于 maven 库和模组。
nestedJars.from file("nested.jar")
// 若启用,输出的 jar 中会包含嵌套的 jar。
addNestedDependencies = true
}
dependencies {
// 设置 Minecraft 版本。
minecraft "com.mojang:minecraft:1.18.1"
// 使用 maven 中的映射。
mappings "net.fabricmc:yarn:1.18.1+build.22:v2"
// 使用官方 mojang 映射
mappings loom.officialMojangMappings()
// 使用官方 mojang 映射和 parchment 的分层映射。
mappings loom.layered() {
officialMojangMappings()
// 使用 parchment 映射。注意:必须手动添加 Parchment maven(https://maven.parchmentmc.org)
parchment("org.parchmentmc.data:parchment-1.17.1:2021.09.05@zip")
}
// 从 maven 中重映射一个模组,并应用到 gradle 的 implementation 配置
// (小的细节:不会准确地应用*到*配置,但是会克隆一份以用于模组依赖)
modImplementation "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 从 maven 中重映射一个模组,并应用到 gradle 的 api 配置
modApi "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 从 maven 中重映射一个模组,并应用到 gradle 的 compileOnly 配置
modCompileOnly "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 从 maven 中重映射一个模组,并应用到 gradle 的 compileOnlyApi 配置
modCompileOnlyApi "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 从 maven 中重映射一个模组,并应用到 gradle 的 runtimeOnly配置
modRuntimeOnly "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 从 maven 中重映射一个模组,并应用到 loom 的 localRuntime 配置。
// 和 runtimeOnly 的做法类似,但是不会暴露到依赖项中。有点像 testRuntimeOnly,不过是对于模组的。
modLocalRuntime "net.fabricmc.fabric-api:fabric-api:0.46.2+1.18"
// 在重映射的 jar 中包含一个模组 jar。不可传递。
include "example:example-mod:1.1.1"
// 在重映射的 jar 中包含一个空模组库。会生成空模组。不可传递。
include "example:example-lib:1.1.1"
// 根据特定的结构 api 版本提供帮助的助手。
modImplementation fabricApi.module("fabric-api-base", "0.46.2+1.18")
// 使用 namedElements 配置,依赖一个 loom 子项目。
implementation project(path: ":name", configuration: "namedElements")
}
==== 解决问题 ====
Loom 或者 gradle 可能会由于缓存文件有问题而失败。运行 ''./gradlew build --refresh-dependencies'' 将强制 gradle 和 loom 重新下载并重新创建所有文件。这可能需要几分钟,但是处理与缓存有关的问题时非常有用。
==== 开发环境设置 ====
Loom 旨在通过简单地在用户选择的 IDE 中设置工作区来开箱即用,因此背后做了很多事以创建带有 Minecraft 的开发环境:
- 从官方渠道下载指定版本 Minecraft 的客户端和服务器 jar。
- 将客户端和服务器 jar 合并到一起以生成一个合并的 jar,并加上 ''@Environment'' 和 ''@EnvironmentInterface'' 注解。
- 下载配置的映射。
- 使用中间映射重映射合并的 jar 产生一个中间 jar。
- 使用 yarn 映射重映射合并的 jar 产生一个映射的 jar。
- 可选的:反编译映射了的 jar,产生一个映射的源 jar 和行映射,并将行映射应用到合并的 jar。
- 添加 Minecraft 的依赖。
- 下载 Minecraft 资源文件(assets)。
- 处理并包含模组增强的依赖。
==== 缓存 ====
* ''${GRADLE_HOME}/caches/fabric-loom'':用户缓存,用户所有 Loom 项目共用的缓存。用来缓存 Minecraft 资源文件(assets)、jars、合并的 jars、中间 jars 和映射的 jars。
* ''.gradle/loom-cache'':根项目持久缓存,项目和子项目共用的缓存。用来缓存重映射的模组以及生成的包含的模组 JAR。
* ''build/loom-cache'':根项目构建缓存。
* ''**/build/loom-cache'':项目和子项目的构建缓存。
==== 依赖配置 ====
* ''minecraft'':定义用于开发环境的 Minecraft 版本。
* ''mappings'':定义用于开发环境的映射。
* ''modCompile''、''modImplementation''、''modApi'' 和''modRuntime'':相当于''compile''、''implementation''、''api''和''runtime''的增强型变种,用于模组依赖。会被重映射以对应开发环境,并且会移除嵌套的 JAR。
* ''include'':生命一个应在 ''remapJar'' 输出中包含的 jar-in-jar 依赖。该依赖配置是不可传递的。对于非模组依赖,Loom 会生成一个模组 JAR,其中 fabric.mod.json 使用模组 ID 的名称和相同版本。
==== 默认配置 ====
* 应用以下插件:''java''、''eclipse'' 和 ''idea''。
* 添加以下的 Maven 仓库:Fabric [[https://maven.fabricmc.net/]]、Mojang [[https://libraries.minecraft.net/]] 和 Maven Central。
* 配置 ''idea'' 扩展以排除目录 ''.gradle''、''build''、''.idea'' 和 ''out'',以下载 javadocs 源,并继承输出目录。
* 配置 ''idea'' 任务,并由 ''genIdeaWorkspace'' 任务确定。
* 配置 ''eclipse'' 任务,并由 ''genEclipseRuns'' 任务确定。
* 如果根项目存在 ''.idea'' 文件夹,下载资源(如果没有及时更新)并在 ''.idea/runConfigurations'' 安装 run 配置。
* 添加 ''net.fabricmc:fabric-mixin-compile-extensions'' 以及其依赖,使用 ''annotationProcessor'' 依赖配置。
* 配置所有的非 test JavaCompile 任务,使用 Mixin 注解处理器的配置。
* 配置 ''remapJar'' 任务,输出名称与 ''jar'' 任务输出的相同的 JAR,然后给 ''jar'' 任务添加一个“dev”分类器。
* 配置 ''remapSourcesJar'' 任务,以处理 ''sourcesJar'' 任务输出,如果该任务存在。
* 将 ''remapJar'' 任务和 ''remapSourcesJar'' 任务添加为 ''build'' 任务的依赖。
* 配置 ''remapJar'' 任务和 ''remapSourcesJar'' 任务以在执行时将其输出添加为 ''archives'' 成品。
* 为每个 MavenPublication (从 ''maven-publish'' 插件):
* 手动将依赖附加到 POM 之后,以进行模组增强的依赖配置,前提是依赖配置具有 Maven 范围。
所有的运行配置都有运行目录 ''${projectDir}/run'' 和 VM 参数 ''-Dfabric.development=true''。运行配置的主类通常是由 Fabric 加载器 JAR 文件的根部的 ''fabric-installer.json'' 文件定义的(如果该文件包含在模组依赖中的话),但是文件可以由模组依赖定义。如果没有找到这样的文件,则主类默认为 ''net.fabricmc.loader.launch.knot.KnotClient'' 和 ''net.fabricmc.loader.launch.knot.KnotServer''。
客户端运行配置是使用 ''--assetsIndex'' 和 ''--assetsDir'' 程序参数配置的,指向包含资源文件和配置的 Minecraft 版本的索引文件的 loom 缓存目录。在 OSX 上运行时,添加了 ''-XstartOnFirstThread'' VM 参数。