Brings PackMerger onto the 2026 Minecraft/Paper line, adds automatic discovery of packs generated by other plugins, and closes the biggest tooling gaps.
Changed
Built against Paper 26.1.2 (Minecraft's new year-based versioning) on Java 25. Mojang switched from the 1.21.x scheme to <year>.<drop>.<patch> after 1.21.11; the build now targets io.papermc.paper: paper-api:26.1.2.build.+, the Gradle toolchain is Java 25, and plugin.yml api-version is 26.1.
Pack-format registry brought current. Added the late 1.21 line (1.21.7–1.21.8 → 64, 1.21.9–1.21.10 → 69, 1.21.11 → 75) and the new 26.x scheme (26.1–26.1.2 → 84, 26.2 → 88). The registry had stopped at 1.21.6, so the pack_format drift check was silently returning UNKNOWN (a no-op) on every server newer than that. Startup now logs the registry coverage vs. the running server version at debug.
Pack-format guardrail understands 26.1's min_format/max_format. Since 1.21.9 the pack.mcmeta format moved from pack_format / supported_formats to min_format / max_format (plain ints, or [major, minor]). The validator now reads both schemas, so the drift check actually runs on 26.1 packs — previously it saw no pack_format and silently skipped. A merged pack still declaring a stale format for an older version is now surfaced instead of shipping silently.
Added
Automatic plugin-pack discovery. PackMerger now detects installed pack-generating plugins — Oraxen, Nexo, ItemsAdder, ModelEngine, EliteMobs, FreeMinecraftModels — stages each one's current generated pack into packs/.plugin-packs/<alias>.zip before every merge, and deep-merges them through the format-aware strategies (not just whole-file overwrite). Reference an alias in priority: to control precedence; override a plugin's source path or disable it under plugin-packs.sources.<alias>. Re-staged on every merge, so <plugin> reload + /pm merge always picks up fresh output. Config: plugin-packs (enabled, merge-delay-seconds, sources).
Missing-integration advisory. When a pack-generating plugin is installed but not being merged (disabled, or no generated pack found), admins are warned on the console at startup and on join.
Auto self-host port. upload.self-host.port: -1 derives the HTTP port from the Minecraft server port + 1, so operators don't have to pick a free port manually.
Bedrock / Geyser conversion (opt-in, experimental). New packmerger-bedrock module converts the merged Java pack's custom items into a Bedrock pack + Geyser custom-item mappings so Bedrock/Floodgate players see the same custom items. Targets the 1.21.4+ item-definition subset (custom_model_data → model → layer0 texture, i.e. 2D item icons; 3D geometry is not yet converted). Writes a .mcpack
<name>.geyser.json to output/bedrock/ and can auto-deploy them into Geyser's packs/ and custom_mappings/ folders. Config: bedrock (enabled — off by default, auto-deploy-to-geyser, geyser-folder, debug); new PackBedrockConvertedEvent. Verify against a live Geyser/Bedrock client before use.
Velocity proxy module (network-wide distribution). New packmerger-velocity plugin offers the merged pack to every player from the proxy, so distribution is network-wide instead of per-backend. The proxy reuses the backend's existing hosting — give it the pack URL in its config.properties, and (optionally) set distribution.proxy-notify: true on backends to push live URL/hash updates over the packmergerpack plugin channel. Shares a new packmerger-common module (the PackInfo + message codec) with the backend.
CI workflow (.github/workflows/ci.yml) running ./gradlew build (full test suite + shaded jar) on every push and PR. Until now nothing ran the tests in CI.
Dependabot (.github/dependabot.yml) for the Gradle and GitHub-Actions ecosystems, keeping dependencies and Action pins current via CI-gated PRs.
Gradle version catalog (gradle/libs.versions.toml) as the single source of dependency versions; de-duplicates the MinIO coordinate that was declared twice.
docs/TESTING.md documenting the per-release Paper + Folia smoke test and the integration-test roadmap for the (currently Bukkit-coupled) I/O layer.
config-version field in config.yml. PackMerger logs a one-line notice when a config predates the current schema, so silently-defaulted new keys are visible.
bStats metrics (plugin id 32086), shaded + relocated. Anonymous usage stats only; opt out globally in plugins/bStats/config.yml. Init is guarded so a scheduler incompatibility never blocks enable.
Build / dependencies
Monorepo. Restructured into a Gradle multi-module build under a uniform packmerger-* naming scheme: packmerger-plugin (the backend jar, packmerger-plugin-<version>.jar), packmerger-velocity (the proxy jar, packmerger-velocity-<version>.jar), and the shaded-in libraries packmerger-bedrock and packmerger-common. Dependency versions live in the shared root gradle/libs.versions.toml.
Gradle wrapper 8.12 → 9.6.0 (required for Java 25); pinned with a checksum.
Shadow plugin 8.3.6 → 9.4.2 (required for Gradle 9).
gson 2.11.0 → 2.14.0, commons-compress 1.27.1 → 1.28.0, minio 8.5.10 → 9.0.3, JUnit 5.11.3 → 6.1.0. MinIO 9.x is a major API rewrite — S3UploadProvider was migrated (io.minio.http.Method → io.minio.Http.Method); the runtime S3 upload path isn't CI-verified.
The release workflow now runs ./gradlew build (tests gate the release) on Java 25 and publishes both jars.
Fixed
PolymathUploadProvider no longer echoes an unbounded upstream response body into the console on a failed upload; error messages are collapsed to a single, length-capped snippet.
Update-check manifest URL pointed at the non-existent /main/ branch (default is master), so the check silently 404'd; both the config default and UpdateChecker.DEFAULT_URL now use /master/. The admin-facing "update available" link points to the SpigotMC page.