Astral Realms Documentation Help

Shop Configuration

Every .yml/.yaml file under <plugin-data-folder>/shops/ is one shop. On enable, and again on /shop reload, PaperShopService#load() calls configurationManager().loadFolder(dataPath.resolve("shops"), ShopConfiguration.class), which walks the folder, deserializes each file into one ShopConfiguration, and indexes it by its own id field (not the filename) in an in-memory map. AstralShop ships one example, shops/wood.yml.

Example

id: "wood" title: "Wood shop" slots: - 10 - 11 - 12 - 13 - 14 - 15 - 16 items: oak_wood: buy-price: 5 sell-price: 5 display: material: "OAK_LOG" name: "&6Wood" lore: - "&7This is a piece of wood" buyable: true sellable: true give-item: false buy-actions: - "[console] give %player% oak_log 1" sell-actions: - "[message] not cool to sell."

Top-level fields

Field

Type

Description

id

String

The shop's identifier. Used as the map key shops are stored under and resolved by (e.g. /shop open's <shop> argument, via PaperShopService#findShopById), and read back into every one of its items as their category (see Category).

title

ComponentWrapper

The shop menu's title. A MiniMessage/legacy-formatted string, placeholder-aware like any other ComponentWrapper field.

slots

List<String>

The inventory slot indices this shop's items occupy. Exposed to the shop's menu blueprint as %shop_slots%; AstralShop itself does not consume it — it's the menu blueprint's job to lay items into these slots.

items

Map<String, ShopItem>

The shop's items, keyed by item id. See items entries.

ShopConfiguration also implements ComplexPlaceholder under the shop namespace, chaining id, title, slots, and items (the latter as an item-provider list). See Placeholders.

items entries

Each key under items: becomes one ShopItem; the YAML map key itself (oak_wood above) is captured as the item's id (ShopItemTypeSerializer reads node.key()). Fields, with their ShopItemTypeSerializer defaults:

Field

Type

Default

Description

buy-price

double

0.0

Price to buy one unit, before per-player modifiers. Charged as buy-price * amount.

sell-price

double

0.0

Payout for selling one unit, before modifiers. Paid as sell-price * amount.

display

ItemStackWrapper

— (required)

The item's appearance, and the exact ItemStack compared against a player's inventory/hand to detect what they're buying or selling (ItemStack#isSimilar). See display.

buyable

boolean

true

If false, /shop buy flows and the [buy-shop-item] action reject with ITEM_NOT_BUYABLE.

sellable

boolean

true

If false, sell flows (including sell-hand and sell-all) reject with ITEM_NOT_SELLABLE and skip the item in sell-all sweeps.

give-item

boolean

true

If true, buying builds a fresh stack from the display wrapper (ItemStackWrapper#get()) and calls setAmount(amount) on it with the purchased quantity — overriding any amount: set inside display — then adds it to the buyer's inventory. If false, nothing is given — buy-actions runs instead. See buy-actions and sell-actions.

buy-actions

AstralCore action list

none

Run on a successful buy, in place of the item grant when give-item is false.

sell-actions

AstralCore action list

none

Run on a successful sell, in addition to the normal payout and item removal.

display

display is an ItemStackWrapper block — deserialized by AstralCore's ItemStackWrapperTypeSerializer, the same serializer (and therefore the exact same block shape) used for a menu item's item-stack. See Menu Items — item-stack for the full field reference; the shipped wood.yml only sets material, name, and lore.

Accepted top-level keys are material, copy-from, name, lore, lore-modifiers, append-lore, amount, enchantments, and item-flags, plus the 19 registered item-component keys (armor-trim, attributes, durability, equippable, food, glow, hidden-components, hide-tooltip, instrument, item-model, leather-armor-color, model-data, potion-effect, potion-color, repair-cost, skull, tool, tooltip, tropical-fish-bucket). material is required unless copy-from is set — omitting both fails the load with SerializationException: "Material is required if copy-from is not provided".

display was previously deserialized by the plain ItemStackTypeSerializer, and three of that serializer's conveniences are gone:

  • It can no longer be written as a bare scalar string (display: "OAK_LOG") — the node must be a map.

  • material is resolved with Material.valueOf(raw.toUpperCase()), not through the Minecraft registry, so a namespaced key such as minecraft:oak_log is rejected with Invalid material: minecraft:oak_log.

  • Custom item-provider namespaces (hdb-<id>, ce-<id>) are not accepted either.

material must therefore be a plain Bukkit Material enum name (case-insensitive) or a %placeholder% resolving to a Material.

buy-actions and sell-actions

Both are AstralCore action lists — "[action-id] args" strings run through PaperActionFactory, exactly like any other action list in the ecosystem. See Actions for the full catalogue of action ids (e.g. [console], [message], [give-item]).

  • Buy: if give-item is true, buy-actions is never run — the configured item is granted directly instead. If give-item is false, the item is not granted and buy-actions runs in its place.

  • Sell: sell-actions always runs after a successful sale (payment deposited, items removed from the seller's inventory) — it isn't a substitute for anything and can be omitted entirely.

Both lists run inside the EconomyService withdraw/deposit completion callback. PaperShopService does not reschedule them onto the main thread, so they execute on whatever thread the economy future completes on, alongside the inventory add/remove and the ITEM_BOUGHT/ITEM_SOLD messages. Action authors must not assume a main-thread context.

Category (shop id on items)

ShopItemTypeSerializer reads the enclosing shop's id back (node.parent().parent().node("id")) and stores it on every ShopItem as shopId. ModifierService uses this as the "category" a ShopCategoryModifier targets, letting a per-player price modifier apply to every item in a shop rather than one specific item. See Per-player price modifiers.

/shop open <player> <shop> (and the [buy-shop-item]/[sell-shop-item] action flows) render a shop through MenuService#openShopMenu, which opens AstralCore's MenuContainer menu id shop (container.computeAndOpen(player, "shop", Map.of("shop", configuration))) — the clicked-or-opened ShopConfiguration is handed to the menu blueprint as its shop parameter, resolvable via %shop_*%/%item_*% placeholders.

AstralShop's own menus/ folder ships empty — it does not include a menus/shop.yml blueprint. A menu blueprint with id shop must be authored under the server's menus/ folder (Menu YAML format, see Menu Items) for /shop open to render anything; without one, opening a shop fails with IllegalArgumentException: Menu blueprint with id 'shop' not found.

Reloading

/shop reload (permission shop.command.reload) calls AstralShop#loadConfiguration(), which re-runs, in order:

  1. shops.load() — reloads every file under shops/.

  2. menus.load() — reloads every menu blueprint under menus/.

  3. loadEnum("messages.yml", ShopMessages.class) — reloads messages.yml.

See Commands for the full /shop command reference.

No config.yml

AstralShop has no config.yml — the shop files under shops/ and messages.yml are the only shipped configuration.

Last modified: 25 September 2026