Getting Started
What the plugin needs, and how to get it running for the first time.
Requirements
| Requirement | Needed for |
|---|---|
| Paper 1.20.1 – 1.21.x | Server software. Forks built on Paper's API work too. |
| Java 17+ | Runtime. |
| required PlaceholderAPI | Every placeholder (%celestial_*%) and the economy balance placeholder in commands mode. |
| required LuckPerms | Rank-based defaults, rank-restricted backgrounds, and the layered rank prefix builder. |
| optional Vault | Charging the server's main economy (economy-mode: vault). |
| optional EdDungeons | Charging a custom currency for the Auction House and paid crates (economy-mode: eddungeons). |
| optional ItemsAdder / Oraxen / Nexo | Custom icons and fonts — GUI items already support custom model data and player-head textures either way. |
| optional PacketEvents | Glow outlines that keep their color alongside TAB. |
| optional FancyHolograms | Floating labels above physical crate blocks. |
Installation
- Drop the jar into
plugins/, alongside PlaceholderAPI and LuckPerms. - Start the server once. It generates
config.yml,messages.yml,crates.ymland every file undercosmetics/with sane, ready-to-use defaults. - Edit whichever files you need — see the breakdown below.
- Apply changes with
/cosmetics reload— no restart needed for config edits.
File layout
plugins/CelestialCosmetics/ ├── config.yml # menus, economy, storage, categories on/off ├── messages.yml # every player-facing string, 100% translatable ├── crates.yml # crate definitions and reward tables └── cosmetics/ ├── tags.yml ├── rebirth.yml ├── customstat1.yml, customstat2.yml, customstat3.yml ├── namecolors.yml ├── chatcolors.yml ├── rankcolors.yml ├── rank-backgrounds.yml ├── emojis.yml ├── glows.yml ├── joinmessages.yml, leavemessages.yml ├── chatemojis.yml └── sets.yml
plugin.getConfig() internally merges ALL of these files into one view, so config.yml, messages.yml and every file under cosmetics/ are all readable and reloadable together — you never have to worry about which physical file a setting lives in.The Vault Model
The one concept that explains why trading, the Auction House and rank defaults all behave the way they do.
A cosmetic isn't a permission flag — it's a quantity stored per player, per cosmetic. A player can own 0, 1, or many copies of the same tag, name color, glow, etc. This is what makes physical trading and reselling possible without ever duplicating anything.
There are two completely separate ways to have access to a cosmetic, and only one of them can be turned into a physical item:
| Access type | Can equip? | Can withdraw / sell? |
|---|---|---|
| Owned copies (quantity > 0 in the vault) | ✅ | ✅ |
| Rank / permission / Set access (no real copies owned) | ✅ | ❌ — prevents infinite duplication from a rank |
If a player's last owned copy is withdrawn or sold while a cosmetic is equipped, it's automatically unequipped — unless their rank or an active Set still grants access, in which case nothing visibly changes for them.
Whole categories can be switched off in config.yml under categories-enabled: a disabled category disappears from every menu (including the admin wizard), its placeholder returns empty, and it can't be equipped, searched, traded, sold, or won from a crate.
Cosmetic Categories
15 independent types, each with its own file under cosmetics/ and its own PlaceholderAPI placeholder.
Custom Stat 1/2/3 work exactly like Rebirth Color but point at any placeholder you choose (customstat1-source-placeholder in config.yml) — a second or third progression system rendered the same way, from any plugin.
Sets bundle any combination of the other 14 into one equip: picking a Set applies every component at once (tag + name color + chat color + …), and unequipping it cleanly reverses only the pieces the Set itself applied.
Example entry, from tags.yml:
tags:
dragon:
display: "fc6c00Dragon"
value: "fc0000&l[fc6c00&lDRAGONfc0000&l]"
icon: DRAGON_HEAD
permission: celestial.cosmetics.tag.dragon
Configuration Files
config.yml at a glance — the sections you'll actually touch.
| Section | Controls |
|---|---|
storage | SQLite (default, single server) or MySQL (syncs the vault, Auction House, crate stock, pity and daily claims across a whole network). |
categories-enabled | One on/off switch per category. |
economy | economy-mode: vault, eddungeons, or commands — see Economy Integration. |
auction | Min/max price, tax percentage, the Auction House button and its confirm menu. |
admin-menu | The /cosmetics admin menu — one creation button per type, plus Reload. |
crates-menu, crate-holograms | The crates list button/title, and floating labels for physical crate blocks (needs FancyHolograms). |
search-menu | The vault-wide search button. |
menu | Main menu title/size, filler item, navigation buttons, one entry per category (slot/material/name/icon), the actions menu (Equip/Withdraw/Auction), and the shared confirm/preview buttons used by both crates and the Auction House. |
rank-defaults | Cosmetics auto-applied per LuckPerms primary group when nothing is equipped. Empty by default. |
Every menu item — button or category — accepts the same fields: enabled, slot, material, custom-model-data, name, lore, and texture (a base64 player-head skin, for ItemsAdder/Oraxen/Nexo icons). Set slot: -1 or enabled: false to hide something entirely.
Translating the Plugin
Every single player-facing string — menu titles, button names, lore, crate text, command responses — is config-driven. Nothing is hardcoded in a way that can't be overridden.
The two files that matter
messages.yml holds every sentence: base messages, trade/Auction House text, crate text, cosmetic descriptions, command responses (cli.*), and the admin creation wizard's prompts (wizard.*).
config.yml holds every menu title and button name — category names, the Auction House/Crates/Search buttons, admin buttons, and navigation (Back/Previous/Next/Unequip).
Type labels: one key, everywhere
A category's display name — "Chat Color", "Rank Effect", etc. — shows up in a lot of places beyond its own menu button: crate reward lore, trade item lore, the Auction House, the actions menu, the admin wizard, and cosmetic descriptions. Rather than hunting all of those down, messages.yml has one dedicated map:
messages: type-labels: tag: Tags chatcolor: Couleur du Chat rankcolor: Effet de Grade # ...one line per category
Change a type's name here once, and it updates everywhere that type is mentioned — keep it in sync with the matching menu.categories.<type>.name in config.yml so the category button and everything else agree.
&/&#RRGGBB color codes and {placeholder} tokens exactly where they are — translate the words around them, never the codes themselves.What stays untranslated by default
The actual cosmetic content — names like "Dragon" or "Dubstep Wobble" inside cosmetics/*.yml — is separate from the UI chrome above, and isn't touched by a UI translation pass unless you edit those files too.
GUI Customization
Every menu is rebuilt from config.yml on open — nothing requires recompiling.
Content slots — where cosmetics are placed in a category submenu — default to the classic centered rectangle, but can be listed explicitly for a compact layout:
menu: # content-slots: [10,11,12,13,14,15,16, 19,20,21,22,23,24,25]
Set menu.filler.enabled: false to remove the background filler entirely — useful on packs where a custom ItemsAdder/Oraxen/Nexo texture already draws one.
Crates
Weighted reward tables, opened with a key or paid currency.
- Weighted rewards — each reward in
crates.ymlhas aweight; odds are shown live in the preview menu. - Stock-limited rewards — cap how many times a reward can ever be won (globally, or per-server without MySQL). Hitting 1 triggers a server-wide broadcast and a permanent "Winners" list in the reward's lore.
- Pity / guarantee —
/cosmetics crate pity <player> <crate> [reset]checks or resets a player's counter toward a guaranteed pull. - Daily keys —
/cosmetics dailyshows cooldowns for any crate flagged as a daily reward. - Physical crate blocks — link any world block to a crate with
/cosmetics crate setblock; left/right-click previews or opens it, with a roulette-style opening animation. Add FancyHolograms for a floating label above it. - Timed events —
/cosmetics crate enable <crate> 7d12h30m(oralways) opens a crate for a duration;disablecloses it early.
/cosmetics crate open <crate> automatically uses a key if the player has one, otherwise it charges the configured price.Trading & Auction House
Withdraw → trade → claim
Withdrawing an owned cosmetic (Shift-Click in the vault, or the Withdraw button in the actions menu) takes one real copy out as a physical, stackable item that carries the cosmetic's identity. Any player can then claim it — right-click works no matter how you do it: holding the item and clicking air or a block, or right-clicking it while it's still sitting on the ground, before even picking it up. Claiming always adds a copy, even on top of ones you already own.
Auction House
A real player marketplace: list an owned cosmetic (Right-Click in the vault, the Auction button in the actions menu, or /cosmetics sell <type> <id> [price]), buyers pay through a confirm menu, and the sale is atomic — a listing can't be bought twice. auction.tax-percent takes a cut on every sale; min-price/max-price bound what can be listed.
Admin Tools
Everything an admin needs without ever hand-editing YAML.
/cosmetics admin opens a menu with one "+ New …" button per category. Clicking one starts a guided chat wizard — id, display name, value, icon, and any type-specific fields (a rebirth prefix/text/suffix, a Set's component list, and so on) — and writes the finished cosmetic straight into the right file.
/cosmetics set <player> <type> <id> force-equips any cosmetic on any player, even one they don't own — useful for events, giveaways, or support tickets. It's gated behind celestial.cosmetics.admin and bypasses ownership checks entirely.
/cosmetics give <player> <type> <id> [amount] adds real, ownable copies to a player's vault (so they can be withdrawn/sold), rather than just equipping one.
Commands
Base command /cosmetics (aliases: /cosmetic, /ccos).
| Command | Description |
|---|---|
/cosmetics | Open the main vault menu. |
/cosmetics ah / auction | Open the Auction House. |
/cosmetics sell <type> <id> [price] | List an owned cosmetic. Omit the price to be prompted in chat. |
/cosmetics crates | Open the crates menu. |
/cosmetics search [query] | Search your whole vault by name or id. |
/cosmetics daily | Show cooldowns for daily-reward crates. |
Admin — require celestial.cosmetics.admin | |
/cosmetics admin | Open the cosmetic-creation menu. |
/cosmetics reload | Reload every configuration file. |
/cosmetics give <player> <type> <id> [amount] | Add real vault copies. |
/cosmetics set <player> <type> <id> | Force-equip, ownership-independent. |
/cosmetics clear <player> <type> | Unequip a category for a player. |
/cosmetics extract <player> <type> <id> | Trigger a withdraw on someone's behalf. |
/cosmetics crate open <crate> | Open a crate (key first, then currency). |
/cosmetics crate givekey <player> <crate> [n] | Give crate keys. |
/cosmetics crate enable <crate> <7d12h30m|always> | Open a crate for a duration. |
/cosmetics crate disable <crate> | Close a crate early. |
/cosmetics crate setblock <crate> [world x y z] | Link a world block to a crate. |
/cosmetics crate removeblock | Unlink the next block you click. |
/cosmetics crate blocks | List every linked crate block. |
/cosmetics crate pity <player> <crate> [reset] | Check or reset a pity counter. |
Permissions
| Node | Default | Grants |
|---|---|---|
celestial.cosmetics.use | true (everyone) | Using /cosmetics and its player subcommands. |
celestial.cosmetics.admin | op | Every admin subcommand: admin, reload, give, set, clear, extract, and all crate management subcommands. |
celestial.cosmetics.<type>.<id> | — | Per-cosmetic access node, e.g. celestial.cosmetics.tag.dragon — set per-entry as permission: in its file. |
Placeholders
Expansion identifier: celestial — register with PlaceholderAPI as usual.
| Placeholder | Returns |
|---|---|
%celestial_tag% | Equipped tag, or empty. |
%celestial_emoji% | A leading space + the equipped emoji, or empty. |
%celestial_namecolor% | Name color code, or namecolor-default. |
%celestial_namecolor_legacy% | Same, snapped to the nearest of the 16 legacy colors — use this (not the hex version) for a nametag plugin's overhead-nametag field, which can't render arbitrary RGB. |
%celestial_rankcolor% | Equipped Rank Effect code. |
%celestial_rankbg% | Equipped Rank Background glyph. |
%celestial_chatcolor% | Chat color code, or chatcolor-default. |
%celestial_rank% / %celestial_rank_tab% | The full assembled rank image (effect + background + text) for chat, or for TAB (uses image offsets). |
%celestial_rankname% | Plain rank name, no color. |
%celestial_rebirth% | Fully styled Rebirth display (empty if the category is disabled). |
%celestial_customstat1/2/3% | Same idea as Rebirth, pointed at whatever placeholder you configured for that slot. |
%celestial_glow% | Equipped Glow color name. |
%celestial_set% | Active Set's display name. |
%celestial_keys_<crateId>% | How many keys for that crate the player is holding. |
Economy Integration
Shared by the Auction House and paid crates. If the chosen mode isn't available at startup, purchases are disabled and a clear warning is printed — nothing is ever given away for free by mistake.
| Mode | Behavior |
|---|---|
vault | Hooks any Vault-compatible plugin (EssentialsX, CMI, …). Charges the server's main balance — the currency field is ignored in this mode. |
eddungeons | Hooks the EdDungeons Currency API directly, for a custom currency (credits, gems, whatever you've configured there). currency selects which one. |
commands | Fully manual — set balance-placeholder plus commands.give/commands.take to match any economy plugin, including PlayerPoints, RivalCredits, or a custom one. |
economy: economy-mode: eddungeons currency: credits currency-display: Credits
Third-Party Integrations
ItemsAdder / Oraxen / Nexo icons
Every configurable icon supports custom-model-data for a resource-pack model, or a texture field (a base64 player-head skin) for a fully custom head texture — no Java, no recompiling.
Glows through TAB (PacketEvents)
Vanilla glow color is carried by scoreboard teams, which TAB also manages — without PacketEvents installed, TAB can silently reset a player's glow color. With it, CelestialCosmetics rewrites the outgoing team packets directly so the color sticks.
FancyHolograms
Purely optional: install it and floating labels appear automatically above every linked crate block, styled from crate-holograms.lines-normal/lines-limited in config.yml. No FancyHolograms, no label — the block still works.
Worked example: wiring into AlonsoChat's chat format
A real recipe for embedding CelestialCosmetics placeholders into another plugin's chat formatter. AlonsoChat parses its own &#RRGGBB hex codes through a special placeholder — %alonsochat_restorehex_<placeholder>% — that re-applies its hex parser on top of ANY other PlaceholderAPI placeholder's output. Since every CelestialCosmetics color placeholder already emits &#RRGGBB-style codes, wrapping them is enough to make them render:
# AlonsoChat formats.yml — one rank's Parts, in order Parts: rebirth: Text: "%alonsochat_restorehex_celestial_rebirth%" rank: Text: "%notranks_prefix_default% %vault_prefix%" player: Text: " %alonsochat_restorehex_celestial_namecolor%%player_name%" suffix: Text: " %alonsochat_restorehex_celestial_tag%" message: Text: "%alonsochat_restorehex_celestial_chatcolor%{MESSAGE}"
The same trick applies to any chat plugin that lets PlaceholderAPI output pass through unmodified — the order of the Parts keys is what controls left-to-right placement, so a Rebirth prefix in front of the rank is just a matter of listing it first.
Troubleshooting & FAQ
Purchases are disabled / "economy unavailable"
The configured economy-mode couldn't be hooked at startup — most often Vault or EdDungeons isn't installed, or loaded after CelestialCosmetics. Check the console on startup for the exact warning, then /cosmetics reload once it's fixed.
A menu button's text won't change no matter what I put in config.yml
Double-check the exact path the button lives under — some buttons are shared across two menus (the crate open-confirm and the Auction House purchase-confirm use the same menu.confirm-buttons entries, for instance), so one edit affects both. If a field genuinely has no effect, make sure you're on the latest build; a couple of button paths were fixed in 4.17.13+.
A rank isn't picking up its background/effect
%celestial_rank%/rankbg read from rank-color.ranks.<LuckPerms primary group> — if a group isn't mapped there, the player falls back to their plain rank name with no styling.
Glow color resets randomly
That's almost always TAB fighting over the scoreboard team packets. Install PacketEvents so CelestialCosmetics can rewrite the team packet after TAB sends its own.
Where do I report a bug or request a feature?
Through the BuiltByBit resource page, or open an issue on the GitHub repo.