Astral Realms Documentation Help

Pet Food

Food items grant a pet experience when fed to it. Each food item is defined by a food blueprint — an id, an ItemStackWrapper template for the physical item, and an experience amount.

Food blueprints live in food/*.yml in the plugin's data folder (sub-folders are not required — the loader walks the whole food/ directory) and are loaded by FoodBlueprintService#load(), which is called from AstralPets#loadConfiguration() (i.e. on boot and on /pets reload, see Commands). Every .yml/.yaml file under food/ is parsed as one FoodBlueprint; the id used everywhere else (commands, the item supplier, tab completion) is the blueprint's own id field, not the file name. AstralPets ships one example blueprint at src/main/resources/food/apple.yml.

Minimal example

id: "apple" experience: 50 item: material: "apple" name: "<red>Apple" lore: - "<gray>A delicious apple." - "<gray>Give your pet 50 experience points"

Top-level fields

Field

Type

Required

Description

id

String

Yes

Food blueprint identifier. Looked up by FoodBlueprintService#findById(id); used as the food's persistent-data tag and as the key under the pets.food item supplier.

item

ItemStackWrapper

Yes

Standard AstralCore item template — material, name, lore, and the rest of the ItemStackWrapper schema. See Menu Items.

experience

double

Yes

XP granted to the pet per unit consumed. apple.yml grants 50.

FoodBlueprint also implements ComplexPlaceholder under the food namespace, chaining id, item (→ an item placeholder, e.g. %food_item_name%), and experience. This is what resolves %food_item_name% in the give-food-success/give-food-failed messages (see Commands).

item

A standard ItemStackWrapper. FoodBlueprintService#buildItemStack(FoodBlueprint) resolves it against the root placeholder container and stamps the resulting stack with a food_blueprint_id persistent-data string (the blueprint's id) — this tag is what marks a stack as pet food everywhere else in the plugin (FoodBlueprintService#isFoodItem/#fromItemStack).

Feeding a pet

Feeding is triggered by right-clicking a spawned pet entity while holding a food item in the main hand — not by eating the item. PetInteractListener (a packet listener) reads the client's interact-entity packet, applies a 150ms per-player cooldown, and — for a main-hand interact that isn't an attack — fires PetInteractEvent(player, pet, entity).

EntityListener#onEntityInteract (priority LOW) handles that event:

  1. Resolves the item in the player's main hand through foodBlueprints().fromItemStack(itemStack).

  2. If it is a food item:

    • If the pet is already at blueprint().maxLevel(), sends pet-max-level and stops — no item is consumed and the menu does not open.

    • Otherwise, the amount consumed is the player's whole stack while sneaking, or 1 while not sneaking — capped so the resulting XP never overshoots the amount needed to reach the pet's max level (amount = ceil(experienceToMax / foodBlueprint.experience()) when the uncapped total would exceed it).

    • The consumed amount is removed from the main-hand stack.

    • PetService#gainExperience(player, pet, entity, foodBlueprint.experience() * amount) is called, and 5 HEART particles spawn at the pet's location.

  3. If the held item is not food, the interact instead opens the pet menu (menus().openPetMenu(player, pet)) — right-clicking an empty-handed pet is how its menu is opened.

PetService#gainExperience:

  • Fires PetGainExperienceEvent(pet, experienceGained, pet.experience() + experienceGained). It isn't Bukkit-Cancellable, but a listener can zero out (or otherwise adjust) experienceGained via its setter; if the value is <= 0 afterward, no XP is applied.

  • If the pet is already at max level, sends pet-max-level and returns.

  • Otherwise loops: while there's enough accumulated experience to clear the next level's threshold (pet.experienceForLevel(nextLevel)) and the pet isn't at maxLevel yet, it fires PetLevelUpEvent(pet, oldLevel, newLevel) (also not Cancellable, but newLevel can be rewritten by a listener), applies the level, and sends pet-level-up.

  • At max level, experience is reset to 0. The pet's effects are removed and reapplied (see Pet Effects), and the entity's nametag is refreshed.

  • Displays a floating experience indicator: a short-lived, player-only PacketTextDisplay spawned just in front of the player's view, rendering experience-gain-indicator ("%experience%/%experienceToNextLevel% XP") and auto-removed ~700ms later by a repeating task.

Consuming food items directly

FoodListener#onItemConsume listens for the vanilla PlayerItemConsumeEvent and cancels it whenever foodBlueprints().isFoodItem(event.getItem()) is true. Since food templates are typically built on edible vanilla materials (apple.yml uses apple), this stops a player from just eating the item for its normal hunger/saturation effect — it must be fed to a pet through the interact flow above to have any effect.

Food item supplier

PetsFoodItemSupplier is registered as an ItemStackSupplier under the namespace pets.food (AstralPaperAPI.registerItemStackSupplier("pets.food", this.foodItemSupplier)), alongside the vanilla/hdb/ce suppliers described in ItemStackSupplier. Its completions() set is rebuilt on reload from every loaded food id as Key.key("pets.food", id) — i.e. pets.food:<foodId> (pets.food:apple for the shipped example). get(String id) and get(Player, Key, int amount) both resolve through FoodBlueprintService#findById#buildItemStack, and keyOf(ItemStack) reverse-looks-up a stack's food id via its persistent-data tag. This is what lets other systems reference a food blueprint as an item source (e.g. pets.food-apple as an ItemStackWrapper material, or through /give), the same way pets:<blueprintId> exposes pet-spawn items under the pets namespace.

Giving food

/pets food <player> <blueprint>

Permission: pets.give.food. Builds the food item via FoodBlueprintService#buildItemStack(FoodBlueprint) and delivers it through AstralCore's mailbox (MailboxService#giveOrAdd) so it reaches the target even without free inventory space. Reports give-food-success/give-food-failed to the sender. <blueprint> is resolved by FoodBlueprintContextResolver against foodBlueprints().findById(id), with @foodBlueprints tab-completing every loaded food id. See Commands for the full command reference.

/pets food Steve apple

Food conversion configuration (currently unused)

config.yml still ships a food-conversion.original-item key, and PetsMessages still declares NOTHING_TO_CONVERT/CONVERSION_SUCCESS (nothing-to-convert/conversion-success in messages.yml):

food-conversion: "original-item": "apple"

These are leftovers from a ConvertPetFoodAction menu action (mapped custom items in a player's inventory to food blueprints and converted them in place) that was removed in a later refactor along with its foodConversion() accessor on PetsConfiguration — the record no longer has a field for this key, so it is silently ignored by the config loader, and nothing in the current plugin reads food-conversion or fires either message. Treat both as dead configuration unless the action is reintroduced.

Last modified: 25 July 2026