Merge pull request 'Build for Minecraft 26.1' (#50) from mc-26 into master

Reviewed-on: #50
This commit was merged in pull request #50.
This commit is contained in:
2026-09-30 19:21:30 +00:00
26 changed files with 290 additions and 68 deletions
+8 -3
View File
@@ -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
+8 -3
View File
@@ -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:
+2 -2
View File
@@ -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
+7 -5
View File
@@ -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/<mc>/`: each version's `gradle.properties` (Minecraft, Fabric Loader and Fabric API versions), plus its `build/` and `run/` folders. Each version is the Gradle project `:<mc>`.
`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/<new>/gradle.properties` with `minecraft_version`, `loader_version` and `fabric_api_version` (copy 1.21.11's and change the values; they're listed at <https://fabricmc.net/develop>). Loom's version is shared, in the root `gradle.properties`.
2. Add `<new>` to `stonecutterVersions` in `settings.gradle.kts`.
3. Build it with `./gradlew :<new>: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 :<new>:runClient`.
3. Build it with `./gradlew :<new>: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 :<new>: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.
+10 -7
View File
@@ -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/<v>/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<JavaCompile>().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 {
+3 -3
View File
@@ -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
# Loom, for every Minecraft version (read by loom-back-compat).
loomx.loom_version=1.17.21
+9 -7
View File
@@ -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,10 @@ include("common")
// 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.
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",
"26.1",
)
stonecutter {
create(rootProject) {
@@ -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,8 +91,27 @@ 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<Identifier, RenderType> UNLIT_CUTOUT = new HashMap<>();
*///?}
private static RenderType entityAlpha(Identifier texture) {
//? if >=1.21.11 {
//? if >=26.1 {
/*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
//return RenderType.dragonExplosionAlpha(texture);
@@ -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<Float> 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;
@@ -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<Float> 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) {
@@ -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()) {
@@ -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;
@@ -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;
}
}
@@ -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;
}
@@ -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);
@@ -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()));
@@ -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);
}
}
@@ -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,
@@ -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<Void> original) {
boolean saturnScreen = Minecraft.getInstance().screen instanceof SaturnScreenFabric;
original.call(saturnScreen ? SaturnScreenFabric.PANORAMA_RENDERER : cubeMap, xRot, yRot);
}
}
*///?} else {
@Mixin(Minecraft.class)
public class GuiRendererMixin {
}
//?}
@@ -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));
+1 -1
View File
@@ -22,7 +22,7 @@
],
"depends": {
"fabricloader": ">=${loader_version}",
"fabric": "*",
"fabric-api": "*",
"minecraft": "${minecraft_version}"
}
}
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -2,7 +2,7 @@
"required": true,
"minVersion": "0.8",
"package": "org.saturnclient.impl.mixins",
"compatibilityLevel": "JAVA_21",
"compatibilityLevel": "${java}",
"client": [
"IdentifierMixin",
"ItemStackMixin",
+3 -2
View File
@@ -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
+20 -8
View File
@@ -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)
}
}
}
@@ -69,17 +76,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")
}
}
+19
View File
@@ -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