▶️ ЗАБЕРИ СВОИ 8 ПОДАРКОВ 🎁 ПРИ СОЗДАНИИ СВОЕГО МАЙНКРАФТ СЕРВЕРА
Моды/Arcadia Prestige
Arcadia Prestige

Arcadia Prestige

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

Оцените первым
618
0
Все версииarcadia-prestige-1.21.1-1.3.0

arcadia-prestige-1.21.1-1.3.0

Release23.08.2026

Список изменений

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 "✨=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: ✨, 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 ✨ 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: ✨ 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, ✨ 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 ✨ 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 "✨=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().

Файлы

arcadia-prestige-1.3.0.jar(240.53 KiB)
Основной
Скачать

Метаданные

Канал релиза

Release

Номер версии

1.21.1-1.3.0

Загрузчики

NeoForge

Версии игры

1.21.1

Загрузок

10

Дата публикации

23.08.2026

Загрузил

ID версии

Главная