帆窝

Fabric模组开发:build.gradle.kts与Gradle简介

引言

build.gradle.kts是使用Gradle构建、Kotlin开发的项目的构建脚本。如果是使用Java开发,则对应的构建脚本应为build.gradle

目前在网络上搜索大多得到的是build.gradle的写法,而这二者之间还存在不少差别;尽管IDEA的Minecraft Development插件可以帮助我们创建模板项目,其中包含开箱即用的build.gradle.kts,但是多了解一点总归不是坏事,对吧~

Gradle任务

如果你也是此前没怎么接触过Java/Kotlin项目,则……在IDEA的右侧一列图表中,有一只很奇怪的爬行动物,这就是Gradle了~

图片[1]-Fabric模组开发:build.gradle.kts与Gradle简介-帆窝

Gradle本身为我们(Java/Kotlin开发者)提供了贯穿项目整个生命周期的管理能力,并且可以通过构建脚本build.gradle.kts/build.gradle定制化其行为。例如,运行Gradle中的build任务,会将我们的项目打包为jar文件——对于我们modder来说就是将模组打包——然后即可将模组上传到平台,或者发送给他人使用~

插件 plugins

不过呢,我们这种开发Minecraft Fabric模组的需求还是太小众了,因此Gradle本体不包含我们需要的全部功能。于是,在build.gradle.kts的头部,我们使用了fabric-loom插件,这是Fabric社区维护的帮助我们开发模组的Gradle插件,提供了我们需要的一切。

plugins {
    kotlin("jvm") version "2.2.21"
    id("fabric-loom") version "1.13-SNAPSHOT"
    id("maven-publish")
}

然后,重新导入Gradle项目,告诉IDEA我们更新了项目。你每次修改build.gradle.kts以及与之相关联的其他构建脚本后,都需要重新导入Gradle项目。IDEA会有浮动图标提示你的。

任务 tasks

现在我们可以在Gradle任务中找到一些很熟悉的字眼,例如:

  • runClient任务可以启动Minecraft客户端,当然是包含了你正在IDEA中开发的模组的。
  • runServer任务可以启动Minecraft服务端。启动的服务端没有独立的GUI,因此你需要在IDEA的终端中与服务端交互。当然也是包含了你正在IDEA中开发的模组的。
  • genSources任务可以生成Minecraft源代码。阅读Minecraft源代码是模组开发必不可少的环节,此任务可以反编译、反混淆Minecraft编译后的字节码,生成人类可读的Java文件。

loom

现在,Fabric会默认启用代码源集分离功能。

loom {
    splitEnvironmentSourceSets()

    mods {
        register("gamehelper") {
            sourceSet("main")
            sourceSet("client")
        }
    }

    // 自定义任务运行时参数
    runs {
        getByName("client") {
            programArgs("--username", "Player114514")
        }
    }
}

以上脚本将我们的模组源代码划分为两个源集,在你的IDEA中分别为src/clientsrc/main,我们称之为client源集和server源集。从现在起,你不应该在main源集中使用client源集内的代码。

具体来说,main源集是你的模组在客户端和服务端都需要运行的代码,而client是你的模组只在客户端运行的代码。现在,模组在被Fabric加载时,客户端代码的加载顺序是main->client,服务端只加载main

你也可以添加新的源集,例如效仿client加一个server,Fabric只是迎合大多数开发者的需要而分了mainserver两个源集。在Minecraft Dev生成的模板项目中,这是已经做好的配置。

现在,你的Kotlin代码应当位于src/client/kotlinsrc/main/kotlin中;如果你需要混合使用(或者只使用)Java,那么你的Java代码应当位于src/client/javasrc/main/java中,嗯……写多了之后你会明白的~

配置源集下面的代码是设置了在runClient任务运行时的额外参数,指定了玩家名为Player114514,否则的话玩家名会是PlayerXXX,其中XXX是随机的三位数字,并且每次启动游戏时都不一样。你有可能需要这样设置来让玩家名称一致,方便你做一些测试;没有这样的需求的话就不必设置了。

仓库 repositories

repositories设置了项目的依赖项所在仓库。仓库指的是Maven仓库,是Java项目的较为流行的打包分发方式。

repositories {
    // Add repositories to retrieve artifacts from in here.
    // You should only use this when depending on other mods because
    // Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
    // See https://docs.gradle.org/current/userguide/declaring_repositories.html
    // for more information about repositories.
    maven {
        name = "fanfan"
        url = uri("https://maven.fanfan.moe/repository/maven-public/")
    }
}

上面的代码中添加了一个名为fanfan的仓库,地址是https://maven.fanfan.moe/repository/maven-public/这是我的仓库啦

