Compatibility
Minecraft: Java Edition
Platforms
Links
Tags
Creators
Details
SoulAuction
One auction house for the whole network: multiple storefronts, any economy, Folia, and trade protection — no fat-jar, no click dupes.
SoulAuction is an auction plugin for Paper 1.21+ and Folia, built for when economy logic must survive region threading and double-purchase on a proxy is unacceptable.
Single JAR: dependencies via Paper libraries, no shade. Fewer conflicts with other plugins and easier updates.
Why admins choose it
Multiple auction houses in one plugin — regular, VIP, event, donation-currency, and more. Each has its own economy (Vault, PlayerPoints, experience, CoinsEngine, items), permissions, and rules. Not three separate AH plugins and not a sync zoo.
Folia first-class — region-aware threading, no legacy scheduler on hot paths. For networks already on Folia or migrating, this is baseline, not a nice extra.
Multi-server — shared MySQL + Redis: one catalog across the network, distributed locks for buy/sell, atomic listing state transitions in the database. Pub/sub refreshes the storefront; SQL is the source of truth, not “whoever wrote the file last”.
Players get a proper AH: GUI, categories, sorting, search and filters, favorite sellers, hub for “my listings / claim / history”. Messages use MiniMessage, HEX, gradients; texts live in lang/messages_*.yml.
Economy under control: seller and buyer taxes, bypass/discount via permissions, slot limits by rank, cooldowns, blacklist, world bans, item whitelist/blacklist, rules for ItemsAdder / Oraxen / MMOItems. Listing TTL; if inventory is full, items go to claim, not the void.
Operations: deal history, audit log, admin GUI, purge, claim recovery, Discord/Telegram deal notifications (async HTTP, no bot on the Minecraft server). /ah reload reloads config and messages.
Built for: Paper/Folia networks with multiple game modes and currencies, a shared auction across proxies, where anti-dupe matters more than “one more GUI button”.
Network and anti-dupe (short)
| Mode | Purpose |
|---|---|
JSON / YAML |
single server, quick start |
SQLITE / MYSQL |
persistence; MYSQL is the proxy foundation |
| Redis + pub/sub | cross-instance locks + catalog cache invalidation |
On a proxy: MYSQL + redis.enabled. Buy/cancel/expiry go through listing claim (lock + Redis NX + UPDATE … WHERE status='ACTIVE'), rollback on payment failure or full inventory. Sell: item held in escrow in GUI; inventory listing uses strict removal before DB write (/ah sell, no clone-without-consume).
Features
- Multi-auction support, separate
open/buy/sellpermissions. - Currencies:
VAULT,PLAYER_POINTS,EXPERIENCE,COINS_ENGINE,ITEM. - Storage:
JSON,YAML,SQLITE,MYSQL. - Listing TTL (timed or unlimited), claim, seller notification on expiry, per-auction fees, sold/cancelled/expired history.
- Listing limits:
soulauction.<auctionId>.<N>,soulauction.all.<N>. - Sort priority:
soulauction.priority.<N>. - Large-sale broadcast, external notifications (Discord/Telegram).
- Sell GUI: amount ±1, pull matching items from inventory (up to max stack); comparison by full serialization (NBT, name, enchants), not “similar” stacks. Escrow until confirm — returned on cancel/close.
- Per-auction price display: custom currency symbol, before/after number, MiniMessage and optional PlaceholderAPI in the symbol (resource pack / ItemsAdder icons, etc.).
- Fake activity: synthetic listings from a nick/item pool, enabled per auction; timer refill, manual
/ah admin fake, toggle in admin GUI. - Seller skins: heads in “Favorite sellers” via SkinsRestorer / Mojang; fallback and one shared skin for all fake sellers (server logo).
Price display (auctions/*.yml)
Each auction has its own fields (defaults in Java; after first start — plugins/SoulAuction/auctions/global.yml and others):
| Field | Purpose |
|---|---|
currencySymbol |
Symbol or icon in GUI and chat. Empty — economy default (Vault $, PlayerPoints PP, items Nx MATERIAL). |
currencySymbolPosition |
BEFORE or AFTER the amount ($100 or 100 ₽). |
currencySymbolPlaceholderApi |
true — resolve %...% from PlaceholderAPI for the viewer (requires PlaceholderAPI). |
listingTtlSeconds |
Listing lifetime in seconds; 0 or less — no expiry. |
Listing TTL and claim
- While a listing is ACTIVE, the browser and “My listings” (if TTL enabled) show time left / expires at in lore; the open browser refreshes the timer every second.
- Expired, unsold: listing removed (
EXPIRED), item goes to claim once (claims.json), not the void. - Seller: chat message (online immediately, offline on join) with expiry notice and [Claim in menu] →
/ah expired [auctionId]; item pickup —/ah claim(expired menu is view-only, slots are not editable). - Listing success: multi-line chat — item, amount, price, auction; separate block for timed or unlimited expiry (
success-listed,success-listed-expiry-timed/success-listed-expiry-unlimitedinlang/messages_*.yml). - Single process: lock on
listingId+ on SQL,ACTIVE→EXPIRED/SOLD/CANCELLEDis atomic; claim is removed from buffer before inventory delivery.
currencySymbol examples:
- Text:
₽,coins,gems - MiniMessage / glyph:
<glyph:coin>(ItemsAdder, Nexo, etc. — per pack docs) - PAPI (with
currencySymbolPlaceholderApi: true):%img_economy%
Custom symbol prices appear everywhere: GUI listings, sell menu, price filter, purchase, history, chat (success-bought, announce, etc.).
Messages (plugins/SoulAuction/lang/)
prefix— prefix for all{prefix}strings.- Keys use MiniMessage;
{price}is already formatted for the auction the player is viewing. - Multi-line chat: key as a YAML list of strings — plugin sends each line separately (
MessageService.send). Single string — as before. Chat search, sell success, expiry, command usage — in this format. - Listing lore placeholders in GUI:
{seller},{price},{id},{auction},{expires_in},{expires_at}(last two when TTL enabled); optionallistingLoreTemplateinauctions/*.yml.
PlaceholderAPI
Expansion registers automatically when PlaceholderAPI is present. Identifier — soulauction, i.e. %soulauction_<param>%. Numbers are formatted for the player's locale (thousands separators). Any placeholder also has a _raw suffix variant — unformatted value (1500000 instead of 1,500,000, enum key instead of lang name) for scoreboards and plugins that need raw numbers.
Counters
| Placeholder | Value |
|---|---|
%soulauction_active_count% |
Player's active listings (aliases: sell_count, purchasable_count) |
%soulauction_expired_count% |
Player's expired items (claims with reason EXPIRED) |
%soulauction_total_active_count% |
All listings on the auction house |
%soulauction_sell_limit% |
Player's global listing limit (permissions + override) |
%soulauction_claims% |
All player items in claim |
%soulauction_listings_all% / %soulauction_listings_<auctionId>% |
Player's active listings (all / specific auction) |
%soulauction_listings_total% |
All listings (unformatted, legacy) |
Player settings
| Placeholder | Value |
|---|---|
%soulauction_selected_sorting% |
Selected sort order |
%soulauction_selected_category% |
Selected category |
%soulauction_selected_currency% |
Currency of the last opened auction (currency-* from lang) |
%soulauction_categories_enabled%, %soulauction_expired_items_enabled%, %soulauction_auction_listing_confirmation_enabled% |
Always true — these systems cannot be disabled in SoulAuction |
Deal statistics
Persistent (data/stats.json); seeded once from existing history on first run. <currency> is economy type: vault, player_points, experience, coins_engine, item.
| Placeholder | Value |
|---|---|
%soulauction_items_sold% |
Items sold by the player |
%soulauction_items_purchased% |
Items purchased by the player |
%soulauction_money_made% |
Money earned by the player (after tax) |
%soulauction_money_spent% |
Money spent by the player (with buyer tax) |
%soulauction_items_sold_<currency>% and similar |
Same, per currency |
%soulauction_total_items_sold%, %soulauction_total_money_made%, etc. |
Server-wide, including _<currency> |
Fake activity
Synthetic listings keep the storefront busy: random nick from the pool, random item, random price in range. These are not real players — purchases work like normal listings (money is consumed, item is delivered). After a fake listing is bought, the plugin may list another one after a delay.
Per-auction enable
There is no global on/off in config.yml — only per auction:
| Where | Field / action |
|---|---|
auctions/<id>.yml |
fake-activity-enabled: true / false (default false) |
/ah admin → right-click an auction |
fake-activity menu |
| Menu → slot 22 | toggle (LIME_DYE / RED_DYE) → auctions/<id>.yml |
| Menu → slot 49 | pool, limits, tick (read-only) |
Example in auctions/global.yml:
fake-activity-enabled: true
Pool (plugins/SoulAuction/fake-activity/)
Path: config.yml → fake-activity.directory (default fake-activity). On first start three files are created:
| File | Contents |
|---|---|
settings.yml |
timers, limits, default prices, admin-fake |
sellers.yml |
seller display names (max 16 chars) |
items.yml |
item pool with weights and price ranges |
Fresh install ships ~120 nicks and ~120 vanilla items (ores, food, tools, 1.21 rares) — edit in YAML anytime.
settings.yml (main fields)
| Field | Purpose |
|---|---|
initial-fill-listings |
listings to create on startup (0 = up to max-total-listings) |
initial-delay-seconds |
delay before the first refill tick |
tick-interval-seconds |
how often to top up toward limits |
after-purchase-delay-seconds |
delay after a fake buy before the same seller lists again |
min-price / max-price |
default range when an item in items.yml has min/max = 0 |
price-variance-percent |
random ±% on the chosen price |
max-listings-per-auction |
fake cap per auctionId |
max-total-listings |
fake cap server-wide |
listing-age-spread-seconds |
random listing age on create (varied TTL timers) |
listings-per-tick |
listings to attempt per tick |
auction-ids |
only these auctions ([] = all with fake-activity-enabled) |
admin-fake.register-seller |
after /ah admin fake, append nick to sellers.yml |
admin-fake.register-item |
after /ah admin fake, append hand item to items.yml |
items.yml entry fields
id— label for yourselfmaterial/amount— vanilla stack (ignored whenitem-base64is set)item-base64— full item snapshot (NBT, custom items)min-price/max-price—0= fromsettings.ymlauction-ids— empty = any auction; otherwise only listed idsweight— relative pick chance (higher = more often)
Manual fake listing
/ah admin fake <nick> <auctionId> <price>
Item must be in the main hand. Listing is created as synthetic immediately. With admin-fake.* enabled, nick and/or item are appended to the pool async.
Behaviour
- Fakes appear in sorting, search, and filters like normal listings.
- Only an admin with
soulauction.command.cancel.anycan/ah cancelthem (seller is a synthetic UUID). - On a proxy with MySQL+Redis, fakes sync like any other listing.
Seller skins (config.yml → seller-skins)
Textures for seller heads in the “Favorite sellers” GUI (hub → star). Browser listings show the item itself, not a head.
SkinsRestorer is a soft dependency; on offline/cracked servers Mojang lookup by nick often fails without it.
source
| Value | Behaviour |
|---|---|
auto |
SkinsRestorer when installed; otherwise Mojang API |
skins-restorer |
SkinsRestorer only |
mojang |
Mojang only (online-mode, real nicks) |
off |
default Steve/Alex heads, no lookup |
fallback-skin and fake-seller-skin
seller-skins:
source: skins-restorer
fallback-skin: "DefaultHead"
fake-seller-skin: "ServerLogo"
| Field | When |
|---|---|
fallback-skin |
seller nick lookup failed → SR resolves this custom skin / nick / URL |
fake-seller-skin |
all synthetic/fake sellers use this skin; nick is ignored |
Values are SkinsRestorer skin names (/sr set skin ServerLogo <url>, etc.).
Lookup chain (SkinsRestorer)
findOrCreateSkinData(nick)— orfake-seller-skinwhen the seller is synthetic and the field is setfindSkinData(fallback-skin)— when configuredgetSkinForPlayer(uuid, nick)— SRdefaultSkinsfrom SkinsRestorer config
With source: mojang, fallback uses the Mojang API by name.
Startup warmup
Async prefetch into SkinsRestorer cache (~100 ms pause between requests):
- if
fake-seller-skinis set — warmup that skin (+fallback-skinwhen set); - otherwise — all fake pool nicks +
fallback-skin.
Console: SkinsRestorer — prefetched X/Y fake seller skins.
GUI lookup is async; the head updates when the texture arrives (no in-memory cache in SoulAuction).
Admin GUI
/ah admin or /ah admin gui [page] — requires soulauction.command.admin or OP.
| Action | Result |
|---|---|
| Left-click an auction | open storefront as a player |
| Right-click an auction | fake-activity menu |
| Slot 22 | toggle fake activity (wool) |
| Slot 49 | pool, limits, tick (read-only) |
| Slot 45 | back to auction list |
| Book in the bottom row | create a new auction (chat wizard) |
Commands
Players
/ah— open the default auction./ah <auctionId>— open a specific auction./ah sell <price>— list the item in hand on the default auction./ah sell <auctionId> <price>— list on the chosen auction./ah sell <price> <auctionId>— alternative argument order./ah my [auctionId]— show your active listings./ah selling [auctionId]— active listings GUI./ah expired [auctionId]— expired items GUI (claim)./ah purchased [auctionId]— purchase history GUI./ah history [auctionId]— sales history GUI./ah search <text>— search: closes GUI, prompt in chat (cancel button in message); after input — refreshed browser and result in chat./ah search <auctionId> <text>— same for a specific auction./ah search cancel— cancel search and return to the auction./ah page <number> [auctionId]— open a specific auction page./ah claim [all]— claim expired/cancelled items./ah cancel <id>— remove your listing and return the item.- Command aliases are configured in
config.ymlviacommandAliases(e.g.ax,auction).
Admins
/ah reload— reload configs and messages./ah purge <days>— purge deal history older than N days./ah admin history <player> [limit]— player history in chat./ah admin selling <player> [auctionId]— player's active listings GUI./ah admin blacklist add|remove <player>— runtime sell blacklist./ah admin recover <claimId>— deliver claim to inventory./ah admin audit [limit]— recent audit entries./ah admin cache stats|rebuild|invalidate— catalog cache./ah admin sellfor <player> <auctionId> <price>— list on behalf of a player (item in hand)./ah admin fake <nick> <auctionId> <price>— synthetic listing under the given nick (item in hand; see fake activity)./ah admin gui [page]— auction list GUI (right-click an auction for fake activity)./ah admin parse tags|nbt— parse NBT/tags of item in hand (custom items)./ah view <player> [auctionId]— player's listings GUI.
Permissions
Basic
soulauction.command.ah— use/ah.soulauction.command.sell— use/ah sell.soulauction.command.reload— use/ah reload.soulauction.command.admin— admin commands and purge.soulauction.priority.10— higher listing sort priority (any N).soulauction.command.my— use/ah my.soulauction.command.claim— use/ah claim.soulauction.command.cancel.any— remove others' listings via/ah cancel.
Per-auction (set in config.yml)
Each auction has separate nodes:
openPermission— open this auction.buyPermission— buy listings in this auction.sellPermission— sell in this auction.
Example:
soulauction.open.vipsoulauction.buy.vipsoulauction.sell.vip
Dynamic listing limits
soulauction.<auctionId>.1— limit 1 active listing in this auction.soulauction.<auctionId>.5— limit 5 active listings in this auction.soulauction.all.1— global limit 1 across all auctions.soulauction.all.5— global limit 5 across all auctions.
If multiple nodes are granted, the highest limit applies.
If none are granted, defaults from limits are used.
Taxes
soulauction.tax.bypass— no sale or purchase tax.soulauction.tax.discount.<percent>— sale tax discount (highest wins).
In auctions/*.yml: saleTaxPercent (from seller), buyTaxPercent (from buyer).
Price bounds
- Global:
limits.minPrice,limits.maxPriceinconfig.yml. - Per-auction:
minPrice,maxPrice(0 = use global). soulauction.price.min.<price>— minimum listing price for a player.soulauction.price.max.<price>— maximum listing price for a player.
Discord and Telegram
In config.yml → notifications section:
- Discord:
notifications.discord.enabled,webhookUrl(channel Incoming Webhook). - Telegram:
notifications.telegram.enabled,botToken,chatId. - Events:
notify-sold,notify-listed,notify-expired,min-pricefilter. - Delivery is async (Java HTTP), no shade, no bot on the Minecraft server.
- Player avatars in Discord:
notifications.discord.show-player-avatars(seller head in author, buyer thumbnail on deals). CDN:avatar-provider=MINOTAR(default) orCRAFATAR.
After /ah reload, new URLs and flags are picked up.

