# Painting - v1.0.0 ## Initial Release ### API UPDATES - **`PaintingAPI`** — Static accessor with `get()`, `setApi()`, and `isAvailable()`. Throws `IllegalStateException` if accessed before the platform plugin registers. - **`IPaintingAPI`** — Platform-neutral facade implemented by both Paper and Velocity. Inspect pack and server registries, check whether a server requires its packs, reload config, and force resends to one player or everyone online. - **`PackDefinition` / `PackVariant`** — Named pack with an ordered list of variants gated by raw protocol numbers and/or version expressions. - **`ServerEntry`** — Regex-keyed group of packs to send for a matching server name, with a `required` flag. - **`PackStatus` enum** — Platform-neutral pack lifecycle state: `ACCEPTED`, `DOWNLOADED`, `LOADED`, `DECLINED`, `FAILED`, `DISCARDED`, `INVALID_URL`, `UNKNOWN`. - **`VersionExpression`** — Parses `>=`, `<=`, `>`, `<`, trailing `+`, and bare versions. Supports comma-separated AND chains and list-level OR via `VersionResolver.evaluateExpressions`. - **`VersionResolver`** — Maintains the version-to-protocol table, loaded from the bundled `version_protocols.yml` (1.21 through 26.1.2 at release) and extensible via plugin config. - **`PackStatusEvent`** — Platform-neutral interface fired when a player's pack state changes. Concrete: `PaperPackStatusEvent` (Bukkit), `VelocityPackStatusEvent`. - **`PackPreSendEvent`** — Platform-neutral cancellable interface fired before each pack offer. Cancellation skips the single pack. Concrete: `PaperPackPreSendEvent`. - **`PackRegistryReloadEvent`** — Platform-neutral interface fired after `/painting reload`. Concrete: `PaperPackRegistryReloadEvent`. ### PLUGIN UPDATES - **Painting-Paper** — Standalone or proxy-aware Paper plugin. Auto-detects mode: switches to `PROXIED` after the first plugin-message receipt, otherwise runs `STANDALONE`. Lockable via `mode: STANDALONE | PROXIED | AUTO`. - **Painting-Velocity** — Velocity proxy plugin. Sends pack offers during the configuration phase so the play state begins with the pack already applied; re-issues on `ServerPostConnect` for backends that don't trigger reconfigure. - **Per-protocol pack selection** — Variants matched first by raw `protocols`, then by `versions` expressions evaluated against the bundled protocol table. - **Required vs optional packs** — `required: true` on a server entry kicks players who decline or fail (after retries). - **Failed-download retry** — `failed-retries` (default `2`) re-issues the offer with `?_painting_retry=N` cache-busting before kicking. Stale CDN edges or transient network blips no longer kick players while others on the same server are unaffected. - **Prompt timeout** — `prompt-timeout` (default `15`) kicks required-pack players who ignore the prompt. - **Kick delay** — `kickdelay` defers the kick after `DECLINED` / `FAILED_DOWNLOAD` so the player sees the reason. - **Append hash to URL** — Opt-in `?hash=<sha1>` query parameter for CDN cache busting. - **Auto hash refresh (Velocity)** — `hash-refresh-throttle` (default `60s`) coalesces hash checks across player joins and a periodic timer. Conditional GET (`If-Modified-Since` / `If-None-Match`) returns 304 when the upstream is unchanged so unchanged packs cost nothing. - **`/painting generatehashes`** — Manual SHA-1 regeneration that downloads every URL, writes hashes back to config, and reloads. Velocity's manual run always forces a refresh, bypassing the throttle window. - **`/painting reload`** — Reloads pack and server registries; logs a warning for server entries referencing unknown packs. - **`/painting resend`** — Re-issues pack offers to every online player. On Velocity, dedupes against `getAppliedResourcePacks()` so already-applied packs are skipped. - **Proxy → backend sync** — Velocity pushes the resolved pack list per-player to each backend over the `painting:sync` plugin message channel. Paper backends running Painting-Paper consume the message, switch to `PROXIED` mode, and dispatch the offers locally so events still fire. - **Redis CI/CD push** — JSON envelope on a Pub/Sub channel triggers `reload`, `update_pack` (reload + optional resend), or `resend`. Velocity owns Redis in hybrid setups. - **MiniMessage messages** — `messages.prompt`, `messages.declined`, `messages.failed` accept MiniMessage formatting. - **Configuration phase pack delivery (Velocity)** — Offers are sent during `PlayerEnteredConfigurationEvent` so the client begins the play state with the pack already applied; this avoids the custom-font width offset caused by races against backend scoreboard/HUD packets. ### INTERNAL UPDATES - **Painting-API** released as a JitPack-published Maven module (`com.github.Lodestones:Painting-API:1.0.0`). - **Painting-Paper** and **Painting-Velocity** built from the parent Maven multi-module project. - Targets **paper-api 1.21+** and **velocity-api 3.4+**. Toolchain: **Java 21**. - Bundled `version_protocols.yml` covers 1.21 — 1.21.11 plus 26.1 — 26.1.2 at release. - Paper events extend `BaseEvent` from Bookshelf-API for parity with other Lodestone plugins.