Описание плагина SoulBuyer:
SoulBuyer — это не ещё один плагин на скупщика «лишь бы содрать с покупателей что-то». Это продвинутая система скупщика ресурсов для Paper 1.21+: живое GUI, динамический рынок, ротация ассортимента, автопродажа с тремя режимами, интеграция с PlayerPoints (отдельный донатный скупщик или выплата целиком в поинты), прогрессия игрока и несколько режимов хранения — от YAML на одном сервере до MySQL + Redis в сети.Здесь нет механики «положи предмет в слот и жди». Игрок открывает меню и сразу видит сетку скупаемых ресурсов с актуальными ценами, очками и рыночным коэффициентом. ЛКМ по предмету — продажа всего такого ресурса из инвентаря; ПКМ — выбор количества в отдельном подменю. Есть фильтры по категориям, сортировки, пагинация и кнопка «продать всё».
Чем больше на сервере продают один ресурс — тем ниже его рыночный коэффициент (с настраиваемым минимумом и постепенным восстановлением). За продажи начисляются очки прогрессии и опыт категорий (руды, мобы, растения, блоки, прочее). Выплата идет через Vault или PlayerPoints (в зависимости от настроек донатного скупщика).
Автопродажа — отдельный донат-плюс: игрок сам выбирает, когда сдавать ресурсы — при подборе, при открытии меню скупщика или прямо из сундука при его открытии. Настраиваются категории, уведомления и минимальная цена.
Ассортимент не застаивается: по таймеру меняется набор скупаемых предметов, сбрасываются рыночные коэффициенты, а игроки получают оповещение в чат. Внизу меню доступна живая панель статистики: сколько осталось до обновления, что продано сегодня и сколько ресурсов сейчас находится в ротации.
Скриншотосы SoulBuyer:
Особенности плагина SoulBuyer:
- Из коробки — ~380 предметов, GUI, ротация, рынок и lang (ru/en) с первого запуска; без ручной возни с конфигами
- Всё настраивается — цены, каталог, слоты GUI, права, лимиты рынка, ротация, автопродажа, economy; тексты в lang/, цифры в config.yml / items.yml
- GUI вместо команд — продажа, фильтры, сортировки, количество, автопродажа; команды только как алиасы
- Живой рынок — цена падает от массовых сдач, decay возвращает к норме; игрок видит коэффициент в lore
- Ротация ассортимента — по таймеру новый набор из пула; оповещение в чат; открытые меню обновляются на месте
- Донатный скупщик — Vault + PlayerPoints: два меню (/buyer и /donbuyer) или один скупщик целиком на поинты; два флага в config
- Автопродажа — донат-плюс: подбор / меню / сундук; категории, мин. цена, уведомления; при dual — выбор, куда платить (монеты или поинты)
- Прогрессия — очки, XP категорий, VIP-множители через LuckPerms; мотивация фармить, не только лутать
- Живая статистика — таймер до ротации, «продано сегодня», размер ассортимента прямо в меню (+ PlaceholderAPI)
- Анимация категорий — иконки фильтров крутят реальные предметы из текущей ротации
- Оптимизация — весь I/O БД асинхронно; main thread не блокируется; HikariCP; Redis только когда нужна сеть
- Хранение на выбор — flat / sqlite / mysql + Redis для нескольких Paper-серверов с общим рынком
- Безопасные продажи — secure storage на транзакцию; при ошибке или выходе предметы возвращаются
- API для других плагинов — SoulBuyerApi: открыть меню, котировки, рынок, прогресс, автопродажа
GUI скупщика
| Зона | Что делает |
|---|---|
| Верхний ряд | Фильтры категорий + блок «Ваш прогресс» (очки, множитель) |
| Сетка 21 слот | Иконки скупаемых предметов: цена, очки, рынок, количество в инвентаре. ЛКМ — продать всё, ПКМ — подменю количества |
| Стрелки ← → | Пагинация (на 4-м ряду, по бокам от сетки) |
| Нижний ряд | Сортировки, «продать всё», автопродажа, панель «До обновления» |
| Категории | «Все» — иконка Vault (хранилище); остальные — живая анимация: иконка меняется и показывает реальный предмет из текущего ассортимента категории |
Автопродажа (изумруд в нижнем ряду): ЛКМ — экран настроек, ПКМ — вкл/выкл. Подробности — в разделе Автопродажа.
Тексты и цвета — в lang/ru.yml, lang/en.yml. Hex-цвета, градиентный заголовок, lore у категорий и предметов настраиваются без пересборки JAR.
Автопродажа
Отдельная механика для доната (soulbuyer.autosell, по умолчанию OP). Игрок настраивает поведение сам — настройки сохраняются per-player (YAML или MySQL).Когда продавать
Один из трёх режимов (переключается кнопкой в меню настроек):| Режим | Иконка | Когда срабатывает |
|---|---|---|
| При подборе | Воронка | Предмет попал в инвентарь с земли или с рыбалки |
| При открытии меню | Изумруд | Открыт GUI скупщика — продаёт всё подходящее из инвентаря игрока |
| При открытии сундука | Сундук | Открыт контейнер — продаёт всё подходящее из слотов сундука. Инвентарь игрока не трогается |
Что ещё настраивается
| Опция | Смысл |
|---|---|
| Вкл/выкл | Мастер-переключатель (центр меню или ПКМ по изумруду в скупщике) |
| Категории | Руды, мобы, растения, блоки, прочее — клик вкл/выкл автопродажу для категории |
| Уведомления | Action bar / чат / выкл |
| Мин. цена | Не продавать предметы дешевле порога (с учётом рынка и множителей) |
| Куда продавать | Только при dual buyer: обычный скупщик (Vault) или донатный (PlayerPoints). Клик по слитку в центре 4-го ряда |
В обоих меню скупщика (обычном и /donbuyer) кнопка автопродажи показывает текущий режим выплаты; настройка — в том же экране автопродажи, куда игрок зашёл из любого меню.
Зачем это серверу
- Донат-плюс без pay-to-win: те же цены и рынок, меньше рутины.
- Сортировка базы: режим «сундук» — открыл хранилище, скупаемый мусор внутри сразу продался, нужное осталось в сундуке.
- АФК-фарм: режим «подбор» — подобрал ресурс, он уже продан.
- Казуал-игроки: режим «меню» — зашёл в /buyer, всё лишнее сдалось само.
PlayerPoints и донатный скупщик
SoulBuyer не привязан только к Vault. Если на сервере стоит PlayerPoints, плагин умеет платить за сдачу ресурсов донатными поинтами — без костылей, отдельными командами и с тем же рынком, ротацией и прогрессией.Один каталог items.yml, одни цены и коэффициенты — меняется только валюта выплаты (монеты сервера или PlayerPoints).
Три режима (секция economy в config.yml)
| player-points-enabled | donate-buyer-enabled | Что видит игрок |
|---|---|---|
| false | — | Классика: /buyer → выплата в Vault |
| true | false | Один скупщик: /buyer → всё в PlayerPoints (Vault не нужен) |
| true | true | Два скупщика: обычный за монеты + донатный за поинты |
player-points-enabled: false # включить PlayerPoints
donate-buyer-enabled: false # true = второе меню только за поинты
Два скупщика — зачем это круто
На донат-серверах часто нужно разделить экономику: фарм → монеты, донатный контент → поинты. SoulBuyer закрывает оба сценария одним JAR:| Меню | Команда | Выплата | Кому |
|---|---|---|---|
| Обычный | /buyer, /sell, /soulbuyer | Vault (монеты) | Все с soulbuyer.use |
| Донатный | /donbuyer, /dbuyer, /ppbuyer | PlayerPoints | С soulbuyer.donate |
- Тот же ассортимент и живой рынок — цена падает от массовых продаж в обоих меню.
- Очки прогрессии и XP категорий начисляются как обычно (это не валюта, а мета-прогресс SoulBuyer).
- Отдельный заголовок и lore у донатного меню — игрок сразу видит, что платят поинтами.
- Автопродажа следует выбору игрока в настройках: при dual-режиме — Vault или PlayerPoints; если включён только PlayerPoints — всегда в поинты.
Fresh install
Дефолты в Java: PlayerPoints выключен, работает только Vault — как раньше. Включение — две строки в config.yml, перезапуск сервера. Команды /donbuyer и алиасы уже в plugin.yml; если донатный режим выключен, /donbuyer ответит, что фича недоступна.Зависимости
| Плагин | Когда нужен |
|---|---|
| Vault + экономика | Дефолт; обязателен при dual-режиме и классическом режиме |
| PlayerPoints | player-points-enabled: true |
Динамический рынок
- Каждый ресурс имеет базовую цену и коэффициент рынка (старт 1.0).
- Массовые продажи снижают коэффициент до настроенного минимума.
- Decay постепенно возвращает цену к 1.0, если ресурс перестают сдавать.
- В режиме mysql + сеть коэффициенты синхронизируются через Redis pub/sub.
Ротация ассортимента
Полный каталог (~380 ванильных ресурсов в items.yml) — это пул. В скупщике одновременно активен случайный поднабор (по умолчанию 48 предметов, минимум из каждой категории).| Параметр | Дефолт | Смысл |
|---|---|---|
| catalog-rotation.enabled | true | Ротация вкл/выкл |
| interval-seconds | 3600 | Интервал смены (1 час) |
| active-item-count | 48 | Сколько предметов в меню |
| reset-market-on-rotation | true | Сброс коэффициентов рынка при смене |
| notify.enabled | true | Сообщение в чат при обновлении |
Панель статистики (центр низа)
Вместо «Страница 1/3» — часы с живым таймером (обновление каждую секунду):- До обновления — оставшееся время до ротации
- Сегодня продано — монеты, очки, позиций
- В ассортименте — сколько ресурсов сейчас в ротации
| Плейсхолдер | Описание |
|---|---|
| {rotation_left} | Таймер до ротации или «отключена» / «скоро» |
| {sold_today_money} | Монет за сегодня |
| {sold_today_points} | Очков за сегодня |
| {sold_today_stacks} | Позиций продано |
| {active_items} | Предметов в текущем ассортименте |
Прогрессия
- Очки за каждую продажу (из base-points предмета × множители).
- XP категорий — растёт от продаж в соответствующей категории.
- Permission-множители — VIP, Premium и др. (берётся максимальный из выданных).
- Бонус доминирующей категории — доп. доход от уровня XP главной категории.
Защита и надёжность
- Secure storage на время транзакции — предметы не теряются при лаге или ошибке.
- При выходе / сбое — возврат непроданного в инвентарь.
- Продажи пишутся в лог (sales.log или таблица soulbuyer_sales) — основа для статистики «сегодня продано».
- Весь I/O с БД — асинхронно, main thread не блокируется.
Хранение данных
| Режим | Когда использовать |
|---|---|
| flat | Один сервер, YAML + лог продаж (дефолт) |
| sqlite | Один сервер, файл data/data.db |
| mysql | Сеть серверов, общая БД + Redis для рынка |
Команды плагина SoulBuyer:
Основная: /soulbuyer. Алиасы открытия меню (настраиваются): /buyer, /sell, /sb, /rbuyer, /bs.Донатный скупщик (только при economy.donate-buyer-enabled: true): /donbuyer. Алиасы: /dbuyer, /ppbuyer, /donatesell.
| Команда | Действие |
|---|---|
| /soulbuyer | Открыть GUI скупщика (Vault или PlayerPoints — по режиму economy) |
| /buyer, /sell, … | То же (алиасы) |
| /donbuyer, /dbuyer, … | Донатное меню — выплата в PlayerPoints (dual-режим) |
| /soulbuyer admin reload | Перезагрузить config, gui, lang |
Права плагина SoulBuyer:
| Нода | Доступ |
|---|---|
| soulbuyer.use | GUI и продажа (обычный скупщик) |
| soulbuyer.donate | Донатное меню /donbuyer (PlayerPoints, dual-режим) |
| soulbuyer.admin | /soulbuyer admin reload |
| soulbuyer.autosell | Автопродажа (донат, OP по умолчанию) |
| soulbuyer.multiplier.vip | Множитель ×1.1 (дефолт) |
| soulbuyer.multiplier.premium | Множитель ×1.25 (дефолт) |
Плэйсхолдеры плагина SoulBuyer:
softdepend — если PAPI на сервере, регистрируется expansion soulbuyer.| Плейсхолдер | Описание |
|---|---|
| %soulbuyer_rotation_left% | Таймер до ротации (текст) |
| %soulbuyer_rotation_seconds% | Секунд до ротации (число) |
| %soulbuyer_sold_today_money% | Монет продано сегодня |
| %soulbuyer_sold_today_points% | Очков за сегодня |
| %soulbuyer_sold_today_stacks% | Позиций продано сегодня |
| %soulbuyer_active_items% | Предметов в текущем ассортименте |
Конфигурация
| Файл | Содержимое |
|---|---|
| config.yml | Хранение, рынок, ротация, прогрессия, права, анимация иконок категорий |
| items.yml | Каталог: id, material, категория, base-price, base-points |
| gui/general.yml, gui/buyer.yml, gui/quantity.yml, gui/autosell.yml | Слоты, материалы кнопок, action-типы |
| lang/ru.yml, lang/en.yml | Заголовки, lore, сообщения, hex-цвета |
| data/rotation.yml | Текущая ротация и время следующей смены |
| data/market.yml | Коэффициенты рынка (режим flat) |
| data/players/ | Прогресс игроков (режим flat) |
Установка плагина SoulBuyer:
Требования
| Компонент | Обязательность |
|---|---|
| Paper 1.21+ | да |
| Java 21 | да |
| Vault + экономика | да по умолчанию; не нужен, если только PlayerPoints (player-points-enabled: true, donate-buyer-enabled: false) |
| PlayerPoints | опционально; нужен при player-points-enabled: true |
| MySQL + Redis | только storage-type: mysql в сети |
| PlaceholderAPI | опционально |
- Скачайте плагин.
- Выключите сервер
- Переместите файл .jar в папку /plugins/ вашего сервера.
- Запустите сервер.
- Готово.
Публичное API
Для других плагинов — интерфейс SoulBuyerApi, регистрируется в Bukkit ServicesManager. Java:
import bm.b0b0b0.soulBuyer.api.SoulBuyerApi;
import bm.b0b0b0.soulBuyer.api.SoulBuyerApiProvider;
import org.bukkit.Bukkit;
SoulBuyerApi api = SoulBuyerApiProvider.get();
// или: Bukkit.getServicesManager().load(SoulBuyerApi.class)
if (api.isReady()) {
api.openBuyerMenu(player);
double points = api.cachedPoints(player);
double multiplier = api.cachedMultiplier(player);
api.quoteItem(player, "diamond").ifPresent(quote -> { /* unitPrice, unitPoints */ });
api.fetchProgress(player.getUniqueId()).thenAccept(progress -> { /* async */ });
}| Метод | Назначение |
|---|---|
| isReady() | Плагин загружен и готов |
| openBuyerMenu(Player) | Открыть GUI скупщика |
| quoteItem / quoteStack | Цена и очки за единицу с учётом рынка и множителей |
| marketCoefficient(itemId) | Текущий коэффициент рынка |
| cachedPoints / cachedMultiplier / cachedCategoryXp | Данные из кэша (после продаж или fetchProgress) |
| fetchProgress(UUID) | Async загрузка прогресса из хранилища |
| isSellable / isInActiveCatalog | Предмет в пуле / в текущей ротации |
| isAutosellFeatureEnabled / canUseAutosell / isAutosellEnabled | Автопродажа |
Публичное API
Интерфейс SoulBuyerApi регистрируется в Bukkit ServicesManager. Другой плагин подключается через depend или softdepend: [SoulBuyer].Подключение
Java:
import bm.b0b0b0.soulBuyer.api.SoulBuyerApi;
import bm.b0b0b0.soulBuyer.api.SoulBuyerApiProvider;
import bm.b0b0b0.soulBuyer.api.SoulBuyerSellDelivery;
import bm.b0b0b0.soulBuyer.api.SoulBuyerSellReturnPolicy;
import org.bukkit.Bukkit;
SoulBuyerApi api = SoulBuyerApiProvider.get();
// или: Bukkit.getServicesManager().load(SoulBuyerApi.class)
if (!api.isReady()) {
return;
}SoulBuyerApiProvider.get() всегда возвращает объект: если SoulBuyer ещё не готов — stub с isReady() == false и no-op методами.
Справочник методов
| Метод | Возврат | Описание |
|---|---|---|
| isReady() | boolean | Плагин загружен, каталог и экономика готовы |
| openBuyerMenu(Player) | — | Открыть стандартное GUI скупщика |
| quoteItem(Player, itemId) | Optional<ItemUnitQuote> | Цена и очки за 1 шт. с учётом рынка, множителей и кол-ва в инвентаре |
| quoteStack(Player, ItemStack) | Optional<ItemUnitQuote> | То же по предмету в руке / слоте |
| marketCoefficient(itemId) | double | Текущий коэффициент рынка (1.0 = база) |
| cachedPoints(Player) | double | Очки прогрессии из кэша |
| cachedMultiplier(Player) | double | Итоговый личный множитель (permission × категория + бустеры) |
| cachedCategoryXp(Player, categoryId) | double | XP категории из кэша |
| fetchProgress(UUID) | CompletableFuture<PlayerProgress> | Async загрузка прогресса из хранилища; обновляет кэш |
| isSellable(itemId) | boolean | Предмет есть в пуле items.yml |
| isInActiveCatalog(itemId) | boolean | Предмет в текущей ротации ассортимента |
| activeCatalogSize() | int | Сколько предметов сейчас в ротации |
| categoryId(itemId) | Optional<String> | Категория из items.yml (ores, mobs, plants, blocks, misc, …) |
| categoryId(ItemStack) | Optional<String> | Категория по material + custom model data |
| isAutosellFeatureEnabled() | boolean | Автопродажа включена на сервере |
| canUseAutosell(Player) | boolean | У игрока право на автопродажу |
| isAutosellEnabled(Player) | boolean | Мастер-переключатель автопродажи у игрока (не детали категорий/предметов) |
| isSaleInProgress(Player) | boolean | Идёт транзакция продажи (secure storage занят) |
| sellAll(Player) | boolean | Продать всё скупаемое из инвентаря |
| sellAll(Player, Runnable) | boolean | То же + callback по завершении |
| sellAll(Player, SoulBuyerSellDelivery, Runnable) | boolean | Продажа с выбором канала уведомления |
| sellItem(Player, itemId) | boolean | Продать весь item-id из инвентаря |
| sellItem(Player, itemId, Runnable) | boolean | То же + callback |
| sellItemAmount(Player, itemId, amount, Runnable) | boolean | Продать указанное количество из инвентаря |
| sellStacks(Player, stacks) | boolean | Продать переданные стеки (см. ниже) |
| sellStacks(Player, stacks, Runnable) | boolean | То же + callback |
| sellStacks(Player, stacks, returnPolicy, delivery, Runnable) | boolean | Полный контроль политики возврата и уведомлений |
Модели
ItemUnitQuote — unitPrice, unitPoints, marketCoefficient, playerMultiplier, inventoryAmount.PlayerProgress — playerId, points, categoryXp (map categoryId → XP).
Продажа из инвентаря
sellAll, sellItem, sellItemAmount работают как кнопки в GUI SoulBuyer: забирают предметы из инвентаря игрока, применяют лимиты, бустеры, рынок, прогрессию, выплату (Vault / PlayerPoints по конфигу). При ошибке непроданное возвращается в инвентарь. Java:
if (api.isSaleInProgress(player)) {
return;
}
api.sellAll(player, SoulBuyerSellDelivery.ACTION_BAR, () -> {
// main thread, после завершения транзакции
player.sendMessage("Готово!");
});Продажа из своего GUI — sellStacks
Для кастомного меню с кнопкой «Продать всё»:- Собери List<ItemStack> из слотов своего GUI.
- Отфильтруй по категории через categoryId, если нужно.
- Убери стеки из GUI до вызова (при CALLER_OWNS_ITEMS).
- Вызови sellStacks.
Java:
List<ItemStack> toSell = new ArrayList<>();
for (ItemStack stack : guiSlots) {
if (stack == null || stack.isEmpty()) {
continue;
}
api.categoryId(stack).filter("ores"::equals).ifPresent(category -> toSell.add(stack.clone()));
}
if (toSell.isEmpty() || api.isSaleInProgress(player)) {
return;
}
// убрать toSell из своего GUI до вызова
boolean started = api.sellStacks(
player,
toSell,
SoulBuyerSellReturnPolicy.CALLER_OWNS_ITEMS,
SoulBuyerSellDelivery.SILENT,
() -> refreshMyGui(player)
);
if (!started) {
// вернуть предметы в GUI вручную
}| SoulBuyerSellReturnPolicy | Поведение при отмене / ошибке |
|---|---|
| RETURN_TO_PLAYER | Дефолт. Непроданное возвращается в инвентарь игрока |
| CALLER_OWNS_ITEMS | SoulBuyer не трогает предметы — откат GUI делает ваш плагин |
| SoulBuyerSellDelivery | Уведомление игроку |
|---|---|
| CHAT | Сообщение в чат (дефолт) |
| ACTION_BAR | Action bar |
| SILENT | Без сообщений SoulBuyer |
Категории — categoryId
Категория берётся из поля category в items.yml для каждого item-id. Типичные значения: ores, mobs, plants, blocks, misc. Свои id допустимы — главное, чтобы совпадали с фильтрами в GUI и автопродаже. Код:
api.categoryId("raw_iron"); // Optional["ores"]
api.categoryId(player.getInventory().getItemInMainHand()); // по material + CMD
api.isSellable("raw_iron"); // true, если id в пуле
api.isInActiveCatalog("raw_iron"); // true, если сейчас в ротацииКэш прогресса
cachedPoints / cachedMultiplier / cachedCategoryXp читают память. После join данные подгружаются async; для точных значений до открытия меню вызови fetchProgress(uuid).join() или дождись callback.fetchProgress безопасен с async-потока; мутация Bukkit API из его callback — только через runTask.
Автопродажа и API: выбор категорий и отдельных item-id настраивается только через GUI SoulBuyer (gui/autosell.yml). Методов toggleAutosellItem / getDisabledItems в SoulBuyerApi нет — для кастомных меню используй sellStacks и свою фильтрацию по categoryId / isInActiveCatalog.
Зависимость в plugin.yml
Код:
softdepend: [SoulBuyer]или depend: [SoulBuyer], если ваш плагин не работает без скупщика.










