promotional bannermobile promotional banner

Arcadia Lib

Arcadia Lib is the shared foundation required by all Arcadia Pets, Prestige and AH. It provides the database layer, player data management and debug mode.

File Details

arcadia-lib-1.2.11

  • R
  • May 19, 2026
  • 2.57 MB
  • 1.4K
  • 1.21.1
  • NeoForge

File Name

arcadia-lib-1.2.11.jar

Supported Versions

  • 1.21.1

Curse Maven Snippet

NeoForge

implementation "curse.maven:arcadia-lib-1493170:8115887"
Curse Maven does not yet support mods that have disabled 3rd party sharing

Learn more about Curse Maven

## [1.2.11] - 2026-05-19 (latest)

### Security

- **C2SDashboardAction payload validation + server-side OPEN_TAB / SWITCH_TAB permission re-check** — Client-supplied `payload` strings were forwarded as-is to `ArcadiaModRegistry.executeServerAction` (e.g. `"pets.summon:" + payload`), letting a crafted packet inject a second `:` and shift the action dispatch. `OPEN_TAB` and the `ext:` form of `SWITCH_TAB` also opened any tab the client asked for, ignoring the card's `permissionNode` — tab-level gating only existed in the client UI. Payloads are now length-capped at 128 chars and matched against a strict whitelist regex; the server re-resolves the target card and rejects any opening attempt that fails `PermissionService.hasPermissionStrict`.
- **`SafeCommandUtil` allow-list + new `runTrusted` template entry point** — `runAsServer(server, command)` previously executed any string at operator level with no sanitization. It now rejects anything outside a narrow prefix allow-list (`tp`, `give`, `playsound`, `particle`, `title`, `tellraw`, `execute`, `effect`, `spawnpoint`, `gamemode`, `say`). A new `runTrusted(server, template, args...)` overload formats a hard-coded template with allow-listed argument tokens, giving downstream mods a safe path when one of them needs to interpolate user input.
- **`PermissionBackend` fail-closed on dedicated servers** — The lenient `NOOP` backend (returning `true` for every node) was the runtime default when LuckPerms was missing. On a dedicated box that meant every player was implicitly granted every node until init finished. New `PermissionBackend.DENY` is the default on dedicated servers; `PermissionBackend.PERMISSIVE` is the singleplayer fallback. The legacy `NOOP` alias now points at `DENY`. `PermissionService.init(backend, isDedicated)` picks the right default automatically.
- **`DebugMode` is now UUID-based** — Previously matched `player.getName().getString()` against `Set.of("SiriusT", "Dev")`. On offline-mode / cracked servers that's spoofable: any client naming themselves `SiriusT` walked in as ADMIN if someone accidentally flipped `ENABLED` true. The set is now `Set<UUID>` and ships empty by default.
- **`NbtSerializer.deserializeTag` size-bounded** — `NbtAccounter.unlimitedHeap()` let a crafted 1 MiB compressed blob expand to gigabytes and OOM the server. Now bounded to a 2 MiB heap budget and rejects Base64 inputs over 1 MiB before decoding.
- **`DashboardMenu.stillValid` ties container liveness to `player.containerMenu == this`** — Returning `true` unconditionally let the menu survive teleports, deaths and dimension changes; any stale C2S packet was still dispatched to the tab handler.
- **`DatabaseConfig` defaults the username / password to empty strings + startup refuses an empty username** — `arcadia_prestige` was the literal default for both fields. If a deployment accidentally relied on that default it became a credential leak. Empty defaults fail-fast at startup with a clear error.

### Fixed

- **`SchedulerService` race on async-scheduled tasks** — `nextId` was a plain `int++` and `currentTick` a plain `long`. An async DB callback calling `delayed()` or `repeating()` raced the tick thread into ID collisions and stale `nextRun` offsets. Converted to `AtomicInteger` / `AtomicLong`. `cancelAll()` no longer resets the ID counter (avoids aliasing stale IDs across sessions).
- **`AchievementManager` duplicate coin reward on crash** — Reward was granted before the DB write was confirmed. A crash between the two left `unlocked=true` in memory but unwritten, so the next session reloaded progress >= target and re-granted the reward. Unlock now persists first and the reward fires only after `saveDbSync` returns true; failed writes withhold the reward and log.
- **`DatabaseManager` main-thread freeze on executor restart window** — When the primary pool was shut down and not yet rebuilt, the previous fallback ran the task inline on the caller — which during `PlayerLoggedInEvent` is the main server thread. Added a long-lived single-thread `FALLBACK_EXECUTOR` that catches that gap. JDBC URLs now bracket IPv6 host literals (`::1` → `[::1]`).
- **`PlayerDataHandler.canClaimDaily` reads from the DB on the dedicated path** — UI-side cooldown check used the local cache; another instance advancing `last_claim` after a cross-server claim made the gate flicker back to "ready". The strict check now mirrors `claimDaily` and hits the DB.
- **`TeleportManager` cooldown structure + tick lookup** — Disconnect cleanup was an O(N) scan of a flat `"uuid:action"` keyed map; now `Map<UUID, Map<String, Long>>` with O(1) removal. The per-tick player resolver now uses `PlayerManager.getPlayer` instead of `ServerLifecycleHooks → PlayerList`.

