Astral Realms Documentation Help

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

  1. A player is handed the skin's item (/pets skin, a shop, a crate — see Item suppliers).

  2. They spawn their pet, then right-click it while holding the item.

  3. 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

pet-skin-already-applied

The pet's blueprint does not list this skin under skins

pet-skin-not-supported

The pet wears a skin this one conflicts with

pet-skin-conflict (%conflict_*%)

The application has not been confirmed yet

pet-skin-confirm (%confirmSeconds%)

A listener cancelled PetSkinApplyEvent

— (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:

id: "wolf-black" conflicts: - "wolf-white" - "wolf-brown"

Key

Type

Description

conflicts

List<String>

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

# plugins/AstralPets/skins/golden-cow.yml id: "golden-cow" name: "<gold>Vache dorée" item: material: "gold_ingot" name: "%skin_name%" lore: - "<gray>Apparence de familier." - "<red>Définitif : une apparence ne se retire pas." entity: type: "cow" baby: true attributes: "minecraft:scale": 1.4 equipment: HEAD: material: "golden_helmet" effects: - type: "glowing"

Field

Type

Required

Description

id

String

Yes

Unique id, referenced from a blueprint's skins list and by /pets skin. A file without one is skipped with a warning; a duplicate id keeps the first file loaded.

name

Component (MiniMessage)

Yes

Display name, used in messages and menus as %skin_name%.

item

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 /pets skin fails for it.

entity

SkinEntity

No

What the skin changes about the spawned mob (below). Omitted means the skin changes nothing.

conflicts

List<String>

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

type

EntityType

Entity type the pet is rendered as. Also decides how much room the spawn-spot search needs.

mob-blueprint

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.

model-id

String

BetterModel model the pet is rendered as, replacing type spoofing entirely.

model-scale

float

Scale of the BetterModel model-id. The visual size of an ordinary (non-model) pet is minecraft:scale under attributes instead.

baby

boolean

Renders the pet as a baby. Only visible on a type that has a baby form; the age is locked, so it never grows up.

hitbox-width

float

Server-side collision box width, in blocks.

hitbox-height

float

Server-side collision box height, in blocks.

attributes

Map<Attribute, Double>

Attributes applied over the pet blueprint's — the skin is applied last, so a skin's minecraft:scale or movement_speed wins.

equipment

Map<EquipmentSlot, ItemStackWrapper>

What the pet wears, per slot (HEAD, CHEST, LEGS, FEET, HAND, OFF_HAND, …). Items are built against the pet, so gear may read %pet_level% and friends.

effects

List<SkinPotionEffect>

Potion effects the pet's mob carries (below).

metadata

List<SkinMetadata>

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

type

PotionEffectType

—

Required; an entry without one is dropped.

amplifier

int

0

Raw Bukkit amplifier (0 = level I).

duration

int

infinite

Ticks.

ambient

boolean

false

particles

boolean

false

icon

boolean

true

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

index

int

Metadata index to write.

type

String

EntityDataTypes constant name: BYTE, SHORT, INT, LONG, FLOAT, BOOLEAN, STRING, COMPONENT or ENTITY_POSE.

value

String

Value to write. Numbers accept hex (0x40) as well as decimal.

# A tropical fish's packed colour/pattern variant entity: type: "tropical_fish" metadata: - index: 17 type: "INT" value: "0x0F0F01"

Allowing a skin on a pet

A pet only ever accepts a skin its own blueprint lists:

# blueprints/epic/moohhh.yml skins: - "baby-skin" - "golden-cow"

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

pet-skin-applied

The skin was applied (%pet_name%, %skin_name%).

pet-skin-already-applied

The pet already wears this skin.

pet-skin-not-supported

The pet's blueprint does not list this skin.

pet-skin-conflict

The pet wears a skin this one conflicts with (%conflict_*% — the worn skin).

pet-skin-confirm

The first right-click, asking for confirmation (%confirmSeconds%).

give-skin-success/give-skin-failed

/pets skin delivery result (%skin_name%, %player_name%).

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

%skin_id%

String

The skin id.

%skin_name%

Component

Display name.

%skin_item%

ItemStack?

The skin's item template; nothing when the file declares none.

%skin_pets%/%skin_petNames%/%skin_petCount%

ItemProvider / Component / int

The pets this skin fits — the reverse of each blueprint's skins list.

%skin_conflicts%/%skin_conflictNames%/%skin_conflictCount%

ItemProvider / Component / int

The skins it shuts out, both directions.

%pet_skins%

ItemProvider<PetSkin>

The skins a pet wears, in application order. Chain each entry into the keys above.

%pet_skinNames%

Component

Their names, comma-joined.

%pet_hasSkins%

boolean

Whether the pet wears any skin.

%pet_skinCount%

int

How many it wears.

%pets_skins%

ItemProvider<PetSkin>

Every skin on the server — a catalogue, needing no player context.

%blueprint_skins%

ItemProvider<PetSkin>

The skins the blueprint accepts, resolved to files.

%blueprint_skinIds%

List<String>

The raw ids it lists.

%blueprint_hasSkins%

boolean

Whether the blueprint accepts any skin.

See Placeholders for the surrounding namespaces.

Last modified: 25 September 2026