From 950a83c48ddf2d56b16a5a9b63c3c9597f0e7a39 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 30 Sep 2026 19:14:46 +0200 Subject: [PATCH 1/4] Use emotes 0.1.2 It adds 26.1-26.3 builds and changes nothing for 1.21.x. Co-Authored-By: Claude Opus 5.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 65fad62..d37aedb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.configuration-cache=false mod_version=0.1.4-beta # Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes) -emotes_version=0.1.1 +emotes_version=0.1.2 # Loom, for every Minecraft version (read in settings.gradle.kts). loom_version=1.17.21 -- 2.54.0 From b018db1851cb0665e3f26b8e117a7960b76af05b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 30 Sep 2026 19:15:02 +0200 Subject: [PATCH 2/4] 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") } } -- 2.54.0 From 4fb48cc647f602a908a7d3a5634183f58c4923ba Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 30 Sep 2026 19:15:10 +0200 Subject: [PATCH 3/4] Build for Minecraft 26.1 Adds 26.1 as a Stonecutter version (step 3 of #39). Its jar also loads on the 26.1.1 and 26.1.2 hotfixes. Most of 26.1's changes are renames: the GUI's render methods became extract methods and GuiGraphics became GuiGraphicsExtractor, which Stonecutter replacements cover along with the moved render state package and GuiRenderState's add methods. The rest are version comments: - The menu panorama is drawn by GuiRenderer from vanilla's cube map; the new GuiRendererMixin swaps in Saturn's while a Saturn screen is open (empty on older versions). - The camera works out the FOV and aligns with its entity in alignWithEntity, so the zoom and freelook hooks move to CameraMixin. - Gamma and the menu blur are read while extracting render state. - The glass blur's post chain takes a Projection and its buffer. - The dragon's alpha render type is gone; capes use entityCutout. - Fabric API renamed KeyBindingHelper to KeyMappingHelper. The docs describe the 26.x build and Java 25. Co-Authored-By: Claude Opus 5.5 --- README.MD | 4 +- VERSION_GUIDE.md | 12 ++--- settings.gradle.kts | 1 + .../impl/cosmetics/utils/ShaderUtils.java | 5 ++- .../features/mixins/entity/CameraMixin.java | 44 ++++++++++++++----- .../mixins/render/GameRendererMixin.java | 22 +++++----- .../render/LightmapTextureManagerMixin.java | 10 +++++ .../features/mixins/render/RenderMixin.java | 3 ++ .../impl/features/render/ZoomFov.java | 19 ++++++++ .../impl/mixins/MinecraftClientMixin.java | 3 ++ .../saturnclient/impl/ui/GlassRenderer.java | 16 +++++++ .../saturnclient/impl/ui/RenderScopeImpl.java | 3 ++ .../impl/ui/SaturnScreenFabric.java | 22 +++++++++- .../saturnclient/event/KeyInputHandler.java | 11 ++++- .../saturnclient/mixin/GuiRendererMixin.java | 35 +++++++++++++++ .../mixin/SplashOverlayMixin.java | 10 ++++- stonecutter.gradle.kts | 13 ++++-- versions/26.1/gradle.properties | 19 ++++++++ 18 files changed, 217 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/saturnclient/impl/features/render/ZoomFov.java create mode 100644 src/main/java/org/saturnclient/saturnclient/mixin/GuiRendererMixin.java create mode 100644 versions/26.1/gradle.properties diff --git a/README.MD b/README.MD index c0c8aab..a13c2d1 100644 --- a/README.MD +++ b/README.MD @@ -18,7 +18,7 @@ Saturn Client is a free, open-source Minecraft client designed to enhance the gameplay experience while staying lightweight and customizable. Our goal is to build a clean, optimized client that the community can contribute to and improve together. -Saturn runs on Fabric and supports **Minecraft 1.21.4 to 1.21.11**, with one build per version. If something doesn't work on your version, please open an issue. +Saturn runs on Fabric and supports **Minecraft 1.21.4 to 1.21.11 and 26.1**, with one build per version (the 26.1 build also runs on 26.1.1 and 26.1.2). If something doesn't work on your version, please open an issue. ## Features @@ -51,7 +51,7 @@ cd saturnclient ./gradlew :1.21.11:runClient # launch a development client ``` -Building needs Java 21. +Building needs Java 21. The 26.x versions compile with Java 25, which Gradle downloads when it isn't installed. ## Contributing diff --git a/VERSION_GUIDE.md b/VERSION_GUIDE.md index febd0d4..431f8e0 100644 --- a/VERSION_GUIDE.md +++ b/VERSION_GUIDE.md @@ -4,21 +4,23 @@ How Saturn Client supports several Minecraft versions, and how to change or add ## Layout -Every supported Minecraft version (1.21.4 to 1.21.11) builds from one copy of the version-specific code with [Stonecutter](https://stonecutter.kikugie.dev/): +Every supported Minecraft version (1.21.4 to 1.21.11, and 26.1) builds from one copy of the version-specific code with [Stonecutter](https://stonecutter.kikugie.dev/): - `common/`: all version-independent code (mods, UI, cosmetics, the server client). It has no Minecraft dependency and reaches the game only through the interfaces in `org.saturnclient.common`. - `src/`: the version-specific code (providers, refs, mixins and the three mixin configs), one copy for every version. Where Minecraft's API differs between versions, the code goes behind version comments (see below). - `versions//`: each version's `gradle.properties` (Minecraft, Fabric Loader and Fabric API versions), plus its `build/` and `run/` folders. Each version is the Gradle project `:`. -`settings.gradle.kts` lists the versions in `stonecutterVersions`. The root `build.gradle.kts` builds each version, and `stonecutter.gradle.kts` configures the root project (shared repositories, the `common` wiring and `buildAll`). Loom's version is `loom_version` in the root `gradle.properties`. +`settings.gradle.kts` lists the versions in `stonecutterVersions`. The root `build.gradle.kts` builds each version, and `stonecutter.gradle.kts` configures the root project (shared repositories, the `common` wiring and `buildAll`). Loom's version is `loomx.loom_version` in the root `gradle.properties`. + +Minecraft is obfuscated up to 1.21.11 and ships with Mojang's names from 26.1. The `loom-back-compat` plugin (`loomx`) applies the matching Loom for each version: the remapping one for 1.21.x, whose jar is `remapJar`'s output, and the plain one for 26.x, whose jar is `jar`'s. 26.x also compiles with Java 25 (21 before), set per version in `build.gradle.kts`; Gradle downloads a missing JDK through the foojay resolver, and CI installs both. An optional `minecraft_compat` in a version's `gradle.properties` widens the Minecraft versions its jar loads on, as 26.1's does for its hotfixes. ## Mappings Every version is built with Mojang's official names (`loom.officialMojangMappings()`), the names Minecraft itself ships from 26.1 on. Yarn isn't used any more: 1.21.11 has the last Yarn release. -Mojang renamed a few classes within 1.21.x. `src/` uses the newest names, and `stonecutter.gradle.kts` lists replacements that swap in the older name when Stonecutter processes an older version (for example `Identifier`, which was `ResourceLocation` before 1.21.11, and `AvatarRenderState`, which was `PlayerRenderState` before 1.21.9). Add a replacement there when a rename touches many lines. A class that only moved package needs just a version comment on its import, and a renamed method a version comment where it's called. +Mojang renames things between versions. `src/` uses the active version's (1.21.11's) names, and `stonecutter.gradle.kts` lists replacements that swap in each version's name when Stonecutter processes it: for example `Identifier`, which was `ResourceLocation` before 1.21.11, `AvatarRenderState`, which was `PlayerRenderState` before 1.21.9, and `GuiGraphics`, which is `GuiGraphicsExtractor` from 26.1. Add a replacement there when a rename touches many lines. A class that only moved package needs just a version comment on its import, and a renamed method a version comment where it's called. -Released jars run on intermediary names (`method_1234`), not Mojang's, and Loom renames everything that refers to Minecraft when it builds them. So a mixin can't implement one of `common`'s Ref interfaces by relying on a vanilla method that happens to have the same name: the vanilla method is renamed in the jar, and the interface method is left without an implementation (`AbstractMethodError`). Shadow the vanilla method and implement the Ref method explicitly, as `WindowMixin` does. For the same reason, Ref methods mustn't share a name and parameters with a method of the class they're mixed into, or the mixin replaces the vanilla method in development. +Released 1.21.x jars run on intermediary names (`method_1234`), not Mojang's, and Loom renames everything that refers to Minecraft when it builds them. So a mixin can't implement one of `common`'s Ref interfaces by relying on a vanilla method that happens to have the same name: the vanilla method is renamed in the jar, and the interface method is left without an implementation (`AbstractMethodError`). Shadow the vanilla method and implement the Ref method explicitly, as `WindowMixin` does. For the same reason, Ref methods mustn't share a name and parameters with a method of the class they're mixed into, or the mixin replaces the vanilla method in development. ## Version-specific code with Stonecutter @@ -72,5 +74,5 @@ For bigger changes, `./gradlew buildAll` builds every version's jar into `build/ 1. Create `versions//gradle.properties` with `minecraft_version`, `loader_version` and `fabric_api_version` (copy 1.21.11's and change the values; they're listed at ). Loom's version is shared, in the root `gradle.properties`. 2. Add `` to `stonecutterVersions` in `settings.gradle.kts`. -3. Build it with `./gradlew ::compileJava` and put what Minecraft changed behind version comments. Check the mixin configs in `src/main/resources/*.mixins.json` as well: a mixin whose target changed fails at startup, not at compile time, so launch it with `./gradlew ::runClient`. +3. Build it with `./gradlew ::compileJava` and put what Minecraft changed behind version comments. Check the mixin configs in `src/main/resources/*.mixins.json` as well: a mixin whose target changed fails at startup (or when its class first loads, such as in a world), not at compile time, so launch it with `./gradlew ::runClient`. 4. The release workflow builds everything through `buildAll`, and "Publish to Modrinth" takes the game version from each jar's name, so neither needs changing. diff --git a/settings.gradle.kts b/settings.gradle.kts index 6730426..26c4dc0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,6 +24,7 @@ include("common") // 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", + "26.1", ) stonecutter { diff --git a/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java b/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java index 4284c36..e6e737c 100644 --- a/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java +++ b/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java @@ -88,7 +88,10 @@ public class ShaderUtils { } private static RenderType entityAlpha(Identifier texture) { - //? if >=1.21.11 { + // 26.1 dropped the dragon's alpha pipeline; with opaque vertices it's an alpha cutout. + //? if >=26.1 { + /*return RenderTypes.entityCutout(texture); + *///?} else if >=1.21.11 { return RenderTypes.dragonExplosionAlpha(texture); //?} else //return RenderType.dragonExplosionAlpha(texture); diff --git a/src/main/java/org/saturnclient/impl/features/mixins/entity/CameraMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/entity/CameraMixin.java index 82d71ec..e0a8c5e 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/entity/CameraMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/entity/CameraMixin.java @@ -1,6 +1,7 @@ package org.saturnclient.impl.features.mixins.entity; import org.saturnclient.impl.features.entity.CameraOverriddenEntity; +import org.saturnclient.impl.features.render.ZoomFov; import org.saturnclient.mod.mods.FreelookMod; import org.saturnclient.saturnclient.SaturnClient; import org.spongepowered.asm.mixin.Mixin; @@ -9,6 +10,8 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +//? if >=26.1 +//import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.Camera; @@ -26,17 +29,38 @@ public abstract class CameraMixin { @Shadow protected abstract void setRotation(float yaw, float pitch); + // Freelook keeps the camera's rotation after vanilla points it along the entity's view. From 26.1 + // that happens in alignWithEntity, which takes the camera's own entity. + //? if >=26.1 { + /*@Shadow + public abstract Entity entity(); + + @Inject(method = "alignWithEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER)) + public void lockRotation(float tickDelta, CallbackInfo ci) { + saturn$lockRotation(entity()); + } + + // The camera works out the FOV from 26.1 (GameRenderer.getFov before). + @Inject(method = "calculateFov", at = @At("RETURN"), cancellable = true) + private void onCalculateFov(float tickDelta, CallbackInfoReturnable cir) { + cir.setReturnValue(ZoomFov.apply(cir.getReturnValue())); + } + *///?} else if >=1.21.11 { @Inject(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER)) - public void lockRotation( - //? if >=1.21.11 { - Level focusedBlock, - //?} else - //BlockGetter focusedBlock, - Entity cameraEntity, - boolean isThirdPerson, - boolean isFrontFacing, - float tickDelta, - CallbackInfo ci) { + public void lockRotation(Level focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing, + float tickDelta, CallbackInfo ci) { + saturn$lockRotation(cameraEntity); + } + //?} else { + /*@Inject(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER)) + public void lockRotation(BlockGetter focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing, + float tickDelta, CallbackInfo ci) { + saturn$lockRotation(cameraEntity); + } + *///?} + + @Unique + private void saturn$lockRotation(Entity cameraEntity) { if (FreelookMod.isFreeLooking() && cameraEntity instanceof LocalPlayer) { CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity; diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java index 79c1eaf..a8f8bbe 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java @@ -8,8 +8,7 @@ import net.minecraft.client.renderer.GameRenderer; //? if >=1.21.6 import org.saturnclient.impl.ui.SaturnScreenFabric; -import org.saturnclient.mod.mods.FreelookMod; -import org.saturnclient.mod.mods.ZoomMod; +import org.saturnclient.impl.features.render.ZoomFov; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,11 +17,11 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; /** - * Overrides the game's field-of-view when {@link ZoomMod} is active. + * Overrides the game's field-of-view when {@link org.saturnclient.mod.mods.ZoomMod} is active. * * The old code checked both {@code Zoom.isZooming} and * {@code Zoom.shouldZoom()} separately. The refactored - * {@link ZoomMod#shouldZoom()} already ORs both conditions + * {@link org.saturnclient.mod.mods.ZoomMod#shouldZoom()} already ORs both conditions * ({@code enabled.value && isZooming}), so only one check is needed. * * No {@link org.saturnclient.common.module.ModProvider} is required @@ -36,20 +35,21 @@ public class GameRendererMixin { * After the vanilla FOV has been computed, divide it by the zoom * level to produce the narrowed field-of-view. */ + // From 26.1 the camera works out the FOV, so CameraMixin applies the zoom there. + //? if <26.1 { @Inject(method = "getFov", at = @At("RETURN"), cancellable = true) private void onGetFov(CallbackInfoReturnable cir) { - if (ZoomMod.shouldZoom()) { - float zoomedFov = cir.getReturnValue() / ZoomMod.getZoomLevel(); - cir.setReturnValue(zoomedFov); - } else if (FreelookMod.shouldZoomOut()) { - float zoomedFov = cir.getReturnValue() / FreelookMod.getZoomLevel(); - cir.setReturnValue(zoomedFov); - } + cir.setReturnValue(ZoomFov.apply(cir.getReturnValue())); } + //?} //? if >=1.21.6 { // From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. + // From 26.1 the setting is read while extracting the options' render state. + //? if >=26.1 + //@Redirect(method = "extractOptions", at = @At(value = "INVOKE", + //? if <26.1 @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Options;getMenuBackgroundBlurriness()I")) private int useSaturnBlur(Options options) { diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/LightmapTextureManagerMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/LightmapTextureManagerMixin.java index d07ea86..466c32d 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/LightmapTextureManagerMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/LightmapTextureManagerMixin.java @@ -3,6 +3,9 @@ package org.saturnclient.impl.features.mixins.render; // Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +//? if >=26.1 { +/*import net.minecraft.client.renderer.LightmapRenderStateExtractor; +*///?} else import net.minecraft.client.renderer.LightTexture; import org.saturnclient.mod.mods.FullbrightMod; @@ -18,6 +21,10 @@ import org.spongepowered.asm.mixin.injection.At; * it is now {@link FullbrightMod}. The injection target and * strategy are unchanged. */ +// From 26.1 the gamma is worked out while extracting the lightmap's render state. +//? if >=26.1 { +/*@Mixin(LightmapRenderStateExtractor.class) +*///?} else @Mixin(LightTexture.class) public class LightmapTextureManagerMixin { @@ -25,6 +32,9 @@ public class LightmapTextureManagerMixin { * If fullbright is active, substitute the feature's brightness * value for whatever vanilla calculated. */ + //? if >=26.1 { + /*@ModifyExpressionValue(method = "extract", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) + *///?} else @ModifyExpressionValue(method = "updateLightTexture", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0)) private float modifyGamma(float original) { if (FullbrightMod.shouldOverrideBrightness()) { diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/RenderMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/RenderMixin.java index acec77b..b827d0d 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/RenderMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/RenderMixin.java @@ -40,6 +40,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Gui.class) public class RenderMixin { + //? if >=26.1 { + /*@Inject(method = "extractChat", at = @At("TAIL")) + *///?} else @Inject(method = "renderChat", at = @At("TAIL")) public void renderMods(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci) { Font textRenderer = SaturnClient.client.font; diff --git a/src/main/java/org/saturnclient/impl/features/render/ZoomFov.java b/src/main/java/org/saturnclient/impl/features/render/ZoomFov.java new file mode 100644 index 0000000..ae67f57 --- /dev/null +++ b/src/main/java/org/saturnclient/impl/features/render/ZoomFov.java @@ -0,0 +1,19 @@ +package org.saturnclient.impl.features.render; + +import org.saturnclient.mod.mods.FreelookMod; +import org.saturnclient.mod.mods.ZoomMod; + +/** The zoom mods' change to the field of view, applied where each version computes it. */ +public final class ZoomFov { + private ZoomFov() { + } + + public static float apply(float fov) { + if (ZoomMod.shouldZoom()) { + return fov / ZoomMod.getZoomLevel(); + } else if (FreelookMod.shouldZoomOut()) { + return fov / FreelookMod.getZoomLevel(); + } + return fov; + } +} diff --git a/src/main/java/org/saturnclient/impl/mixins/MinecraftClientMixin.java b/src/main/java/org/saturnclient/impl/mixins/MinecraftClientMixin.java index 8d4754f..b34a78e 100644 --- a/src/main/java/org/saturnclient/impl/mixins/MinecraftClientMixin.java +++ b/src/main/java/org/saturnclient/impl/mixins/MinecraftClientMixin.java @@ -92,6 +92,9 @@ public abstract class MinecraftClientMixin implements MinecraftClientRef { break; case Options: + //? if >=26.1 { + /*this.setScreen(new OptionsScreen(exitTarget, this.options, ((Minecraft) (Object) this).level != null)); + *///?} else this.setScreen(new OptionsScreen(exitTarget, this.options)); break; } diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java index fc4149d..fbcd17a 100644 --- a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -37,6 +37,10 @@ import net.minecraft.client.renderer.PostChain; import net.minecraft.client.renderer.RenderPipelines; import com.mojang.blaze3d.pipeline.TextureTarget; import net.minecraft.client.renderer.LevelTargetBundle; +//? if >=26.1 { +/*import net.minecraft.client.renderer.Projection; +import net.minecraft.client.renderer.ProjectionMatrixBuffer; +*///?} else import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; import net.minecraft.client.renderer.texture.AbstractTexture; import net.minecraft.client.gui.render.TextureSetup; @@ -54,6 +58,15 @@ public final class GlassRenderer { .build(); private static final Identifier BLUR = Identifier.withDefaultNamespace("post_effect/blur.json"); + //? if >=26.1 { + /*private static final Projection PROJECTION = new Projection(); + private static final ProjectionMatrixBuffer PROJECTION_BUFFER = new ProjectionMatrixBuffer("saturn glass"); + + static { + // As vanilla sets up its post chains' projection; the chain sizes it when it runs. + PROJECTION.setupOrtho(0.1f, 1000f, 1f, 1f, false); + } + *///?} else private static final CachedOrthoProjectionMatrixBuffer PROJECTION = new CachedOrthoProjectionMatrixBuffer("saturn glass", 0.1f, 1000f, false); // For the blurs' intermediate targets, freed once unused for a few frames private static final CrossFrameResourcePool POOL = new CrossFrameResourcePool(3); @@ -140,6 +153,9 @@ public final class GlassRenderer { PostChainConfig pipeline = PostChainConfig.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); return PostChain.load(pipeline, SaturnClient.client.getTextureManager(), + //? if >=26.1 { + /*LevelTargetBundle.MAIN_TARGETS, Identifier.fromNamespaceAndPath("saturnclient", "glass_blur_" + blur), PROJECTION, PROJECTION_BUFFER); + *///?} else LevelTargetBundle.MAIN_TARGETS, Identifier.fromNamespaceAndPath("saturnclient", "glass_blur_" + blur), PROJECTION); } catch (Exception e) { SaturnClient.LOGGER.error("Couldn't load the glass blur", e); diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index 806cc20..5d30546 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -325,6 +325,9 @@ public class RenderScopeImpl implements RenderScope { entity, seed); try { + //? if >=26.1 { + /*this.state.submitItem(new GuiItemRenderState( + *///?} else this.state.submitItem(new GuiItemRenderState(stack.getItem().getName().toString(), new Matrix3x2f(this.matrices.stack), keyedItemRenderState, x, y, this.scissorStack.peekLast())); diff --git a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java index 1cf7ee6..ede40e2 100644 --- a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java +++ b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java @@ -13,6 +13,9 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.renderer.CubeMap; import net.minecraft.client.gui.GuiGraphics; +//? if >=26.1 { +/*import net.minecraft.client.renderer.Panorama; +*///?} else import net.minecraft.client.renderer.PanoramaRenderer; import net.minecraft.client.gui.screens.Screen; //? if >=1.21.6 { @@ -34,13 +37,18 @@ import net.minecraft.resources.Identifier; public class SaturnScreenFabric extends Screen implements ScreenProvider { protected static final Identifier PANORAMA = Identifier.fromNamespaceAndPath("saturnclient", "textures/gui/title/background/panorama"); - protected static final CubeMap PANORAMA_RENDERER = new CubeMap(PANORAMA); + public static final CubeMap PANORAMA_RENDERER = new CubeMap(PANORAMA); + // From 26.1 vanilla draws the panorama from its own cube map; GuiRendererMixin swaps in Saturn's. + //? if >=26.1 { + /*public static final Panorama ROTATING_PANORAMA_RENDERER = new Panorama(); + *///?} else { public static final PanoramaRenderer ROTATING_PANORAMA_RENDERER; static { ROTATING_PANORAMA_RENDERER = new PanoramaRenderer(PANORAMA_RENDERER); } + //?} //? if <1.21.6 //private final CrossFrameResourcePool pool = new CrossFrameResourcePool(3); @@ -81,6 +89,9 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { //? if <1.21.6 //@SuppressWarnings("deprecation") @Override + //? if >=26.1 { + /*public void extractRenderState(GuiGraphics context, int mouseX, int mouseY, float delta) { + *///?} else public void render(GuiGraphics context, int mouseX, int mouseY, float delta) { if (screen.start == null) { screen.start = Instant.now(); @@ -128,10 +139,16 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { // Vanilla's background would cover Saturn's panorama, and blurs by the vanilla setting. The // strength of this blur comes from GameRendererMixin, which gives Saturn's setting instead. @Override + //? if >=26.1 { + /*public void extractBackground(GuiGraphics context, int mouseX, int mouseY, float delta) { + *///?} else public void renderBackground(GuiGraphics context, int mouseX, int mouseY, float delta) { // The panorama renderer has no opacity (and takes no delta) from 1.21.6, so it's drawn fully // opaque. if (isShowingPanorama()) { + //? if >=26.1 { + /*ROTATING_PANORAMA_RENDERER.extractRenderState(context, this.width, this.height, true); + *///?} else ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true); } @@ -140,6 +157,9 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { } if (minecraft.level != null) { + //? if >=26.1 { + /*extractMenuBackground(context); + *///?} else renderMenuBackground(context); } } diff --git a/src/main/java/org/saturnclient/saturnclient/event/KeyInputHandler.java b/src/main/java/org/saturnclient/saturnclient/event/KeyInputHandler.java index c54d853..bd5bdbb 100644 --- a/src/main/java/org/saturnclient/saturnclient/event/KeyInputHandler.java +++ b/src/main/java/org/saturnclient/saturnclient/event/KeyInputHandler.java @@ -1,6 +1,9 @@ package org.saturnclient.saturnclient.event; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +//? if >=26.1 { +/*import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; +*///?} else import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.KeyMapping; import com.mojang.blaze3d.platform.InputConstants; @@ -32,7 +35,13 @@ public class KeyInputHandler { private static KeyMapping mainMenuKeyBinding; public static void register() { - //? if >=1.21.9 { + //? if >=26.1 { + /*mainMenuKeyBinding = KeyMappingHelper.registerKeyMapping( + new KeyMapping( + MAIN_MENU_KEY_ID, + DEFAULT_MENU_KEY, + KeyMapping.Category.register(KEY_CATEGORY))); + *///?} else if >=1.21.9 { mainMenuKeyBinding = KeyBindingHelper.registerKeyBinding( new KeyMapping( MAIN_MENU_KEY_ID, diff --git a/src/main/java/org/saturnclient/saturnclient/mixin/GuiRendererMixin.java b/src/main/java/org/saturnclient/saturnclient/mixin/GuiRendererMixin.java new file mode 100644 index 0000000..2cdd373 --- /dev/null +++ b/src/main/java/org/saturnclient/saturnclient/mixin/GuiRendererMixin.java @@ -0,0 +1,35 @@ +package org.saturnclient.saturnclient.mixin; + +//? if >=26.1 { +/*import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.render.GuiRenderer; +import net.minecraft.client.renderer.CubeMap; +import org.saturnclient.impl.ui.SaturnScreenFabric; +import org.spongepowered.asm.mixin.injection.At; +*///?} else +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; + +/** + * From 26.1 GuiRenderer draws the menu panorama from its own cube map, which is vanilla's. While a + * Saturn screen is open, this draws Saturn's panorama instead. Before 26.1 SaturnScreenFabric draws + * the panorama itself, so this mixin is empty there, and it targets Minecraft because GuiRenderer + * doesn't exist before 1.21.6. It stays registered, keeping saturnclient.mixins.json the same for + * every version. + */ +//? if >=26.1 { +/*@Mixin(GuiRenderer.class) +public class GuiRendererMixin { + @WrapOperation(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/CubeMap;render(FF)V")) + private void saturn$drawSaturnPanorama(CubeMap cubeMap, float xRot, float yRot, Operation original) { + boolean saturnScreen = Minecraft.getInstance().screen instanceof SaturnScreenFabric; + original.call(saturnScreen ? SaturnScreenFabric.PANORAMA_RENDERER : cubeMap, xRot, yRot); + } +} +*///?} else { +@Mixin(Minecraft.class) +public class GuiRendererMixin { +} +//?} diff --git a/src/main/java/org/saturnclient/saturnclient/mixin/SplashOverlayMixin.java b/src/main/java/org/saturnclient/saturnclient/mixin/SplashOverlayMixin.java index 9772149..791a20f 100644 --- a/src/main/java/org/saturnclient/saturnclient/mixin/SplashOverlayMixin.java +++ b/src/main/java/org/saturnclient/saturnclient/mixin/SplashOverlayMixin.java @@ -54,6 +54,9 @@ public abstract class SplashOverlayMixin { * @reason Replace vanilla splash rendering completely but keep exit logic */ @Overwrite + //? if >=26.1 { + /*public void extractRenderState(GuiGraphics context, int mouseX, int mouseY, float delta) { + *///?} else public void render(GuiGraphics context, int mouseX, int mouseY, float delta) { int screenWidth = context.guiWidth(); int screenHeight = context.guiHeight(); @@ -116,6 +119,9 @@ public abstract class SplashOverlayMixin { private void renderCurrentScreen(GuiGraphics context, int mouseX, int mouseY, float delta) { if (minecraft.screen != null) { + //? if >=26.1 { + /*minecraft.screen.extractRenderState(context, mouseX, mouseY, delta); + *///?} else minecraft.screen.render(context, mouseX, mouseY, delta); } } @@ -129,7 +135,9 @@ public abstract class SplashOverlayMixin { private void renderProgressBar(RenderScope scope, GuiGraphics context, int screenWidth, int screenHeight, int alpha) { int progressY = ((screenHeight - 5) / 2) + 25; - //? if >=1.21.11 { + //? if >=26.1 { + /*context.outline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF)); + *///?} else if >=1.21.11 { context.renderOutline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF)); //?} else if >=1.21.9 { /*context.submitOutline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF)); diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index e5a91ad..f6092e1 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -6,16 +6,23 @@ plugins { // "Reset active project" before committing. stonecutter active "1.21.11" -// src/ uses the newest Mojang names. These classes were renamed in the versions given, so Stonecutter -// swaps in the older names when it processes an older version. +// src/ uses 1.21.11's Mojang names (the active version). These names changed in the versions given, +// so Stonecutter swaps in each version's name when it processes it. stonecutter parameters { for ((since, old, new) in listOf( Triple("1.21.11", "ResourceLocation", "Identifier"), Triple("1.21.9", "PlayerRenderer", "AvatarRenderer"), Triple("1.21.9", "PlayerRenderState", "AvatarRenderState"), + Triple("26.1", "GuiGraphics", "GuiGraphicsExtractor"), + Triple("26.1", "net.minecraft.client.gui.render.state", "net.minecraft.client.renderer.state.gui"), + // GuiRenderState's methods + Triple("26.1", "submitGuiElement", "addGuiElement"), + Triple("26.1", "submitText", "addText"), + Triple("26.1", "submitItem", "addItem"), + Triple("26.1", "submitPicturesInPictureState", "addPicturesInPictureState"), )) { replacements.regex(eval(current.version, ">=$since")) { - replace("\\b$old\\b", new, "\\b$new\\b", old) + replace("\\b${Regex.escape(old)}\\b", new, "\\b${Regex.escape(new)}\\b", old) } } } diff --git a/versions/26.1/gradle.properties b/versions/26.1/gradle.properties new file mode 100644 index 0000000..01554f7 --- /dev/null +++ b/versions/26.1/gradle.properties @@ -0,0 +1,19 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349 +org.gradle.configuration-cache=false + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=26.1 +# Built against 26.1 and allowed on its hotfixes (26.1.1, 26.1.2). +minecraft_compat=~26.1 +loader_version=0.19.5 + +fabric_api_version=0.145.1+26.1 + +# Mod Properties +maven_group=org.saturnclient +archives_base_name=saturn-client -- 2.54.0 From 1fdbf82abdd4f7dc7153b5d9f57ec8b07e042664 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 30 Sep 2026 19:26:59 +0200 Subject: [PATCH 4/4] Draw hats and cloaks unlit on 26.1 Before 26.1 they use the dragon's alpha pipeline, whose shader only samples the texture. entityCutout, which 26.1 used instead, applies the lightmap and directional shading, so shadows and light from the world showed on them. 26.1 builds the same unlit cutout from the entity shader: the emissive snippet without the lightmap, plus NO_CARDINAL_LIGHTING and NO_OVERLAY. Co-Authored-By: Claude Opus 5.5 --- .../impl/cosmetics/utils/ShaderUtils.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java b/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java index e6e737c..40bab50 100644 --- a/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java +++ b/src/main/java/org/saturnclient/impl/cosmetics/utils/ShaderUtils.java @@ -26,6 +26,10 @@ import net.minecraft.client.renderer.blockentity.AbstractEndPortalRenderer; import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer; *///?} import net.minecraft.resources.Identifier; +//? if >=26.1 { +/*import java.util.HashMap; +import java.util.Map; +*///?} public class ShaderUtils { // Render pipelines exist from 1.21.5; before that the layer names a shader program directly. @@ -87,10 +91,26 @@ public class ShaderUtils { } } + // Cosmetics are drawn unlit, without the lightmap or the directional shading, as the dragon's + // alpha pipeline drew them before 26.1. 26.1 dropped that pipeline, so this is the entity shader + // with its lighting and overlay turned off. + //? if >=26.1 { + /*public static final RenderPipeline UNLIT_CUTOUT_PIPELINE = RenderPipelines.register( + RenderPipeline.builder(new RenderPipeline.Snippet[] { + RenderPipelines.ENTITY_EMISSIVE_SNIPPET }) + .withLocation(Identifier.fromNamespaceAndPath("saturnclient", "pipeline/unlit_cutout")) + .withShaderDefine("ALPHA_CUTOUT", 0.1f) + .withShaderDefine("NO_CARDINAL_LIGHTING") + .withShaderDefine("NO_OVERLAY") + .withCull(false) + .build()); + private static final Map UNLIT_CUTOUT = new HashMap<>(); + *///?} + private static RenderType entityAlpha(Identifier texture) { - // 26.1 dropped the dragon's alpha pipeline; with opaque vertices it's an alpha cutout. //? if >=26.1 { - /*return RenderTypes.entityCutout(texture); + /*return UNLIT_CUTOUT.computeIfAbsent(texture, id -> RenderType.create("saturn_unlit_cutout", + RenderSetup.builder(UNLIT_CUTOUT_PIPELINE).withTexture("Sampler0", id).createRenderSetup())); *///?} else if >=1.21.11 { return RenderTypes.dragonExplosionAlpha(texture); //?} else -- 2.54.0