Actions
AstralPlayerShops#onEnable registers seven AstralCore actions — the [action-id] <argument> ... bracket syntax usable anywhere the configuration layer accepts an actions: list (menu items, dialog buttons, event hooks).
Six are registered with the plain, plugin-scoped registerAction:
toggle-playershops-recap is registered separately with registerActionGlobally. AstralCore's action registry checks a plugin's own registrations first and falls back to the shared global registry, so the six registerAction entries only resolve inside menu/dialog configuration that AstralPlayerShops itself parses, while toggle-playershops-recap resolves from any plugin's action list — it can be wired into a cross-plugin settings menu alongside other AstralSync-backed notification toggles.
Every argument is declared as a PlaceholderWrapper record component and resolved with context.parseWrapper(...) (or the equivalent wrapper.get(context)) when the action runs. Simple types (Integer, Boolean, ShopType) accept a literal or a %placeholder%; object types (Location, ItemStack, PlayerShop, UUID) can only resolve through a %placeholder% that the caller's context already exposes as that object — e.g. %shop% (a PlayerShop, exposed wherever a shop object is placed in the menu/dialog context — see Placeholders), or %item%/%location%, which the shop-creation flow puts directly into the "playershops-creation" dialog's context. The action always acts on context.executor() — there is no separate "target player" argument.
create-playershop
Creates a shop on the container the executor is standing at, wrapping ShopService#create.
Arg | Type | Description |
|---|---|---|
| Location placeholder | The container's block location. |
| ItemStack placeholder | The item the shop trades. Cloned and reduced to a stack of 1 before creation. |
| ShopType placeholder |
|
| Integer placeholder | Price per unit. |
| Boolean placeholder | Whether other town members with |
| Boolean placeholder | Whether proceeds route through the town's bank account instead of the owner's own balance. |
If town-economy is true, the action looks up the town at location and requires the executor to hold BANK_WITHDRAW there; if there is no town or the check fails, it sends NO_WITHDRAWAL_PERMISSION and stops — no shop is created, and ShopService#create is never called. If town-economy is false this check is skipped at the action level, but ShopService#create unconditionally requires the block to sit inside a town regardless of town-shared/town-economy (sends AstralTown's ERROR_OCCURRED otherwise) and separately re-validates the owner's shop limit, that no shop already exists at that location, and fires the cancellable PlayerShopCreateEvent — see Shops and Developer API. On success the action sends SHOP_CREATED with the item and %price% substituted; on an unexpected exception it logs the error and sends CREATION_FAILED. If ShopService#create itself rejects the shop (limit reached, duplicate location, cancelled event), it has already messaged the player and the action sends nothing further.
edit-playershop
Updates an existing shop's price, type, town-shared flag, and economy routing, wrapping ShopService#update.
Arg | Type | Description |
|---|---|---|
| PlayerShop placeholder | The shop to edit, e.g. |
| ShopType placeholder | New |
| Integer placeholder | New price per unit. |
| Boolean placeholder | New town-shared flag. |
| Boolean placeholder | New economy-routing flag. |
Requires shop.hasPermission(executor) — true for the shop's owner, for a playershops.admin holder, or (since the check is the same one interaction/edit/manage all share) for any town member holding USE_PLAYER_SHOPS if the shop is already town-shared. Failing that sends NOT_OWNER. If town-economy is true, it separately requires the executor to hold the town's BANK_WITHDRAW at the shop's location (NO_WITHDRAWAL_PERMISSION otherwise) — same guard as create-playershop. Passing both checks, the action applies an optimistic update: it writes the new price/type/town-shared directly onto the in-memory PlayerShop, and either resolves the shop's town UUID as the new economyAccount (when town-economy is true) or clears economyAccount to null (routing sales to the owner's own balance) — all before the database write completes. If ShopService#update fails, all four fields are rolled back to their pre-edit values, the error is logged, and SHOP_UPDATE_FAILED is sent. On success, ShopService#refreshShop runs (updates the sign/hologram) and SHOP_UPDATE_SUCCESS is sent with the shop placeholder.
buy-playershop-item
Buys amount units from shop, on behalf of the executor.
Arg | Type | Description |
|---|---|---|
| PlayerShop placeholder | The shop to buy from, e.g. |
| Integer placeholder | Quantity to buy. |
Calls ShopService#buy(player, shop, shop.location().getBlock(), amount) directly — the same overload InteractionListener drives when a non-owner right-clicks a BUY-type shop, which opens the "playershops-buy" menu that this action is typically wired into. The action performs no clamping itself: a non-positive amount is a silent no-op, and ShopService#buy re-validates the chest still holds at least amount, transfers price * amount from the buyer to shop.economyAccount(), re-checks stock again on the main thread, removes the items (custom-item aware via ContainerUtils), delivers them to the player (falling back to the mailbox if their inventory is full), refreshes the sign, sends PURCHASE_SUCCESS, notifies the owner with BUY_NOTIFICATION if their recap preference is enabled, and records the sale — see Shops and Activity Recaps.
sell-playershop-item
Sells up to amount units of shop's item, out of the executor's inventory, into shop.
Arg | Type | Description |
|---|---|---|
| PlayerShop placeholder | The shop to sell into, e.g. |
| Integer placeholder | Quantity to sell. |
Unlike buy-playershop-item, this action clamps before calling the service: it first caps amount down to how many matching items the executor's inventory actually holds, then — back on the main thread — checks the chest has enough empty capacity for that (already inventory-clamped) amount; if it doesn't, it sends CHEST_FULL and aborts entirely rather than selling a partial quantity. Only then does it call ShopService#sell(player, shop, chestBlock, finalAmount) — the same overload the "playershops-sell" menu uses — which independently re-validates chest space and inventory count before transferring funds and items.
open-playershops-container
Opens the shop's backing container inventory for the executor.
Arg | Type | Description |
|---|---|---|
| UUID placeholder | The shop's |
Looks up the shop by UUID via ShopService#findByUniqueId, resolves the block at the shop's stored coordinates in the executor's current world, and opens its inventory if that block is a Container (chest, barrel, shulker box, etc.). If the shop id doesn't resolve, or the block is no longer a container, sends UNEXPECTED_ERROR. There is no ownership check in the action itself — gate access through the menu button's own requirements (this action is the natural fit for a management button inside the "playershops-main" menu, which is only opened for players shop.hasPermission already approved — see InteractionListener).
open-playershops-transactions
Opens the shop's transaction history menu for the executor.
Arg | Type | Description |
|---|---|---|
| UUID placeholder | The shop's |
Looks up the shop by UUID via ShopService#findByUniqueId and, if found, calls MenuService#openTransactionHistoryMenu, which loads the shop's transactions and opens the "playershops-history" menu with shop and transactions in its context. If the shop id doesn't resolve, sends UNEXPECTED_ERROR. As with open-playershops-container, there is no permission check in the action itself.
toggle-playershops-recap
Toggles the executor's away-recap notification preference. No arguments.
Registered globally (see above), so this is callable from any plugin's menu/dialog configuration, not just AstralPlayerShops's own. Looks up the executor's PSPlayerData via SyncAPI#findData; if present, flips recapEnabled and sends RECAP_ENABLED or RECAP_DISABLED to match the new state. If the player's synced data isn't loaded, sends UNEXPECTED_ERROR instead. The resulting state is exposed back for rendering a toggle button via %playershops_recapNotification% — see Placeholders and Activity Recaps.
Action Summary Table
ID | Scope | Arguments | Effect |
|---|---|---|---|
| Local |
| Creates a shop on the given container. |
| Local |
| Updates an existing shop's price/type/sharing/economy routing. |
| Local |
| Buys |
| Local |
| Sells up to |
| Local |
| Opens the shop's backing container inventory. |
| Local |
| Opens the shop's |
| Global | (none) | Toggles the executor's recap notification preference. |
See also
AstralCore Actions — the
[action-id] <argument> <param=value>syntax and globaldelay/asyncparameters shared by every action on the network.Shops — the creation, buy/sell, and editing mechanics these actions wrap.
Activity Recaps — the recap preference
toggle-playershops-recapflips.Placeholders —
%shop%/%shop_*%,%playershops_recapNotification%, and the other context placeholders these actions consume.Developer API —
PlayerShopCreateEventand friends, fired underneathcreate-playershop.Commands —
playershops.admin, which also bypasses theedit-playershopownership check.