### Performance

- **`ArcadiaHubScreen` no longer rebuilds its row layout every frame** — A fresh `TreeMap` plus row `ArrayList`s were allocated inside `render()` 60 times per second despite the layout never changing while the screen is open. Layout is now computed once in `init()` and read directly by `render()`.
- **`CreativeSearchHandler` caches the search `EditBox`** — Iterating `screen.children()` on every post-render frame was wasted work; cached via `WeakReference` and invalidated on screen open.

### Added

- **`SanctionRepository` — persistent, cross-server moderation store** — Backs the staff mute system with an `arcadia_sanctions` MySQL table (target, kind, expiry, reason, issued_by, revoked). A mute issued on server A is now in force on servers B and C and survives a restart. Hot-path checks still read from an in-memory cache hydrated on player join via `StaffActions.onPlayerJoin`. `Kind` is an enum so ban / warn / kick can extend the same table later without a schema migration.
- **`AuditLog` — append-only staff action trail** — Every mute, unmute, day-advance and future ban/warn/kick is written to `arcadia_audit` (timestamp, actor, target, action, detail). `AuditLog.getLast(target, limit)` returns the most recent entries for a player — ready for a future `/arcadia history` view.
- **`/arcadia reload [module]`** — Hot-reload of lib-owned configs without restarting the server. Modules: `all`, `database`, `permissions`, `staff`, `economy`. Gated behind `StaffRole.ADMIN` (silent at command-discovery time, explicit denial only on actual invocation so it doesn't spam tab-completion).

### Sécurité

- **Validation des payloads `C2SDashboardAction` + re-vérif serveur de la permission sur `OPEN_TAB` / `SWITCH_TAB`** — Les `payload` clients étaient transmis tels quels à `ArcadiaModRegistry.executeServerAction` (ex. `"pets.summon:" + payload`) : un paquet forgé pouvait injecter un second `:` et déplacer le dispatch d'action. `OPEN_TAB` et la forme `ext:` de `SWITCH_TAB` ouvraient aussi n'importe quel onglet demandé par le client en ignorant le `permissionNode` de la carte — la gate ne vivait qu'en UI. Les payloads sont maintenant plafonnés à 128 caractères et filtrés par regex stricte ; le serveur re-résout la carte cible et rejette toute ouverture qui ne passe pas `PermissionService.hasPermissionStrict`.
- **Allow-list `SafeCommandUtil` + nouveau point d'entrée `runTrusted`** — `runAsServer(server, command)` exécutait n'importe quelle chaîne au niveau opérateur sans sanitization. Il refuse maintenant tout ce qui sort d'une liste blanche étroite (`tp`, `give`, `playsound`, `particle`, `title`, `tellraw`, `execute`, `effect`, `spawnpoint`, `gamemode`, `say`). Une nouvelle surcharge `runTrusted(server, template, args...)` formate un template figé avec des arguments validés contre une whitelist — voie sûre pour les mods qui doivent interpoler de l'input utilisateur.
- **`PermissionBackend` fail-closed sur serveur dédié** — Le backend permissif `NOOP` (qui retournait `true` partout) servait de défaut runtime tant que LuckPerms n'avait pas init. Sur un dédié ça signifie que chaque joueur disposait implicitement de chaque node pendant la fenêtre. Nouveau `PermissionBackend.DENY` comme défaut sur dédié ; `PermissionBackend.PERMISSIVE` reste le fallback solo. L'alias `NOOP` historique pointe désormais sur `DENY`. `PermissionService.init(backend, isDedicated)` choisit automatiquement le bon défaut.
- **`DebugMode` passe sur authentification par UUID** — Avant : match du nom Mojang contre `Set.of("SiriusT", "Dev")`. Sur un serveur offline / cracked c'est spoofable : n'importe quel client nommé `SiriusT` rentrait ADMIN si quelqu'un flippait `ENABLED` à true. Le set est maintenant `Set<UUID>` et vide par défaut.
- **`NbtSerializer.deserializeTag` borné en taille** — `NbtAccounter.unlimitedHeap()` laissait un blob compressé d'1 MiB se décompresser en gigaoctets et faire OOM le serveur. Borné maintenant à 2 MiB de budget heap et rejet des entrées Base64 supérieures à 1 MiB avant décodage.
- **`DashboardMenu.stillValid` lie la validité du conteneur à `player.containerMenu == this`** — Retourner `true` inconditionnellement laissait le menu survivre aux téléportations, morts et changements de dimension ; tout paquet C2S stale était quand même dispatché au handler d'onglet.
- **`DatabaseConfig` met user / password à chaîne vide par défaut + refus au démarrage si username vide** — `arcadia_prestige` était la valeur littérale par défaut des deux champs. Un déploiement qui s'appuyait sur ce défaut devenait un leak de credential. Défauts vides → fail-fast au démarrage avec erreur claire.

### Correctifs

- **Race `SchedulerService` sur les tâches programmées en async** — `nextId` était un `int++` brut et `currentTick` un `long` brut. Un callback DB async appelant `delayed()` ou `repeating()` se mettait en concurrence avec le tick thread et provoquait des collisions d'ID + des offsets `nextRun` stales. Passés en `AtomicInteger` / `AtomicLong`. `cancelAll()` ne reset plus le compteur d'ID (évite l'aliasing entre sessions).
- **Doublon de récompense `AchievementManager` au crash** — La récompense était accordée avant la confirmation d'écriture en base. Un crash entre les deux laissait `unlocked=true` en mémoire mais non écrit, donc la session suivante rechargeait la progression >= target et re-versait la récompense. L'unlock persiste maintenant d'abord et la récompense ne tombe qu'après le retour `true` de `saveDbSync` ; les écritures échouées retiennent la récompense et logguent.
- **Gel du main thread `DatabaseManager` pendant la fenêtre de redémarrage de l'executor** — Quand le pool primaire était shutdown et pas encore reconstruit, le fallback précédent exécutait la tâche inline sur l'appelant — c'est-à-dire le main thread serveur pendant `PlayerLoggedInEvent`. Ajout d'un `FALLBACK_EXECUTOR` single-thread longue durée pour boucher ce trou. Les URLs JDBC bracketent désormais les hôtes IPv6 littéraux (`::1` → `[::1]`).
- **`PlayerDataHandler.canClaimDaily` lit depuis la DB sur le chemin dédié** — Le check de cooldown côté UI tapait dans le cache local ; une autre instance qui avançait `last_claim` après un claim cross-serveur faisait clignoter la gate à "prêt". Le check strict reflète maintenant `claimDaily` et tape la base.
- **Structure des cooldowns `TeleportManager` + lookup tick** — Le cleanup de déconnexion était un scan O(N) d'un map plat clé `"uuid:action"` ; maintenant `Map<UUID, Map<String, Long>>` avec retrait O(1). Le résolveur de joueur par tick passe par `PlayerManager.getPlayer` au lieu de `ServerLifecycleHooks → PlayerList`.

### Performance

- **`ArcadiaHubScreen` ne reconstruit plus son layout par ligne à chaque frame** — Un nouveau `TreeMap` + des `ArrayList` par ligne étaient alloués dans `render()` 60 fois par seconde alors que le layout ne bouge pas tant que l'écran est ouvert. Layout calculé une fois dans `init()` et lu directement par `render()`.
- **`CreativeSearchHandler` cache la search `EditBox`** — Itérer `screen.children()` à chaque frame post-render était du travail jeté ; cache par `WeakReference` invalidé à l'ouverture d'écran.

### Ajouts

- **`SanctionRepository` — store de modération persistant cross-serveur** — Backe le système de mutes staff avec une table MySQL `arcadia_sanctions` (target, kind, expiry, reason, issued_by, revoked). Un mute posé sur le serveur A s'applique sur B et C et survit au restart. Les checks hot-path lisent toujours un cache mémoire hydraté au join via `StaffActions.onPlayerJoin`. `Kind` est un enum pour que ban / warn / kick étendent la même table sans migration de schema.
- **`AuditLog` — trail append-only des actions staff** — Chaque mute, unmute, day-advance et futur ban/warn/kick est écrit dans `arcadia_audit` (timestamp, actor, target, action, detail). `AuditLog.getLast(target, limit)` retourne les entrées les plus récentes pour un joueur — prêt pour une future vue `/arcadia history`.
- **`/arcadia reload [module]`** — Hot-reload des configs lib sans restart serveur. Modules : `all`, `database`, `permissions`, `staff`, `economy`. Gated derrière `StaffRole.ADMIN` (silencieux à la découverte de commande, refus explicite seulement à l'invocation réelle pour ne pas spammer la tab-completion).