Overview
AstralPets is the collectible pet system for the AstralRealms network. Players acquire pets as items, consume the item to add the pet to their collection, feed it food to level it up, and equip (activate) up to a configurable number of pets at once to receive passive bonuses — stats, periodic money, potion effects, storage, no-fall, and modifiers to AstralShop / AstralJobs / AstralRotatingShop. One equipped pet can also be spawned: rendered in the world, following the player, and (for some blueprints) rideable.
Under the hood a spawned pet is driven by a hybrid, packet-based entity engine rather than a normal Bukkit mob. Each PetEntity pairs:
a real, server-side vanilla mob (a
Pig- orParrot-derived NMS entity chosen byEntityTags.FLYING) that is actually added to the world and drives physics, collision and goal-based pathfinding — including a customFollowOwnerGoalbuilt on vanillaPathNavigation(GroundPathNavigation/FlyingPathNavigation/WallClimberNavigationdepending on the entity type) — witha separate, fully plugin-controlled packet entity (
PacketEntity) that is the only thing players ever actually see: it owns its own entity ID, per-viewer spawn/metadata/destroy packets, custom equipment and a floating name tag.
BaseEntityPacketListener (a PacketEvents listener) is what stitches the two together: every packet the real mob would normally broadcast (spawn, metadata, equipment, sound, status, NBT) is cancelled outright, while its movement packets (teleport, relative move, rotation, head/look, passengers, velocity) are rewritten to use the packet entity's ID and resent — so the client only ever perceives the fake, fully controlled entity moving around.
Pets, blueprints and player data
Concept | Type | Notes |
|---|---|---|
Pet |
| An owned instance: a random |
PetBlueprint |
| The template: |
PetRarity |
|
|
PetPlayerData |
| Per-player, synced via AstralSync: the full |
A pet is added to PetPlayerData by right-clicking with its item (PetListener#onConsume), which consumes the item and calls PetPlayerData#addPet. The reverse — [pickup-pet] — rebuilds the item stack via PetService#buildItemStack and removes the pet from the player's data.
Leveling
Pets gain experience only by being fed: right-clicking a spawned pet with a registered food item fires PetInteractEvent, and EntityListener looks the item up against foodBlueprints() and calls PetService#gainExperience(player, pet, entity, foodBlueprint.experience() * amount).
The XP required for the pet's next level is
Pet#experienceForLevel(level), evaluated from its rarity'slevelExpression(e.g. the shippedcommonrarity is32+((level-1)^2/4)).gainExperienceloops applying level-ups (firingPetLevelUpEventeach time) while accumulated XP covers the next level's requirement, up to the blueprint'smaxLevel; at max level further food is rejected withPET_MAX_LEVELand XP is not accumulated.Every level-up recalculates the pet's storage size from its
storageeffect and re-applies all of the pet's effects (Pet#removeEffect+Pet#applyEffect) so level-scaled values immediately update.A short-lived floating text display shows XP gained above the player's head after every feed.
Passive effects
Effects live on the blueprint as a list of WrappedPetEffect (display text, next-level text, a Map<Integer, Double> of cumulative values per level, a maxValue/maxLevel, and the underlying PetEffect). Every PetEffect implementation is level-scaling: value(level) sums every entry at or below the pet's current level. Effects are (re)applied to the owner on equip (PetEquipEvent), on player-data load, and after every level-up, and removed on unequip (PetUnequipEvent), quitting, or being fed past max level; unequipping one pet re-applies the remaining active pets afterwards so overlapping effect types on other equipped pets aren't lost.
Registered id | Class | Target system | Behavior |
|---|---|---|---|
|
| AstralStats | Adds a |
|
| Core economy | Deposits money on a per-effect |
|
| Bukkit | Grants an infinite |
|
| — | Sizes the pet's own storage inventory, openable via |
|
| — | Unlocks riding once its cumulative value reaches 1 and the blueprint's |
|
| — | Cancels fall damage for the owner while any active pet has a positive value at its level. |
|
| AstralShop | Adds an item- or category-scoped |
|
| AstralJobs | Adds a |
|
| AstralRotatingShop | Adds a percentage modifier via |
See Pet Effects for the full per-effect configuration shape.
Active vs. equipped pets
Active (equipped) pets contribute their passive effects regardless of whether one is currently spawned.
[equip-pet]enforcesmax-active-pets(config.yml, default1, overridable per permission — the shipped default grantsgroup.roturiera cap of2) and refuses to equip two pets that share the same blueprint (CANNOT_EQUIP_IDENTICAL_PET).Selected/spawned is a single pet —
PetPlayerData#selectedPetId— that is actually rendered in the world.[spawn-pet]despawns any pet already spawned for that player first (EntityService#spawncallsfindByPlayer(player).forEach(PetEntity::remove)), so only one pet can follow the player at a time even if several are active.Spawning is gated by the WorldGuard integration: if the
petsflag denies the location,[spawn-pet]aborts withPET_SPAWN_DENIED_HERE.[ride-pet]mounts the owner on the currently spawned pet if its blueprint isrideableand itsrideableeffect value at the current level is at least 1.
Menus & commands
/pets(aliasespet,familiers,familier) opens thepets-mainmenu by default; give/food/reset/ reload are admin subcommands under the same base command. See Commands./pokedex(aliasesanimalerie,bestiaire) opens thepokedexmenu, populated from a max-levelPetinstance cached per loaded blueprint (BlueprintService#cachedPets) — a bestiary of every blueprint regardless of ownership.Renaming runs through a dialog whose title/body/field/button text comes from
rename-dialoginconfig.yml, enforcingmax-pet-name-lengthbefore invoking[rename-pet].
See Pet Blueprints for authoring blueprints and Pet Food for food items, the feeding flow, and the food item supplier. (config.yml's food-conversion key and the nothing-to-convert/conversion-success messages are leftovers from a removed conversion feature — they are currently dead and read/fired by nothing in the plugin.)
WorldGuard integration
If WorldGuard is present, WorldGuardHook registers a custom StateFlag named pets (default state ALLOW) during onLoad, and registers a SessionManager handler (PetsWGSessionHandler) during onEnable. WorldGuardHook.isPetsAllowed(player, location) returns false only when the flag resolves to DENY for that location — [spawn-pet] is the only caller, so the flag blocks spawning a pet in a region, not owning or equipping one. WorldGuard absent (or the flag failing to register) always allows spawning.
Requirements
Dependency | Required | Notes |
|---|---|---|
Paper 1.21+ | Yes |
|
AstralCore | Yes |
|
packetevents | Yes | Drives the packet entity engine and the |
AstralStats | Yes | Backs the |
AstralShop | Yes | Backs the |
AstralJobs | Yes | Backs the |
AstralRotatingShop | Yes | Backs the |
AstralSync | Functional | Not listed in |
WorldGuard | Optional (softdepend) | Registers the |
WorldEdit | Optional (softdepend) | Used by WorldGuard's location adapter. |
Architecture at a glance
Getter | Class | Responsibility |
|---|---|---|
|
| Loads pet blueprints from the |
|
| Loads food items and their XP values. |
|
| XP/level-up logic, building pet |
|
| Packet-entity lifecycle: spawn/despawn, viewer & world tracking, riding, spawn-location resolution. |
|
|
|
|
| The rename dialog. |
Player-triggered behavior is exposed as AstralCore actions rather than direct API calls:
Registered name | Class | Purpose |
|---|---|---|
|
| Marks a pet active, subject to |
|
| Marks a pet inactive and removes its effects. |
|
| Spawns the pet's packet entity (WorldGuard-gated) and sets it as selected. |
|
| Removes the spawned packet entity and clears the selected pet. |
|
| Converts a pet back into an item stack in the player's inventory/mailbox and removes it from their data. |
|
| Opens the pet's storage inventory (no-op if its storage size is 0). |
|
| Mounts the owner on the currently spawned pet if it's rideable at its level. |
|
| Renames a pet, enforcing |
For configuration keys and defaults see Configuration; for the full command surface see Commands; for placeholders see Placeholders; for static/programmatic APIs see Developer API.