Support Minecraft 26.1 through 26.2, and fix the Base64Coder crash they introduce - #178
Support Minecraft 26.1 through 26.2, and fix the Base64Coder crash they introduce#178Aelshi-nui wants to merge 4 commits into
Conversation
- add FoliaDetector and SchedulerAdapter - switch schedulers to Folia-safe Consumer<ScheduledTask> API - fix region-based thread-safety issues - exclude optional integrations via stub JARs - tested on Folia 1.21.11 and Paper 1.21
Minecraft switched to year based version numbers after 1.21.11, so the
drops are 26.1 and up rather than 1.26.x. Build against spigot-api 26.2;
the source is verified to compile against 1.21, 1.21.11, 26.1, 26.1.1,
26.1.2 and 26.2. api-version stays at 1.21 because it is a minimum, so
one jar still loads on 1.21 servers.
Two things actually broke on 26.x:
- Every 26.x Spigot ships snakeyaml 2.6, which removed
Base64Coder, so the legacy item paths died with
ClassNotFoundException: Base64Coder. BukkitSerialization now uses
java.util.Base64 with the same 76 character lines and trailing break,
so saved shops stay byte for byte identical, and the decoder accepts
LF, CRLF and unwrapped data.
- PlayerHeadIncompatibilityDetector decided whether it applied with
version.contains("1.21.1") and friends, which match nothing on 26.x,
so it quietly disabled itself and let the empty player head NPE back
in. versionUtil now parses versions into numbers and offers
isAtLeast(), which orders both schemes correctly since 26 > 1.
Dependency updates: worldguard 7.0.18, worldedit 7.4.5, towny 0.103.2.0,
bentobox 2.7.0, GriefPrevention 18.0.0, LandsAPI 7.25.4, blocklocker
1.13, DynmapCoreAPI 3.8, VaultAPI 1.7.1, bstats 3.2.1, sqlite-jdbc
3.53.2.1, gson 2.14.0 and annotations 24.1.0 (the last two match what
spigot-api 26.2 provides). Towny leaks Adventure types through Resident
and Town now, so adventure-api is on the compile classpath only.
The CMI, Essentials, Residence and BlockLocker integrations were
excluded from the build because their jars were unavailable. They now
point at real artifacts (CMI-API 9.8.6.4, EssentialsX 2.21.2, Residence
6.0.2.3, blocklocker 1.13) and the worth handler and Essentials sign
conflict wiring is enabled again. Only LWCX stays out, since nothing
publishes a resolvable artifact for it.
Version bumped to 5.2.3.
target/ was committed, so every build showed up as a diff and the repository carried ~389k lines of generated files. It is ignored now, along with FOLIA_COMPATIBILITY.md, which stays a local note. The README covered the same Folia information, so the link to it is gone and the short version lives in the README instead.
The shade plugin rewrites it on every package, so it only ever showed up as noise in diffs. Same reasoning as target/.
|
Thanks for this — the version-detection rework (versionUtil.isAtLeast()) and the Base64Coder replacement are solid, both root-caused and correctly handled for backward compat. I checked all the bumped dependencies (Towny, One thing I need clarified before anything else:
Other things I'd like addressed before merging:
Heads up — I've already got my own overlapping fixes queued (Base64Coder in particular, and a few of the others touch the same files), so I'll be PRing separately and merging whichever lands cleaner/first rather than blocking |
|
I went through everything and checked each one, and you're right on all of them. The main thing I missed was the build. My matrix runs were on JDK 25, so I didn't catch that WorldGuard 7.0.18 and WorldEdit 7.4.3+ require Java 25. Pinning WorldGuard 7.0.17 and WorldEdit 7.4.2 works cleanly on both JDK 21 and 25. The README's 1.13.2 support was also wrong since the plugin uses "api-version: 1.21". Everything else checks out too, including the file modes, Folia logging, optional flags, and sign validation. One note: LWCX 2.2.5 doesn't seem to exist in any public repo I checked, so it may only be resolving from your local ".m2". That should either use a real repo or fail loudly instead of silently throwing "ClassNotFoundException". I won't have time to work through these this week, so go ahead and merge yours. It'll be cleaner. I'm happy to close this one or rebase anything left afterward. |
Minecraft changed how it numbers releases in 2026. The drops after 1.21.11 are 26.1, 26.1.1, 26.1.2 and 26.2, with the leading "1." gone for good, so these are newer than 1.21.11 even though the numbers look smaller. This branch gets SignShop running on all four of them while still loading on 1.21 servers, and bumps the version to 5.2.3.
Two things genuinely broke on the new drops, and neither was visible at build time.
The first one crashes shops outright. Every 26.x Spigot ships SnakeYAML 2.6, and 2.6 deleted the Base64Coder class that 2.2 and earlier provided. BukkitSerialization imported that class straight from the server's copy of SnakeYAML, so the moment anything touched the legacy item paths, it died with ClassNotFoundException: Base64Coder. Since it only happens at runtime, and only when reading or converting older shop data, it looks like a data problem rather than a library one. It now encodes with java.util.Base64 instead. The output is deliberately identical to what SnakeYAML produced, right down to the 76 character lines and the trailing line break, so re-saving a shop leaves sellers.yml byte for byte the same as before, and the decoder accepts LF, CRLF and completely unwrapped input so nothing written by an older build is rejected.
The second one is quieter and worse for it. PlayerHeadIncompatibilityDetector decided whether it applied by matching on the version string, with checks like version.contains("1.21.1") and a couple of guesses at future versions. On a 26.x server none of those match, so the detector concluded it was irrelevant and switched itself off, which let the empty player head NPE back in on exactly the versions that still have the bug. Version handling now lives in versionUtil, which parses versions into numbers and exposes isAtLeast. Because 26 is greater than 1, both numbering schemes order correctly against each other without any special cases, and the detector simply asks whether the server is at least 1.21.10.
The build now targets spigot-api 26.2, and the source is verified to compile against 1.21, 1.21.11, 26.1, 26.1.1, 26.1.2 and 26.2, so nothing here depends on API that only exists in the newest drop. api-version in plugin.yml deliberately stays at 1.21, because that field is a minimum rather than a target: a server refuses a plugin that asks for something newer than itself, so leaving it alone is what lets one jar cover 1.21 through 26.2. There is a spigot.version property if you want to build against an older drop.
Dependencies moved up to releases that work on the new drops: WorldGuard 7.0.18, which itself declares api-version 26.1, WorldEdit 7.4.5, Towny 0.103.2.0, BentoBox 2.7.0, GriefPrevention 18.0.0, Lands 7.25.4, BlockLocker 1.13, Dynmap 3.8, Vault 1.7.1, bStats 3.2.1 and sqlite-jdbc 3.53.2.1. gson 2.14.0 and annotations 24.1.0 were picked to match exactly what spigot-api 26.2 hands the plugin at runtime. Towny is built against paper-api these days and its Resident and Town classes now extend Adventure audiences, so javac needs those interfaces to resolve the types; adventure-api is on the compile classpath for that reason alone and nothing calls into it, which keeps the plugin working on plain Spigot.
While updating dependencies it turned out the CMI, Essentials, Residence and BlockLocker integrations had been excluded from compilation because their jars were unavailable, which left the worth handlers and the Essentials sign conflict check disabled. They now point at real artifacts, CMI-API 9.8.6.4, EssentialsX 2.21.2, Residence 6.0.2.3 and BlockLocker 1.13, and the wiring is enabled again behind the usual plugin presence guards. LWCX is the one exception, since nothing publishes an artifact for it that Maven can resolve, so its hook stays out of the build with a comment explaining why.
The last two commits stop tracking target/ and dependency-reduced-pom.xml, which were committed build output. They do not affect behaviour, they just stop every local build from showing up as a diff.
On verification: mvn clean package succeeds, the compile matrix across the six versions above passes, the replacement Base64 encoder was checked against SnakeYAML 2.2 and produces identical output for eleven payload sizes from empty up to 64 KB, and the version comparison has twenty assertions covering the ordering across the scheme change. What has not happened yet is a run on a live 26.x server, so the runtime side rests on the API and library evidence rather than on play testing.
One thing worth knowing before reading the file list: most of the changed file count is file permission differences the fork carries, not content. The files with actual changes are the pom, .gitignore, README, versionUtil, BukkitSerialization, PlayerHeadIncompatibilityDetector, IncompatibilityDetector, SignShop, SignShopServerListener and the Folia scheduling classes.