Astral Realms Documentation Help

Configuration

plugins/AstralPlayerShops/ holds config.yml (recap window, per-player shop limits, sign lines, the container/item restrictions, and the periodic sign-refresh task) and database.properties (the JDBC/HikariCP connection pool). Both are loaded into the PSConfiguration record on startup, and config.yml is re-parsed by /ps reload (playershops.reload) — but not every value takes effect immediately once re-parsed, and database.properties isn't re-read at all; see Reloading.

config.yml is parsed with Configurate: a camelCase Java field maps to a kebab-case YAML key (recapIntervalrecap-interval, chunksPerTickchunks-per-tick), and the Duration field accepts shorthand like 24h.

config.yml

recap-interval: "24h" limits: default: 0 permissions: example.permission.one: 5 example.permission.two: 10 sign: - "Seller: %shop_ownerName%" - "Stock: %stock%" - "Price: $%shop_price%" - "%shop_typeFormatted%" blacklisted-containers: - barrel - blast_furnace - brewing_stand - chest - dispenser - dropper - furnace - hopper - smoker - shulker_box ignored-items: - "chest" - "trapped_chest" - "hopper" - "oak_sign" - "spruce_sign" - "birch_sign" - "jungle_sign" - "acacia_sign" - "dark_oak_sign" - "crimson_sign" - "warped_sign" - "item_frame" - "painting" - "armor_stand" updates: interval-ticks: 100 chunks-per-tick: 20 enabled-worlds: - "world" enabled-groups: - "build"

Field

Type

Description

recap-interval

Duration

Lookback window for /ps recap. See Recap window.

limits

object

Per-player concurrent shop cap. See Shop limits.

sign

Component list (≤ 4 lines)

Lines rendered on a shop's sign. See Sign lines.

blacklisted-containers

Set<Material>

Container types that can never become a shop. See Container & item restrictions.

ignored-items

Set<Material>

Item types that can never be used as shop stock. See Container & item restrictions.

updates

object

Tuning for the periodic sign/stock refresh task. See Sign & stock refresh.

Recap window

recap-interval (default 24h) is the lookback window for the on-demand /ps recap command (see Commands): it computes recap entries from System.currentTimeMillis() - recap-interval up to now. It only applies to that command — the automatic "while you were away" recap shown on login instead uses the player's last-logout timestamp, not recap-interval. See Activity Recaps for both flows.

Shop limits

Field

Type

Description

limits.default

int

Floor limit applied to every player, regardless of permissions. Shipped as 0 — a player with no matching permission below cannot create any shop.

limits.permissions

Map<String, Integer>

Permission node → max concurrent shops.

The effective limit for a player is the highest value among limits.default and every permission node in limits.permissions the player holds — lower-tier permissions don't cap a player who also holds a higher one:

limit(player) = max(default, max { value for (perm, value) in permissions if player.hasPermission(perm) })

ShopService.create checks this before creating a shop: if the player's current shop count (from the database) is >= the limit, creation is rejected with LIMIT_REACHED (placeholder %limit%) and no dialog opens.

Sign lines

