Pet Skins
A skin is a permanent look a pet can take on: another entity type, a baby form, a size, gear, a potion effect, a per-species colour. It changes nothing about what the pet is — same blueprint, level, effects and inventory — and the pet keeps spawning its own AstralMobs mob-blueprint, so it walks, follows and animates exactly as before and simply looks different.
Skins live in plugins/AstralPets/skins/*.yml, one file per skin, loaded by SkinService before the blueprints.
Applying a skin
A player is handed the skin's item (
/pets skin, a shop, a crate — see Item suppliers).They spawn their pet, then right-click it while holding the item.
The first click only asks — see Confirmation. A second click within the configured window applies the skin, consumes one item, and the pet wears it for the rest of its life, including if it is picked back up as an item and given to someone else.
Skins stack. A pet wears a list of skins, in application order, and they are merged into one look when it spawns: later-applied skins win wherever two set the same thing, everything else adds up. A pet can therefore take a body, a hat and a colour from three different files. What it cannot do is wear the same skin twice, or two skins that conflict.
Application is refused, with a message and without consuming the item, when:
Condition | Message |
|---|---|
The pet already wears this skin |
|
The pet's blueprint does not list this skin under |
|
The pet wears a skin this one conflicts with |
|
The application has not been confirmed yet |
|
A listener cancelled | — (silent) |
The player is no longer holding that skin item by the time the interaction is processed | — (silent) |
The held item is re-read on the main thread rather than trusted from the packet thread, so a player who swaps, drops or moves it in between never has the wrong item consumed. The item is consumed after the skin is on the pet, so a refused application never eats it.
Because the rendered type, size and gear are fixed when the mob spawns, applying a skin to a pet that is currently out respawns it in place so the new look takes effect immediately. Applying to a pet that is not spawned works too — the skin simply shows on its next spawn.
The blueprint is re-resolved by id at application time rather than read off the pet, so a skin added to a blueprint during a reload works without the owner reconnecting first.
Confirmation
Applying is irreversible and spends the item, so the first right-click only asks: the player is sent pet-skin-confirm (with %confirmSeconds% bound) and nothing else happens — no event, no item consumed, no change to the pet. A second right-click on the same pet, with the same skin, within skin-confirmation-delay, goes through.
Anything else asks again rather than applying: a different pet, a different skin, or simply letting the window lapse. Only one question is pending per player at a time — asking about a second replaces the first. Setting the delay to zero or a negative value turns confirmation off and applies on the first click; a config written before the setting existed defaults to 10 seconds rather than to no confirmation at all.
Every other refusal is checked before the question is asked, so a player is never asked to confirm something that would then be rejected.
Conflicts
Two skins that contradict each other — two coat colours for the same wolf, say — would otherwise merge silently, the later one winning whatever they both set, leaving the loser paid for and unremovable. A skin file can therefore declare which skins it refuses to be worn alongside:
Key | Type | Description |
|---|---|---|
|
| Ids of skins this one cannot be worn with. Optional; defaults to empty. |
The conflict holds both ways round, so a pair only has to be written down in one of the two files. A pet already wearing either one refuses the other with pet-skin-conflict.
On load, SkinService checks every declared conflict once all skins are in the map (a conflict may name a skin loaded later) and warns about ones that name nothing: "Pet skin 'x' conflicts with 'y', which no file in skins/ declares. That conflict can never trigger."
For lore that warns before a player spends the item, %skin_conflictNames%/%skin_conflicts%/%skin_conflictCount% render both directions of the conflict — see Placeholders.
Skin YAML
Field | Type | Required | Description |
|---|---|---|---|
| String | Yes | Unique id, referenced from a blueprint's |
| Component (MiniMessage) | Yes | Display name, used in messages and menus as |
| ItemStackWrapper | No | The item that grants the skin. A skin without one still works for pets already wearing it — it just cannot be handed out, and |
| SkinEntity | No | What the skin changes about the spawned mob (below). Omitted means the skin changes nothing. |
|
| No | Ids of skins this one cannot be worn alongside. See Conflicts. |
entity
Everything here is optional; anything left out keeps what the pet blueprint and its mob-blueprint already say. The whole block is handed to AstralMobs as spawn overrides.
Field | Type | Description |
|---|---|---|
|
| Entity type the pet is rendered as. Also decides how much room the spawn-spot search needs. |
| String | AstralMobs blueprint the pet spawns as instead of its usual one. Only needed when the look also needs different behaviour (a flying skin for a walking pet); leave it out otherwise, since replacing it also replaces the pet's goals, movement and idle animations. |
| String | BetterModel model the pet is rendered as, replacing type spoofing entirely. |
| float | Scale of the BetterModel |
| boolean | Renders the pet as a baby. Only visible on a |
| float | Server-side collision box width, in blocks. |
| float | Server-side collision box height, in blocks. |
|
| Attributes applied over the pet blueprint's — the skin is applied last, so a skin's |
|
| What the pet wears, per slot ( |
|
| Potion effects the pet's mob carries (below). |
|
| Raw client-side entity metadata, for per-species looks with no dedicated key (below). |
effects entries
Applied once when the pet spawns and never re-evaluated — the effect is part of the skin, so it lasts as long as the mob does.
Field | Type | Default | Description |
|---|---|---|---|
|
| — | Required; an entry without one is dropped. |
| int |
| Raw Bukkit amplifier ( |
| int | infinite | Ticks. |
| boolean |
| |
| boolean |
| |
| boolean |
|
metadata entries
Indices 0-15 mean the same thing on every mob (glowing, pose, …); everything above belongs to the rendered type and is where the per-species looks live.
Field | Type | Description |
|---|---|---|
| int | Metadata index to write. |
| String |
|
| String | Value to write. Numbers accept hex ( |
Allowing a skin on a pet
A pet only ever accepts a skin its own blueprint lists:
Every skins entry naming a skin that no file declares is logged as a warning after each load (SkinService#validateBlueprintReferences) — otherwise a typo would only surface as a player being told their pet does not accept the skin.
Unknown ids are kept as written rather than dropped, because skins are validated after the blueprints and a later reload may legitimately introduce one.
Item supplier
Skin items are registered with AstralCore as the pets.skins item-stack supplier, so anywhere a supplier-backed item key is accepted the skin can be referenced as pets.skins:<skin-id>. The built item carries the skin id in its PDC under the astralpets:skin_id key, which is what the interaction (and SkinService#isSkinItem) reads it back from.
The skin item is inert otherwise
A skin item has exactly one use, so SkinListener blocks every vanilla use of the material it is built on: using the item on blocks or entities, placing it, crafting with it (including in an auto-crafter, where the preview is blanked as well), being dispensed, and being picked up by a non-player entity. Eating one is already blocked by the food listener.
Only the item use is denied — a player holding a skin item can still open doors and chests.
Commands and messages
/pets skin <player> <skin> (pets.give.skin) mails a skin item to a player — see Commands.
Message key | Sent when |
|---|---|
| The skin was applied ( |
| The pet already wears this skin. |
| The pet's blueprint does not list this skin. |
| The pet wears a skin this one conflicts with ( |
| The first right-click, asking for confirmation ( |
|
|
Placeholders
A PetSkin resolves under the skin namespace wherever one is registered — the skin item's own template, the skin messages, and each entry of %pet_skins% on a pet that wears any.
Placeholder | Type | Description |
|---|---|---|
| String | The skin id. |
| Component | Display name. |
| ItemStack? | The skin's item template; nothing when the file declares none. |
| ItemProvider / Component / int | The pets this skin fits — the reverse of each blueprint's |
| ItemProvider / Component / int | The skins it shuts out, both directions. |
| ItemProvider<PetSkin> | The skins a pet wears, in application order. Chain each entry into the keys above. |
| Component | Their names, comma-joined. |
| boolean | Whether the pet wears any skin. |
| int | How many it wears. |
| ItemProvider<PetSkin> | Every skin on the server — a catalogue, needing no player context. |
| ItemProvider<PetSkin> | The skins the blueprint accepts, resolved to files. |
|
| The raw ids it lists. |
| boolean | Whether the blueprint accepts any skin. |
See Placeholders for the surrounding namespaces.