Table of Contents

Fabric Loom

Fabric Loom,或者简称为 Loom,是个 Gradle 插件,用于 Fabric 生态系统的模组开发中。Loom 提供了在开发环境中安装 Minecraft 和模组的一系列工具,所以你可以针对 Minecraft 混淆及其发行版和版本之间的差异来将 Loom 与 Minecraft 链接起来。Loom 还提供了用于 Fabric 加载器的运行配置、mixin 编译处理,以及用于 Fabric Loader 的 jar-in-jar 系统的工具。

常用任务

依赖子项目

设立依赖其他 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 的开发环境:

  1. 从官方渠道下载指定版本 Minecraft 的客户端和服务器 jar。
  2. 将客户端和服务器 jar 合并到一起以生成一个合并的 jar,并加上 @Environment@EnvironmentInterface 注解。
  3. 下载配置的映射。
  4. 使用中间映射重映射合并的 jar 产生一个中间 jar。
  5. 使用 yarn 映射重映射合并的 jar 产生一个映射的 jar。
  6. 可选的:反编译映射了的 jar,产生一个映射的源 jar 和行映射,并将行映射应用到合并的 jar。
  7. 添加 Minecraft 的依赖。
  8. 下载 Minecraft 资源文件(assets)。
  9. 处理并包含模组增强的依赖。

缓存

依赖配置

默认配置

所有的运行配置都有运行目录 ${projectDir}/run 和 VM 参数 -Dfabric.development=true。运行配置的主类通常是由 Fabric 加载器 JAR 文件的根部的 fabric-installer.json 文件定义的(如果该文件包含在模组依赖中的话),但是文件可以由模组依赖定义。如果没有找到这样的文件,则主类默认为 net.fabricmc.loader.launch.knot.KnotClientnet.fabricmc.loader.launch.knot.KnotServer

客户端运行配置是使用 –assetsIndex–assetsDir 程序参数配置的,指向包含资源文件和配置的 Minecraft 版本的索引文件的 loom 缓存目录。在 OSX 上运行时,添加了 -XstartOnFirstThread VM 参数。