Step 1 of #8. 1.21.11's version-specific code moves from versions/1.21.11/src to the top-level src/, built by Stonecutter as the project :1.21.11. The other versions still build from their own copies as :mc-<v> until they're folded in. - settings.gradle: applies Stonecutter (Groovy controller) with stonecutterVersions = ["1.21.11"] and skips those folders when including the old mc-<v> projects. - The old root build.gradle becomes stonecutter.gradle (the root project's script, with the shared repositories, common wiring and buildAll), and 1.21.11's build.gradle becomes the root build.gradle that Stonecutter runs for each version. versions/1.21.11 keeps its gradle.properties and run/. - The jar has the same 415 entries, fabric.mod.json and mixin configs as before; the only class differences also appear when rebuilding an untouched version today. - scripts/port.sh treats src/ as the active Stonecutter version, so changes port between it and the old copies both ways. - README and VERSION_GUIDE describe the layout, version comments and how to add a version. Co-Authored-By: Claude Opus 5.5 <[email protected]>
4.7 KiB
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 (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 inorg.saturnclient.common.src/: the version-specific code (providers, refs, mixins and the three mixin configs) for the versions built by Stonecutter, currently 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>, andversions/<mc>/holds only itsgradle.properties(Minecraft, Yarn, Fabric Loader and Fabric API versions),build/andrun/.versions/<mc>/src/: the versions not moved yet (1.21.4 to 1.21.10), each a full copy, as the Gradle project:mc-<mc>with its ownbuild.gradle.
settings.gradle lists the Stonecutter versions in stonecutterVersions and includes every other folder under versions/ as :mc-<mc>. The root build.gradle builds each Stonecutter version, and stonecutter.gradle configures the root project (shared repositories, the common wiring and buildAll).
Version-specific code with Stonecutter
The code in src/ is always plain Java for the active version (1.21.11, set in stonecutter.gradle). Code for other versions sits in comments that Stonecutter swaps before building each version:
//? 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) {*/
./gradlew "Set active project to <mc>"(or the Stonecutter IntelliJ plugin) rewritessrc/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, sosrc/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:
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.9,1.21.10 # 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
oursand the ported change intheirs. 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:
./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:
- Create
versions/<new>/gradle.propertieswithminecraft_version,yarn_mappings,loader_versionandfabric_api_version(copy 1.21.11's and change the values; they're listed at https://fabricmc.net/develop). - Add
<new>tostonecutterVersionsinsettings.gradle. - Build it with
./gradlew :<new>:compileJavaand put what Minecraft changed behind version comments. Check the mixin configs insrc/main/resources/*.mixins.jsonas well: a mixin whose target changed fails at startup, not at compile time, so launch it with./gradlew :<new>:runClient. - The release workflow builds everything through
buildAll, and "Publish to Modrinth" takes the game version from each jar's name, so neither needs changing.