From b018db1851cb0665e3f26b8e117a7960b76af05b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 30 Sep 2026 19:15:02 +0200 Subject: [PATCH] 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 --- .gitea/workflows/check.yml | 11 ++++++++--- .gitea/workflows/release.yml | 11 ++++++++--- build.gradle.kts | 17 ++++++++++------- gradle.properties | 4 ++-- settings.gradle.kts | 15 ++++++++------- src/main/resources/fabric.mod.json | 2 +- src/main/resources/features.mixins.json | 2 +- src/main/resources/impl.mixins.json | 2 +- src/main/resources/saturnclient.mixins.json | 5 +++-- stonecutter.gradle.kts | 15 ++++++++++----- 10 files changed, 52 insertions(+), 32 deletions(-) diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml index c884a0c..28c1857 100644 --- a/.gitea/workflows/check.yml +++ b/.gitea/workflows/check.yml @@ -19,15 +19,20 @@ jobs: # 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 # 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 with: distribution: temurin - java-version: 21 + java-version: | + 25 + 21 cache: gradle - name: Compile all versions run: | chmod +x gradlew # --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 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 1f1f194..12bbcee 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -47,11 +47,15 @@ jobs: # 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 # 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 with: distribution: temurin - java-version: 21 + java-version: | + 25 + 21 cache: gradle - name: Build all versions @@ -59,7 +63,8 @@ jobs: chmod +x gradlew # buildAll remaps every Minecraft version (two at a time, to stay within the # 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 env: diff --git a/build.gradle.kts b/build.gradle.kts index afb70ae..90e9b88 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,14 @@ // Builds one Minecraft version from the shared src/. Stonecutter runs this for each version in // stonecutterVersions (settings.gradle.kts), with that version's versions//gradle.properties. plugins { - id("net.fabricmc.fabric-loom-remap") + id("dev.kikugie.loom-back-compat") `maven-publish` } 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 version = "${property("mod_version")}+$minecraftVersion" @@ -17,7 +20,7 @@ base { dependencies { minecraft("com.mojang:minecraft:$minecraftVersion") - mappings(loom.officialMojangMappings()) + loomx.applyMojangMappings() modImplementation("net.fabricmc:fabric-loader:$loaderVersion") modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") @@ -42,23 +45,23 @@ dependencies { tasks.processResources { val values = mapOf( "version" to project.version.toString(), - "minecraft_version" to minecraftVersion, + "minecraft_version" to minecraftCompat, "loader_version" to loaderVersion, + "java" to "JAVA_$javaVersion", ) inputs.properties(values) filteringCharset = "UTF-8" - filesMatching("fabric.mod.json") { expand(values) } + filesMatching(listOf("fabric.mod.json", "*.mixins.json")) { expand(values) } } tasks.withType().configureEach { - options.release = 21 + options.release = javaVersion } java { // Loom attaches the sources jar to remapSourcesJar and build. withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion = JavaLanguageVersion.of(javaVersion) } tasks.jar { diff --git a/gradle.properties b/gradle.properties index d37aedb..1e1dc8d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,5 +11,5 @@ mod_version=0.1.4-beta # Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes) emotes_version=0.1.2 -# Loom, for every Minecraft version (read in settings.gradle.kts). -loom_version=1.17.21 +# Loom, for every Minecraft version (read by loom-back-compat). +loomx.loom_version=1.17.21 diff --git a/settings.gradle.kts b/settings.gradle.kts index 8fcbb8a..6730426 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,16 +5,15 @@ pluginManagement { gradlePluginPortal() 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 { + // 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" + // 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" @@ -23,7 +22,9 @@ include("common") // Versions built by Stonecutter from the shared src/ (see stonecutter.gradle.kts). Each one's // versions//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 { create(rootProject) { diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b5d0419..bb90202 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -22,7 +22,7 @@ ], "depends": { "fabricloader": ">=${loader_version}", - "fabric": "*", + "fabric-api": "*", "minecraft": "${minecraft_version}" } } diff --git a/src/main/resources/features.mixins.json b/src/main/resources/features.mixins.json index 4c74195..6a42779 100644 --- a/src/main/resources/features.mixins.json +++ b/src/main/resources/features.mixins.json @@ -2,7 +2,7 @@ "required": true, "minVersion": "0.8", "package": "org.saturnclient.impl.features.mixins", - "compatibilityLevel": "JAVA_21", + "compatibilityLevel": "${java}", "client": [ "render.GameRendererMixin", "render.LightmapTextureManagerMixin", diff --git a/src/main/resources/impl.mixins.json b/src/main/resources/impl.mixins.json index 094792b..5420903 100644 --- a/src/main/resources/impl.mixins.json +++ b/src/main/resources/impl.mixins.json @@ -2,7 +2,7 @@ "required": true, "minVersion": "0.8", "package": "org.saturnclient.impl.mixins", - "compatibilityLevel": "JAVA_21", + "compatibilityLevel": "${java}", "client": [ "IdentifierMixin", "ItemStackMixin", diff --git a/src/main/resources/saturnclient.mixins.json b/src/main/resources/saturnclient.mixins.json index d831845..94b4c34 100644 --- a/src/main/resources/saturnclient.mixins.json +++ b/src/main/resources/saturnclient.mixins.json @@ -2,14 +2,15 @@ "required": true, "minVersion": "0.8", "package": "org.saturnclient.saturnclient.mixin", - "compatibilityLevel": "JAVA_21", + "compatibilityLevel": "${java}", "client": [ "TabListMixin", "PlayerEntityRendererMixin", "WhoAmI", "DrawContextAccessor", "SplashOverlayMixin", - "SetScreenMixin" + "SetScreenMixin", + "GuiRendererMixin" ], "injectors": { "defaultRequire": 1 diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 98b6560..e5a91ad 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -69,17 +69,22 @@ subprojects { tasks.register("buildAll") { 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" } } - dependsOn(remapJars) + // The mod jar is remapJar's output for 1.21.x, and jar's for 26.1+, which isn't obfuscated. + 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 { val outputDir = layout.buildDirectory.dir("allJars").get().asFile 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") } .forEach { it.copyTo(outputDir.resolve(it.name), overwrite = true) } - println("All remapped jars collected in: $outputDir") + println("All mod jars collected in: $outputDir") } }