sign is a list of up to 4 component-wrapper lines (MiniMessage/legacy strings, resolved through AstralCore's placeholder wrapper system) rendered onto the shop's wall sign. It refreshes on shop creation, after every buy/sell, and on the periodic sign-refresh tick (see Sign & stock refresh). A 5th line is rejected — a vanilla sign only has 4 lines — and the refresh throws instead of partially updating.

Placeholders resolve through a PlaceholderContainer scoped just to this render: the shop itself under the shop namespace, plus a stock value computed live from the backing container. The shipped file uses:

Placeholder

Description

%shop_ownerName%

Owner's player name, captured at shop creation (not live-updated on a later name change).

%shop_price%

Price per item.

%shop_typeFormatted%

The BUY_TYPE/SELL_TYPE message component for the shop's ShopType.

%stock%

For a BUY shop: items currently available in the container to buy (or the OUT_OF_STOCK message at 0). For a SELL shop: empty slots left to sell into (or the FULL message at 0).

Any other shop-namespaced field also resolves here — %shop_id%, %shop_owner%, %shop_material%, %shop_type%, %shop_item%, %shop_location%, %shop_townShared%, %shop_townEconomy% — see Shops for what each one carries.

Container & item restrictions

Field

Type

Description

blacklisted-containers

Set<Material>

Container block types that can never be turned into a shop.

ignored-items

Set<Material>

Item types that can never be used as a shop's stock item.

blacklisted-containers is checked in two places:

  • Shop creation — sneak-right-clicking a container with an item in hand only opens the creation dialog if the container's type is not in this set.

  • Placement near a shop — placing a new container block normally triggers a check that cancels the placement (NO_PERMISSION_PLACE_NEAR) if it's adjacent to a shop the placer doesn't have permission on. Blacklisted container types skip this check entirely — they can be placed next to someone else's shop freely, since they could never become a competing shop themselves.

ignored-items is checked only at creation time: if the item held while sneak-right-clicking a container is in this set, the creation dialog never opens. The shipped list blocks every placeable sign type (a shop can't be stocked with the signs it renders itself onto), chest/trapped_chest/hopper (containers as stock), and item_frame/painting/armor_stand (placeable entities, not stackable block/item stock).

Sign & stock refresh

Field

Type

Default

Description

interval-ticks

int

100

Ticks between runs of the periodic refresh task, after a fixed 100-tick (5s) initial delay from startup.

chunks-per-tick

int

20

Max chunks the task processes per run.

enabled-worlds

Set<String>

["world"]

Worlds the refresh task (and chunk-load hologram spawning) operates in.

enabled-groups

Set<String>

["build"]

AstralCore server groups where the refresh task runs at all.

updates drives ChunkTickTask, the repeating task that refreshes every shop's sign (stock counts, etc.) without waiting for a buy/sell to trigger it:

  • The task only exists on a server whose group (AstralPaperAPI.serverInformation().group()) is in enabled-groups — otherwise ShopService never creates or schedules it, and that server gets no periodic refresh at all.

  • When it runs, each pass round-robins through the currently loaded chunks of every world in enabled-worlds, ticking up to chunks-per-tick chunks that haven't been processed in the current cycle (tracked per world); once every loaded chunk has been processed the cycle resets. A chunk that throws during processing is still marked processed, so one broken chunk can't stall the rotation. Chunks that unload are evicted from the processed set to avoid unbounded growth.

  • enabled-worlds also gates item-display (hologram) spawning on chunk load — ShopService.loadChunk (called from ChunkLoadEvent and once per already-loaded chunk during startup) is a no-op for worlds outside this set. Shop creation, buying, and selling are not restricted to enabled-worlds — only the sign-refresh task and hologram spawning are.

  • Fallbacks live in the Updates record's compact constructor: a non-positive interval-ticks or chunks-per-tick, or a missing enabled-worlds/enabled-groups key, silently falls back to 100/20/[world]/[build]. Omitting the whole updates: block does the same, applied one level up in PSConfiguration.

database.properties

jdbcUrl=jdbc:mariadb://localhost:3306/playershops?useSSL=false driverClassName=org.mariadb.jdbc.Driver dataSource.user=testuser dataSource.password=test623 poolName=AstralPlayershops

A standard HikariCP/JDBC properties file, copied from the JAR the first time the plugin starts (an existing file on disk is never overwritten). For the connection keys (jdbcUrl, driverClassName, dataSource.user/password, poolName), see the AstralCore database docs — AstralPlayerShops reuses the same DatabaseService. The shipped file points at a playershops schema with pool name AstralPlayershops.

Reloading

/ps reload (playershops.reload) calls loadConfiguration(), the same method run on startup: it re-copies database.properties (a no-op once the file exists), reloads config.yml into a fresh PSConfiguration, reloads messages.yml, and reloads the menu/dialog definitions. It does not reconnect the database, and it does not reconstruct ShopService — both are only built once, in onEnable:

Applies immediately

Requires a full restart

recap-interval, limits, sign, blacklisted-containers, ignored-items — read fresh off plugin.configuration() on every use

updates (interval-ticks, chunks-per-tick, enabled-worlds, enabled-groups) — snapshotted into ChunkTickTask and the scheduled task's period/gate once, when ShopService is constructed in onEnable. See Sign & stock refresh.

database.propertiesDatabaseService.connect() runs once in onEnable.

See Commands for the command surface.

Last modified: 25 July 2026