Astral Realms Documentation Help

Overview

AstralShop is the config-driven category shop plugin for AstralRealms. Each shop is a YAML file in the shops/ folder, loaded into a ShopConfiguration and rendered through the AstralCore menu system under menu id shop. Players buy items, sell the item they're holding, or sell everything sellable in their inventory in one go; every transaction settles through AstralCore's EconomyService.

Shops and items

  • A shop (ShopConfiguration) is one YAML file: an id, a title, the inventory slots it fills, and a map of items keyed by item id.

  • Each item (ShopItem) carries its own buy-price and sell-price, a display item stack, independent buyable/sellable toggles, a give-item flag, and buy-actions/sell-actions lists run through the AstralCore action framework.

  • Shops are loaded from the shops/ folder on enable and on /shop reload; the plugin ships one example, shops/wood.yml.

  • Opening a shop hands its ShopConfiguration to the shop menu blueprint as the shop parameter (MenuService#openShopMenuMenuContainer#computeAndOpen(player, "shop", Map.of("shop", configuration))). AstralShop's own menus/ folder ships empty — a menu blueprint with id shop must exist under the server's menus/ folder for /shop open (or the buy/sell actions below) to render anything.

See Shop Configuration for the full YAML shape.

Buying, selling, and sell-all

All three operations run through PaperShopService and settle via AstralCore's EconomyService, resolved through AstralPaperAPI.getService(EconomyService.class) (withdraw on buy, deposit on sell):

  • Buy validates the item is buyable, computes the price (base price times amount, then per-player modifiers), and withdraws it from the buyer. If give-item is true, the item's display stack (cloned, amount set) is added to the buyer's inventory — checked ahead of time for free space, failing with INVENTORY_FULL if there isn't room. If give-item is false, nothing is given; the item's buy-actions list runs instead.

  • Sell (by shop/item id, or by the item currently in the player's hand) validates the item is sellable and that the player holds at least the amount being sold, computes the price, deposits it, removes the items from the inventory, and runs the item's sell-actions (if present).

  • Sell-all iterates every loaded shop — or, when scoped to one shop, just that shop — and every sellable item, sells whatever quantity of it the player currently holds, and reports one combined total (SOLD_ALL_ITEMS) rather than a message per item.

Both buy and sell fire a cancellable event after pricing (and, for buy, after the inventory-space check) but before the economy call — ShopBuyEvent/ShopSellEvent — and stop immediately if the event is cancelled. If EconomyService isn't registered, or the withdraw/deposit call fails or errors, the transaction also aborts and the player is messaged instead of being partially charged.

Per-player price modifiers

ModifierService layers per-player price adjustments on top of a shop item's configured buy-price/sell-price. A ShopModifier is keyed by a Key, scoped to BUY, SELL, or BOTH (ShopModifierScope), typed RELATIVE (a percentage) or ABSOLUTE (a flat amount) (ShopModifierType), and targets either a whole shop category (ShopCategoryModifier, matched by the shop's id), one specific item (ShopItemModifier, matched by the item's id), or — for a plain, untargeted ShopModifier — every item in that scope.

At price-compute time, every one of a player's modifiers that matches the requested scope and target is combined: RELATIVE values sum into a single scalar, ABSOLUTE values are added directly to the running total, and the result is (base + Σabsolute) * (1 + Σrelative). Modifiers live entirely in memory per player — added, removed, and queried through ShopAPI — and are cleared on quit (ModifiersListener); they are never persisted.

Registered services

Getter

Class

Responsibility

modifiers()

ModifierService

Stores and computes per-player ShopModifiers.

shops()

PaperShopService

Loads shops and drives buy/sell/sell-all; implements the ShopService SPI.

menus()

MenuService

Loads and opens the shop menu blueprint via AstralCore's MenuContainer.

Actions, commands, and placeholders

Surface

Summary

[buy-shop-item], [sell-shop-item], [sell-all]

AstralCore action-list entries wired to the buy/sell/sell-all flows above. See Actions.

/shop

open <player> <shop> and reload, gated by shop.command (+ per-subcommand permission). See Commands.

/sellall

Sells everything sellable in the player's inventory (shop.sell.all).

/sellhand (alias /sell)

Sells the item in the player's main hand (shop.sell.hand).

%shop_*%/%item_*%

ShopConfiguration and ShopItem placeholders exposed to shop menus. See Placeholders.

Developer API

  • ShopService — an AstralCore SPI interface — is implemented by PaperShopService and registered on AstralPaperAPI, giving other plugins buy/sell eligibility checks and price lookups (canBeBought/canBeSold, calculateBuyPrice/calculateSellPrice, with and without per-player modifiers) without depending on AstralShop's internal classes.

  • ShopAPI is a static utility for adding, removing, and checking a player's ShopModifiers.

  • ShopBuyEvent and ShopSellEvent (both extending the cancellable ShopEvent) fire before the economy transaction and expose (and allow rewriting) the computed price.

Full signatures in Developer API.

Dependencies

Dependency

Required

Notes

Paper 1.21+

Yes

api-version: '1.21'.

AstralCore

Yes

AstralPaperPlugin base, menus, actions, placeholders, configuration loading, the EconomyService and ShopService SPIs.

AstralShop ships messages.yml and one example shop, shops/wood.yml; it has no config.yml.

Where to go next

Last modified: 25 July 2026