Check / compile (pull_request) Successful in 7m8s
Step 3 of #8. 1.21.9's copy was identical to the old 1.21.10 one, so the existing version comments already cover it and no code changes: 1.21.9 is added to stonecutterVersions and versions/1.21.9/src is removed. Like 1.21.10 in step 2, 1.21.9 now renders hats (1.21.11's render-queue implementation) and gets the display-name null check. The jar has the same entries, fabric.mod.json and mixin configs as before; only those classes and ShaderUtils's entityAlpha helper differ in bytecode. Co-Authored-By: Claude Opus 5.5 <[email protected]>
80 lines
5.7 KiB
Markdown
80 lines
5.7 KiB
Markdown
# Version guide
|
|
|
|
How Saturn Client supports several Minecraft versions, and how to change or add one.
|
|
|
|
## Layout
|
|
|
|
Saturn is moving from one full copy of the version-specific code per Minecraft version to a single copy built with [Stonecutter](https://stonecutter.kikugie.dev/) (issue #8). During the move there are two kinds of version:
|
|
|
|
- `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) for the versions built by Stonecutter, currently **1.21.9 to 1.21.11**. It's one copy: where Minecraft's API differs between them, the code goes behind version comments (see below). Each of these versions is the Gradle project `:<mc>`, and `versions/<mc>/` holds only its `gradle.properties` (Minecraft, Yarn, Fabric Loader and Fabric API versions), `build/` and `run/`.
|
|
- `versions/<mc>/src/`: the versions not moved yet (1.21.4 to 1.21.8), each a full copy, as the Gradle project `:mc-<mc>` with its own `build.gradle`.
|
|
|
|
`settings.gradle.kts` lists the Stonecutter versions in `stonecutterVersions` and includes every other folder under `versions/` as `:mc-<mc>`. The root `build.gradle.kts` builds each Stonecutter version, and `stonecutter.gradle.kts` configures the root project (shared repositories, the `common` wiring and `buildAll`). Loom's version for the Stonecutter versions is `loom_version` in the root `gradle.properties`.
|
|
|
|
## Version-specific code with Stonecutter
|
|
|
|
The code in `src/` is always plain Java for the **active** version (1.21.11, set in `stonecutter.gradle.kts`). Code for other versions sits in comments that Stonecutter swaps before building each version:
|
|
|
|
```java
|
|
//? if >=1.21.9 {
|
|
public void render(MatrixStack matrices, OrderedRenderCommandQueue queue, int light, PlayerEntityRenderState state, float limbAngle, float limbDistance) {
|
|
//?} else
|
|
//public void render(MatrixStack matrices, VertexConsumerProvider consumers, int light, PlayerEntityRenderState state, float limbAngle, float limbDistance) {
|
|
```
|
|
|
|
A condition without braces covers the next line only. For several lines, use braces and wrap the inactive block in `/* … */`:
|
|
|
|
```java
|
|
//? if >=1.21.11 {
|
|
import net.minecraft.client.render.RenderLayers;
|
|
import net.minecraft.client.render.RenderSetup;
|
|
//?} else {
|
|
/*import net.minecraft.client.render.RenderPhase.Textures;
|
|
*///?}
|
|
```
|
|
|
|
Stonecutter rewrites inactive code into its own layout when switching versions (a single inactive line becomes `//code`), so write it that way to begin with, or run a switch and a reset before committing.
|
|
|
|
When a newer version leaves something out that an older one implements, keep the older version's code behind a condition and mark the newer branch with a `TODO`, rather than dropping it for every version. `WorldFeatureImpl.getWorldAge` and `SaturnScreenFabric.resize` are examples.
|
|
|
|
- `./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.
|
|
- Each version's processed copy is compiled from `versions/<mc>/build/generated/stonecutter/`, which is also where to look when a non-active version fails to compile.
|
|
|
|
The `emotes` repository uses the same setup, and its `BentCubeRenderer` and `PlayerModelMixin` are small examples of version comments.
|
|
|
|
## Porting a change to the other versions
|
|
|
|
Make the change in one version, usually the newest, then run:
|
|
|
|
```sh
|
|
scripts/port.sh # port uncommitted changes to every other version
|
|
scripts/port.sh --dry-run # see what would apply cleanly first
|
|
scripts/port.sh --to 1.21.7,1.21.8 # only some versions
|
|
scripts/port.sh --commit <rev> # port what a commit changed instead
|
|
```
|
|
|
|
The script applies the diff under one version's source to each other version with `git apply --3way`. The Stonecutter `src/` counts as one version (1.21.11), so changes port between it and the old copies in both directions:
|
|
|
|
- **applied**: the files matched, and nothing else is needed.
|
|
- **CONFLICTS**: that version's code really differs. The file gets normal conflict markers, with the version's own code in `ours` and the ported change in `theirs`. Adapt the change to that version's API and resolve.
|
|
- **already has this change**: nothing to do, which is safe to see when re-running.
|
|
|
|
Only source is ported. `build.gradle` and `gradle.properties` are version-specific on purpose. Then build everything:
|
|
|
|
```sh
|
|
./gradlew compileJava
|
|
```
|
|
|
|
The **Check** workflow runs the same compile on every pull request, so a version that was missed or mis-ported fails before merging.
|
|
|
|
## Adding a Minecraft version
|
|
|
|
Add new versions to Stonecutter rather than copying a folder:
|
|
|
|
1. Create `versions/<new>/gradle.properties` with `minecraft_version`, `yarn_mappings`, `loader_version` and `fabric_api_version` (copy 1.21.11's and change the values; they're listed at <https://fabricmc.net/develop>). Loom's version is shared, in the root `gradle.properties`.
|
|
2. Add `<new>` to `stonecutterVersions` in `settings.gradle.kts`.
|
|
3. Build it with `./gradlew :<new>:compileJava` and put what Minecraft changed behind version comments. Check the mixin configs in `src/main/resources/*.mixins.json` as well: a mixin whose target changed fails at startup, not at compile time, so launch it with `./gradlew :<new>:runClient`.
|
|
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.
|