promotional bannermobile promotional banner

Arcadia Prestige

Adds particle cosmetics and a 24-day daily-reward calendar to the Arcadia hub. Luck-perms gated.
Back to Files

arcadia-prestige-1.21.1-1.3.0

File namearcadia-prestige-1.3.0.jar
Uploader
AminoquizAminoquiz
Uploaded
Aug 23, 2026
Downloads
4
Size
240.5 KB
Mod Loaders
NeoForge
File ID
8713654
Type
R
Release
Supported game versions
  • 1.21.1

Curse Maven Snippet

NeoForge

implementation "curse.maven:arcadia-prestige-1493497:8713654"

Learn more about Curse Maven

What's new

Added

  • Solo progression, earn cosmetics by playing. New [solo_progression] config section turns cosmetic effects into something a singleplayer world or a private server can actually work towards. Players accumulate Prestige Points from play time, mob and boss kills, quest claims, daily rewards, daily milestones, duel wins and advancements (every source has its own configurable rate, and 0 disables it), then spend them to unlock any effect permanently. Costs are set per tier (vip / vip_plus / mvp / founder) with per-effect overrides in a "rainbow=25000,orbit=500" style string, and an optional daily_cap bounds how much can be earned per real day.
    • mode is what keeps the shop economy intact. AUTO (the default) enables progression only when no permission backend is bound, which is the signature of a singleplayer world, a LAN game or a private server; an official server running LuckPerms is left completely untouched. ALWAYS enables it everywhere, NEVER guarantees it never runs. Unlocks are additive and never remove rank-granted access, and switching the system off later leaves stored unlocks on disk, simply unconsulted.
    • Two storage backends. The database is used whenever arcadia-lib has an active pool; otherwise progress is written to config/arcadia/prestige/solo_progress.json. Without that fallback progression would reset on every restart in exactly the setup it exists for. Writes are debounced and flushed off the main thread, on logout and on server stop.
    • Anti-AFK. The per-minute reward only pays out when the player's position or view angle changed since the previous sample, so mining in place still counts but an AFK pool does not.
    • Bad config cannot open the shop up. An unrecognised mode is corrected back to AUTO on load rather than crashing or landing in an undefined state, and because AUTO switches itself off wherever LuckPerms is bound, a typo on a ranked server leaves progression disabled. Malformed overrides entries are skipped individually with a named warning while the valid ones still apply. Both paths were exercised on a running server.
    • New commands: /arcadia_prestige progress (own summary, next unlock, what is affordable), plus OP-only progress status|give|set|reset|unlock|lock.
  • Ten effects made of real blocks and items, bringing the roster to 65: A third wave, all of them static, all carrying solid game models on top of their particles. Grimoire (VIP) circles three books at chest height, Harvest (VIP) walks pumpkins and hay up a rising helix, Apiary (VIP) keeps a loose swarm of honeycomb that never falls into step, Forge (VIP+) turns an anvil, an iron block and a blast furnace in a shower of sparks, Obelisk (VIP+) stacks a chiselled stone column above the head, Hoard (MVP) drags a wide low ring of gold, emerald, diamond and lapis, Arsenal (MVP) swings netherite weapons on a vertical ring that itself rotates, Orrery (Founder) runs a working solar system with a glowstone star and three planets on widening rings, Sentinels (Founder) locks two crying obsidian guardians to the shoulders so they follow the body rather than the world, and Regalia (Founder) wears a crown band of gold, diamond and netherite.
    • The block companion system is data now. Comet and Meteor had carried a block since 1.2.x through a hardcoded pair of block states and two hand-written orbits. What is drawn and how it moves is declared next to the effect itself, in one of nine placement patterns, so an effect and its companion can no longer drift apart.
    • Blocks and items are the same thing. Every piece goes through the item renderer with the identity display context, so a block item comes out as a small solid cube and a flat item as the sprite players know from the ground, with the scale meaning the same for both. That is what lets Arsenal carry an actual trident and Grimoire an actual enchanted book.
    • The cost is bounded on purpose. A model draw is far more expensive than a particle and none of them batch. Pieces are halved past half the configured render distance, the pass stops at 96 pieces per frame across everyone in view, and render_block_companions = false skips it entirely. The particle passes for these ten are deliberately restrained so they light the pieces rather than compete with them.
  • Twenty further effects, bringing the roster to 55: A second wave, five per rank tier. Static: Firefly and Ember (VIP), Crystal and Tide (VIP+), Sanctum, Vortex and Beacon (MVP), Abyss, Aurora and Seraph (Founder). Movement: Dash, Dust and Windrun (VIP), Frostpath, Thunderstep and Magma (VIP+), Stardust and Venom (MVP), Spectre and Celestial (Founder). The Cosmetics tab now spans five pages: pages 0 to 2 are the 40 static effects, pages 3 and 4 the 25 movement ones, so a player looking for one kind never has to hunt across a mixed page. The tier shown is a built-in default used for solo-progression pricing and for the UI label when LuckPerms has no opinion; on a ranked server the group holding the node still decides.
  • Ten new cosmetic effects, bringing the roster to 35: Static: Frost (hex ice crown, frozen rune ring, drifting motes), Arcane (three counter-rotating runic circles with inscribed glyphs and inward-drawn mana), Lotus (breathing petal mandala at the feet with pollen lift-off), Eclipse (dark disk above the head with a flaring corona and solar prominences), Prism (rotating triangular prism casting a spectral refraction fan). Movement: Bloom (flowers sprouting in your footsteps), Runes (glowing glyph tablets left behind), Tempest (wind vortex and cloud puffs churning behind you), Phoenix (beating burning wings with an ember wake), Inferno (scorched ground, heat haze and lava spurts). The Cosmetics tab now spans three pages (Static I-IV, Movement I-III).
  • Client config, config/arcadia/prestige/client.toml. Purely local rendering preferences, nothing sent to the server and nothing affecting gameplay: particle quality (AUTO follows the vanilla particle slider, plus HIGH/MEDIUM/LOW/OFF), max_visible_effects, max_distance, near_distance, hide_own_effects_first_person (now persisted across restarts instead of resetting every launch), show_other_players_effects, hide_invisible_players, render_block_companions and reopen_dashboard_on_exit.
  • Feature switches, [features]. Independent master switches for cosmetics, daily_rewards, quests, elo and effect_preview. Turning one off stops its event tracking and shows a clear notice in its tab; nothing is deleted, so flipping it back on restores the saved state.
  • Quality-of-life commands: /arcadia_prestige effect <id> and effect off (with tab completion over every effect id, permission-checked server-side), preview [effect], stats (streak, ELO, quests, cosmetics owned, points), quests and leaderboard (both documented in the README since 1.2.x but never actually implemented), and cosmetics rescan (documented in CosmeticPermissionScanner's javadoc since 1.2.14 but never wired up).
  • Preview HUD names the row: With 65 effects a bare "21/65" says nothing about where you are, so the overlay now shows which dashboard row the effect belongs to, using the same label the Cosmetics tab uses. Shift-scroll steps by five, which is exactly one row, so the readout doubles as a guide to what that jump does.
  • Keybinds: Equip previewed effect (default R) and two unbound-by-default keys, Open effect preview and Show/hide your own effects, so installing the mod never steals a key a player already uses.
  • [performance] config section: batch_login_sync, quest_cache_days and leaderboard_cache_seconds expose the tuning behind the fixes below.

Fixed

  • Effect preview was completely dead: Clicking the spyglass in the Cosmetics tab did nothing at all. The click handler lived in com.arcadia.prestige.client.DashboardScreen, a class that is never registered: since the dashboard moved to arcadia-lib the menu is bound to com.arcadia.lib.client.DashboardScreen, so Prestige's copy was unreachable dead code and its mouseClicked never ran. Server-side, CosmeticsTabHandler.handleClick had no case for slot 47 either, so the click was swallowed on both sides. Preview is now driven from the server: the tab handler answers slot 47 by sending a new S2CStartPreview payload, which works regardless of which screen implementation is showing the menu and lets the server ship the authoritative unlocked-effect list with it. The dead DashboardScreen class has been removed.
  • Preview no longer fights with effect syncing: It used to overwrite the shared PlayerEffectCache entry and restore it on exit, so an S2CParticleSync arriving mid-preview clobbered what the player was looking at, and an interrupted exit could leave the wrong effect cached. Preview state now lives entirely in EffectPreviewHandler and is merged in at render time, so it cannot be disturbed and needs no restore step.
  • Client effect state leaked across worlds: PlayerEffectCache.clear() existed but had zero callers. Effects synced on one server stayed in memory after disconnect and reappeared in the next world joined in the same session, on whichever players happened to reuse those UUIDs. Now cleared on ClientPlayerNetworkEvent.LoggingOut, together with the preview state and the renderer's position history.
  • ParticleRenderer position history grew without bound: trailPositions and lastPositions were plain HashMaps that were only ever written to. Every player ever seen with an effect kept an entry, plus up to 14 Vec3 objects each, for the lifetime of the client. They are now swept periodically and cleared on disconnect.
  • Quest cache grew without bound: QuestManager.CACHE kept every day a player had ever been seen on, for every player, for the process lifetime. Days beyond performance.quest_cache_days are now evicted, and a player's entry is dropped on logout.
  • Body-attached effects streamed out behind a moving player: Halos, rings, wings and discs visibly lagged while walking or sprinting instead of staying on the player. Minecraft particles are fire-and-forget world-space objects and nothing can attach one to an entity after it spawns; DustParticleBase gives them a lifetime of up to 40 ticks, so a player sprinting at 0.28 blocks per tick left a static ring up to eleven blocks behind by the time it expired. Every emission site now routes through a helper that adds the owner's per-tick motion to the particle's velocity, for body-attached effects only, since movement effects are meant to be left behind. The carry is scaled per particle type against the decompiled vanilla source: Ă—10 for dust (which multiplies the supplied velocity by 0.1), Ă—1 for End Rod (which uses it directly), and skipped for types like Portal that treat the argument as a displacement amplitude rather than a velocity, where injecting a player's speed would distort the shape instead of moving it. A further 1.3Ă— over-compensation offsets the friction vanilla applies and cannot be disabled. This cannot be exact: measured across a dust particle's life at sprint speed it cuts the mean gap from 2.12 blocks to 1.18, and an End Rod's from 2.57 to 1.14, rather than to zero. Perfect adhesion would require custom entities, not particles.
  • Trail effects drew a dotted line instead of a trail: Rainbow, Stardust and Dash placed particles only on trail-history samples, which sit about 0.56 blocks apart at sprint speed. Each segment is now subdivided to a target spacing so the ribbon stays continuous at any pace. Dash additionally reused its lane index as the interpolation factor, so its streaks bunched at the start of every segment instead of spanning it. Subdivision is clamped tightly: measured at a looser clamp Rainbow reached 416 particles per pass against 112 before, so its history was shortened from 14 samples to 10 to pay for the added density, landing at 144.
  • A null mob type crashed the duel result handler: PlayerEloData.withWin called mobType.isEmpty() straight on its argument, so a DuelResultEvent carrying a null mob type took down the whole ELO update with a NullPointerException. EloDatabase.load reached the same code with ResultSet.getString, which is null for a NULL column. The record now normalises null to blank in its constructor, covering every construction path, and withWin keeps the existing favourite rather than throwing. Found by compiling the class on its own and running it, since neither it nor EloCalculator has any Minecraft dependency.
  • Unlock ordering was discarded on every save: PlayerProgress stores unlocks in a LinkedHashSet for deterministic ordering, then handed out Set.copyOf, whose iteration order is unspecified and randomised per JVM run. The persisted CSV was therefore rewritten in a different order on every save even when nothing had changed, churning solo_progress.json and issuing pointless database updates.
  • Cosmetics tab lost its page indicator: the progression summary was placed in slot 49, which is where DashboardTabHandler.buildBottomBar writes the "page X / Y" paper. It is now in slot 48 and both coexist.
  • Solo progression had nothing to unlock in singleplayer: PermissionService.canUseCosmetic delegates to the lenient hasPermission, and with no permission backend bound the lib installs PERMISSIVE on an integrated server, which grants every permission. So a singleplayer world reported every cosmetic as already owned and progression could never sell anything, in exactly the environment it was written for. CosmeticAccess.hasRankGrant now ignores that blanket grant while progression is active, and only then: a singleplayer world running mode = "NEVER" still has every cosmetic free exactly as before 1.3.0, and a LuckPerms server is unaffected either way. PermissionService.canUseCosmetic is now called from a single place so a correction lands everywhere at once.
  • Equipping from preview did nothing, but claimed it worked: the preview's equip key sent the lib's C2SDashboardAction.SELECT_PARTICLE, which dispatches to player.containerMenu and returns silently unless that menu is a DashboardMenu. Preview deliberately closes the dashboard, so the server ignored every equip while the client optimistically printed "Equipped …". A dedicated C2SEquipEffect payload now carries it, revalidates access server-side through CosmeticAccess, and the confirmation message comes from the side that actually saves.
  • QuĂ©bec French was missing 75 strings: fr_ca.json had never been brought up to date with fr_fr.json, so a large part of the hub, the quest tab, the leaderboard tab, the daily tab and every command message fell back to English. All live keys are now translated in both French files.

Performance

  • Allocation-free particle tick loop: The per-tick nearest-player selection built a HashMap of boxed distances, copied the entry set into an ArrayList and full-sorted it, ten times a second, to then use at most ten results. It is now a bounded insertion into reusable fixed arrays: no collections, no boxing, no sort.
  • Gradient colours come from lookup tables: Rainbow allocated a fresh DustParticleOptions per particle: up to 14 trail positions Ă— 8 lanes = 112 allocations per render call, per player in view, ten times a second. Trail, Comet, Galaxy, Pulsar, Shockwave, Binary and Prism did the same on a smaller scale. All of them now read from pre-built tables (a 32Ă—8 hue/size grid, plus a 16-step ramp per gradient effect); quantisation at particle scale is visually indistinguishable.
  • Effect dispatch no longer allocates: renderEffect called effectId.toLowerCase(Locale.ROOT) on every dispatch. Ids are canonicalised once at the cache boundary and the switch reads them directly.
  • Math.random() replaced by the shared Random: Helix, Meteor, Comet, Galaxy and Hearts used Math.random(), which goes through a globally shared synchronized generator, on the render thread.
  • Bulk login sync: Joining a server sent one packet per online player with an active effect, so a full 100-slot server produced a burst of ~100 tiny payloads on every single join. They are now bundled into one S2CParticleBulkSync (toggleable via performance.batch_login_sync).
  • ELO leaderboard is memoized: Opening the leaderboard tab re-sorted the entire rating cache, or re-queried the database, on every render including refreshes triggered by unrelated clicks. Results are cached for performance.leaderboard_cache_seconds and invalidated when a duel moves a rating.
  • Cosmetic particles respect the vanilla particle slider: At the default quality = AUTO, Decreased halves density and Minimal renders nothing. A player who already turned vanilla particles down should not have to hunt for a second setting.
  • Invisible and spectator players are skipped: Effects no longer render through an Invisibility potion or for spectators, which was both a rendering cost and a PvP information leak.
  • Rendering pauses when the game does: The tick loop now exits early on Minecraft.isPaused().
  • Per-pass particle budget: max_visible_effects counts players, not cost, and effects are nowhere near equally expensive: Hearts emits about 2 particles per pass, Rainbow about 112, a 50x spread that a player cap treats identically. Every effect now carries a render weight in the registry, and the client stops adding further-away effects once particle_budget (default 600) is used up. Typical mixed play spends about 250 and is unaffected; ten players all wearing Rainbow drop from ten rendered to six. The nearest effect is exempt, so the budget can never blank the screen, and 0 restores pure count-based limiting.

Changed

  • Single source of truth for cosmetic effects: New com.arcadia.prestige.cosmetic package (CosmeticEffects, CosmeticEffect, CosmeticGroup, CosmeticTier, CosmeticAccess). The effect list used to be duplicated across four places (the Cosmetics tab rows, CosmeticPermissionScanner.ALL_COSMETICS, EffectPreviewHandler.ALL_EFFECTS and the renderer's movement set), and drift between them is precisely what made whole GUI rows unclickable in 1.2.15. Adding an effect is now one line in CosmeticEffects, one render branch and one translation key per language.
  • All access checks route through CosmeticAccess: A single place answers "may this player use this effect?", combining the LuckPerms grant with the progression unlock. That is what lets progression be switched off server-wide with every call site following automatically.
  • Network protocol version bumped to 2: An older client and a newer server now refuse to connect rather than silently mis-decoding the new payloads.
  • Declared arcadia_lib dependency corrected to [1.2.14,): It was [1.0.0,), which no version of Prestige has been able to honour for some time. 1.3.0 calls PermissionService.hasRealBackend, ArcadiaConfigPaths.dataFile, JsonFileStore, DatabaseManager.isDatabaseActive, LibMenus and StaffPetEntities, none of which exist in early lib releases, so an out-of-date library passed the dependency check and then died on a NoSuchMethodError mid-startup. NeoForge now reports the real requirement before loading anything. The optional arcadia_pets and arcadia_ah ranges are deliberately left permissive: those are reached through the registry and reflection with vanilla fallbacks, so an old version degrades gracefully rather than needing to be blocked. The release workflow's generated notes were still advertising 1.2.0 as well.
  • Dead PrestigeHubScreen and PrestigeCard removed: Prestige's own full-screen hub, superseded when the hub moved to arcadia-lib and /arcadia_prestige started calling ArcadiaLibNet.sendOpenHub. Neither had a single reference left: no Java call site, no resource entry, and the mod registers no screens at all. PrestigeCard also carried a latent crash, resolving LibModItems.ARCADIA_STAR.get() from a static initializer, which throws if the class is touched before the item registry is frozen. The arcadia_prestige.hub.* translation keys are deliberately kept: unused strings cost nothing at runtime, and removing one that another Arcadia module turns out to reference would put a raw key on screen.
  • Dead ArcadiaConfig removed: A static holder with zero references left anywhere in the mod. Every value in it had already been superseded: grades by PrestigeConfig and lib's PermissionConfig, particle distances and visible count by the new PrestigeClientConfig, and server_id by lib's ServerContext back in 1.2.2. It also carried placeholder database constants including a DB_PASS field, which has no business sitting in source even as a dev default.

The three entries below describe work carried out in arcadia-lib, not in this mod. They were already pending in the Unreleased section and are listed here because they change how Prestige behaves. No file in arcadia-lib was modified as part of 1.3.0; Prestige consumes it as a read-only compileOnly dependency.

  • Staff pet entities removed, moved to arcadia-lib. StaffPetEntity, StaffPetEntities, StaffPetRenderer and the 17 entity registrations now live in arcadia-lib (≥ 1.2.10). Lets arcadia-pets and arcadia-ah render Mythic staff pets without requiring Prestige to be installed (e.g. on a singleplayer save with only Pets + Lib). Existing pet items / AH listings carrying the legacy arcadia_prestige:staff_* mob type are migrated automatically by lib's namespace helper, no save migration needed.
  • Staff entity translation keys moved to arcadia-lib (entity.arcadia_lib.staff_*).
  • Database / core service init removed from ArcadiaDashboard: DatabaseConfig.SPEC registration plus the calls to DatabaseManager.initialize(), PlayerDataHandler.init(), EconomyService.init(), PermissionService.init() and the matching shutdown() chain now live in ArcadiaLib (≥ 1.2.10). Was the actual reason AH and Pets stopped working in any modset that didn't include Prestige.

Ajouts

  • Progression solo, gagner des cosmĂ©tiques en jouant. La nouvelle section de config [solo_progression] transforme les effets cosmĂ©tiques en quelque chose qu'un monde solo ou un serveur privĂ© peut rĂ©ellement viser. Les joueurs accumulent des Points de Prestige via le temps de jeu, les monstres et boss tuĂ©s, les quĂªtes rĂ©clamĂ©es, les rĂ©compenses journalières, les paliers journaliers, les duels gagnĂ©s et les progrès (chaque source a son propre taux configurable, 0 la dĂ©sactive), puis les dĂ©pensent pour dĂ©bloquer dĂ©finitivement n'importe quel effet. Les coĂ»ts se règlent par palier (vip / vip_plus / mvp / founder) avec des surcharges par effet dans une chaĂ®ne du style "rainbow=25000,orbit=500", et un daily_cap optionnel borne le gain par journĂ©e rĂ©elle.
    • C'est mode qui protège l'Ă©conomie de la boutique. AUTO (par dĂ©faut) n'active la progression que si aucun backend de permissions n'est liĂ©, ce qui est la signature d'un monde solo, d'une partie LAN ou d'un serveur privĂ© ; un serveur officiel sous LuckPerms n'est absolument pas touchĂ©. ALWAYS l'active partout, NEVER garantit qu'elle ne tourne jamais. Les dĂ©blocages sont additifs, ne retirent jamais un accès accordĂ© par un grade, et dĂ©sactiver le système plus tard laisse les dĂ©blocages sur disque, simplement ignorĂ©s.
    • Deux backends de stockage. La base de donnĂ©es est utilisĂ©e dès qu'arcadia-lib a un pool actif ; sinon la progression est Ă©crite dans config/arcadia/prestige/solo_progress.json. Sans ce fallback, la progression se rĂ©initialiserait Ă  chaque redĂ©marrage prĂ©cisĂ©ment dans la configuration pour laquelle elle existe. Les Ă©critures sont regroupĂ©es et vidĂ©es hors du thread principal, Ă  la dĂ©connexion et Ă  l'arrĂªt du serveur.
    • Anti-AFK. La rĂ©compense par minute n'est versĂ©e que si la position ou l'angle de vue du joueur a changĂ© depuis le relevĂ© prĂ©cĂ©dent : miner sur place compte toujours, une ferme AFK non.
    • Nouvelles commandes : /arcadia_prestige progress (rĂ©sumĂ© personnel, prochain dĂ©blocage, ce qui est accessible), plus progress status|give|set|reset|unlock|lock rĂ©servĂ©es aux OP.
  • Dix effets faits de vrais blocs et objets, portant le total Ă  65 : Une troisième vague, tous statiques, tous portant de vrais modèles de jeu en plus de leurs particules. Grimoire (VIP) fait tourner trois livres Ă  hauteur de poitrine, Moisson (VIP) fait monter citrouilles et bottes de foin le long d'une hĂ©lice, Rucher (VIP) entretient un essaim de rayons de miel dont aucune pièce n'est en phase, Forge (VIP+) fait tourner une enclume, un bloc de fer et un haut fourneau dans une gerbe d'Ă©tincelles, ObĂ©lisque (VIP+) empile une colonne de pierre sculptĂ©e au-dessus de la tĂªte, TrĂ©sor (MVP) traĂ®ne un large anneau bas d'or, d'Ă©meraude, de diamant et de lapis, Arsenal (MVP) balance des armes en netherite sur un anneau vertical qui tourne lui-mĂªme, PlanĂ©taire (Founder) fait fonctionner un vrai système solaire avec une Ă©toile de pierre lumineuse et trois planètes sur des anneaux croissants, Sentinelles (Founder) fixe deux gardiens d'obsidienne pleureuse aux Ă©paules pour qu'ils suivent le corps et non le monde, et Couronne (Founder) porte un bandeau d'or, de diamant et de netherite.
    • Le système de compagnons est devenu une donnĂ©e. Comète et MĂ©tĂ©ore portaient un bloc depuis les 1.2.x, via une paire d'Ă©tats de bloc en dur et deux orbites Ă©crites Ă  la main. Ce qui est affichĂ© et comment ça bouge se dĂ©clare dĂ©sormais Ă  cĂ´tĂ© de l'effet lui-mĂªme, dans l'un des neuf motifs de placement, pour qu'un effet et son compagnon ne puissent plus diverger.
    • Blocs et objets sont la mĂªme chose. Chaque pièce passe par le renderer d'items avec le contexte d'affichage identitĂ© : un item de bloc sort en petit cube solide et un item plat en sprite tel qu'on le connaĂ®t au sol, l'Ă©chelle voulant dire la mĂªme chose pour les deux. C'est ce qui permet Ă  Arsenal de porter un vrai trident et Ă  Grimoire un vrai livre enchantĂ©.
    • Le coĂ»t est bornĂ© volontairement. Un rendu de modèle est bien plus cher qu'une particule et aucun ne se groupe avec les autres. Le nombre de pièces est divisĂ© par deux au-delĂ  de la moitiĂ© de la distance de rendu configurĂ©e, la passe s'arrĂªte Ă  96 pièces par frame tous joueurs confondus, et render_block_companions = false la saute entièrement. Les passes de particules de ces dix effets sont volontairement sobres, pour Ă©clairer les pièces plutĂ´t que leur faire concurrence.
  • Vingt effets supplĂ©mentaires, portant le total Ă  55 : Une seconde vague, cinq par palier de grade. Statiques : Lucioles et Braises (VIP), Cristal et MarĂ©e (VIP+), Sanctuaire, Vortex et Balise (MVP), Abysse, Aurore et SĂ©raphin (Founder). Mouvement : Élan, Poussière et Course du Vent (VIP), Sentier GelĂ©, Pas de Foudre et Magma (VIP+), Poussière d'Étoiles et Venin (MVP), Spectre et Constellation (Founder). L'onglet CosmĂ©tiques compte dĂ©sormais cinq pages : les pages 0 Ă  2 regroupent les 40 effets statiques, les pages 3 et 4 les 25 effets de mouvement, pour qu'un joueur cherchant un type n'ait jamais Ă  fouiller une page mĂ©langĂ©e. Le palier affichĂ© est une valeur par dĂ©faut interne, utilisĂ©e pour le prix en progression solo et pour le libellĂ© quand LuckPerms ne dit rien ; sur un serveur Ă  grades, c'est le groupe qui dĂ©tient le node qui dĂ©cide.
  • Dix nouveaux effets cosmĂ©tiques, portant le total Ă  35 : Statiques : Givre (couronne de glace hexagonale, cercle de runes gelĂ©es, particules flottantes), Arcane (trois cercles runiques contra-rotatifs avec glyphes inscrits et mana attirĂ©e vers le centre), Lotus (mandala de pĂ©tales au sol qui respire, avec envol de pollen), Éclipse (disque sombre au-dessus de la tĂªte, couronne solaire pulsante et protubĂ©rances), Prisme (prisme triangulaire rotatif projetant un Ă©ventail de rĂ©fraction spectral). Mouvement : Floraison (des fleurs poussent dans tes pas), Runes (tablettes de glyphes lumineux laissĂ©es derrière toi), TempĂªte (vortex de vent et bouffĂ©es de nuages dans ton sillage), PhĂ©nix (ailes enflammĂ©es battantes et traĂ®nĂ©e de braises), Brasier (sol calcinĂ©, air brĂ»lant et jets de lave). L'onglet CosmĂ©tiques compte dĂ©sormais trois pages (Statique I-IV, Mouvement I-III).
  • Config client, config/arcadia/prestige/client.toml. PrĂ©fĂ©rences de rendu purement locales, rien n'est envoyĂ© au serveur et rien n'affecte le gameplay : quality des particules (AUTO suit le curseur de particules vanilla, plus HIGH/MEDIUM/LOW/OFF), max_visible_effects, max_distance, near_distance, hide_own_effects_first_person (dĂ©sormais conservĂ© entre les redĂ©marrages au lieu de se rĂ©initialiser Ă  chaque lancement), show_other_players_effects, hide_invisible_players, render_block_companions et reopen_dashboard_on_exit.
  • Interrupteurs de fonctionnalitĂ©s, [features]. Interrupteurs indĂ©pendants pour cosmetics, daily_rewards, quests, elo et effect_preview. En dĂ©sactiver un arrĂªte son suivi d'Ă©vĂ©nements et affiche un message clair dans son onglet ; rien n'est supprimĂ©, donc le rĂ©activer restaure l'Ă©tat sauvegardĂ©.
  • Commandes de confort : /arcadia_prestige effect <id> et effect off (avec complĂ©tion sur tous les ids d'effets, vĂ©rification de permission cĂ´tĂ© serveur), preview [effect], stats (sĂ©rie, ELO, quĂªtes, cosmĂ©tiques possĂ©dĂ©s, points), quests et leaderboard (toutes deux documentĂ©es dans le README depuis les 1.2.x mais jamais rĂ©ellement implĂ©mentĂ©es), et cosmetics rescan (documentĂ©e dans la javadoc de CosmeticPermissionScanner depuis la 1.2.14 mais jamais branchĂ©e).
  • Le HUD d'aperçu nomme la ligne : Avec 65 effets, un simple « 21/65 » ne dit rien sur l'endroit oĂ¹ l'on se trouve : l'overlay affiche dĂ©sormais la ligne du dashboard Ă  laquelle appartient l'effet, avec le mĂªme libellĂ© que l'onglet CosmĂ©tiques. Le shift-molette avance de cinq, soit exactement une ligne, donc l'indication sert aussi de repère sur ce que fait ce saut.
  • Raccourcis clavier : Équiper l'effet prĂ©visualisĂ© (par dĂ©faut R) et deux touches non assignĂ©es par dĂ©faut, Ouvrir l'aperçu d'effets et Afficher/masquer ses propres effets, pour qu'installer le mod ne vole jamais une touche dĂ©jĂ  utilisĂ©e par le joueur.
  • Section de config [performance] : batch_login_sync, quest_cache_days et leaderboard_cache_seconds exposent le rĂ©glage derrière les correctifs ci-dessous.

Correctifs

  • L'aperçu d'effets Ă©tait totalement mort : Cliquer sur la longue-vue dans l'onglet CosmĂ©tiques ne faisait strictement rien. Le gestionnaire de clic vivait dans com.arcadia.prestige.client.DashboardScreen, une classe jamais enregistrĂ©e : depuis que le dashboard a migrĂ© vers arcadia-lib, le menu est liĂ© Ă  com.arcadia.lib.client.DashboardScreen, donc la copie de Prestige Ă©tait du code mort inatteignable et son mouseClicked ne s'exĂ©cutait jamais. CĂ´tĂ© serveur, CosmeticsTabHandler.handleClick n'avait pas non plus de cas pour le slot 47, donc le clic Ă©tait avalĂ© des deux cĂ´tĂ©s. L'aperçu est dĂ©sormais pilotĂ© par le serveur : le handler d'onglet rĂ©pond au slot 47 en envoyant un nouveau payload S2CStartPreview, ce qui fonctionne quelle que soit l'implĂ©mentation d'Ă©cran affichant le menu et permet au serveur de transmettre avec lui la liste faisant autoritĂ© des effets dĂ©bloquĂ©s. La classe morte DashboardScreen a Ă©tĂ© supprimĂ©e.
  • L'aperçu n'entre plus en conflit avec la synchro d'effets : Il Ă©crasait auparavant l'entrĂ©e partagĂ©e de PlayerEffectCache et la restaurait Ă  la sortie, donc un S2CParticleSync arrivant pendant l'aperçu Ă©crasait ce que le joueur regardait, et une sortie interrompue pouvait laisser le mauvais effet en cache. L'Ă©tat d'aperçu vit maintenant entièrement dans EffectPreviewHandler et est fusionnĂ© au moment du rendu : il ne peut plus Ăªtre perturbĂ© et n'a besoin d'aucune Ă©tape de restauration.
  • L'Ă©tat d'effets cĂ´tĂ© client fuyait entre les mondes : PlayerEffectCache.clear() existait mais n'avait aucun appelant. Les effets synchronisĂ©s sur un serveur restaient en mĂ©moire après dĂ©connexion et rĂ©apparaissaient dans le monde suivant rejoint durant la mĂªme session, sur les joueurs qui rĂ©utilisaient ces UUID. DĂ©sormais vidĂ© sur ClientPlayerNetworkEvent.LoggingOut, avec l'Ă©tat d'aperçu et l'historique de positions du renderer.
  • L'historique de positions de ParticleRenderer grossissait sans limite : trailPositions et lastPositions Ă©taient de simples HashMap dans lesquelles on ne faisait qu'Ă©crire. Chaque joueur jamais vu avec un effet gardait une entrĂ©e, plus jusqu'Ă  14 objets Vec3 chacun, pour toute la durĂ©e de vie du client. Elles sont maintenant balayĂ©es pĂ©riodiquement et vidĂ©es Ă  la dĂ©connexion.
  • Le cache de quĂªtes grossissait sans limite : QuestManager.CACHE conservait chaque jour oĂ¹ un joueur avait Ă©tĂ© vu, pour chaque joueur, pour toute la durĂ©e du processus. Les jours au-delĂ  de performance.quest_cache_days sont maintenant Ă©vincĂ©s, et l'entrĂ©e d'un joueur est supprimĂ©e Ă  sa dĂ©connexion.
  • Les effets attachĂ©s au corps traĂ®naient derrière un joueur en mouvement : Halos, anneaux, ailes et disques dĂ©crochaient visiblement en marchant ou en sprintant au lieu de rester sur le joueur. Les particules Minecraft sont des objets en espace monde de type « Ă©mettre et oublier » : rien ne peut en attacher une Ă  une entitĂ© après l'Ă©mission, et DustParticleBase leur donne une durĂ©e de vie allant jusqu'Ă  40 ticks. Un joueur sprintant Ă  0,28 bloc par tick laissait donc un anneau statique jusqu'Ă  onze blocs derrière lui avant qu'il ne disparaisse. Tous les points d'Ă©mission passent dĂ©sormais par un helper qui ajoute le mouvement par tick du porteur Ă  la vitesse de la particule, uniquement pour les effets attachĂ©s au corps : les effets de mouvement sont censĂ©s rester derrière. Le report est calibrĂ© par type de particule contre la source vanilla dĂ©compilĂ©e : Ă—10 pour les poussières (qui multiplient la vitesse fournie par 0,1), Ă—1 pour l'End Rod (qui l'utilise telle quelle), et ignorĂ© pour les types comme Portal qui traitent l'argument comme une amplitude de dĂ©placement et non une vitesse, oĂ¹ injecter la vitesse d'un joueur dĂ©formerait la figure au lieu de la dĂ©placer. Une sur-compensation de 1,3Ă— compense en plus la friction que vanilla applique et qu'on ne peut pas dĂ©sactiver. Ce ne peut pas Ăªtre exact : mesurĂ© sur la durĂ©e de vie d'une poussière en sprint, l'Ă©cart moyen passe de 2,12 blocs Ă  1,18, et celui d'un End Rod de 2,57 Ă  1,14, pas Ă  zĂ©ro. Une adhĂ©rence parfaite exigerait des entitĂ©s custom, pas des particules.
  • Les effets de traĂ®nĂ©e dessinaient une ligne pointillĂ©e plutĂ´t qu'une traĂ®nĂ©e : Arc-en-Ciel, Poussière d'Étoiles et Élan ne posaient des particules que sur les Ă©chantillons d'historique, espacĂ©s d'environ 0,56 bloc en sprint. Chaque segment est maintenant subdivisĂ© selon un espacement cible pour que le ruban reste continu quelle que soit l'allure. Élan rĂ©utilisait en plus son index de voie comme facteur d'interpolation, donc ses traits se tassaient au dĂ©but de chaque segment au lieu de le parcourir. La subdivision est plafonnĂ©e serrĂ© : mesurĂ© avec un plafond plus large, Arc-en-Ciel atteignait 416 particules par passe contre 112 auparavant, donc son historique passe de 14 Ă©chantillons Ă  10 pour payer la densitĂ© ajoutĂ©e, atterrissant Ă  144.
  • Un type de mob null faisait planter le gestionnaire de rĂ©sultat de duel : PlayerEloData.withWin appelait mobType.isEmpty() directement sur son argument, donc un DuelResultEvent portant un type de mob null faisait tomber toute la mise Ă  jour ELO sur une NullPointerException. EloDatabase.load atteignait le mĂªme code via ResultSet.getString, qui vaut null pour une colonne NULL. Le record normalise dĂ©sormais null en chaĂ®ne vide dans son constructeur, ce qui couvre tous les chemins de construction, et withWin conserve le favori existant au lieu de lever. TrouvĂ© en compilant la classe seule et en l'exĂ©cutant, ni elle ni EloCalculator n'ayant de dĂ©pendance Minecraft.
  • L'ordre des dĂ©blocages Ă©tait perdu Ă  chaque sauvegarde : PlayerProgress stocke les dĂ©blocages dans un LinkedHashSet pour garantir un ordre dĂ©terministe, puis renvoyait un Set.copyOf, dont l'ordre d'itĂ©ration est non spĂ©cifiĂ© et randomisĂ© Ă  chaque JVM. Le CSV persistĂ© Ă©tait donc réécrit dans un ordre diffĂ©rent Ă  chaque sauvegarde mĂªme sans modification, provoquant du churn sur solo_progress.json et des mises Ă  jour inutiles en base.
  • L'onglet CosmĂ©tiques perdait son indicateur de page : le rĂ©sumĂ© de progression Ă©tait placĂ© au slot 49, lĂ  oĂ¹ DashboardTabHandler.buildBottomBar Ă©crit le papier « page X / Y ». Il est maintenant au slot 48 et les deux coexistent.
  • La progression solo n'avait rien Ă  dĂ©bloquer en solo : PermissionService.canUseCosmetic dĂ©lègue au hasPermission permissif, et sans backend de permissions la lib installe PERMISSIVE sur un serveur intĂ©grĂ©, qui accorde toutes les permissions. Un monde solo signalait donc tous les cosmĂ©tiques comme dĂ©jĂ  possĂ©dĂ©s et la progression ne pouvait rien vendre, prĂ©cisĂ©ment dans l'environnement pour lequel elle a Ă©tĂ© Ă©crite. CosmeticAccess.hasRankGrant ignore dĂ©sormais cet octroi global tant que la progression est active, et seulement dans ce cas : un monde solo en mode = "NEVER" garde tous les cosmĂ©tiques gratuits exactement comme avant la 1.3.0, et un serveur LuckPerms n'est affectĂ© ni dans un cas ni dans l'autre. PermissionService.canUseCosmetic n'est plus appelĂ© que d'un seul endroit, pour qu'une correction s'applique partout d'un coup.
  • Équiper depuis l'aperçu ne faisait rien, tout en prĂ©tendant avoir rĂ©ussi : la touche d'Ă©quipement de l'aperçu envoyait le C2SDashboardAction.SELECT_PARTICLE de la lib, qui dispatche vers player.containerMenu et sort silencieusement si ce menu n'est pas un DashboardMenu. L'aperçu ferme volontairement le dashboard, donc le serveur ignorait chaque Ă©quipement pendant que le client affichait « … Ă©quipĂ© » de façon optimiste. Un payload dĂ©diĂ© C2SEquipEffect le transporte dĂ©sormais, revalide l'accès cĂ´tĂ© serveur via CosmeticAccess, et le message de confirmation vient du cĂ´tĂ© qui sauvegarde rĂ©ellement.
  • Il manquait 75 chaĂ®nes en français quĂ©bĂ©cois : fr_ca.json n'avait jamais Ă©tĂ© remis Ă  niveau avec fr_fr.json, donc une grande partie du hub, de l'onglet quĂªtes, de l'onglet classement, de l'onglet journalier et tous les messages de commandes retombaient sur l'anglais. Toutes les clĂ©s vivantes sont dĂ©sormais traduites dans les deux fichiers français.

Performance

  • Boucle de tick des particules sans allocation : La sĂ©lection par tick des joueurs les plus proches construisait une HashMap de distances boxĂ©es, recopiait l'ensemble des entrĂ©es dans une ArrayList et la triait entièrement, dix fois par seconde, pour n'utiliser au plus que dix rĂ©sultats. C'est dĂ©sormais une insertion bornĂ©e dans des tableaux fixes rĂ©utilisĂ©s : aucune collection, aucun boxing, aucun tri.
  • Les couleurs de dĂ©gradĂ© viennent de tables de lookup : Arc-en-Ciel allouait un DustParticleOptions neuf par particule : jusqu'Ă  14 positions de traĂ®nĂ©e Ă— 8 voies = 112 allocations par appel de rendu, par joueur visible, dix fois par seconde. TraĂ®nĂ©e, Comète, Galaxie, Pulsar, Onde de Choc, Binaire et Prisme faisaient de mĂªme Ă  plus petite Ă©chelle. Tous lisent maintenant dans des tables prĂ©-construites (une grille teinte/taille 32Ă—8, plus une rampe de 16 pas par effet Ă  dĂ©gradĂ©) ; la quantification est visuellement indiscernable Ă  l'Ă©chelle d'une particule.
  • La rĂ©partition des effets n'alloue plus : renderEffect appelait effectId.toLowerCase(Locale.ROOT) Ă  chaque rĂ©partition. Les ids sont canonisĂ©s une seule fois Ă  l'entrĂ©e du cache et le switch les lit directement.
  • Math.random() remplacĂ© par le Random partagĂ© : HĂ©lice, MĂ©tĂ©ore, Comète, Galaxie et CÅ“urs utilisaient Math.random(), qui passe par un gĂ©nĂ©rateur synchronisĂ© global, sur le thread de rendu.
  • Synchro de connexion groupĂ©e : Rejoindre un serveur envoyait un paquet par joueur en ligne ayant un effet actif, donc un serveur plein de 100 slots produisait une rafale d'environ 100 minuscules payloads Ă  chaque connexion. Ils sont maintenant regroupĂ©s en un seul S2CParticleBulkSync (dĂ©sactivable via performance.batch_login_sync).
  • Le classement ELO est mĂ©moĂ¯sĂ© : Ouvrir l'onglet classement retriait tout le cache de ratings, ou re-interrogeait la base, Ă  chaque rendu, y compris les rafraĂ®chissements dĂ©clenchĂ©s par des clics sans rapport. Les rĂ©sultats sont mis en cache pour performance.leaderboard_cache_seconds et invalidĂ©s dès qu'un duel dĂ©place un rating.
  • Les particules cosmĂ©tiques respectent le curseur de particules vanilla : Avec quality = AUTO (par dĂ©faut), RĂ©duites divise la densitĂ© par deux et Minimales n'affiche rien. Un joueur qui a dĂ©jĂ  baissĂ© les particules vanilla ne devrait pas avoir Ă  chercher un second rĂ©glage.
  • Les joueurs invisibles et spectateurs sont ignorĂ©s : Les effets ne traversent plus une potion d'InvisibilitĂ© et ne s'affichent plus pour les spectateurs, ce qui Ă©tait Ă  la fois un coĂ»t de rendu et une fuite d'information en PvP.
  • Le rendu se met en pause avec le jeu : La boucle de tick sort maintenant immĂ©diatement sur Minecraft.isPaused().
  • Budget de particules par passe : max_visible_effects compte des joueurs, pas un coĂ»t, et les effets sont loin d'Ăªtre Ă©quivalents : CÅ“urs Ă©met environ 2 particules par passe, Arc-en-Ciel environ 112, un Ă©cart de 50x qu'un plafond de joueurs traite Ă  l'identique. Chaque effet porte dĂ©sormais un poids de rendu dans le registre, et le client cesse d'ajouter les effets les plus lointains une fois particle_budget (600 par dĂ©faut) Ă©puisĂ©. Une partie mixte typique consomme environ 250 et n'est pas affectĂ©e ; dix joueurs tous en Arc-en-Ciel passent de dix rendus Ă  six. L'effet le plus proche est exemptĂ©, donc le budget ne peut jamais vider l'Ă©cran, et 0 rĂ©tablit la limitation par simple comptage.

Modifications

  • Source unique de vĂ©ritĂ© pour les effets cosmĂ©tiques : Nouveau package com.arcadia.prestige.cosmetic (CosmeticEffects, CosmeticEffect, CosmeticGroup, CosmeticTier, CosmeticAccess). La liste des effets Ă©tait dupliquĂ©e Ă  quatre endroits (les lignes de l'onglet CosmĂ©tiques, CosmeticPermissionScanner.ALL_COSMETICS, EffectPreviewHandler.ALL_EFFECTS et l'ensemble « mouvement » du renderer), et la dĂ©rive entre eux est prĂ©cisĂ©ment ce qui rendait des lignes entières du GUI incliquables en 1.2.15. Ajouter un effet demande dĂ©sormais une ligne dans CosmeticEffects, une branche de rendu et une clĂ© de traduction par langue.
  • Toutes les vĂ©rifications d'accès passent par CosmeticAccess : Un seul endroit rĂ©pond Ă  « ce joueur peut-il utiliser cet effet ? », en combinant l'octroi LuckPerms et le dĂ©blocage par progression. C'est ce qui permet de dĂ©sactiver la progression Ă  l'Ă©chelle du serveur en laissant tous les appelants suivre automatiquement.
  • Version du protocole rĂ©seau passĂ©e Ă  2 : Un client ancien et un serveur rĂ©cent refusent maintenant de se connecter au lieu de mal dĂ©coder silencieusement les nouveaux payloads.
  • DĂ©pendance arcadia_lib dĂ©clarĂ©e corrigĂ©e en [1.2.14,) : Elle Ă©tait Ă  [1.0.0,), ce qu'aucune version de Prestige ne pouvait honorer depuis un moment. La 1.3.0 appelle PermissionService.hasRealBackend, ArcadiaConfigPaths.dataFile, JsonFileStore, DatabaseManager.isDatabaseActive, LibMenus et StaffPetEntities, absents des premières versions de la lib : une bibliothèque pĂ©rimĂ©e passait donc la vĂ©rification de dĂ©pendance puis mourait sur un NoSuchMethodError en plein dĂ©marrage. NeoForge annonce dĂ©sormais la vraie exigence avant de charger quoi que ce soit. Les plages optionnelles arcadia_pets et arcadia_ah restent volontairement permissives : elles passent par le registre et la rĂ©flexion avec des replis vanilla, donc une version ancienne dĂ©grade proprement au lieu de devoir Ăªtre bloquĂ©e. Les notes gĂ©nĂ©rĂ©es par le workflow de release annonçaient elles aussi encore 1.2.0.
  • PrestigeHubScreen et PrestigeCard morts supprimĂ©s : Le hub plein Ă©cran propre Ă  Prestige, rendu obsolète quand le hub a migrĂ© vers arcadia-lib et que /arcadia_prestige s'est mis Ă  appeler ArcadiaLibNet.sendOpenHub. Aucun des deux n'avait la moindre rĂ©fĂ©rence restante : aucun site d'appel Java, aucune entrĂ©e dans les resources, et le mod n'enregistre aucun Ă©cran. PrestigeCard portait en prime un plantage latent, rĂ©solvant LibModItems.ARCADIA_STAR.get() depuis un initialiseur statique, ce qui lève une exception si la classe est touchĂ©e avant le gel du registre d'items. Les clĂ©s de traduction arcadia_prestige.hub.* sont volontairement conservĂ©es : des chaĂ®nes inutilisĂ©es ne coĂ»tent rien Ă  l'exĂ©cution, alors qu'en supprimer une qu'un autre module Arcadia rĂ©fĂ©rencerait afficherait une clĂ© brute Ă  l'Ă©cran.
  • ArcadiaConfig mort supprimĂ© : Un porteur de constantes statiques sans aucune rĂ©fĂ©rence restante dans le mod. Chacune de ses valeurs Ă©tait dĂ©jĂ  remplacĂ©e : les grades par PrestigeConfig et le PermissionConfig de la lib, les distances et le nombre de particules visibles par le nouveau PrestigeClientConfig, et server_id par le ServerContext de la lib depuis la 1.2.2. Il portait aussi des constantes de base de donnĂ©es de remplissage dont un champ DB_PASS, qui n'a rien Ă  faire dans le code source mĂªme comme valeur de dĂ©veloppement.

Les trois entrées ci-dessous décrivent des travaux réalisés dans arcadia-lib, pas dans ce mod. Elles étaient déjà en attente dans la section Unreleased et figurent ici parce qu'elles changent le comportement de Prestige. Aucun fichier d'arcadia-lib n'a été modifié dans le cadre de la 1.3.0 ; Prestige la consomme comme dépendance compileOnly en lecture seule.

  • EntitĂ©s des pets staff retirĂ©es, dĂ©placĂ©es vers arcadia-lib. StaffPetEntity, StaffPetEntities, StaffPetRenderer et les 17 enregistrements d'entitĂ© vivent maintenant dans arcadia-lib (≥ 1.2.10). arcadia-pets et arcadia-ah peuvent afficher les pets staff Mythic sans avoir besoin d'installer Prestige (ex. en solo avec seulement Pets + Lib). Les pet items / annonces HDV existants qui portent l'ancien namespace arcadia_prestige:staff_* sont migrĂ©s automatiquement par le helper de namespace de lib, aucune migration de save nĂ©cessaire.
  • ClĂ©s de traduction d'entitĂ© staff dĂ©placĂ©es dans arcadia-lib (entity.arcadia_lib.staff_*).
  • Init DB / services centraux retirĂ© de ArcadiaDashboard : L'enregistrement de DatabaseConfig.SPEC plus les appels Ă  DatabaseManager.initialize(), PlayerDataHandler.init(), EconomyService.init(), PermissionService.init() et la chaĂ®ne de shutdown() correspondante vivent maintenant dans ArcadiaLib (≥ 1.2.10). C'Ă©tait la vraie raison pour laquelle l'HDV et Pets ne marchaient plus dans un modset sans Prestige. ArcadiaDashboard.onServerAboutToStart se rĂ©sume maintenant Ă  CosmeticPermissionScanner.init().