# 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.