Build with loom-back-compat and a Java toolchain per version

Prepares the build for 26.x, which ships unobfuscated and needs Java 25
(#39):

- loom-back-compat applies the remapping Loom to 1.21.x and would apply
  the plain one to 26.x. loom_version becomes loomx.loom_version.
- Each version compiles with its Java toolchain (21 for 1.21.x, 25 for
  26.x), and the mixin configs take their compatibility level from it.
  The foojay resolver downloads a missing JDK; CI installs 21 and 25.
- buildAll collects remapJar's output, or jar's where there's nothing
  to remap.
- fabric.mod.json depends on fabric-api, Fabric API's mod id; 26.x's
  jar no longer provides the old "fabric" alias.

Nothing changes for the 1.21.x jars.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-30 19:15:02 +02:00
co-authored by claude
parent 950a83c48d
commit b018db1851
10 changed files with 52 additions and 32 deletions
+8 -3
View File
@@ -19,15 +19,20 @@ jobs:
# The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's # The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's
# dependencies, Loom's Minecraft cache and the build cache in the runner's # dependencies, Loom's Minecraft cache and the build cache in the runner's
# actions cache. # actions cache.
- name: Set up Java 21 # 1.21.x builds with Java 21 and 26.x with Java 25. The last version listed is the default
# (Gradle runs on it), and the Gradle commands below point toolchain detection at both.
- name: Set up Java 25 and 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: |
25
21
cache: gradle cache: gradle
- name: Compile all versions - name: Compile all versions
run: | run: |
chmod +x gradlew chmod +x gradlew
# --continue reports every version that fails, not just the first. # --continue reports every version that fails, not just the first.
./gradlew compileJava --no-daemon --stacktrace --continue ./gradlew compileJava --no-daemon --stacktrace --continue \
-Porg.gradle.java.installations.fromEnv=JAVA_HOME_21_X64,JAVA_HOME_25_X64
+8 -3
View File
@@ -47,11 +47,15 @@ jobs:
# The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's # The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's
# dependencies, Loom's Minecraft cache and the build cache in the runner's # dependencies, Loom's Minecraft cache and the build cache in the runner's
# actions cache. # actions cache.
- name: Set up Java 21 # 1.21.x builds with Java 21 and 26.x with Java 25. The last version listed is the default
# (Gradle runs on it), and the Gradle commands below point toolchain detection at both.
- name: Set up Java 25 and 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: |
25
21
cache: gradle cache: gradle
- name: Build all versions - name: Build all versions
@@ -59,7 +63,8 @@ jobs:
chmod +x gradlew chmod +x gradlew
# buildAll remaps every Minecraft version (two at a time, to stay within the # buildAll remaps every Minecraft version (two at a time, to stay within the
# Gradle heap) into build/allJars. # Gradle heap) into build/allJars.
./gradlew buildAll --no-daemon --stacktrace ./gradlew buildAll --no-daemon --stacktrace \
-Porg.gradle.java.installations.fromEnv=JAVA_HOME_21_X64,JAVA_HOME_25_X64
- name: Upload jars to the release - name: Upload jars to the release
env: env:
+10 -7
View File
@@ -1,11 +1,14 @@
// Builds one Minecraft version from the shared src/. Stonecutter runs this for each version in // Builds one Minecraft version from the shared src/. Stonecutter runs this for each version in
// stonecutterVersions (settings.gradle.kts), with that version's versions/<v>/gradle.properties. // stonecutterVersions (settings.gradle.kts), with that version's versions/<v>/gradle.properties.
plugins { plugins {
id("net.fabricmc.fabric-loom-remap") id("dev.kikugie.loom-back-compat")
`maven-publish` `maven-publish`
} }
val minecraftVersion = property("minecraft_version") as String val minecraftVersion = property("minecraft_version") as String
// The Minecraft versions the jar loads on (fabric.mod.json); a range where hotfixes share a jar.
val minecraftCompat = findProperty("minecraft_compat") as String? ?: minecraftVersion
val javaVersion = if (sc.current.parsed >= "26.1") 25 else 21
val loaderVersion = property("loader_version") as String val loaderVersion = property("loader_version") as String
version = "${property("mod_version")}+$minecraftVersion" version = "${property("mod_version")}+$minecraftVersion"
@@ -17,7 +20,7 @@ base {
dependencies { dependencies {
minecraft("com.mojang:minecraft:$minecraftVersion") minecraft("com.mojang:minecraft:$minecraftVersion")
mappings(loom.officialMojangMappings()) loomx.applyMojangMappings()
modImplementation("net.fabricmc:fabric-loader:$loaderVersion") modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}")
@@ -42,23 +45,23 @@ dependencies {
tasks.processResources { tasks.processResources {
val values = mapOf( val values = mapOf(
"version" to project.version.toString(), "version" to project.version.toString(),
"minecraft_version" to minecraftVersion, "minecraft_version" to minecraftCompat,
"loader_version" to loaderVersion, "loader_version" to loaderVersion,
"java" to "JAVA_$javaVersion",
) )
inputs.properties(values) inputs.properties(values)
filteringCharset = "UTF-8" filteringCharset = "UTF-8"
filesMatching("fabric.mod.json") { expand(values) } filesMatching(listOf("fabric.mod.json", "*.mixins.json")) { expand(values) }
} }
tasks.withType<JavaCompile>().configureEach { tasks.withType<JavaCompile>().configureEach {
options.release = 21 options.release = javaVersion
} }
java { java {
// Loom attaches the sources jar to remapSourcesJar and build. // Loom attaches the sources jar to remapSourcesJar and build.
withSourcesJar() withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21 toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
targetCompatibility = JavaVersion.VERSION_21
} }
tasks.jar { tasks.jar {
+2 -2
View File
@@ -11,5 +11,5 @@ mod_version=0.1.4-beta
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes) # Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes)
emotes_version=0.1.2 emotes_version=0.1.2
# Loom, for every Minecraft version (read in settings.gradle.kts). # Loom, for every Minecraft version (read by loom-back-compat).
loom_version=1.17.21 loomx.loom_version=1.17.21
+8 -7
View File
@@ -5,16 +5,15 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
mavenCentral() mavenCentral()
} }
// Loom's version for build.gradle.kts, which can't read properties in its plugins block.
val loom_version: String by settings
plugins {
id("net.fabricmc.fabric-loom-remap") version loom_version
}
} }
plugins { plugins {
// Downloads the Java toolchain a version needs (Java 25 for 26.1+) when it isn't installed.
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
id("dev.kikugie.stonecutter") version "0.9.8" id("dev.kikugie.stonecutter") version "0.9.8"
// Picks the Loom variant per version: remapping for 1.21.x, plain for the unobfuscated 26.1+.
// Its version is loomx.loom_version in gradle.properties.
id("dev.kikugie.loom-back-compat") version "0.4.2"
} }
rootProject.name = "saturn-client" rootProject.name = "saturn-client"
@@ -23,7 +22,9 @@ include("common")
// Versions built by Stonecutter from the shared src/ (see stonecutter.gradle.kts). Each one's // Versions built by Stonecutter from the shared src/ (see stonecutter.gradle.kts). Each one's
// versions/<v>/gradle.properties holds its Minecraft, Fabric Loader and Fabric API versions. // versions/<v>/gradle.properties holds its Minecraft, Fabric Loader and Fabric API versions.
val stonecutterVersions = listOf("1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11") val stonecutterVersions = listOf(
"1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11",
)
stonecutter { stonecutter {
create(rootProject) { create(rootProject) {
+1 -1
View File
@@ -22,7 +22,7 @@
], ],
"depends": { "depends": {
"fabricloader": ">=${loader_version}", "fabricloader": ">=${loader_version}",
"fabric": "*", "fabric-api": "*",
"minecraft": "${minecraft_version}" "minecraft": "${minecraft_version}"
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.impl.features.mixins", "package": "org.saturnclient.impl.features.mixins",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"render.GameRendererMixin", "render.GameRendererMixin",
"render.LightmapTextureManagerMixin", "render.LightmapTextureManagerMixin",
+1 -1
View File
@@ -2,7 +2,7 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.impl.mixins", "package": "org.saturnclient.impl.mixins",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"IdentifierMixin", "IdentifierMixin",
"ItemStackMixin", "ItemStackMixin",
+3 -2
View File
@@ -2,14 +2,15 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.saturnclient.mixin", "package": "org.saturnclient.saturnclient.mixin",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"TabListMixin", "TabListMixin",
"PlayerEntityRendererMixin", "PlayerEntityRendererMixin",
"WhoAmI", "WhoAmI",
"DrawContextAccessor", "DrawContextAccessor",
"SplashOverlayMixin", "SplashOverlayMixin",
"SetScreenMixin" "SetScreenMixin",
"GuiRendererMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
+10 -5
View File
@@ -69,17 +69,22 @@ subprojects {
tasks.register("buildAll") { tasks.register("buildAll") {
group = "build" group = "build"
description = "Builds every Minecraft version's remapped jar and collects them in build/allJars" description = "Builds every Minecraft version's mod jar and collects them in build/allJars"
val remapJars = subprojects.flatMap { it.tasks.matching { task -> task.name == "remapJar" } } // The mod jar is remapJar's output for 1.21.x, and jar's for 26.1+, which isn't obfuscated.
dependsOn(remapJars) val modJars = subprojects.filter { it.name != "common" }.flatMap { project ->
val unobfuscated = project.name.substringBefore('.').toInt() >= 26
val task = if (unobfuscated) "jar" else "remapJar"
project.tasks.matching { it.name == task }
}
dependsOn(modJars)
doLast { doLast {
val outputDir = layout.buildDirectory.dir("allJars").get().asFile val outputDir = layout.buildDirectory.dir("allJars").get().asFile
outputDir.mkdirs() outputDir.mkdirs()
remapJars.map { (it as AbstractArchiveTask).archiveFile.get().asFile } modJars.map { (it as AbstractArchiveTask).archiveFile.get().asFile }
.filter { it.exists() && !it.name.contains("sources") } .filter { it.exists() && !it.name.contains("sources") }
.forEach { it.copyTo(outputDir.resolve(it.name), overwrite = true) } .forEach { it.copyTo(outputDir.resolve(it.name), overwrite = true) }
println("All remapped jars collected in: $outputDir") println("All mod jars collected in: $outputDir")
} }
} }