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: anid, atitle, the inventoryslotsit fills, and a map ofitemskeyed by item id.Each item (
ShopItem) carries its ownbuy-priceandsell-price, adisplayitem stack, independentbuyable/sellabletoggles, agive-itemflag, andbuy-actions/sell-actionslists 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
ShopConfigurationto theshopmenu blueprint as theshopparameter (MenuService#openShopMenu→MenuContainer#computeAndOpen(player, "shop", Map.of("shop", configuration))). AstralShop's ownmenus/folder ships empty — a menu blueprint with idshopmust exist under the server'smenus/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. Ifgive-itemistrue, the item'sdisplaystack (cloned, amount set) is added to the buyer's inventory — checked ahead of time for free space, failing withINVENTORY_FULLif there isn't room. Ifgive-itemisfalse, nothing is given; the item'sbuy-actionslist runs instead.Sell (by shop/item id, or by the item currently in the player's hand) validates the item is
sellableand 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'ssell-actions(if present).Sell-all iterates every loaded shop — or, when scoped to one shop, just that shop — and every
sellableitem, 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 |
|---|---|---|
|
| Stores and computes per-player |
|
| Loads shops and drives buy/sell/sell-all; implements the |
|
| Loads and opens the |
Actions, commands, and placeholders
Surface | Summary |
|---|---|
| AstralCore action-list entries wired to the buy/sell/sell-all flows above. See Actions. |
|
|
| Sells everything sellable in the player's inventory ( |
| Sells the item in the player's main hand ( |
|
|
Developer API
ShopService— an AstralCore SPI interface — is implemented byPaperShopServiceand registered onAstralPaperAPI, 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.ShopAPIis a static utility for adding, removing, and checking a player'sShopModifiers.ShopBuyEventandShopSellEvent(both extending the cancellableShopEvent) fire before the economy transaction and expose (and allow rewriting) the computedprice.
Full signatures in Developer API.
Dependencies
Dependency | Required | Notes |
|---|---|---|
Paper 1.21+ | Yes |
|
AstralCore | Yes |
|
AstralShop ships messages.yml and one example shop, shops/wood.yml; it has no config.yml.
Where to go next
Shop Configuration — the
ShopConfiguration/ShopItemYAML shape.Commands —
/shop,/sellall,/sellhandin full.Actions —
[buy-shop-item],[sell-shop-item],[sell-all].Placeholders — the
%shop_*%/%item_*%namespaces.Messages — the message-key catalogue.
Developer API —
ShopService,ShopAPI, and the buy/sell events.