diff --git a/.github/targets.json b/.github/targets.json index f7f148b..519e3fd 100644 --- a/.github/targets.json +++ b/.github/targets.json @@ -1,318 +1,452 @@ [ { "branch": "fabric", - "minecraft": "1.14.4" + "minecraft": "1.14.4", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.15" + "minecraft": "1.15", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.15.1" + "minecraft": "1.15.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.15.2" + "minecraft": "1.15.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16" + "minecraft": "1.16", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16.1" + "minecraft": "1.16.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16.2" + "minecraft": "1.16.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16.3" + "minecraft": "1.16.3", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16.4" + "minecraft": "1.16.4", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.16.5" + "minecraft": "1.16.5", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.17" + "minecraft": "1.17", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.17.1" + "minecraft": "1.17.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.18" + "minecraft": "1.18", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.18.1" + "minecraft": "1.18.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.18.2" + "minecraft": "1.18.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.19" + "minecraft": "1.19", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.19.1" + "minecraft": "1.19.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.19.2" + "minecraft": "1.19.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.19.3" + "minecraft": "1.19.3", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.19.4" + "minecraft": "1.19.4", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20" + "minecraft": "1.20", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.1" + "minecraft": "1.20.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.2" + "minecraft": "1.20.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.3" + "minecraft": "1.20.3", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.4" + "minecraft": "1.20.4", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.5" + "minecraft": "1.20.5", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.20.6" + "minecraft": "1.20.6", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21" + "minecraft": "1.21", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.1" + "minecraft": "1.21.1", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.2" + "minecraft": "1.21.2", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.3" + "minecraft": "1.21.3", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.4" + "minecraft": "1.21.4", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.5" + "minecraft": "1.21.5", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.6" + "minecraft": "1.21.6", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.7" + "minecraft": "1.21.7", + "java": 21 }, { "branch": "fabric", - "minecraft": "1.21.8" + "minecraft": "1.21.8", + "java": 21 + }, + { + "branch": "fabric", + "minecraft": "1.21.9", + "java": 21 + }, + { + "branch": "fabric", + "minecraft": "1.21.10", + "java": 21 + }, + { + "branch": "fabric", + "minecraft": "1.21.11", + "java": 21 }, { "branch": "forge", - "minecraft": "1.14.4" + "minecraft": "1.14.4", + "java": 21 }, { "branch": "forge", - "minecraft": "1.15" + "minecraft": "1.15", + "java": 21 }, { "branch": "forge", - "minecraft": "1.15.1" + "minecraft": "1.15.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.15.2" + "minecraft": "1.15.2", + "java": 21 }, { "branch": "forge", - "minecraft": "1.16.3" + "minecraft": "1.16.3", + "java": 21 }, { "branch": "forge", - "minecraft": "1.16.4" + "minecraft": "1.16.4", + "java": 21 }, { "branch": "forge", - "minecraft": "1.16.5" + "minecraft": "1.16.5", + "java": 21 }, { "branch": "forge", - "minecraft": "1.17.1" + "minecraft": "1.17.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.18" + "minecraft": "1.18", + "java": 21 }, { "branch": "forge", - "minecraft": "1.18.1" + "minecraft": "1.18.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.18.2" + "minecraft": "1.18.2", + "java": 21 }, { "branch": "forge", - "minecraft": "1.19" + "minecraft": "1.19", + "java": 21 }, { "branch": "forge", - "minecraft": "1.19.1" + "minecraft": "1.19.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.19.2" + "minecraft": "1.19.2", + "java": 21 }, { "branch": "forge", - "minecraft": "1.19.3" + "minecraft": "1.19.3", + "java": 21 }, { "branch": "forge", - "minecraft": "1.19.4" + "minecraft": "1.19.4", + "java": 21 }, { "branch": "forge", - "minecraft": "1.20" + "minecraft": "1.20", + "java": 21 }, { "branch": "forge", - "minecraft": "1.20.1" + "minecraft": "1.20.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.20.2" + "minecraft": "1.20.2", + "java": 21 }, { "branch": "forge", - "minecraft": "1.20.4" + "minecraft": "1.20.4", + "java": 21 }, { "branch": "forge", - "minecraft": "1.20.6" + "minecraft": "1.20.6", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21" + "minecraft": "1.21", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.1" + "minecraft": "1.21.1", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.3" + "minecraft": "1.21.3", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.4" + "minecraft": "1.21.4", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.5" + "minecraft": "1.21.5", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.6" + "minecraft": "1.21.6", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.7" + "minecraft": "1.21.7", + "java": 21 }, { "branch": "forge", - "minecraft": "1.21.8" + "minecraft": "1.21.8", + "java": 21 + }, + { + "branch": "forge", + "minecraft": "1.21.9", + "java": 21 + }, + { + "branch": "forge", + "minecraft": "1.21.10", + "java": 21 + }, + { + "branch": "forge", + "minecraft": "1.21.11", + "java": 21 + }, + { + "branch": "neoforge", + "minecraft": "1.20.2", + "java": 21 + }, + { + "branch": "neoforge", + "minecraft": "1.20.3", + "java": 21 + }, + { + "branch": "neoforge", + "minecraft": "1.20.4", + "java": 21 + }, + { + "branch": "neoforge", + "minecraft": "1.20.5", + "java": 21 + }, + { + "branch": "neoforge", + "minecraft": "1.20.6", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.20.2" + "minecraft": "1.21", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.20.3" + "minecraft": "1.21.1", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.20.4" + "minecraft": "1.21.2", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.20.5" + "minecraft": "1.21.3", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.20.6" + "minecraft": "1.21.4", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21" + "minecraft": "1.21.5", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21.1" + "minecraft": "1.21.6", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21.2" + "minecraft": "1.21.7", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21.3" + "minecraft": "1.21.8", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21.4" + "minecraft": "1.21.9", + "java": 21 }, { "branch": "neoforge", - "minecraft": "1.21.5" + "minecraft": "26.1", + "java": 25 }, { "branch": "neoforge", - "minecraft": "1.21.6" + "minecraft": "26.1.1", + "java": 25 }, { "branch": "neoforge", - "minecraft": "1.21.7" + "minecraft": "26.1.2", + "java": 25 }, { "branch": "neoforge", - "minecraft": "1.21.8" + "minecraft": "26.2", + "java": 25 } ] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9429488..fda578a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,14 +36,18 @@ jobs: steps: - uses: actions/checkout@v4 + # The JVM the Gradle daemon itself runs on, not a compile target - every + # target sets its own `options.release`. Minecraft 26 refuses to set up + # under anything below 25, and Loom on the older versions has only ever + # been exercised on 21, so each target names the one it wants. - uses: actions/setup-java@v4 with: distribution: temurin - java-version: 21 + java-version: ${{ matrix.target.java }} - uses: gradle/actions/setup-gradle@v4 - # Retried because Maven Central rate limits us: 79 jobs, each one + # Retried because Maven Central rate limits us: 90 jobs, each one # fetching its own Minecraft and Forge artifacts from shared runner IPs, # and no writable Actions cache on a pull request branch. Some of those # downloads are Loom's own rather than Gradle's, so they get no retry or diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dcd05c5..727bfa8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 # A jar carries the version from gradle.properties, not the tag. Without - # this, tagging v7.0.1 while mod_version still says 7.0.0 publishes 80 + # this, tagging v7.0.1 while mod_version still says 7.0.0 publishes 90 # files under the old version, and CurseForge files can't be replaced. - name: Check the tag matches mod_version if: github.event_name == 'release' @@ -46,7 +46,7 @@ jobs: # Uploads are independent; one bad target shouldn't cancel the rest. fail-fast: false # Each job decompiles its own Minecraft, so a target costs minutes, and - # there are 80 of them - serial uploads would run most of a day. Four at + # there are 90 of them - serial uploads would run most of a day. Four at # a time matches the build workflow's headroom against Maven Central and # stays well inside both platforms' upload rate limits. max-parallel: 4 @@ -55,10 +55,12 @@ jobs: steps: - uses: actions/checkout@v4 + # Per-target, for the same reason as the build workflow: Minecraft 26 + # needs a Java 25 daemon, the rest are built on 21. - uses: actions/setup-java@v4 with: distribution: temurin - java-version: 21 + java-version: ${{ matrix.target.java }} - uses: gradle/actions/setup-gradle@v4 diff --git a/README.md b/README.md index ebe2a06..82e2ed3 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,15 @@ on one. Every loader and every Minecraft version is built from this one branch. -Every Minecraft release each loader ever shipped for, from 1.14.4 to 1.21.8 — -80 jars in all. +Every Minecraft release each loader ever shipped for, from 1.14.4 to 26.2 — +90 jars in all. | Loader | Minecraft versions | |----------|-------------------------------------------------------------------------------| -| Fabric | 1.14.4, 1.15–1.15.2, 1.16–1.16.5, 1.17–1.17.1, 1.18–1.18.2, 1.19–1.19.4, 1.20–1.20.6, 1.21–1.21.8 | +| Fabric | 1.14.4, 1.15–1.15.2, 1.16–1.16.5, 1.17–1.17.1, 1.18–1.18.2, 1.19–1.19.4, 1.20–1.20.6, 1.21–1.21.11 | | Quilt | same jars as Fabric | -| Forge | 1.14.4, 1.15–1.15.2, 1.16.3–1.16.5, 1.17.1, 1.18–1.18.2, 1.19–1.19.4, 1.20–1.20.4, 1.20.6, 1.21, 1.21.1, 1.21.3–1.21.8 | -| NeoForge | 1.20.2–1.20.6, 1.21–1.21.8 | +| Forge | 1.14.4, 1.15–1.15.2, 1.16.3–1.16.5, 1.17.1, 1.18–1.18.2, 1.19–1.19.4, 1.20–1.20.4, 1.20.6, 1.21, 1.21.1, 1.21.3–1.21.11 | +| NeoForge | 1.20.2–1.20.6, 1.21–1.21.9, 26.1–26.1.2, 26.2 | Quilt Loader reads `fabric.mod.json` directly, so the Fabric jar *is* the Quilt jar — there's nothing extra to build, and releases are tagged for both. @@ -41,6 +41,15 @@ dies remapping Minecraft on a name conflict, and on 1.16.2 the mixin annotation processor is given no SRG mappings and so can't write a refmap. Fabric covers both versions. +Nothing here ships a refmap any more. Current Loom defaults to +`useLegacyMixinAp = false`: instead of running Mixin's annotation processor to +emit a name map alongside the jar, it rewrites the mixin annotations themselves +into SRG inside `remapJar`. The jars are equivalent — the mapping is in the +class files rather than in a JSON file next to them — but it means +`cleancut.mixins.json` must *not* declare a `refmap`, and no build script here +may add Mixin as an `annotationProcessor`. Doing either points the build at +mapping data that is never generated. + On Forge 1.14.4, 1.15 and 1.15.1 the mod carries its own copy of Mixin, because Forge only started bundling Mixin partway through 1.15.2. See `forge/src/main/java/zone/rong/cleancut/bootstrap/`. @@ -65,8 +74,8 @@ the Forge fork from before the rename and still lives under `net.minecraftforge`, so the **Forge 1.20.1 jar is the NeoForge 1.20.1 jar** — it's the same API, and building it twice would produce the same mod. -1.21.8 is the upper limit for every loader, for the toolchain reason described -under [Newer Minecraft](#newer-minecraft). +The ceilings differ per loader — 1.21.11 on Fabric and Forge, 26.2 on NeoForge — +for the reasons described under [Newer Minecraft](#newer-minecraft). ## How the repository is laid out @@ -104,11 +113,20 @@ the logic, and a mixin that decides where vanilla gets interrupted. ./gradlew :neoforge:1.21.4:build ``` -Jars land in `/versions//build/libs/`. Build with **JDK 21** — -each target compiles down to the release level its Minecraft version needs, so -one JDK covers all 82. It has to be 21 and not something newer: this build is on -Gradle 8, which rejects JDK 25 with `Unsupported class file major version 69`. -Set `JAVA_HOME` if your default `java` is a later release. +Jars land in `/versions//build/libs/`. Build with **JDK 21**, +except for the Minecraft 26 targets, which need **JDK 25**: + +```sh +./gradlew :neoforge:26.2:build # needs JAVA_HOME on a JDK 25 +``` + +That is the JVM the Gradle daemon runs on, not what the mod compiles against — +each target sets its own `options.release`, so one daemon covers every release +level from 8 upwards. Loom refuses to set Minecraft 26 up under anything below +25 (`Minecraft 26.1 requires Java 25 but Gradle is using 21`), and the older +targets have only been exercised on 21. `.github/targets.json` carries the +version each target wants, and CI installs it per job. Set `JAVA_HOME` to +switch locally. `./gradlew build` with no arguments builds every target in sequence, which takes a while — the per-version Minecraft decompile dominates. CI builds them in @@ -132,27 +150,44 @@ every target, which is slow — they're for IDE work, not part of building. ## Newer Minecraft -1.21.8 is where this build stops, and the reason is the toolchain rather than -the mod. From 1.21.9 Yarn ships unpick v3 data, which needs Fabric Loom 1.17, -which needs Gradle 9 — and the Gradle wrapper and the Stonecutter version are -properties of the whole build, not of one branch, so this can't be mixed with -the Gradle 8 stack the older versions are on. - -Forge and NeoForge do have 1.21.9+ builds, and being Mojang-mapped they never -touch unpick. They stop at 1.21.8 anyway so that all three loaders cover the -same range, and because the Architectury Loom pinned here predates those -versions. Raising their ceiling is a row in the `versionData` table plus -whatever the compiler then complains about — worth doing on its own, rather -than mixed into a Gradle 9 migration. - -The same wall stands in front of Minecraft's new version scheme (26.1.2 and -later), which additionally has no Yarn mappings at all and would have to be -built against Mojang's. - -Getting there means either moving the entire build to Gradle 9, Stonecutter -0.9 and current Loom — which risks the 1.14-1.16 targets, whose support in -current Loom is unverified — or keeping this build as it is and adding a -second, modern Gradle build beside it in the same branch. +This build is on Gradle 9, Stonecutter 0.9 and current Loom, because Minecraft +26 needs a Java 25 daemon and Gradle 8 cannot run on Java 25 at all — it rejects +the class files. The wrapper and the Stonecutter version are properties of the +whole build rather than of one branch, so that migration was all-or-nothing. + +The obvious worry about moving the whole build forward is that current Loom +drops the oldest targets, and it very nearly did. Forge 1.14.4, 1.15 and 1.15.1 +— the three that shade their own Mixin — failed with `Unable to locate +obfuscation mapping for @Redirect target`. The cause was on this side, not +Loom's: those three added Mixin as an `annotationProcessor` by hand, and current +Loom no longer passes the processor any mapping arguments because it no longer +uses the processor. Dropping those lines fixes all three. See the note on +refmaps under [Supported versions](#supported-versions), and don't reintroduce +them. + +Every other target from 1.14.4 up builds unchanged, so there is one build here, +not a legacy one and a modern one. + +What is left is per-loader, and none of it is a toolchain problem: + +**NeoForge skips 1.21.10 and 1.21.11.** From 21.10 its published artifact no +longer carries `data/server.lzma` where Architectury Loom expects it. The +packaging moved out from under Loom, so a version bump on this side doesn't fix +it — building those two means NeoForge's own toolchain instead of Loom, which is +a different build rather than a different number. It picks back up at 26.1. +Fabric and Forge cover 1.21.10 and 1.21.11. + +**Forge stops at 1.21.11.** On 26.x Loom throws an NPE setting Minecraft up on +the Forge platform. NeoForge 26.x goes through the same Loom and works, so this +is Forge-platform-specific. + +**Fabric stops at 1.21.11 because 26.x has no Yarn.** Intermediary exists, Yarn +does not. The Fabric sources here are written in Yarn names, so 26.x on Fabric +means either Mojang mappings — and the class names in `fabric/src` change +wholesale, since `MinecraftClient` becomes `Minecraft` and so on — or nothing. +Forge and NeoForge are already Mojang-mapped and don't have this problem. + +Yarn's unpick v3 is *not* a wall, despite an earlier note here saying so. ## Adding a Minecraft version @@ -160,7 +195,9 @@ second, modern Gradle build beside it in the same branch. 2. Add a row to that loader's `versionData` table in `/build.gradle` with the mappings or loader build and the Java release level. The dependency ranges that go into the mod metadata are derived from those, not written out. -3. Add it to `.github/targets.json` so CI builds it. +3. Add it to `.github/targets.json` so CI builds it, with the `java` the Gradle + daemon needs for it — 21 for everything so far except Minecraft 26, which + needs 25. Then build it. If an API moved, the compiler will say so, and the fix is another `//? if` block around the two alternatives. diff --git a/fabric/build.gradle b/fabric/build.gradle index 4ddd8f0..19e9c65 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,8 +1,17 @@ plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + // Minecraft 26 ships unobfuscated, and Fabric Loom has no mode for that - + // it requires a mappings dependency that no longer exists for those + // versions. Architectury Loom's no-remap plugin does, and it drives the + // Fabric platform as well as the other two. + id 'fabric-loom' version '1.17.17' apply false + id 'dev.architectury.loom-no-remap' version '1.17.485' apply false id 'me.modmuss50.mod-publish-plugin' version '2.2.0' } +apply plugin: stonecutter.current.version.startsWith('26') + ? 'dev.architectury.loom-no-remap' + : 'fabric-loom' + // Everything that differs between Minecraft versions on this loader. Adding a // version means adding a row here and listing it in settings.gradle. // `mappings` is the Yarn build; `java` is the release level that version of @@ -44,9 +53,14 @@ def versionData = [ '1.21.6': [mappings: '1.21.6+build.1', java: 21], '1.21.7': [mappings: '1.21.7+build.8', java: 21], '1.21.8': [mappings: '1.21.8+build.1', java: 21], + '1.21.9' : [mappings: '1.21.9+build.1', java: 21], + '1.21.10': [mappings: '1.21.10+build.3', java: 21], + '1.21.11': [mappings: '1.21.11+build.6', java: 21], + '26.2' : [mappings: null, java: 25], ] def minecraftVersion = stonecutter.current.version +def unobfuscated = minecraftVersion.startsWith('26') def target = versionData[minecraftVersion] if (target == null) { throw new GradleException("No Fabric build data for Minecraft ${minecraftVersion}; add a row to fabric/build.gradle") @@ -61,8 +75,20 @@ base { dependencies { minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings "net.fabricmc:yarn:${target.mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" + // Minecraft 26 is unobfuscated: no Yarn, no Mojang mappings, nothing to + // declare. Yarn existed to give obfuscated names meaning, and the names + // now come in the jar. + if (target.mappings != null) { + mappings "net.fabricmc:yarn:${target.mappings}:v2" + } + // modImplementation exists to remap a dependency out of intermediary on + // the way in. With nothing obfuscated the no-remap plugin doesn't register + // it, and a plain dependency is already the right thing. + if (unobfuscated) { + implementation "net.fabricmc:fabric-loader:${fabric_loader_version}" + } else { + modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" + } } processResources { @@ -100,7 +126,9 @@ jar { } publishMods { - file = tasks.remapJar.archiveFile + // No remapJar where nothing is obfuscated: the jar off the compiler is + // already the jar that ships. + file = (unobfuscated ? tasks.jar : tasks.remapJar).archiveFile displayName = "${mod_name} ${mod_version} - Fabric ${minecraftVersion}" version = "${mod_version}+${minecraftVersion}-fabric" changelog = providers.environmentVariable('CHANGELOG') diff --git a/forge/build.gradle b/forge/build.gradle index deadbcd..69b65b9 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,8 +1,17 @@ plugins { - id 'dev.architectury.loom' version '1.10-SNAPSHOT' + // Minecraft 26 ships unobfuscated, and Loom takes a different plugin for + // that - remapping a jar that was never obfuscated is not a no-op, it is a + // step that has nothing to work from. Both are declared and one is applied + // below, once the version being built is known. + id 'dev.architectury.loom' version '1.17.485' apply false + id 'dev.architectury.loom-no-remap' version '1.17.485' apply false id 'me.modmuss50.mod-publish-plugin' version '2.2.0' } +apply plugin: stonecutter.current.version.startsWith('26') + ? 'dev.architectury.loom-no-remap' + : 'dev.architectury.loom' + // Everything that differs between Minecraft versions on this loader. Adding a // version means adding a row here and listing it in settings.gradle. // Forge/NeoForge build numbers are pinned: Architectury Loom resolves the @@ -48,9 +57,17 @@ def versionData = [ '1.21.6': [forge: '1.21.6-56.0.9', java: 21], '1.21.7': [forge: '1.21.7-57.0.3', java: 21], '1.21.8': [forge: '1.21.8-58.1.0', java: 21], + '1.21.9': [forge: '1.21.9-59.0.5', java: 21], + '1.21.10': [forge: '1.21.10-60.1.13', java: 21], + '1.21.11': [forge: '1.21.11-61.1.14', java: 21], + '26.1' : [forge: '26.1-62.0.9', java: 25], + '26.1.1': [forge: '26.1.1-63.0.2', java: 25], + '26.1.2': [forge: '26.1.2-64.1.0', java: 25], + '26.2' : [forge: '26.2-65.1.0', java: 25], ] def minecraftVersion = stonecutter.current.version +def unobfuscated = minecraftVersion.startsWith('26') def target = versionData[minecraftVersion] if (target == null) { throw new GradleException("No Forge build data for Minecraft ${minecraftVersion}; add a row to forge/build.gradle") @@ -109,30 +126,44 @@ configurations { loom { silentMojangMappingsLicense() - mixin { - // Must match the "refmap" entry in cleancut.mixins.json - Forge reads - // the name from the config rather than having it injected. - defaultRefmapName = "${mod_id}.refmap.json" - } - forge { - mixinConfig "${mod_id}.mixins.json" + // No refmap anywhere in here, on any version. A refmap maps mixin targets + // back to obfuscated names, and this Loom doesn't produce one: it defaults + // to `useLegacyMixinAp = false` and rewrites the annotations themselves + // into SRG inside remapJar. Declaring one in cleancut.mixins.json would + // just point Mixin at a file the build never writes. + // + // mixinConfig still matters - it puts the config in the jar manifest, + // which is how Forge finds it. The no-remap plugin used for Minecraft 26 + // sets that up itself before this script runs. + if (!unobfuscated) { + forge { + mixinConfig "${mod_id}.mixins.json" + } } } dependencies { minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings loom.officialMojangMappings() + // Minecraft 26 ships unobfuscated - the jar already carries real names, + // and Mojang stopped publishing mappings because nothing is left to map. + // Declaring any mappings there is an error, not a no-op. + if (!unobfuscated) { + mappings loom.officialMojangMappings() + } forge "net.minecraftforge:forge:${target.forge}" if (shadesMixin) { - // Compile against Mixin and run the annotation processor over it, so - // the refmap still gets generated, then put Mixin in the jar. + // Compile against Mixin, then put it in the jar. + // + // Deliberately no `annotationProcessor` for Mixin here. This Loom runs + // with `useLegacyMixinAp = false`: it rewrites mixin annotations into + // SRG names inside remapJar instead of having Mixin's processor emit a + // refmap. Adding the processor by hand puts it on the compile path + // without any of the mapping arguments Loom used to pass with it, and + // it then fails outright with "Unable to locate obfuscation mapping + // for @Redirect target". Every other Forge version here already builds + // through the remapper; these three are no different. compileOnly "org.spongepowered:mixin:${mixinVersion}" - annotationProcessor "org.spongepowered:mixin:${mixinVersion}" - // Mixin's POM marks these provided, so the annotation processor would - // otherwise start up and immediately fail on NoClassDefFoundError. - annotationProcessor 'com.google.code.gson:gson:2.8.9' - annotationProcessor 'com.google.guava:guava:31.1-jre' shaded "org.spongepowered:mixin:${mixinVersion}" // Just asm-util, not the ASM it was built against - taking its // transitives would put a whole second copy of ASM in the jar. @@ -213,7 +244,9 @@ jar { } publishMods { - file = tasks.remapJar.archiveFile + // No remapJar where nothing is obfuscated: the jar off the compiler is + // already the jar that ships. + file = (unobfuscated ? tasks.jar : tasks.remapJar).archiveFile displayName = "${mod_name} ${mod_version} - Forge ${minecraftVersion}" version = "${mod_version}+${minecraftVersion}-forge" changelog = providers.environmentVariable('CHANGELOG') diff --git a/forge/src/main/resources/cleancut.mixins.json b/forge/src/main/resources/cleancut.mixins.json index 0d67ba8..2eaa224 100644 --- a/forge/src/main/resources/cleancut.mixins.json +++ b/forge/src/main/resources/cleancut.mixins.json @@ -3,7 +3,6 @@ "minVersion": "0.8", "package": "zone.rong.cleancut.mixin", "compatibilityLevel": "${mixin_compat}", - "refmap": "${mod_id}.refmap.json", "client": [ "MinecraftMixin" ], diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e583..1b33c55 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 59af5fc..a351597 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Multi-version CleanCut build distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..23d15a9 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -133,22 +133,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd3..db3a6ac 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/neoforge/build.gradle b/neoforge/build.gradle index de3b0d3..30e9d42 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -1,8 +1,17 @@ plugins { - id 'dev.architectury.loom' version '1.10-SNAPSHOT' + // Minecraft 26 ships unobfuscated, and Loom takes a different plugin for + // that - remapping a jar that was never obfuscated is not a no-op, it is a + // step that has nothing to work from. Both are declared and one is applied + // below, once the version being built is known. + id 'dev.architectury.loom' version '1.17.485' apply false + id 'dev.architectury.loom-no-remap' version '1.17.485' apply false id 'me.modmuss50.mod-publish-plugin' version '2.2.0' } +apply plugin: stonecutter.current.version.startsWith('26') + ? 'dev.architectury.loom-no-remap' + : 'dev.architectury.loom' + // Everything that differs between Minecraft versions on this loader. Adding a // version means adding a row here and listing it in settings.gradle. // Pinned rather than a range: Architectury Loom resolves the userdev artifact @@ -25,9 +34,15 @@ def versionData = [ '1.21.6': [neoforge: '21.6.20-beta', java: 21], // no stable build '1.21.7': [neoforge: '21.7.25-beta', java: 21], // no stable build '1.21.8': [neoforge: '21.8.54', java: 21], + '1.21.9': [neoforge: '21.9.16-beta', java: 21], // no stable build + '26.1' : [neoforge: '26.1.0.19-beta', java: 25], // no stable build + '26.1.1': [neoforge: '26.1.1.15-beta', java: 25], // no stable build + '26.1.2': [neoforge: '26.1.2.93', java: 25], // .94 has no installer + '26.2' : [neoforge: '26.2.0.41-beta', java: 25], // no stable build ] def minecraftVersion = stonecutter.current.version +def unobfuscated = minecraftVersion.startsWith('26') def target = versionData[minecraftVersion] if (target == null) { throw new GradleException("No NeoForge build data for Minecraft ${minecraftVersion}; add a row to neoforge/build.gradle") @@ -35,7 +50,11 @@ if (target == null) { // Derived rather than written out per row: the NeoForge range is "this // NeoForge line or newer", and the Minecraft range is this version only. -def neoLine = target.neoforge.tokenize('.')[0..1].join('.') +// Taken from the Minecraft version rather than NeoForge's own, because the +// two don't have the same shape: 1.21.8 is NeoForge 21.8.x, three parts, while +// 26.1.2 is NeoForge 26.1.2.x, four. Dropping Minecraft's leading "1." gives +// the line in both schemes. +def neoLine = minecraftVersion.startsWith('1.') ? minecraftVersion.substring(2) : minecraftVersion def neoforgeRange = "[${neoLine},)" // `loaderVersion` is not that. It is the version of the javafml language // provider, which NeoForge numbers on its own line - 1.21.8 ships javafml @@ -81,7 +100,12 @@ loom { dependencies { minecraft "com.mojang:minecraft:${minecraftVersion}" - mappings loom.officialMojangMappings() + // Minecraft 26 ships unobfuscated - the jar already carries real names, + // and Mojang stopped publishing mappings because nothing is left to map. + // Declaring any mappings there is an error, not a no-op. + if (!unobfuscated) { + mappings loom.officialMojangMappings() + } neoForge "net.neoforged:neoforge:${target.neoforge}" } @@ -126,7 +150,9 @@ jar { } publishMods { - file = tasks.remapJar.archiveFile + // No remapJar where nothing is obfuscated: the jar off the compiler is + // already the jar that ships. + file = (unobfuscated ? tasks.jar : tasks.remapJar).archiveFile displayName = "${mod_name} ${mod_version} - NeoForge ${minecraftVersion}" version = "${mod_version}+${minecraftVersion}-neoforge" changelog = providers.environmentVariable('CHANGELOG') diff --git a/neoforge/src/main/java/zone/rong/cleancut/mixin/MinecraftMixin.java b/neoforge/src/main/java/zone/rong/cleancut/mixin/MinecraftMixin.java index e059e9f..ba97578 100644 --- a/neoforge/src/main/java/zone/rong/cleancut/mixin/MinecraftMixin.java +++ b/neoforge/src/main/java/zone/rong/cleancut/mixin/MinecraftMixin.java @@ -66,10 +66,16 @@ public class MinecraftMixin { if (entity == null) { return null; } + //? if <26 { InteractionResult result = this.gameMode.interactAt(this.player, entity, new EntityHitResult(entity), hand); if (!CleanCut.accepted(result)) { result = this.gameMode.interact(this.player, entity, hand); } + //?} else { + /*// 26 merged the two: interact now takes the hit result, and the + // separate interactAt is gone. + InteractionResult result = this.gameMode.interact(this.player, entity, new EntityHitResult(entity), hand); + *///?} // Anything vanilla would call a hit stops here - the caller swings for // us. Anything else falls through to the block we were pointing at. return CleanCut.accepted(result) ? InteractionResult.SUCCESS : null; diff --git a/settings.gradle b/settings.gradle index e4d0179..532d140 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,7 +22,7 @@ pluginManagement { } plugins { - id 'dev.kikugie.stonecutter' version '0.7.11' + id 'dev.kikugie.stonecutter' version '0.9.7' } // One branch per mod loader, one version per supported Minecraft release. @@ -32,9 +32,9 @@ stonecutter { create(rootProject) { centralScript.set('build.gradle') - // Every Minecraft release each loader ever shipped for, from 1.14.4 up - // to the 1.21.8 ceiling. Gaps are releases that loader skipped, not - // omissions - see the tables in /build.gradle. + // Every Minecraft release each loader ever shipped for, from 1.14.4 + // up. Gaps are releases that loader skipped, not omissions - see the + // tables in /build.gradle. branch('fabric') { fabric -> fabric.versions( '1.14.4', @@ -45,9 +45,11 @@ stonecutter { '1.19', '1.19.1', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2', '1.20.3', '1.20.4', '1.20.5', '1.20.6', '1.21', '1.21.1', '1.21.2', '1.21.3', '1.21.4', '1.21.5', - '1.21.6', '1.21.7', '1.21.8' - // 1.21.9+ needs Yarn's unpick v3, which needs Loom 1.17, - // which needs Gradle 9. See "Newer Minecraft" in README.md. + '1.21.6', '1.21.7', '1.21.8', + '1.21.9', '1.21.10', '1.21.11' + // 26.x builds as far as the compiler on this branch, but + // these sources are Yarn-named and 26 is Mojang-named. + // See "Newer Minecraft" in README.md. ) } @@ -65,7 +67,10 @@ stonecutter { '1.19', '1.19.1', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2', '1.20.4', '1.20.6', '1.21', '1.21.1', '1.21.3', '1.21.4', '1.21.5', - '1.21.6', '1.21.7', '1.21.8' + '1.21.6', '1.21.7', '1.21.8', + '1.21.9', '1.21.10', '1.21.11' + // 26.x: Loom throws an NPE setting Minecraft up on the + // Forge platform. NeoForge 26.x works. ) } @@ -76,7 +81,11 @@ stonecutter { neoforge.versions( '1.20.2', '1.20.3', '1.20.4', '1.20.5', '1.20.6', '1.21', '1.21.1', '1.21.2', '1.21.3', '1.21.4', '1.21.5', - '1.21.6', '1.21.7', '1.21.8' + '1.21.6', '1.21.7', '1.21.8', + '1.21.9', + // 1.21.10 and 1.21.11 are packaged in a way this Loom + // can't read - see "Newer Minecraft" in README.md. + '26.1', '26.1.1', '26.1.2', '26.2' ) }