你可以在这里添加多个仓库,优先级为从上到下。对于你的项目需要的每个依赖,Gradle会从上到下依次在每个仓库中尝试查找,找到之后就从那里下载。Maven中央仓库永远位于最后,如果所有仓库都找不到需要的依赖则视作异常。

你的模组如果需要一些前置模组作为依赖的话,就需要在构建脚本的下一部分指定这些依赖,在这一部分指定依赖储存在的仓库。例如,Cloth Config和Modmenu作为较为常用(且经常作为依赖)的两个模组,分别位于https://maven.shedaniel.me/https://maven.terraformersmc.com/两个仓库中,那么你就需要在这里添加这两个仓库。

依赖 dependencies

在普通的Gradle构建的项目中,依赖是指你的项目需要使用到的其他项目。例如,为了在你的项目中使用Google的Gson(一个用来处理JSON格式数据结构的包),你需要使用implementation("com.google.code.gson:gson:2.10.1@jar")来指定你的项目需要2.10.1版本的Gson。

现在,你需要在这里指定更多的依赖,例如:

dependencies {
    implementation("cn.mangofanfan:tools:1.0.5")
    include("cn.mangofanfan:tools:1.0.5")
    
    minecraft("com.mojang:minecraft:${project.property("minecraft_version")}")
    mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2")
    
    modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}")
    modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}")
    modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")

    // 将libgui包含在本模组中
    val libguiVersion = project.property("libgui_version")
    "modClientImplementation"("io.github.cottonmc:LibGui:${libguiVersion}")
    include("io.github.cottonmc:LibGui:${libguiVersion}")

    "modClientImplementation"("me.shedaniel.cloth:cloth-config-fabric:${project.property("cloth_config_version")}")
    "modClientImplementation"("com.terraformersmc:modmenu:${project.property("modmenu_version")}")
}
  • implementation依然用于指定依赖。
  • minecraft用来指定Minecraft版本,这里的解析结果是com.mojang:minecraft:1.21.10
  • mappings用来指定Yarn映射的版本。Yarn映射是社区维护的将Minecraft代码反混淆的映射表。
  • modImplementation用于指定你正在开发的模组依赖的其他模组。其中,Fabric Loader是Fabric加载器本体,Fabric API是Fabric提供的API,这两者务必包含在内;Fabric Language Kotlin是使用Kotlin开发的模组的通用依赖。我们的项目自然而然需要包含以上三者。
  • include用来指定将某个东西打包进你的jar文件内。例如,我们将libgui塞到我们的模组jar包内,这样用户就无需再单独安装libgui模组。include此模组除了该模组体量小、功能基本之外,主要还有原因是该模组的1.21.10版本并没有上传给用户,因此用户无法自己下载。
  • 同时,还有被引号括起来的modClientImplementation。类似于modImplementationmodClientImplementation指定的是仅在客户端环境下依赖的模组,同理还有modServerImplementation。模组开发者应当考虑到模组运行在客户端与服务端时的区别,下面会更详细地说明。
    • 使用引号括起modClientImplementation是因为IDEA会警告此方法不存在,但实际上该方法是在Gradle运行时动态生成的。引号会让IDEA和Kotlin编译器不再针对此进行警告。

资源处理 tasks.processResources

tasks.processResources {
    inputs.property("version", project.version)
    inputs.property("minecraft_version", project.property("minecraft_version"))
    inputs.property("loader_version", project.property("loader_version"))
    filteringCharset = "UTF-8"

    filesMatching("fabric.mod.json") {
        expand(
            "version" to project.version,
            "minecraft_version" to project.property("minecraft_version"),
            "loader_version" to project.property("loader_version"),
            "kotlin_loader_version" to project.property("kotlin_loader_version"),
            "fabric_version" to project.property("fabric_version"),
            "cloth_config_version" to project.property("cloth_config_version"),
            "libgui_version" to project.property("libgui_version")
        )
    }
}

处理模组使用到的资源文件,具体下来的话是src/client/kotlin/resourcessrc/main/kotlin/resources中的文件。主要用于处理fabric.mod.json,因为此文件中声明了你正在开发的模组的当前版本与所需的依赖模组信息(包括依赖模组的版本范围、Fabric Loader本身的版本范围、Minecraft本体的版本范围),而这些是需要在构建脚本中也用到的,并且很可能会随着你的开发而更改。既然如此,也就没必要在fabric.mod.json中重复写了,直接从脚本中获取然后解析进去即可。

如果你的模组添加了更多的资源文件,例如你添加了自己的方块、物品、生物等,你也需要把他们的贴图放在资源目录下,组织起assets/...的文件格式,这又涉及到Minecraft资源包的范畴了(或者说这是开发Minecraft资源包的前置知识?),总之这里不多做涉及了~

后续

未完待续……

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容