# Overview Painting is a resource pack delivery suite for Minecraft servers running Paper, Velocity, or both. It picks the correct pack variant for each player based on their protocol version, retries failed downloads with cache-busting, and keeps SHA-1 hashes in sync with the upstream URL — all without requiring you to redeploy the server. --- ## What is Painting? Painting solves the everyday pain of running a multi-version server with resource packs. Different Minecraft versions need different pack formats, CDN edges go stale, and hash drift causes silent download failures. Painting handles all of that: - **Version-aware variants** — each pack maps a list of variants; the correct one is sent based on the player's protocol or a version expression like `1.21+` or `>=1.21.4,<26.1`. - **Retry on failure** — `FAILED_DOWNLOAD` re-issues the offer with a cache-busting query parameter before the player is kicked. - **Automatic hash refresh** *(Velocity)* — pack URLs are checked with `If-Modified-Since` / `If-None-Match` and the local hash is updated when the upstream changes. No manual `/painting generatehashes` after every CDN deploy. - **Standalone or proxied** — runs on a single Paper server, on Velocity, or both at once. When both are installed, Velocity owns the pack list and pushes it down to backends via a plugin message. - **Redis CI/CD hook** — push `reload`, `update_pack`, or `resend` actions over Redis Pub/Sub from your build pipeline. --- ## Modes | Mode | What runs | Use it when | |---|---|---| | **Standalone (Paper)** | `Painting-Paper` only | Single-server setup, no proxy. | | **Proxied (Velocity)** | `Painting-Velocity` only | Velocity proxy with backends that don't run Painting. | | **Hybrid** | Both | Velocity is authoritative; Paper backends respect the pushed list and still emit local API events. | The Paper plugin auto-detects mode: if it receives a `painting:sync` plugin message, it switches to `PROXIED`. Otherwise it stays `STANDALONE`. Lock the mode explicitly with `mode: STANDALONE` or `mode: PROXIED` in `config.yml`. --- ## Features - **Per-protocol pack variants** — match by raw protocol numbers or version expressions (`1.21+`, `>=1.21.4,<26.1`, `26.1+`). - **Bundled version table** — `version_protocols.yml` ships protocol numbers from 1.21 through 26.1; override or extend in config. - **Required vs optional packs** — `required: true` kicks players who decline or fail; optional packs never kick. - **Cache-busting retry** — configurable `failed-retries` re-offers with `?_painting_retry=N`. - **Prompt timeout** — kick players who ignore the resource pack prompt after N seconds (required packs only). - **Configurable kick delay** — defer the kick after `DECLINED` / `FAILED_DOWNLOAD` so the player sees the message. - **Conditional GET hash refresh** *(Velocity)* — throttled, in-flight-deduped, runs at config phase before pack offer. - **Plugin-message proxy sync** — Velocity pushes the resolved pack list to each backend on connect. - **Redis Pub/Sub control plane** — JSON envelope with `reload`, `update_pack`, `resend` actions. - **Append hash to URL** — opt-in `?hash=<sha1>` query parameter for CDN cache busting. - **Developer API** — read pack/server registries, fire events on pack send and status, force resends. --- ## Dependencies | Dependency | Version | Type | |---|---|---| | Paper API | 1.21+ | Server platform (Painting-Paper) | | Velocity API | 3.4+ | Proxy platform (Painting-Velocity) | | Java | 21 | Runtime | CommandAPI, MiniMessage, Jedis, and PacketEvents are shaded into the plugin jar. No external plugins are required. --- ## Optional Integrations | Plugin | Purpose | |---|---| | Redis | CI/CD push channel for `reload`, `update_pack`, `resend`. | | Bookshelf | Painting-Paper events extend `BaseEvent` from Bookshelf-API; Bookshelf is recommended but not required. | --- ## Related Pages - [[Painting/Server Owners/Configuration]] — full config breakdown - [[Painting/Server Owners/Commands]] — command reference - [[Painting/Developers/Overview]] — developer API documentation