PackMerger icon

PackMerger -----

Merge, Host, and Distribute Multiple Resource Packs on Paper 1.21+




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.
  • GitHub Actions bumped: checkout v4 → v7, setup-java v4 → v5, gradle/actions v4 → v6, action-gh-release v2 → v3.
  • 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.
----------, Jun 19, 2026

Operator-experience release. Correctness of the merge engine is settled from
1.0.x; this one focuses on observability, validation, admin workflow, and
external integrations.

Added
  • Per-file merge provenance log. Every output path now records which pack
    wrote it, every contributor (in merge order), the merge strategy used, and
    whether it was a true merge vs single-contributor pass-through. Persisted
    to output/<merged-pack>.zip.provenance.json so restarts don't blank the
    state. Exposed via PackMerger.getLastMergeProvenance().
  • /pm inspect command family.
    • /pm inspect — top-line summary of the last merge
    • /pm inspect <pack> — per-pack detail (files won + files overridden)
    • /pm inspect collisions — list of every path touched by 2+ packs
    • /pm inspect export — writes the full plain-text report to
      output/last-merge-report.txt
  • Plugin API + Bukkit events. New sh.pcx.packmerger.api.PackMergerApi
    interface accessible via plugin.getApi() with accessors for the current
    pack URL, SHA-1, last merge time, and provenance, plus triggerMerge().
    Six new Bukkit events fire at their natural call sites:
    PackMergeStartedEvent, PackMergedEvent, PackUploadedEvent,
    PackUploadFailedEvent, PackValidationFailedEvent,
    PackSentToPlayerEvent. See docs/api-example.java for a sample listener.
    Tagged @Experimental until 1.2 to allow iteration in 1.1.x.
  • pack_format vs server-version guardrail. PackValidator now warns
    when the merged pack's pack_format doesn't match the running Minecraft
    version. Honors supported_formats ranges (int, array, and
    {min_inclusive, max_inclusive} object shapes). Config key:
    validation.pack-format-check (warn | error | off).
  • Rollback on validation failure. Merges now write to
    <output>.new.zip first; validation runs against the temp, and the
    previous output is kept live if validation errors trip. Fires
    PackValidationFailedEvent with rolledBack=true so listeners can
    page someone. Config: validation.rollback-on-errors (default true)
    and validation.fail-on-warnings (default false).
  • Orphan asset detection. New OrphanDetector scans the merged output
    for unreferenced .png and .ogg files and reports them as warnings.
    Reference discovery covers models, item definitions, blockstates, atlases
    (with directory glob expansion), font providers, and sounds.json. Config:
    validation.detect-orphans (default true) and
    validation.orphan-report-limit (default 20).
  • /pm priority in-game reordering. No more YAML edit + reload:
    /pm priority list|up|down|top|bottom|set <pack> <n>. Persists via
    plugin.saveConfig() and triggers an immediate re-merge. Note: Bukkit's
    config writer doesn't preserve comments on save — documented tradeoff.
  • Profiles / presets. New profiles: + active-profile: config keys
    let operators flip between whole pack compositions atomically via
    /pm profile switch <name>. When profiles aren't in use (or the section
    is absent), PackMerger falls back to root-level keys — fully backwards-
    compatible with 1.0.x configs.
  • Remote pack sources (HTTP). New remote-packs: config section lets
    admins declare pack aliases whose contents come from an HTTP(S) URL.
    Packs are downloaded into packs/.remote-cache/<alias>.zip and
    recognized in the merge pipeline by their alias. ETag / Last-Modified
    caching, env-var substitution for secrets, bearer + basic auth, HTTPS-
    required-by-default. New /pm fetch [alias] command. Zero new runtime
    deps — uses the JDK's HttpClient.
  • S3-compatible upload provider. New provider: "s3" setting with
    support for AWS S3, Cloudflare R2, and Backblaze B2 (all via the S3
    API). Bundled MinIO SDK, fully shaded + relocated. Supports
    content-addressed or stable key strategies, public-read or presigned
    (private) ACLs, and a retention policy for content-addressed buckets.
    Jar grows from ~270 KB to ~13 MB as a result; document in release notes.
  • Two new PluginLogger categories: validation() (light purple) and
    remote() (blue).
  • Runtime dependency loader. New PackMergerLoader downloads MinIO and
    its transitive closure from Maven Central on first enable, verifies each
    artifact against a build-time SHA-256, and loads them through an isolated
    classloader. Cached in plugins/PackMerger/libraries/ for subsequent
    starts. Drops the shipped jar from ~13 MB to ~360 KB.
  • Update check. On enable, the plugin polls versions.json in the repo
    to see if a newer release is available and surfaces it in the console +
    as a chat notice to admins on join. Advisory only — no auto-download.
    Config: update-check.enabled / update-check.url.
  • Actual Folia support. Swapped every BukkitScheduler call to the
    right Folia scheduler (AsyncScheduler for periodic async work,
    GlobalRegionScheduler / entity scheduler where a specific thread matters).
    PackDistributor.sendPack now self-schedules on the player's region so
    callers don't have to know about threading. plugin.yml declares
    folia-supported: true. Paper behavior is unchanged — the scheduler
    APIs we use exist on both.
Changed
  • PackMergeEngine.merge() now accepts an optional target-file override
    (merge(File)), used by PackMerger for the write-to-temp-then-validate
    flow. Backwards-compatible: merge() keeps writing to
    plugin.getOutputFile().
  • Merge provenance moved from a single fixed-name file
    (output/.merge-provenance.json) to a sidecar keyed by output name
    (output/<merged-pack>.zip.provenance.json). Lets rollback rename the
    zip and sidecar together.
  • FileWatcher now explicitly ignores dot-prefixed entries so the
    .remote-cache/ subdirectory can't trigger cascading merges.
Dependencies
  • MinIO Java SDK 8.5.10 + its transitive closure (OkHttp, Okio, Kotlin
    stdlib, Jackson, BouncyCastle, Guava, Xerial Snappy) is now downloaded
    at runtime by the loader rather than shaded into the plugin jar.
    Manifest and SHA-256 digests live in RuntimeDependencies.java
    (auto-generated at build time from the runtimeDownload Gradle
    configuration).
----------, Apr 17, 2026

Resource Information
Author:
----------
Total Downloads: 71
First Release: Feb 18, 2026
Last Update: Jun 19, 2026
Category: ---------------
All-Time Rating:
1 ratings
Version -----
Released: --------------------
Downloads: ------
Version Rating:
----------------------
-- ratings