API Reference
Player Shops API Reference
Section titled “Player Shops API Reference”Event Bus
Section titled “Event Bus”Other mods can subscribe to ShopEventBus.getInstance() to react to shop activity.
Event types
Section titled “Event types”| Event | Fires when |
|---|---|
ShopCreateEvent | A new shop is persisted (player or admin) |
ShopDeleteEvent | A shop is removed (carries reason) |
ShopBuyEvent | A buy transaction completes |
ShopSellEvent | A sell transaction completes |
RentalStartedEvent | A rental slot is rented (fixed) or an auction won + payment cleared |
RentalExpiredEvent | A rental ended (window elapsed, force-expire, release-early) - carries Reason enum |
RentalBidPlacedEvent | A bid lands on an auction |
RentalAuctionWonEvent | Auction finalised with a paying winner |
Subscribing
Section titled “Subscribing”import com.kyuubisoft.shops.event.ShopEventBus;import com.kyuubisoft.shops.event.ShopCreateEvent;
ShopEventBus.getInstance().subscribe(ShopCreateEvent.class, evt -> { UUID shopId = evt.getShopId(); UUID owner = evt.getOwnerUuid(); String name = evt.getName(); // your handler});The bus is singleton, plugin-survival, and fires on the world thread. Keep handlers fast.
Service classes
Section titled “Service classes”The plugin exposes several services via ShopPlugin.getInstance().
ShopManager
Section titled “ShopManager”ShopPlugin plugin = ShopPlugin.getInstance();ShopManager mgr = plugin.getShopManager();
ShopData shop = mgr.getShop(shopId);List<ShopData> ownedByPlayer = mgr.getShopsByOwner(playerUuid);List<ShopData> all = mgr.getAllShops();ShopData rentalShell = mgr.getShopByRentalSlotId(slotId);ShopService
Section titled “ShopService”The transactional layer. Returns typed result objects so your code can branch on specific failures.
ShopService svc = plugin.getShopService();
// Create a player shopCreateShopResult r = svc.createPlayerShop( playerRef, player, name, category, description, worldName, x, y, z);
// Buy with reasoned failurePurchaseResult pr = svc.purchaseItemWithReason( buyerRef, shopId, slotIndex, quantity);if (pr.isSuccess()) { /* ... */ }else if (pr.getErrorKey().equals("shop.buy.fail.no_stock")) { /* ... */ }
// Sellboolean ok = svc.sellItem(sellerRef, shopId, itemId, quantity);
// Buy/extend a directory listingListingResult lr = svc.purchaseListing(player, playerRef, shopId, days);RentalService
Section titled “RentalService”RentalService rs = plugin.getRentalService();
RentalSlotData slot = rs.getSlot(slotId);List<RentalSlotData> all = rs.getAllSlots();List<RentalSlotData> renterRentals = rs.getRentalsForPlayer(playerUuid);
// Programmatic createRentalSlotData created = rs.createFixedSlot( "MyRental", worldName, x, y, z, rotY, pricePerDay, maxDays, stationId);
// Programmatic actionsRentResult rr = rs.rentSlot(playerRef, player, slotId, days);BidResult br = rs.placeBid(bidderRef, player, slotId, amount);boolean expired = rs.releaseEarly(playerRef, slotId);boolean deleted = rs.deleteSlot(slotId);MailboxService
Section titled “MailboxService”MailboxService mb = plugin.getMailboxService();
mb.createItemMail(ownerUuid, shopId, shopName, buyerName, itemId, qty, bsonMeta);mb.createMoneyMail(ownerUuid, shopId, shopName, amount, "buyback");int unclaimed = mb.countUnclaimedForPlayer(playerUuid);ShopEconomyBridge
Section titled “ShopEconomyBridge”Reflection-based wrapper over Core’s ExternalEconomyBridge (preferred) or VaultUnlocked direct.
ShopEconomyBridge eco = plugin.getEconomyBridge();if (eco.isAvailable()) { boolean has = eco.has(playerUuid, 100); boolean withdrew = eco.withdraw(playerUuid, 50); boolean deposited = eco.deposit(playerUuid, 50); String formatted = eco.format(123.45); // "123.45 Gold" String currency = eco.getCurrencyName(); // "Gold"}Permission helpers
Section titled “Permission helpers”PermissionLimits is the wildcard-aware permission resolver.
import com.kyuubisoft.shops.util.PermissionLimits;
int maxShops = PermissionLimits.resolveMaxShops(player, configDefault);int maxItems = PermissionLimits.resolveMaxItems(player, configDefault);int maxRentals = PermissionLimits.resolveMaxRentals(player, configDefault);
boolean hasWildcard = PermissionLimits.hasWildcard(player);boolean hasPermanent = PermissionLimits.hasExplicit(player, "ks.shop.list.permanent");hasExplicit returns false when the wildcard sentinel matches, so it only fires for genuine explicit grants.
Database schema
Section titled “Database schema”Tables created with IF NOT EXISTS on init. Both SQLite + MySQL dialects are shipped.
| Table | Purpose |
|---|---|
shop_shops | Shops (player + admin). Columns include id, name, type, owner_uuid, position, listed_until, featured_until, open, packed, NPC fields, category, tags, rating + revenue counters, rental_slot_id + rental_expires_at mirrors |
shop_items | Per-shop item config: itemId, prices, stock, slot index, category, daily limits, BSON metadata |
shop_transactions | Buy/sell history per shop |
shop_ratings | Per-rater ratings (composite key) |
shop_notifications | Stored notifications per player UUID |
shop_mailbox | Pending item + money mails |
shop_rental_slots | Rental slot config + runtime: position, mode, max_days, price_per_day or auction params, rented_by, rented_until, auction_ends_at, current high bidder |
shop_rental_bids | Bid history (slot_id, bidder, amount, timestamp). Indexed by (slot_id, timestamp DESC) |
shop_player_flags | Per-player flags. Currently: free_listing_used (BOOLEAN). Persists across shop delete |
Source-of-truth columns vs mirrors
Section titled “Source-of-truth columns vs mirrors”| Concern | Authoritative | Mirror |
|---|---|---|
| Rental expiry | shop_rental_slots.rented_until | shop_shops.rental_expires_at |
| Listing window | shop_shops.listed_until | (none) |
For DB hand-edits while testing: stop server first, edit only the authoritative column, start server.
Configuration access
Section titled “Configuration access”ShopConfig cfg = plugin.getShopConfig();ShopConfig.ConfigData data = cfg.getData();
int maxShops = data.playerShops.maxShopsPerPlayer;boolean rentalEnabled = data.rentalStations.enabled;int delaySec = data.npc.spawnDelaySecondsOnJoin;
cfg.reload(); // reload from diskcfg.save(); // persist current in-memory stateLocalisation
Section titled “Localisation”ShopI18n resolves keys per-player via the player’s preferred language.
ShopI18n i18n = plugin.getI18n();String msg = i18n.get(playerRef, "shop.create.success", shopName);String fallback = i18n.get("shop.error.no_permission"); // server default langi18n files live at <server>/mods/kyuubisoft_shops/localization/<lang>.json. The plugin auto-migrates per-locale files when _localization_version increments - your custom strings survive a version bump.
Adding a custom subcommand
Section titled “Adding a custom subcommand”Subclasses of AbstractPlayerCommand or CommandBase can be registered via the existing command collections. Easiest is to extend an existing command pattern - see ShopCommand and ShopAdminCommand for examples. Use withRequiredArg / withOptionalArg and read via ctx.get(arg) / ctx.provided(arg).