Compare commits

..
16 Commits
Author SHA1 Message Date
selimaj-dev 7b312f54ae Merge pull request 'Bump the version to 0.1.5-beta' (#53) from release-0.1.5-beta into master
Release / build (release) Successful in 4m38s
Reviewed-on: #53
2026-09-30 20:34:50 +00:00
selimaj-devandclaude a1dcd25710 Bump the version to 0.1.5-beta
Check / compile (pull_request) Successful in 2m57s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 22:33:22 +02:00
selimaj-dev 068c15d781 Merge pull request 'Build for Minecraft 26.3' (#52) from mc-26.3 into master
Reviewed-on: #52
2026-09-30 20:31:32 +00:00
selimaj-devandclaude 46c7bacd04 Build for Minecraft 26.3
Check / compile (pull_request) Successful in 16m19s
Adds 26.3 as a Stonecutter version, the last step of #39.

Input: 26.3 replaces GLFW with SDL, which numbers keys (scancodes),
modifiers and mouse buttons differently. Saturn keeps GLFW's numbers,
which common uses and the config's keybinds store, so saved keybinds
stay valid. The new Keys class converts at the boundary: key events,
modifiers and mouse buttons coming into Saturn's screens, and key codes
going to InputConstants. Its table pairs GLFW's codes with 26.3's
InputConstants of the same name. Key names come from the game on 26.3,
and version-specific code uses InputConstants' key constants rather
than GLFW's.

Rendering: Blaze3D's API moved to renderpearl, which Stonecutter
replacements cover. PoseStack's quaternion mulPose became rotate, and
TextureTarget takes a depth format instead of a flag. 26.3 compiles
shaders through SPIR-V and reorders the transforms block, so glass.fsh
takes explicit locations and the new order behind defines that
GlassRenderer sets, and the unlit cosmetics pipeline declares its
color target, which 26.3 requires.

OptionsScreen no longer takes whether a world is open.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 22:12:50 +02:00
selimaj-dev effdbe1c84 Merge pull request 'Build for Minecraft 26.2 and fix the glass blur on 26.x' (#51) from mc-26.2 into master
Reviewed-on: #51
2026-09-30 19:51:07 +00:00
selimaj-devandclaude 4b776663bb Point common at the removal of the old theme color upgrade
Check / compile (pull_request) Successful in 15m55s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:49:25 +02:00
selimaj-devandclaude 86f326a8b4 Blur the glass backdrop once the frame's world is drawn on 26.x
From 26.1 the GUI is built before the frame is drawn. The glass copies
the main framebuffer while the GUI is built, so it copied the last
frame, glass included, and each frame blurred the previous blur until
it bloomed.

On 26.1+ captureBackdrop now only prepares the backdrop and queues its
blur. GuiRendererMixin copies and blurs the queued backdrops as the GUI
renderer is about to draw, with the world and panorama drawn and no GUI
yet. Pending backdrops are never released before they're drawn.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:46:14 +02:00
selimaj-devandclaude 6b1b79062c Note in the version guide that version blocks can nest
Stonecutter 0.9 writes an inactive block inside another as /^ ... ^/,
so nesting works (GlassRenderer relies on it since 26.1). else if is
still clearer for several ranges of the same code.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:31:32 +02:00
selimaj-devandclaude a4ed30111c Build for Minecraft 26.2
Adds 26.2 as a Stonecutter version (#39).

26.2 splits the HUD out of Gui into Hud and moves the open screen, the
overlay and the toasts from Minecraft to Gui. A new Screens helper gets
and sets the screen and clears the overlay on every version, and
MinecraftClientMixin opens screens through it instead of shadowing
Minecraft.setScreen. SetScreenMixin targets Gui.setScreen and the HUD
hook Hud.extractChat on 26.2.

26.2's rendering API declares samplers through bind group layouts and
gives render targets a GpuFormat, so the glass pipeline adds SAMPLER1's
layout and its backdrops are RGBA8_UNORM like vanilla's targets. The
main render target is the game renderer's, the panorama no longer takes
a fade flag, and toasts are added with SystemToast.add.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:31:32 +02:00
selimaj-dev bd58bfd0a0 Merge pull request 'Build for Minecraft 26.1' (#50) from mc-26 into master
Reviewed-on: #50
2026-09-30 19:21:30 +00:00
selimaj-devandclaude 1fdbf82abd Draw hats and cloaks unlit on 26.1
Check / compile (pull_request) Successful in 13m28s
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 <[email protected]>
2026-09-30 19:26:59 +02:00
selimaj-devandclaude 4fb48cc647 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 <[email protected]>
2026-09-30 19:15:10 +02:00
selimaj-devandclaude b018db1851 Build with loom-back-compat and a Java toolchain per version
Prepares the build for 26.x, which ships unobfuscated and needs Java 25
(#39):

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

Nothing changes for the 1.21.x jars.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 19:15:02 +02:00
selimaj-devandclaude 950a83c48d 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 <[email protected]>
2026-09-30 19:14:46 +02:00
selimaj-dev a7b2e3be8e Merge pull request 'Build every version with Mojang mappings instead of Yarn' (#49) from mojang-mappings into master
Reviewed-on: #49
2026-09-30 16:17:05 +00:00
selimaj-devandclaude 55067e9761 Point common at its master merge commit
Check / compile (pull_request) Successful in 11m53s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 18:04:12 +02:00
38 changed files with 755 additions and 109 deletions
+8 -3
View File
@@ -19,15 +19,20 @@ jobs:
# The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's # The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's
# dependencies, Loom's Minecraft cache and the build cache in the runner's # dependencies, Loom's Minecraft cache and the build cache in the runner's
# actions cache. # actions cache.
- name: Set up Java 21 # 1.21.x builds with Java 21 and 26.x with Java 25. The last version listed is the default
# (Gradle runs on it), and the Gradle commands below point toolchain detection at both.
- name: Set up Java 25 and 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: |
25
21
cache: gradle cache: gradle
- name: Compile all versions - name: Compile all versions
run: | run: |
chmod +x gradlew chmod +x gradlew
# --continue reports every version that fails, not just the first. # --continue reports every version that fails, not just the first.
./gradlew compileJava --no-daemon --stacktrace --continue ./gradlew compileJava --no-daemon --stacktrace --continue \
-Porg.gradle.java.installations.fromEnv=JAVA_HOME_21_X64,JAVA_HOME_25_X64
+8 -3
View File
@@ -47,11 +47,15 @@ jobs:
# The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's # The JDK comes from the runner's tool cache. `cache: gradle` stores Gradle's
# dependencies, Loom's Minecraft cache and the build cache in the runner's # dependencies, Loom's Minecraft cache and the build cache in the runner's
# actions cache. # actions cache.
- name: Set up Java 21 # 1.21.x builds with Java 21 and 26.x with Java 25. The last version listed is the default
# (Gradle runs on it), and the Gradle commands below point toolchain detection at both.
- name: Set up Java 25 and 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
distribution: temurin distribution: temurin
java-version: 21 java-version: |
25
21
cache: gradle cache: gradle
- name: Build all versions - name: Build all versions
@@ -59,7 +63,8 @@ jobs:
chmod +x gradlew chmod +x gradlew
# buildAll remaps every Minecraft version (two at a time, to stay within the # buildAll remaps every Minecraft version (two at a time, to stay within the
# Gradle heap) into build/allJars. # Gradle heap) into build/allJars.
./gradlew buildAll --no-daemon --stacktrace ./gradlew buildAll --no-daemon --stacktrace \
-Porg.gradle.java.installations.fromEnv=JAVA_HOME_21_X64,JAVA_HOME_25_X64
- name: Upload jars to the release - name: Upload jars to the release
env: env:
+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. 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. 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 to 26.3**, 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 ## Features
@@ -51,7 +51,7 @@ cd saturnclient
./gradlew :1.21.11:runClient # launch a development client ./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 ## Contributing
+12 -6
View File
@@ -4,21 +4,27 @@ How Saturn Client supports several Minecraft versions, and how to change or add
## Layout ## 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 to 26.3) 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`. - `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). - `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>`. - `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 ## 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. 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, and only for a name that appears nowhere else in `src/`: replacements work both ways, so the new name is also turned back into the old one for older versions (a `mulPose`/`rotate` replacement would rename `MatrixStackRef.rotate` too). From 26.2 the open screen and overlay live on `Gui` rather than `Minecraft`, so code goes through `impl.ui.Screens` for them.
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. From 26.3 Minecraft uses SDL instead of GLFW, with SDL's numbers for keys (scancodes), modifiers and mouse buttons. Saturn keeps GLFW's numbers, which `common` uses and the config's keybinds store, and `impl.provider.Keys` converts at the boundary: key events and mouse buttons coming in, and key codes going to the game. Use `InputConstants`' key constants rather than GLFW's in version-specific code.
26.3 also compiles every shader through SPIR-V, so shader inputs and outputs need an explicit `layout(location = …)`. Saturn's `glass.fsh` is shared by every version from 1.21.6 and switches its layout with the defines `GlassRenderer` sets, as it already did for `HAS_LINE_WIDTH`. 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 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 ## Version-specific code with Stonecutter
@@ -48,7 +54,7 @@ When a newer version leaves something out that an older one implements, keep the
A whole class that only exists for some versions (such as `MatrixStackMixin`, which only makes sense before 1.21.6's GUI rewrite) can keep an empty class body on the other versions. Before splitting a class like that, look for a hook that works on every version: nametags used to hook label rendering, which 1.21.9 changed, and now replace the name while the render state is built (`LivingEntityRendererMixin`), with no version condition at all. An empty mixin changes nothing, and it keeps the mixin configs the same for every version, since JSON can't hold version comments. A whole class that only exists for some versions (such as `MatrixStackMixin`, which only makes sense before 1.21.6's GUI rewrite) can keep an empty class body on the other versions. Before splitting a class like that, look for a hook that works on every version: nametags used to hook label rendering, which 1.21.9 changed, and now replace the name while the render state is built (`LivingEntityRendererMixin`), with no version condition at all. An empty mixin changes nothing, and it keeps the mixin configs the same for every version, since JSON can't hold version comments.
Inactive code sits inside a `/* … */` comment, so it can't contain `/* … */` comments itself: use `//` comments in code that's only active for some versions. For the same reason, don't nest multi-line version blocks; use `//?} else if <condition> {` for more than two version ranges, as in `PlayerEntityRendererMixin` and `SaturnRenderState`. Inactive code sits inside a `/* … */` comment, so it can't contain `/* … */` comments itself: use `//` comments in code that's only active for some versions. Version blocks can nest (Stonecutter writes an inactive block inside another as `/^ … ^/`, as in `GlassRenderer`), but for more than two ranges of the same thing, prefer `//?} else if <condition> {`, as in `PlayerEntityRendererMixin` and `SaturnRenderState`.
- `./gradlew "Set active project to <mc>"` (or the Stonecutter IntelliJ plugin) rewrites `src/` so that version's code is the uncommented one, for editing it in the IDE. - `./gradlew "Set active project to <mc>"` (or the Stonecutter IntelliJ plugin) rewrites `src/` so that version's code is the uncommented one, for editing it in the IDE.
- `./gradlew "Reset active project"` switches back to 1.21.11. **Run it before committing**, so `src/` is always committed with 1.21.11 active. - `./gradlew "Reset active project"` switches back to 1.21.11. **Run it before committing**, so `src/` is always committed with 1.21.11 active.
@@ -72,5 +78,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`. 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`. 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. 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 // Builds one Minecraft version from the shared src/. Stonecutter runs this for each version in
// stonecutterVersions (settings.gradle.kts), with that version's versions/<v>/gradle.properties. // stonecutterVersions (settings.gradle.kts), with that version's versions/<v>/gradle.properties.
plugins { plugins {
id("net.fabricmc.fabric-loom-remap") id("dev.kikugie.loom-back-compat")
`maven-publish` `maven-publish`
} }
val minecraftVersion = property("minecraft_version") as String val minecraftVersion = property("minecraft_version") as String
// The Minecraft versions the jar loads on (fabric.mod.json); a range where hotfixes share a jar.
val minecraftCompat = findProperty("minecraft_compat") as String? ?: minecraftVersion
val javaVersion = if (sc.current.parsed >= "26.1") 25 else 21
val loaderVersion = property("loader_version") as String val loaderVersion = property("loader_version") as String
version = "${property("mod_version")}+$minecraftVersion" version = "${property("mod_version")}+$minecraftVersion"
@@ -17,7 +20,7 @@ base {
dependencies { dependencies {
minecraft("com.mojang:minecraft:$minecraftVersion") minecraft("com.mojang:minecraft:$minecraftVersion")
mappings(loom.officialMojangMappings()) loomx.applyMojangMappings()
modImplementation("net.fabricmc:fabric-loader:$loaderVersion") modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}")
@@ -42,23 +45,23 @@ dependencies {
tasks.processResources { tasks.processResources {
val values = mapOf( val values = mapOf(
"version" to project.version.toString(), "version" to project.version.toString(),
"minecraft_version" to minecraftVersion, "minecraft_version" to minecraftCompat,
"loader_version" to loaderVersion, "loader_version" to loaderVersion,
"java" to "JAVA_$javaVersion",
) )
inputs.properties(values) inputs.properties(values)
filteringCharset = "UTF-8" filteringCharset = "UTF-8"
filesMatching("fabric.mod.json") { expand(values) } filesMatching(listOf("fabric.mod.json", "*.mixins.json")) { expand(values) }
} }
tasks.withType<JavaCompile>().configureEach { tasks.withType<JavaCompile>().configureEach {
options.release = 21 options.release = javaVersion
} }
java { java {
// Loom attaches the sources jar to remapSourcesJar and build. // Loom attaches the sources jar to remapSourcesJar and build.
withSourcesJar() withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21 toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
targetCompatibility = JavaVersion.VERSION_21
} }
tasks.jar { tasks.jar {
+1 -1
Submodule common updated: a6a324a61a...e8f686b2d3
+4 -4
View File
@@ -6,10 +6,10 @@ org.gradle.caching=true
# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349 # IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
org.gradle.configuration-cache=false org.gradle.configuration-cache=false
mod_version=0.1.4-beta mod_version=0.1.5-beta
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes) # 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, for every Minecraft version (read by loom-back-compat).
loom_version=1.17.21 loomx.loom_version=1.17.21
+9 -7
View File
@@ -5,16 +5,15 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
mavenCentral() mavenCentral()
} }
// Loom's version for build.gradle.kts, which can't read properties in its plugins block.
val loom_version: String by settings
plugins {
id("net.fabricmc.fabric-loom-remap") version loom_version
}
} }
plugins { plugins {
// Downloads the Java toolchain a version needs (Java 25 for 26.1+) when it isn't installed.
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
id("dev.kikugie.stonecutter") version "0.9.8" id("dev.kikugie.stonecutter") version "0.9.8"
// Picks the Loom variant per version: remapping for 1.21.x, plain for the unobfuscated 26.1+.
// Its version is loomx.loom_version in gradle.properties.
id("dev.kikugie.loom-back-compat") version "0.4.2"
} }
rootProject.name = "saturn-client" rootProject.name = "saturn-client"
@@ -23,7 +22,10 @@ include("common")
// Versions built by Stonecutter from the shared src/ (see stonecutter.gradle.kts). Each one's // Versions built by Stonecutter from the shared src/ (see stonecutter.gradle.kts). Each one's
// versions/<v>/gradle.properties holds its Minecraft, Fabric Loader and Fabric API versions. // versions/<v>/gradle.properties holds its Minecraft, Fabric Loader and Fabric API versions.
val stonecutterVersions = listOf("1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11") val stonecutterVersions = listOf(
"1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11",
"26.1", "26.2", "26.3",
)
stonecutter { stonecutter {
create(rootProject) { create(rootProject) {
@@ -435,7 +435,11 @@ public class CloakFeatureRenderer extends RenderLayer<AvatarRenderState, PlayerM
matrices.translate(top[0] / 16.0f, top[1] / 16.0f, top[2] / 16.0f); matrices.translate(top[0] / 16.0f, top[1] / 16.0f, top[2] / 16.0f);
Vec3 axis = bend.axis(); Vec3 axis = bend.axis();
matrices.mulPose(new Quaternionf().rotationAxis(bend.angleAt(0), axis.x(), axis.y(), axis.z())); Quaternionf rotation = new Quaternionf().rotationAxis(bend.angleAt(0), axis.x(), axis.y(), axis.z());
//? if >=26.3 {
/*matrices.rotate(rotation);
*///?} else
matrices.mulPose(rotation);
} }
// Shift array left and insert new value at the start // Shift array left and insert new value at the start
@@ -18,6 +18,7 @@ import net.minecraft.client.renderer.SubmitNodeCollector;
//?} else //?} else
//import net.minecraft.client.renderer.MultiBufferSource; //import net.minecraft.client.renderer.MultiBufferSource;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import org.joml.Quaternionf;
import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.resources.Identifier; import net.minecraft.resources.Identifier;
@@ -89,21 +90,29 @@ public class ObjModel {
int light, int overlay) { int light, int overlay) {
matrices.pushPose(); matrices.pushPose();
matrices.mulPose(com.mojang.math.Axis.XP.rotationDegrees(180.0f)); rotate(matrices, com.mojang.math.Axis.XP.rotationDegrees(180.0f));
matrices.mulPose(com.mojang.math.Axis.YP.rotationDegrees(180.0f)); rotate(matrices, com.mojang.math.Axis.YP.rotationDegrees(180.0f));
if (this.config != null) { if (this.config != null) {
matrices.translate(config.transform.x, config.transform.y, config.transform.z); matrices.translate(config.transform.x, config.transform.y, config.transform.z);
matrices.scale(config.scale.x, config.scale.y, config.scale.z); matrices.scale(config.scale.x, config.scale.y, config.scale.z);
matrices.mulPose(com.mojang.math.Axis.XP.rotationDegrees(config.rotation.x)); rotate(matrices, com.mojang.math.Axis.XP.rotationDegrees(config.rotation.x));
matrices.mulPose(com.mojang.math.Axis.YP.rotationDegrees(config.rotation.y)); rotate(matrices, com.mojang.math.Axis.YP.rotationDegrees(config.rotation.y));
matrices.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(config.rotation.z)); rotate(matrices, com.mojang.math.Axis.ZP.rotationDegrees(config.rotation.z));
} }
ObjRenderer.renderObj(this.obj, mtlMap, matrices, output, light, overlay); ObjRenderer.renderObj(this.obj, mtlMap, matrices, output, light, overlay);
matrices.popPose(); matrices.popPose();
} }
// 26.3 renamed PoseStack's quaternion mulPose to rotate.
private static void rotate(PoseStack matrices, Quaternionf rotation) {
//? if >=26.3 {
/*matrices.rotate(rotation);
*///?} else
matrices.mulPose(rotation);
}
} }
@@ -26,6 +26,12 @@ import net.minecraft.client.renderer.blockentity.AbstractEndPortalRenderer;
import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer; import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer;
*///?} *///?}
import net.minecraft.resources.Identifier; import net.minecraft.resources.Identifier;
//? if >=26.3
//import com.mojang.renderpearl.api.pipeline.ColorTargetState;
//? if >=26.1 {
/*import java.util.HashMap;
import java.util.Map;
*///?}
public class ShaderUtils { public class ShaderUtils {
// Render pipelines exist from 1.21.5; before that the layer names a shader program directly. // Render pipelines exist from 1.21.5; before that the layer names a shader program directly.
@@ -87,8 +93,30 @@ 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)
// From 26.3 the snippet leaves the color target to each pipeline.
//? if >=26.3
//.withColorTargetState(ColorTargetState.DEFAULT)
.build());
private static final Map<Identifier, RenderType> UNLIT_CUTOUT = new HashMap<>();
*///?}
private static RenderType entityAlpha(Identifier texture) { 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); return RenderTypes.dragonExplosionAlpha(texture);
//?} else //?} else
//return RenderType.dragonExplosionAlpha(texture); //return RenderType.dragonExplosionAlpha(texture);
@@ -1,6 +1,7 @@
package org.saturnclient.impl.features.mixins.entity; package org.saturnclient.impl.features.mixins.entity;
import org.saturnclient.impl.features.entity.CameraOverriddenEntity; import org.saturnclient.impl.features.entity.CameraOverriddenEntity;
import org.saturnclient.impl.features.render.ZoomFov;
import org.saturnclient.mod.mods.FreelookMod; import org.saturnclient.mod.mods.FreelookMod;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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.player.LocalPlayer;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
@@ -26,17 +29,38 @@ public abstract class CameraMixin {
@Shadow @Shadow
protected abstract void setRotation(float yaw, float pitch); 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)) @Inject(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER))
public void lockRotation( public void lockRotation(Level focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing,
//? if >=1.21.11 { float tickDelta, CallbackInfo ci) {
Level focusedBlock, saturn$lockRotation(cameraEntity);
//?} else }
//BlockGetter focusedBlock, //?} else {
Entity cameraEntity, /*@Inject(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER))
boolean isThirdPerson, public void lockRotation(BlockGetter focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing,
boolean isFrontFacing, float tickDelta, CallbackInfo ci) {
float tickDelta, saturn$lockRotation(cameraEntity);
CallbackInfo ci) { }
*///?}
@Unique
private void saturn$lockRotation(Entity cameraEntity) {
if (FreelookMod.isFreeLooking() && cameraEntity instanceof LocalPlayer) { if (FreelookMod.isFreeLooking() && cameraEntity instanceof LocalPlayer) {
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity; CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity;
@@ -8,8 +8,9 @@ import net.minecraft.client.renderer.GameRenderer;
//? if >=1.21.6 //? if >=1.21.6
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.mod.mods.FreelookMod; //? if >=1.21.6
import org.saturnclient.mod.mods.ZoomMod; import org.saturnclient.impl.ui.Screens;
import org.saturnclient.impl.features.render.ZoomFov;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -18,11 +19,11 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 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 * The old code checked both {@code Zoom.isZooming} and
* {@code Zoom.shouldZoom()} separately. The refactored * {@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. * ({@code enabled.value && isZooming}), so only one check is needed.
* *
* No {@link org.saturnclient.common.module.ModProvider} is required * No {@link org.saturnclient.common.module.ModProvider} is required
@@ -36,24 +37,25 @@ public class GameRendererMixin {
* After the vanilla FOV has been computed, divide it by the zoom * After the vanilla FOV has been computed, divide it by the zoom
* level to produce the narrowed field-of-view. * 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) @Inject(method = "getFov", at = @At("RETURN"), cancellable = true)
private void onGetFov(CallbackInfoReturnable<Float> cir) { private void onGetFov(CallbackInfoReturnable<Float> cir) {
if (ZoomMod.shouldZoom()) { cir.setReturnValue(ZoomFov.apply(cir.getReturnValue()));
float zoomedFov = cir.getReturnValue() / ZoomMod.getZoomLevel();
cir.setReturnValue(zoomedFov);
} else if (FreelookMod.shouldZoomOut()) {
float zoomedFov = cir.getReturnValue() / FreelookMod.getZoomLevel();
cir.setReturnValue(zoomedFov);
}
} }
//?}
//? if >=1.21.6 { //? if >=1.21.6 {
// From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a // 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. // 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", @Redirect(method = "render", at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/Options;getMenuBackgroundBlurriness()I")) target = "Lnet/minecraft/client/Options;getMenuBackgroundBlurriness()I"))
private int useSaturnBlur(Options options) { private int useSaturnBlur(Options options) {
if (Minecraft.getInstance().screen instanceof SaturnScreenFabric saturnScreen) { if (Screens.current() instanceof SaturnScreenFabric saturnScreen) {
return saturnScreen.screen.getBackgroundBlur(); return saturnScreen.screen.getBackgroundBlur();
} }
@@ -3,6 +3,9 @@ package org.saturnclient.impl.features.mixins.render;
// Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils // Code approach adapted from Gamma Utils: https://github.com/Sjouwer/gamma-utils
import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
//? if >=26.1 {
/*import net.minecraft.client.renderer.LightmapRenderStateExtractor;
*///?} else
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import org.saturnclient.mod.mods.FullbrightMod; 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 * it is now {@link FullbrightMod}. The injection target and
* strategy are unchanged. * 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) @Mixin(LightTexture.class)
public class LightmapTextureManagerMixin { public class LightmapTextureManagerMixin {
@@ -25,6 +32,9 @@ public class LightmapTextureManagerMixin {
* If fullbright is active, substitute the feature's brightness * If fullbright is active, substitute the feature's brightness
* value for whatever vanilla calculated. * 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)) @ModifyExpressionValue(method = "updateLightTexture", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F", ordinal = 0))
private float modifyGamma(float original) { private float modifyGamma(float original) {
if (FullbrightMod.shouldOverrideBrightness()) { if (FullbrightMod.shouldOverrideBrightness()) {
@@ -2,11 +2,15 @@ package org.saturnclient.impl.features.mixins.render;
import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
//? if >=26.2 {
/*import net.minecraft.client.gui.Hud;
*///?} else
import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.Gui;
import net.minecraft.client.DeltaTracker; import net.minecraft.client.DeltaTracker;
import org.saturnclient.impl.ui.RenderScopeImpl; import org.saturnclient.impl.ui.RenderScopeImpl;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.impl.ui.Screens;
import org.saturnclient.mod.HudMod; import org.saturnclient.mod.HudMod;
import org.saturnclient.mod.Mod; import org.saturnclient.mod.Mod;
import org.saturnclient.mod.ModLayout; import org.saturnclient.mod.ModLayout;
@@ -37,9 +41,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* {@link SaturnScreenFabric} / {@link HudEditor} pair; that is * {@link SaturnScreenFabric} / {@link HudEditor} pair; that is
* unchanged infrastructure. * unchanged infrastructure.
*/ */
// The HUD is drawn by Hud from 26.2 (by Gui before).
//? if >=26.2 {
/*@Mixin(Hud.class)
*///?} else
@Mixin(Gui.class) @Mixin(Gui.class)
public class RenderMixin { public class RenderMixin {
//? if >=26.1 {
/*@Inject(method = "extractChat", at = @At("TAIL"))
*///?} else
@Inject(method = "renderChat", at = @At("TAIL")) @Inject(method = "renderChat", at = @At("TAIL"))
public void renderMods(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci) { public void renderMods(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci) {
Font textRenderer = SaturnClient.client.font; Font textRenderer = SaturnClient.client.font;
@@ -48,7 +59,7 @@ public class RenderMixin {
// can draw its own preview layout without double-drawing. // can draw its own preview layout without double-drawing.
if (textRenderer == null) if (textRenderer == null)
return; return;
if (SaturnClient.client.screen instanceof SaturnScreenFabric f if (Screens.current() instanceof SaturnScreenFabric f
&& f.screen instanceof HudEditor) && f.screen instanceof HudEditor)
return; return;
@@ -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;
}
}
@@ -4,11 +4,11 @@ import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.util.UUID; import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.common.ref.game.MinecraftClientRef; import org.saturnclient.common.ref.game.MinecraftClientRef;
import org.saturnclient.common.ref.render.WindowRef; import org.saturnclient.common.ref.render.WindowRef;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.impl.ui.Screens;
import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.screens.TitleMenu; import org.saturnclient.ui.screens.TitleMenu;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -16,7 +16,6 @@ import org.spongepowered.asm.mixin.Shadow;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.options.OptionsScreen;
import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen; import net.minecraft.client.gui.screens.worldselection.SelectWorldScreen;
@@ -43,9 +42,6 @@ public abstract class MinecraftClientMixin implements MinecraftClientRef {
@Shadow @Shadow
private User user; private User user;
@Shadow
public abstract void setScreen(@Nullable Screen screen);
@Shadow @Shadow
public abstract void stop(); public abstract void stop();
@@ -75,7 +71,7 @@ public abstract class MinecraftClientMixin implements MinecraftClientRef {
@Override @Override
public void setScreen(SaturnScreen screen) { public void setScreen(SaturnScreen screen) {
this.setScreen(new SaturnScreenFabric(screen)); Screens.set(new SaturnScreenFabric(screen));
} }
@Override @Override
@@ -84,15 +80,21 @@ public abstract class MinecraftClientMixin implements MinecraftClientRef {
switch (screen) { switch (screen) {
case SelectWorld: case SelectWorld:
this.setScreen(new SelectWorldScreen(exitTarget)); Screens.set(new SelectWorldScreen(exitTarget));
break; break;
case Multiplayer: case Multiplayer:
this.setScreen(new JoinMultiplayerScreen(exitTarget)); Screens.set(new JoinMultiplayerScreen(exitTarget));
break; break;
case Options: case Options:
this.setScreen(new OptionsScreen(exitTarget, this.options)); // Only 26.1 and 26.2 take whether a world is open.
//? if >=26.3 {
/*Screens.set(new OptionsScreen(exitTarget, this.options));
*///?} else if >=26.1 {
/*Screens.set(new OptionsScreen(exitTarget, this.options, ((Minecraft) (Object) this).level != null));
*///?} else
Screens.set(new OptionsScreen(exitTarget, this.options));
break; break;
} }
} }
@@ -8,6 +8,9 @@ import org.saturnclient.common.ref.game.EffectRef;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
//? if >=26.2 {
/*import net.minecraft.client.gui.Hud;
*///?} else
import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.Gui;
import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectInstance;
@@ -55,6 +58,9 @@ public abstract class StatusEffectMixin implements EffectRef {
//? if >=1.21.6 { //? if >=1.21.6 {
@Override @Override
public IdentifierRef getIconId() { public IdentifierRef getIconId() {
//? if >=26.2 {
/*Identifier id = Hud.getMobEffectSprite(getEffect());
*///?} else
Identifier id = Gui.getMobEffectSprite(getEffect()); Identifier id = Gui.getMobEffectSprite(getEffect());
return IdentifierRef.of(id.getNamespace(), "textures/" + id.getPath() + ".png"); return IdentifierRef.of(id.getNamespace(), "textures/" + id.getPath() + ".png");
@@ -1,22 +1,36 @@
package org.saturnclient.impl.provider; package org.saturnclient.impl.provider;
// 26.3 uses SDL, not GLFW; Keys converts Saturn's GLFW key codes to its own.
//? if >=26.3 {
/*import com.mojang.blaze3d.platform.InputConstants;
*///?} else
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.saturnclient.common.provider.GLFWProvider; import org.saturnclient.common.provider.GLFWProvider;
import org.saturnclient.impl.ui.Screens;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
public class GLFWProviderImpl implements GLFWProvider { public class GLFWProviderImpl implements GLFWProvider {
@Override @Override
public boolean isKeyPressed(int key) { public boolean isKeyPressed(int key) {
//? if >=26.3 {
/*int gameKey = Keys.toGame(key);
return gameKey >= 0 && InputConstants.isKeyDown(gameKey) && Screens.current() == null;
*///?} else {
//? if >=1.21.9 { //? if >=1.21.9 {
long window = SaturnClient.client.getWindow().handle(); long window = SaturnClient.client.getWindow().handle();
//?} else //?} else
//long window = SaturnClient.client.getWindow().getWindow(); //long window = SaturnClient.client.getWindow().getWindow();
return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS
&& SaturnClient.client.screen == null; && Screens.current() == null;
//?}
} }
@Override @Override
public String getKeyName(int key) { public String getKeyName(int key) {
//? if >=26.3 {
/*int gameKey = Keys.toGame(key);
return gameKey < 0 ? null : InputConstants.Type.KEYBOARD.getOrCreate(gameKey).getDisplayName().getString();
*///?} else
return GLFW.glfwGetKeyName(key, GLFW.glfwGetKeyScancode(key)); return GLFW.glfwGetKeyName(key, GLFW.glfwGetKeyScancode(key));
} }
} }
@@ -0,0 +1,189 @@
package org.saturnclient.impl.provider;
//? if >=26.3 {
/*import com.mojang.blaze3d.platform.InputConstants;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.saturnclient.common.provider.GLFWProvider;
*///?}
/**
* Saturn's key codes are GLFW's, which the config's keybinds store. From 26.3 Minecraft uses SDL,
* whose keys (scancodes), modifiers and mouse buttons are numbered differently, so these convert
* between the two there. Before 26.3 Minecraft's numbers are GLFW's and pass through.
*/
public final class Keys {
private Keys() {
}
//? if >=26.3 {
/*private static final Int2IntOpenHashMap TO_GAME = new Int2IntOpenHashMap();
private static final Int2IntOpenHashMap TO_SATURN = new Int2IntOpenHashMap();
private static void key(int glfw, int game) {
TO_GAME.put(glfw, game);
TO_SATURN.put(game, glfw);
}
// GLFW's key codes, and the same keys' constants in 26.3
static {
TO_GAME.defaultReturnValue(-1);
TO_SATURN.defaultReturnValue(-1);
key(32, InputConstants.KEY_SPACE);
key(39, InputConstants.KEY_APOSTROPHE);
key(44, InputConstants.KEY_COMMA);
key(45, InputConstants.KEY_MINUS);
key(46, InputConstants.KEY_PERIOD);
key(47, InputConstants.KEY_SLASH);
key(48, InputConstants.KEY_0);
key(49, InputConstants.KEY_1);
key(50, InputConstants.KEY_2);
key(51, InputConstants.KEY_3);
key(52, InputConstants.KEY_4);
key(53, InputConstants.KEY_5);
key(54, InputConstants.KEY_6);
key(55, InputConstants.KEY_7);
key(56, InputConstants.KEY_8);
key(57, InputConstants.KEY_9);
key(59, InputConstants.KEY_SEMICOLON);
key(61, InputConstants.KEY_EQUALS);
key(65, InputConstants.KEY_A);
key(66, InputConstants.KEY_B);
key(67, InputConstants.KEY_C);
key(68, InputConstants.KEY_D);
key(69, InputConstants.KEY_E);
key(70, InputConstants.KEY_F);
key(71, InputConstants.KEY_G);
key(72, InputConstants.KEY_H);
key(73, InputConstants.KEY_I);
key(74, InputConstants.KEY_J);
key(75, InputConstants.KEY_K);
key(76, InputConstants.KEY_L);
key(77, InputConstants.KEY_M);
key(78, InputConstants.KEY_N);
key(79, InputConstants.KEY_O);
key(80, InputConstants.KEY_P);
key(81, InputConstants.KEY_Q);
key(82, InputConstants.KEY_R);
key(83, InputConstants.KEY_S);
key(84, InputConstants.KEY_T);
key(85, InputConstants.KEY_U);
key(86, InputConstants.KEY_V);
key(87, InputConstants.KEY_W);
key(88, InputConstants.KEY_X);
key(89, InputConstants.KEY_Y);
key(90, InputConstants.KEY_Z);
key(91, InputConstants.KEY_LBRACKET);
key(92, InputConstants.KEY_BACKSLASH);
key(93, InputConstants.KEY_RBRACKET);
key(96, InputConstants.KEY_GRAVE);
key(256, InputConstants.KEY_ESCAPE);
key(257, InputConstants.KEY_RETURN);
key(258, InputConstants.KEY_TAB);
key(259, InputConstants.KEY_BACKSPACE);
key(260, InputConstants.KEY_INSERT);
key(261, InputConstants.KEY_DELETE);
key(262, InputConstants.KEY_RIGHT);
key(263, InputConstants.KEY_LEFT);
key(264, InputConstants.KEY_DOWN);
key(265, InputConstants.KEY_UP);
key(266, InputConstants.KEY_PAGEUP);
key(267, InputConstants.KEY_PAGEDOWN);
key(268, InputConstants.KEY_HOME);
key(269, InputConstants.KEY_END);
key(280, InputConstants.KEY_CAPSLOCK);
key(281, InputConstants.KEY_SCROLLLOCK);
key(282, InputConstants.KEY_NUMLOCK);
key(283, InputConstants.KEY_PRINTSCREEN);
key(284, InputConstants.KEY_PAUSE);
key(290, InputConstants.KEY_F1);
key(291, InputConstants.KEY_F2);
key(292, InputConstants.KEY_F3);
key(293, InputConstants.KEY_F4);
key(294, InputConstants.KEY_F5);
key(295, InputConstants.KEY_F6);
key(296, InputConstants.KEY_F7);
key(297, InputConstants.KEY_F8);
key(298, InputConstants.KEY_F9);
key(299, InputConstants.KEY_F10);
key(300, InputConstants.KEY_F11);
key(301, InputConstants.KEY_F12);
key(302, InputConstants.KEY_F13);
key(303, InputConstants.KEY_F14);
key(304, InputConstants.KEY_F15);
key(305, InputConstants.KEY_F16);
key(306, InputConstants.KEY_F17);
key(307, InputConstants.KEY_F18);
key(308, InputConstants.KEY_F19);
key(309, InputConstants.KEY_F20);
key(310, InputConstants.KEY_F21);
key(311, InputConstants.KEY_F22);
key(312, InputConstants.KEY_F23);
key(313, InputConstants.KEY_F24);
key(320, InputConstants.KEY_NUMPAD0);
key(321, InputConstants.KEY_NUMPAD1);
key(322, InputConstants.KEY_NUMPAD2);
key(323, InputConstants.KEY_NUMPAD3);
key(324, InputConstants.KEY_NUMPAD4);
key(325, InputConstants.KEY_NUMPAD5);
key(326, InputConstants.KEY_NUMPAD6);
key(327, InputConstants.KEY_NUMPAD7);
key(328, InputConstants.KEY_NUMPAD8);
key(329, InputConstants.KEY_NUMPAD9);
key(330, InputConstants.KEY_NUMPADCOMMA);
key(332, InputConstants.KEY_MULTIPLY);
key(334, InputConstants.KEY_ADD);
key(335, InputConstants.KEY_NUMPADENTER);
key(336, InputConstants.KEY_NUMPADEQUALS);
key(340, InputConstants.KEY_LSHIFT);
key(341, InputConstants.KEY_LCONTROL);
key(342, InputConstants.KEY_LALT);
key(343, InputConstants.KEY_LGUI);
key(344, InputConstants.KEY_RSHIFT);
key(345, InputConstants.KEY_RCONTROL);
key(346, InputConstants.KEY_RALT);
key(347, InputConstants.KEY_RGUI);
}
*///?}
/** A Saturn (GLFW) key code as the game's, or -1 */
public static int toGame(int key) {
//? if >=26.3 {
/*return TO_GAME.get(key);
*///?} else
return key;
}
/** A key code from the game as Saturn's (GLFW), or -1 */
public static int toSaturn(int key) {
//? if >=26.3 {
/*return TO_SATURN.get(key);
*///?} else
return key;
}
/** The game's modifier flags as GLFW's */
public static int modifiersToSaturn(int modifiers) {
//? if >=26.3 {
/*int glfw = 0;
if ((modifiers & InputConstants.MOD_SHIFT) != 0) glfw |= GLFWProvider.GLFW_MOD_SHIFT;
if ((modifiers & InputConstants.MOD_CONTROL) != 0) glfw |= GLFWProvider.GLFW_MOD_CONTROL;
if ((modifiers & InputConstants.MOD_ALT) != 0) glfw |= GLFWProvider.GLFW_MOD_ALT;
if ((modifiers & InputConstants.MOD_SUPER) != 0) glfw |= GLFWProvider.GLFW_MOD_SUPER;
if ((modifiers & InputConstants.MOD_CAPS_LOCK) != 0) glfw |= GLFWProvider.GLFW_MOD_CAPS_LOCK;
if ((modifiers & InputConstants.MOD_NUM_LOCK) != 0) glfw |= GLFWProvider.GLFW_MOD_NUM_LOCK;
return glfw;
*///?} else
return modifiers;
}
/** A mouse button from the game as GLFW's (left 0, right 1, middle 2, then the side buttons) */
public static int mouseButtonToSaturn(int button) {
//? if >=26.3 {
/*if (button == InputConstants.MOUSE_BUTTON_LEFT) return 0;
if (button == InputConstants.MOUSE_BUTTON_RIGHT) return 1;
if (button == InputConstants.MOUSE_BUTTON_MIDDLE) return 2;
return button - 1;
*///?} else
return button;
}
}
@@ -86,8 +86,13 @@ public class SaturnProviderImpl implements SaturnProvider {
@Override @Override
public void showWarning(String title, String message) { public void showWarning(String title, String message) {
Minecraft client = Minecraft.getInstance(); Minecraft client = Minecraft.getInstance();
//? if >=26.2 {
/*client.execute(() -> SystemToast.add(client.gui.toastManager(), WARNING_TOAST, Component.literal(title),
Component.literal(message)));
*///?} else {
client.execute(() -> client.getToastManager().addToast( client.execute(() -> client.getToastManager().addToast(
SystemToast.multiline(client, WARNING_TOAST, Component.literal(title), Component.literal(message)))); SystemToast.multiline(client, WARNING_TOAST, Component.literal(title), Component.literal(message))));
//?}
} }
@Override @Override
@@ -17,7 +17,11 @@ package org.saturnclient.impl.ui;
//? if >=1.21.6 { //? if >=1.21.6 {
import java.io.Reader; import java.io.Reader;
import java.util.HashMap; import java.util.HashMap;
//? if >=26.1
//import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
//? if >=26.1
//import java.util.Set;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -37,6 +41,14 @@ import net.minecraft.client.renderer.PostChain;
import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.renderer.RenderPipelines;
import com.mojang.blaze3d.pipeline.TextureTarget; import com.mojang.blaze3d.pipeline.TextureTarget;
import net.minecraft.client.renderer.LevelTargetBundle; import net.minecraft.client.renderer.LevelTargetBundle;
//? if >=26.2 {
/*import com.mojang.blaze3d.GpuFormat;
import net.minecraft.client.renderer.BindGroupLayouts;
*///?}
//? 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.CachedOrthoProjectionMatrixBuffer;
import net.minecraft.client.renderer.texture.AbstractTexture; import net.minecraft.client.renderer.texture.AbstractTexture;
import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.client.gui.render.TextureSetup;
@@ -47,13 +59,28 @@ public final class GlassRenderer {
public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.GUI_TEXTURED_SNIPPET) public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.GUI_TEXTURED_SNIPPET)
.withLocation(Identifier.fromNamespaceAndPath("saturnclient", "pipeline/glass")) .withLocation(Identifier.fromNamespaceAndPath("saturnclient", "pipeline/glass"))
.withFragmentShader(Identifier.fromNamespaceAndPath("saturnclient", "core/glass")) .withFragmentShader(Identifier.fromNamespaceAndPath("saturnclient", "core/glass"))
//? if >=26.2 {
/*.withBindGroupLayout(BindGroupLayouts.SAMPLER1)
*///?} else
.withSampler("Sampler1") .withSampler("Sampler1")
// The transforms block ends with LineWidth before 1.21.11 (see glass.fsh). // The transforms block ends with LineWidth before 1.21.11 (see glass.fsh).
//? if <1.21.11 //? if <1.21.11
//.withShaderDefine("HAS_LINE_WIDTH") //.withShaderDefine("HAS_LINE_WIDTH")
// 26.3 compiles through SPIR-V and moved TextureMat up the transforms block.
//? if >=26.3
//.withShaderDefine("EXPLICIT_LOCATIONS").withShaderDefine("TEXTURE_MAT_SECOND")
.build(); .build();
private static final Identifier BLUR = Identifier.withDefaultNamespace("post_effect/blur.json"); 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); 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 // For the blurs' intermediate targets, freed once unused for a few frames
private static final CrossFrameResourcePool POOL = new CrossFrameResourcePool(3); private static final CrossFrameResourcePool POOL = new CrossFrameResourcePool(3);
@@ -65,15 +92,35 @@ public final class GlassRenderer {
private GlassRenderer() { private GlassRenderer() {
} }
// From 26.1 the GUI is built before the frame's world is drawn, so the main framebuffer still
// holds the last frame, glass included. The copies wait until the GUI renderer is about to draw
// (GuiRendererMixin), when it holds this frame's world and panorama.
//? if >=26.1 {
/*private static final Set<Integer> PENDING = new LinkedHashSet<>();
*///?}
private static RenderTarget mainTarget() {
//? if >=26.2 {
/*return SaturnClient.client.gameRenderer.mainRenderTarget();
*///?} else
return SaturnClient.client.getMainRenderTarget();
}
// Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, while // Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, while
// the screen builds its GUI, before drawing glass with it. // the screen builds its GUI, before drawing glass with it.
public static void captureBackdrop(int blur) { public static void captureBackdrop(int blur) {
RenderTarget main = SaturnClient.client.getMainRenderTarget(); RenderTarget main = mainTarget();
int width = main.width; int width = main.width;
int height = main.height; int height = main.height;
TextureTarget backdrop = BACKDROPS.get(blur); TextureTarget backdrop = BACKDROPS.get(blur);
if (backdrop == null) { if (backdrop == null) {
// From 26.3 the depth format picks the depth buffer (none here); 26.2 takes a flag.
//? if >=26.3 {
/*backdrop = new TextureTarget("Saturn glass " + blur, width, height, GpuFormat.RGBA8_UNORM, null);
*///?} else if >=26.2 {
/*backdrop = new TextureTarget("Saturn glass " + blur, width, height, false, GpuFormat.RGBA8_UNORM);
*///?} else
backdrop = new TextureTarget("Saturn glass " + blur, width, height, false); backdrop = new TextureTarget("Saturn glass " + blur, width, height, false);
BACKDROPS.put(blur, backdrop); BACKDROPS.put(blur, backdrop);
} else if (backdrop.width != width || backdrop.height != height) { } else if (backdrop.width != width || backdrop.height != height) {
@@ -82,6 +129,29 @@ public final class GlassRenderer {
//? if <1.21.11 //? if <1.21.11
//backdrop.getColorTexture().setTextureFilter(FilterMode.LINEAR, false); //backdrop.getColorTexture().setTextureFilter(FilterMode.LINEAR, false);
//? if >=26.1 {
/*PENDING.add(blur);
}
// Copies and blurs the backdrops the GUI asked for this frame. GuiRendererMixin calls it once the
// world and the panorama are drawn, before the GUI.
public static void blurPendingBackdrops() {
RenderTarget main = mainTarget();
for (int blur : PENDING) {
TextureTarget backdrop = BACKDROPS.get(blur);
// The GUI already refers to the backdrop's texture, so after a resize it keeps the last
// frame's until the next one.
if (backdrop != null && backdrop.width == main.width && backdrop.height == main.height) {
blurBackdrop(main, backdrop, blur);
}
}
PENDING.clear();
}
private static void blurBackdrop(RenderTarget main, TextureTarget backdrop, int blur) {
int width = main.width;
int height = main.height;
*///?}
RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(
main.getColorTexture(), backdrop.getColorTexture(), 0, 0, 0, 0, 0, width, height); main.getColorTexture(), backdrop.getColorTexture(), 0, 0, 0, 0, 0, width, height);
@@ -106,6 +176,9 @@ public final class GlassRenderer {
private static void release(int kept) { private static void release(int kept) {
BACKDROPS.entrySet().removeIf(entry -> { BACKDROPS.entrySet().removeIf(entry -> {
boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); boolean unused = entry.getKey() != kept && !isSet(entry.getKey());
// The GUI already refers to this frame's pending backdrops.
//? if >=26.1
//unused &= !PENDING.contains(entry.getKey());
if (unused) { if (unused) {
entry.getValue().destroyBuffers(); entry.getValue().destroyBuffers();
} }
@@ -113,6 +186,9 @@ public final class GlassRenderer {
}); });
BLURS.entrySet().removeIf(entry -> { BLURS.entrySet().removeIf(entry -> {
boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); boolean unused = entry.getKey() != kept && !isSet(entry.getKey());
// The GUI already refers to this frame's pending backdrops.
//? if >=26.1
//unused &= !PENDING.contains(entry.getKey());
if (unused && entry.getValue() != null) { if (unused && entry.getValue() != null) {
entry.getValue().close(); entry.getValue().close();
} }
@@ -140,6 +216,9 @@ public final class GlassRenderer {
PostChainConfig pipeline = PostChainConfig.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); PostChainConfig pipeline = PostChainConfig.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow();
return PostChain.load(pipeline, SaturnClient.client.getTextureManager(), 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); LevelTargetBundle.MAIN_TARGETS, Identifier.fromNamespaceAndPath("saturnclient", "glass_blur_" + blur), PROJECTION);
} catch (Exception e) { } catch (Exception e) {
SaturnClient.LOGGER.error("Couldn't load the glass blur", e); SaturnClient.LOGGER.error("Couldn't load the glass blur", e);
@@ -325,6 +325,9 @@ public class RenderScopeImpl implements RenderScope {
entity, seed); entity, seed);
try { try {
//? if >=26.1 {
/*this.state.submitItem(new GuiItemRenderState(
*///?} else
this.state.submitItem(new GuiItemRenderState(stack.getItem().getName().toString(), this.state.submitItem(new GuiItemRenderState(stack.getItem().getName().toString(),
new Matrix3x2f(this.matrices.stack), keyedItemRenderState, x, y, new Matrix3x2f(this.matrices.stack), keyedItemRenderState, x, y,
this.scissorStack.peekLast())); this.scissorStack.peekLast()));
@@ -4,6 +4,7 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.impl.provider.Keys;
import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.SaturnScreen.ScreenProvider; import org.saturnclient.ui.SaturnScreen.ScreenProvider;
@@ -13,6 +14,9 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.renderer.CubeMap; import net.minecraft.client.renderer.CubeMap;
import net.minecraft.client.gui.GuiGraphics; 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.renderer.PanoramaRenderer;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
//? if >=1.21.6 { //? if >=1.21.6 {
@@ -34,13 +38,18 @@ import net.minecraft.resources.Identifier;
public class SaturnScreenFabric extends Screen implements ScreenProvider { public class SaturnScreenFabric extends Screen implements ScreenProvider {
protected static final Identifier PANORAMA = Identifier.fromNamespaceAndPath("saturnclient", protected static final Identifier PANORAMA = Identifier.fromNamespaceAndPath("saturnclient",
"textures/gui/title/background/panorama"); "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; public static final PanoramaRenderer ROTATING_PANORAMA_RENDERER;
static { static {
ROTATING_PANORAMA_RENDERER = new PanoramaRenderer(PANORAMA_RENDERER); ROTATING_PANORAMA_RENDERER = new PanoramaRenderer(PANORAMA_RENDERER);
} }
//?}
//? if <1.21.6 //? if <1.21.6
//private final CrossFrameResourcePool pool = new CrossFrameResourcePool(3); //private final CrossFrameResourcePool pool = new CrossFrameResourcePool(3);
@@ -81,6 +90,9 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
//? if <1.21.6 //? if <1.21.6
//@SuppressWarnings("deprecation") //@SuppressWarnings("deprecation")
@Override @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) { public void render(GuiGraphics context, int mouseX, int mouseY, float delta) {
if (screen.start == null) { if (screen.start == null) {
screen.start = Instant.now(); screen.start = Instant.now();
@@ -128,10 +140,18 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
// Vanilla's background would cover Saturn's panorama, and blurs by the vanilla setting. The // 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. // strength of this blur comes from GameRendererMixin, which gives Saturn's setting instead.
@Override @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) { 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 // The panorama renderer has no opacity (and takes no delta) from 1.21.6, so it's drawn fully
// opaque. // opaque.
if (isShowingPanorama()) { if (isShowingPanorama()) {
//? if >=26.2 {
/*ROTATING_PANORAMA_RENDERER.extractRenderState(context, this.width, this.height);
*///?} else if >=26.1 {
/*ROTATING_PANORAMA_RENDERER.extractRenderState(context, this.width, this.height, true);
*///?} else
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true); ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true);
} }
@@ -140,6 +160,9 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
} }
if (minecraft.level != null) { if (minecraft.level != null) {
//? if >=26.1 {
/*extractMenuBackground(context);
*///?} else
renderMenuBackground(context); renderMenuBackground(context);
} }
} }
@@ -154,7 +177,7 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
//? if >=1.21.9 { //? if >=1.21.9 {
@Override @Override
public boolean mouseClicked(MouseButtonEvent click, boolean doubled) { public boolean mouseClicked(MouseButtonEvent click, boolean doubled) {
if (screen.mouseClicked(click.x(), click.y(), click.button())) { if (screen.mouseClicked(click.x(), click.y(), Keys.mouseButtonToSaturn(click.button()))) {
return true; return true;
} }
@@ -164,7 +187,7 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
@Override @Override
public boolean mouseDragged(MouseButtonEvent click, double offsetX, double offsetY) { public boolean mouseDragged(MouseButtonEvent click, double offsetX, double offsetY) {
// offsetX and offsetY are the drag deltas, as mouseDragged's deltaX and deltaY were before 1.21.9. // offsetX and offsetY are the drag deltas, as mouseDragged's deltaX and deltaY were before 1.21.9.
if (screen.mouseDragged(click.x(), click.y(), click.button(), offsetX, offsetY)) { if (screen.mouseDragged(click.x(), click.y(), Keys.mouseButtonToSaturn(click.button()), offsetX, offsetY)) {
return true; return true;
} }
@@ -173,7 +196,7 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
@Override @Override
public boolean mouseReleased(MouseButtonEvent click) { public boolean mouseReleased(MouseButtonEvent click) {
if (screen.mouseReleased(click.x(), click.y(), click.button())) { if (screen.mouseReleased(click.x(), click.y(), Keys.mouseButtonToSaturn(click.button()))) {
return true; return true;
} }
@@ -216,6 +239,10 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
//? if >=1.21.9 { //? if >=1.21.9 {
@Override @Override
public boolean keyPressed(KeyEvent input) { public boolean keyPressed(KeyEvent input) {
// 26.3's key() is SDL's scancode; Saturn's key codes are GLFW's (see Keys).
//? if >=26.3 {
/*if (screen.keyPressed(Keys.toSaturn(input.key()), input.key(), Keys.modifiersToSaturn(input.modifiers()))) {
*///?} else
if (screen.keyPressed(input.input(), input.scancode(), input.modifiers())) { if (screen.keyPressed(input.input(), input.scancode(), input.modifiers())) {
return true; return true;
} }
@@ -0,0 +1,33 @@
package org.saturnclient.impl.ui;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
/** The open screen and overlay, which Minecraft holds before 26.2 and its Gui from 26.2. */
public final class Screens {
private Screens() {
}
public static @Nullable Screen current() {
//? if >=26.2 {
/*return Minecraft.getInstance().gui.screen();
*///?} else
return Minecraft.getInstance().screen;
}
public static void set(@Nullable Screen screen) {
//? if >=26.2 {
/*Minecraft.getInstance().gui.setScreen(screen);
*///?} else
Minecraft.getInstance().setScreen(screen);
}
public static void clearOverlay() {
//? if >=26.2 {
/*Minecraft.getInstance().gui.setOverlay(null);
*///?} else
Minecraft.getInstance().setOverlay(null);
}
}
@@ -1,6 +1,9 @@
package org.saturnclient.saturnclient.event; package org.saturnclient.saturnclient.event;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 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.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.platform.InputConstants;
@@ -13,10 +16,10 @@ import org.saturnclient.common.provider.Providers;
import org.saturnclient.client.ServiceClient; import org.saturnclient.client.ServiceClient;
import org.saturnclient.emotes.fabric.Emotes; import org.saturnclient.emotes.fabric.Emotes;
import org.saturnclient.impl.ui.SaturnScreenFabric; import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.impl.ui.Screens;
import org.saturnclient.ui.screens.EmoteWheel; import org.saturnclient.ui.screens.EmoteWheel;
import org.saturnclient.ui.screens.ShiftMenu; import org.saturnclient.ui.screens.ShiftMenu;
import org.lwjgl.glfw.GLFW;
/** /**
* Handles keyboard input events for the Saturn Client. * Handles keyboard input events for the Saturn Client.
@@ -28,11 +31,17 @@ public class KeyInputHandler {
//?} else //?} else
//private static final String KEY_CATEGORY = "key.category.saturnclient"; //private static final String KEY_CATEGORY = "key.category.saturnclient";
private static final String MAIN_MENU_KEY_ID = "key.saturnclient.main_menu"; private static final String MAIN_MENU_KEY_ID = "key.saturnclient.main_menu";
private static final int DEFAULT_MENU_KEY = GLFW.GLFW_KEY_RIGHT_SHIFT; private static final int DEFAULT_MENU_KEY = InputConstants.KEY_RSHIFT;
private static KeyMapping mainMenuKeyBinding; private static KeyMapping mainMenuKeyBinding;
public static void register() { 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( mainMenuKeyBinding = KeyBindingHelper.registerKeyBinding(
new KeyMapping( new KeyMapping(
MAIN_MENU_KEY_ID, MAIN_MENU_KEY_ID,
@@ -56,13 +65,15 @@ public class KeyInputHandler {
// from the screen's own key handling would reopen it on the next tick, since the key is // from the screen's own key handling would reopen it on the next tick, since the key is
// still down and no screen is open by then. // still down and no screen is open by then.
if (Config.openEmoteWheel.wasKeyPressed() if (Config.openEmoteWheel.wasKeyPressed()
//? if >=1.21.9 { //? if >=26.3 {
&& !InputConstants.isKeyDown(client.getWindow(), GLFW.GLFW_KEY_F3)) { /*&& !InputConstants.isKeyDown(InputConstants.KEY_F3)) {
*///?} else if >=1.21.9 {
&& !InputConstants.isKeyDown(client.getWindow(), InputConstants.KEY_F3)) {
//?} else //?} else
//&& !InputConstants.isKeyDown(client.getWindow().getWindow(), GLFW.GLFW_KEY_F3)) { //&& !InputConstants.isKeyDown(client.getWindow().getWindow(), InputConstants.KEY_F3)) {
if (client.screen == null) { if (Screens.current() == null) {
Providers.saturn.getClient().setScreen(new EmoteWheel()); Providers.saturn.getClient().setScreen(new EmoteWheel());
} else if (client.screen instanceof SaturnScreenFabric saturnScreen } else if (Screens.current() instanceof SaturnScreenFabric saturnScreen
&& saturnScreen.screen instanceof EmoteWheel) { && saturnScreen.screen instanceof EmoteWheel) {
saturnScreen.onClose(); saturnScreen.onClose();
} }
@@ -0,0 +1,46 @@
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.gui.render.GuiRenderer;
import net.minecraft.client.renderer.CubeMap;
import org.saturnclient.impl.ui.GlassRenderer;
import org.saturnclient.impl.ui.SaturnScreenFabric;
import org.saturnclient.impl.ui.Screens;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
*///?} 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. It also blurs the glass backdrops
* once the panorama is drawn. 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 = Screens.current() instanceof SaturnScreenFabric;
original.call(saturnScreen ? SaturnScreenFabric.PANORAMA_RENDERER : cubeMap, xRot, yRot);
}
// The glass backdrops are copied here, with the frame's world and panorama drawn and the GUI not
// yet: the GUI is built before the frame is drawn (see GlassRenderer).
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/render/GuiRenderer;prepare()V"))
private void saturn$blurGlassBackdrops(CallbackInfo ci) {
GlassRenderer.blurPendingBackdrops();
}
}
*///?} else {
@Mixin(Minecraft.class)
public class GuiRendererMixin {
}
//?}
@@ -8,9 +8,15 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
//? if >=26.2
//import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
// Screens are opened through Gui from 26.2 (through Minecraft before).
//? if >=26.2 {
/*@Mixin(Gui.class)
*///?} else
@Mixin(Minecraft.class) @Mixin(Minecraft.class)
public class SetScreenMixin { public class SetScreenMixin {
@ModifyVariable(method = "setScreen", at = @At("HEAD"), argsOnly = true) @ModifyVariable(method = "setScreen", at = @At("HEAD"), argsOnly = true)
@@ -18,7 +24,7 @@ public class SetScreenMixin {
if (Config.saturnTitleScreen.value) { if (Config.saturnTitleScreen.value) {
if (screen instanceof TitleScreen) if (screen instanceof TitleScreen)
return new SaturnScreenFabric(new TitleMenu()); return new SaturnScreenFabric(new TitleMenu());
if (screen == null && ((Minecraft) (Object) this).level == null) if (screen == null && Minecraft.getInstance().level == null)
return new SaturnScreenFabric(new TitleMenu()); return new SaturnScreenFabric(new TitleMenu());
} }
@@ -1,5 +1,7 @@
package org.saturnclient.saturnclient.mixin; package org.saturnclient.saturnclient.mixin;
import org.saturnclient.impl.ui.Screens;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -54,6 +56,9 @@ public abstract class SplashOverlayMixin {
* @reason Replace vanilla splash rendering completely but keep exit logic * @reason Replace vanilla splash rendering completely but keep exit logic
*/ */
@Overwrite @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) { public void render(GuiGraphics context, int mouseX, int mouseY, float delta) {
int screenWidth = context.guiWidth(); int screenWidth = context.guiWidth();
int screenHeight = context.guiHeight(); int screenHeight = context.guiHeight();
@@ -107,7 +112,7 @@ public abstract class SplashOverlayMixin {
// Remove overlay after fade-out completes // Remove overlay after fade-out completes
if (fadeOutProgress >= 2.0F) { if (fadeOutProgress >= 2.0F) {
minecraft.setOverlay(null); Screens.clearOverlay();
} }
// Handle reload completion and exceptions // Handle reload completion and exceptions
@@ -115,8 +120,11 @@ public abstract class SplashOverlayMixin {
} }
private void renderCurrentScreen(GuiGraphics context, int mouseX, int mouseY, float delta) { private void renderCurrentScreen(GuiGraphics context, int mouseX, int mouseY, float delta) {
if (minecraft.screen != null) { if (Screens.current() != null) {
minecraft.screen.render(context, mouseX, mouseY, delta); //? if >=26.1 {
/*Screens.current().extractRenderState(context, mouseX, mouseY, delta);
*///?} else
Screens.current().render(context, mouseX, mouseY, delta);
} }
} }
@@ -129,7 +137,9 @@ public abstract class SplashOverlayMixin {
private void renderProgressBar(RenderScope scope, GuiGraphics context, int screenWidth, int screenHeight, private void renderProgressBar(RenderScope scope, GuiGraphics context, int screenWidth, int screenHeight,
int alpha) { int alpha) {
int progressY = ((screenHeight - 5) / 2) + 25; 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)); context.renderOutline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF));
//?} else if >=1.21.9 { //?} else if >=1.21.9 {
/*context.submitOutline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF)); /*context.submitOutline((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF));
@@ -165,11 +175,11 @@ public abstract class SplashOverlayMixin {
//? if >=1.21.11 //? if >=1.21.11
Window window = minecraft.getWindow(); Window window = minecraft.getWindow();
if (minecraft.screen != null) { if (Screens.current() != null) {
//? if >=1.21.11 { //? if >=1.21.11 {
minecraft.screen.init(window.getGuiScaledWidth(), window.getGuiScaledHeight()); Screens.current().init(window.getGuiScaledWidth(), window.getGuiScaledHeight());
//?} else //?} else
//minecraft.screen.init(minecraft, context.guiWidth(), context.guiHeight()); //Screens.current().init(minecraft, context.guiWidth(), context.guiHeight());
} }
} }
} }
@@ -1,16 +1,26 @@
#version 330 #version 330
// 26.3 compiles shaders through SPIR-V, which needs every input and output's location.
#ifdef EXPLICIT_LOCATIONS
#extension GL_ARB_separate_shader_objects : require
#endif
// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. For 1.21.6 and // Saturn's glass: the blurred game behind the screen, cut to a panel's shape. For 1.21.6 and
// later; glass_pre_1_21_6.fsh (with glass.json, the program 1.21.4 loads) is for older versions. // later; glass_pre_1_21_6.fsh (with glass.json, the program 1.21.4 loads) is for older versions.
// Vanilla's transforms, laid out as the version has them: 1.21.6 to 1.21.10 end with LineWidth, which // Vanilla's transforms, laid out as the version has them: 1.21.6 to 1.21.10 end with LineWidth, which
// 1.21.11 dropped (GlassRenderer defines HAS_LINE_WIDTH there). Not imported: the include only exists // 1.21.11 dropped (GlassRenderer defines HAS_LINE_WIDTH there), and 26.3 moved TextureMat up
// from 1.21.6, and 1.21.4 and 1.21.5 still read every shader's imports while loading resources. // (TEXTURE_MAT_SECOND). Not imported: the include only exists from 1.21.6, and 1.21.4 and 1.21.5 still
// read every shader's imports while loading resources.
layout(std140) uniform DynamicTransforms { layout(std140) uniform DynamicTransforms {
mat4 ModelViewMat; mat4 ModelViewMat;
#ifdef TEXTURE_MAT_SECOND
mat4 TextureMat;
#endif
vec4 ColorModulator; vec4 ColorModulator;
vec3 ModelOffset; vec3 ModelOffset;
#ifndef TEXTURE_MAT_SECOND
mat4 TextureMat; mat4 TextureMat;
#endif
#ifdef HAS_LINE_WIDTH #ifdef HAS_LINE_WIDTH
float LineWidth; float LineWidth;
#endif #endif
@@ -21,10 +31,17 @@ uniform sampler2D Sampler0;
// The game behind the screen, blurred, the size of the framebuffer // The game behind the screen, blurred, the size of the framebuffer
uniform sampler2D Sampler1; uniform sampler2D Sampler1;
#ifdef EXPLICIT_LOCATIONS
layout(location = 0) in vec2 texCoord0;
layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec4 fragColor;
#else
in vec2 texCoord0; in vec2 texCoord0;
in vec4 vertexColor; in vec4 vertexColor;
out vec4 fragColor; out vec4 fragColor;
#endif
void main() { void main() {
float mask = texture(Sampler0, texCoord0).a * vertexColor.a; float mask = texture(Sampler0, texCoord0).a * vertexColor.a;
+1 -1
View File
@@ -22,7 +22,7 @@
], ],
"depends": { "depends": {
"fabricloader": ">=${loader_version}", "fabricloader": ">=${loader_version}",
"fabric": "*", "fabric-api": "*",
"minecraft": "${minecraft_version}" "minecraft": "${minecraft_version}"
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.impl.features.mixins", "package": "org.saturnclient.impl.features.mixins",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"render.GameRendererMixin", "render.GameRendererMixin",
"render.LightmapTextureManagerMixin", "render.LightmapTextureManagerMixin",
+1 -1
View File
@@ -2,7 +2,7 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.impl.mixins", "package": "org.saturnclient.impl.mixins",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"IdentifierMixin", "IdentifierMixin",
"ItemStackMixin", "ItemStackMixin",
+3 -2
View File
@@ -2,14 +2,15 @@
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "org.saturnclient.saturnclient.mixin", "package": "org.saturnclient.saturnclient.mixin",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "${java}",
"client": [ "client": [
"TabListMixin", "TabListMixin",
"PlayerEntityRendererMixin", "PlayerEntityRendererMixin",
"WhoAmI", "WhoAmI",
"DrawContextAccessor", "DrawContextAccessor",
"SplashOverlayMixin", "SplashOverlayMixin",
"SetScreenMixin" "SetScreenMixin",
"GuiRendererMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
+24 -8
View File
@@ -6,16 +6,27 @@ plugins {
// "Reset active project" before committing. // "Reset active project" before committing.
stonecutter active "1.21.11" stonecutter active "1.21.11"
// src/ uses the newest Mojang names. These classes were renamed in the versions given, so Stonecutter // src/ uses 1.21.11's Mojang names (the active version). These names changed in the versions given,
// swaps in the older names when it processes an older version. // so Stonecutter swaps in each version's name when it processes it.
stonecutter parameters { stonecutter parameters {
for ((since, old, new) in listOf( for ((since, old, new) in listOf(
Triple("1.21.11", "ResourceLocation", "Identifier"), Triple("1.21.11", "ResourceLocation", "Identifier"),
Triple("1.21.9", "PlayerRenderer", "AvatarRenderer"), Triple("1.21.9", "PlayerRenderer", "AvatarRenderer"),
Triple("1.21.9", "PlayerRenderState", "AvatarRenderState"), 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"),
// 26.3 moves Blaze3D's API to renderpearl
Triple("26.3", "com.mojang.blaze3d.GpuFormat", "com.mojang.renderpearl.api.GpuFormat"),
Triple("26.3", "com.mojang.blaze3d.pipeline.RenderPipeline", "com.mojang.renderpearl.api.pipeline.RenderPipeline"),
Triple("26.3", "com.mojang.blaze3d.textures", "com.mojang.renderpearl.api.textures"),
)) { )) {
replacements.regex(eval(current.version, ">=$since")) { 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 +80,22 @@ subprojects {
tasks.register("buildAll") { tasks.register("buildAll") {
group = "build" group = "build"
description = "Builds every Minecraft version's remapped jar and collects them in build/allJars" description = "Builds every Minecraft version's mod jar and collects them in build/allJars"
val remapJars = subprojects.flatMap { it.tasks.matching { task -> task.name == "remapJar" } } // The mod jar is remapJar's output for 1.21.x, and jar's for 26.1+, which isn't obfuscated.
dependsOn(remapJars) val modJars = subprojects.filter { it.name != "common" }.flatMap { project ->
val unobfuscated = project.name.substringBefore('.').toInt() >= 26
val task = if (unobfuscated) "jar" else "remapJar"
project.tasks.matching { it.name == task }
}
dependsOn(modJars)
doLast { doLast {
val outputDir = layout.buildDirectory.dir("allJars").get().asFile val outputDir = layout.buildDirectory.dir("allJars").get().asFile
outputDir.mkdirs() outputDir.mkdirs()
remapJars.map { (it as AbstractArchiveTask).archiveFile.get().asFile } modJars.map { (it as AbstractArchiveTask).archiveFile.get().asFile }
.filter { it.exists() && !it.name.contains("sources") } .filter { it.exists() && !it.name.contains("sources") }
.forEach { it.copyTo(outputDir.resolve(it.name), overwrite = true) } .forEach { it.copyTo(outputDir.resolve(it.name), overwrite = true) }
println("All remapped jars collected in: $outputDir") println("All mod jars collected in: $outputDir")
} }
} }
+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
+17
View File
@@ -0,0 +1,17 @@
# 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.2
loader_version=0.19.5
fabric_api_version=0.161.0+26.2
# Mod Properties
maven_group=org.saturnclient
archives_base_name=saturn-client
+17
View File
@@ -0,0 +1,17 @@
# 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.3
loader_version=0.19.5
fabric_api_version=0.161.0+26.3
# Mod Properties
maven_group=org.saturnclient
archives_base_name=saturn-client