DiscordSRV для SMP: продвинутая настройка, role-sync и автоматизация ивентов

DiscordSRV для SMP: продвинутая настройка, role-sync и автоматизация ивентов

Базовый чат-бридж DiscordSRV ставится за десять минут, и на этом большинство админов останавливается. В этом гайде идём дальше: синк ролей с LuckPerms, привязка аккаунтов через /link, отдельные каналы для смертей и достижений, консоль-канал, мост на голосовой чат и тюнинг под 200+ онлайна.

Что считаем уже сделанным

Перед продвинутой частью предполагаем рабочую базовую связку. Если её нет, коротко: создаёте Discord-приложение в Developer Portal, включаете три privileged intents (PRESENCE, SERVER MEMBERS, MESSAGE CONTENT), кидаете токен в plugins/DiscordSRV/config.yml в поле BotToken, добавляете бота на сервер с правами Administrator на этапе теста. После рестарта /discord в чате должен показать связь.

Дальше речь идёт про DiscordSRV 1.27.x под Paper 1.20-1.21, LuckPerms 5.4+ и Java 21. На Folia DiscordSRV пока не работает в полном объёме, об этом ниже в FAQ.

Group Sync: связка LuckPerms групп с Discord ролями

Цель: игрок донатит в Discord, бот сам добавляет его в группу vip на сервере. Или наоборот, админ выдаёт ранг в LuckPerms, и Discord автоматически вешает соответствующую роль.

Файл plugins/DiscordSRV/synchronization.yml:

GroupRoleSynchronizationGroupsAndRolesToSync:
  "vip": "987654321098765432"
  "donator": "987654321098765433"
  "moderator": "987654321098765434"
  "admin": "987654321098765435"
  "default": "987654321098765436"

GroupRoleSynchronizationCycleTime: 5
GroupRoleSynchronizationOnLink: true
GroupRoleSynchronizationOneWay: false
GroupRoleSynchronizationMinecraftIsAuthoritative: true
GroupRoleSynchronizationPrimaryGroupOnly: false
GroupRoleSynchronizationEnableDenyPermission: true

Числовые ID ролей берутся через ПКМ по роли в Discord при включённом Developer Mode. Слева в кавычках имя группы LuckPerms ровно как в /lp group <name> info.

Ключевые поля разберём отдельно. GroupRoleSynchronizationCycleTime задаёт период автосинка в минутах, 5 это разумный баланс. GroupRoleSynchronizationOneWay: false включает двунаправленную синхронизацию: добавили роль в Discord, добавилась группа в LP, и наоборот. MinecraftIsAuthoritative решает кто главный при конфликте: если в LP группы нет, а роль в Discord стоит, бот её снимет (потому что Minecraft authoritative).

В сценарии донат-сервера обычно ставят OneWay: true и MinecraftIsAuthoritative: false, потому что покупка делается через Discord-бота вроде Tebex или MineStore, и оттуда роль приходит первой.

Чтобы синк работал, бот должен иметь permission Manage Roles в Discord, и его собственная роль должна быть в иерархии выше управляемых ролей. Это частая ошибка: бот не выдаёт @Admin именно потому, что роль @DiscordSRV ниже @Admin в списке ролей сервера.

Account linking: /link изнутри игры

Без привязки аккаунтов role-sync ничего не сделает, бот не знает кому какие роли давать. По умолчанию игрок пишет в чат /discord link, бот в DM присылает 6-значный код, игрок вставляет код в выделенный канал #verification или в DM боту.

Конфиг канала верификации в config.yml:

Channels:
  global: "987654321111111111"
  verification: "987654321222222222"
  console: "987654321333333333"
  deaths: "987654321444444444"
  achievements: "987654321555555555"

Дальше в linking.yml подкрутить требования:

RequireLinkedAccountToPlay: true
RequireLinkedAccountToPlayMessage: "&cПривяжи Discord: /discord link"
DiscordRequiredRoleToJoin: "987654321666666666"
SendCodeAsDirectMessage: true
ConfirmCodeInPublicChannel: false

Если у вас донатный SMP или whitelist-only сервер, RequireLinkedAccountToPlay: true отрезает альты: каждый аккаунт должен быть привязан к Discord, на одном Discord-аккаунте обычно одна привязка. DiscordRequiredRoleToJoin идёт ещё дальше: подключается только тот, у кого в Discord есть указанная роль (например, прошёл правила и нажал реакцию на rules-канале).

Channel routing: разные миры в разные каналы

На SMP с парой миров типа world, world_resource, creative_plots зеркалить весь чат в один канал быстро превращается в кашу. DiscordSRV из коробки роутит весь общий чат в global, но в связке с VentureChat или ChatControl можно повесить каналы на конкретные миры или префиксы.

Пример с VentureChat. В plugins/VentureChat/channels.yml есть каналы Global, Local, Resources. Дальше в plugins/DiscordSRV/config.yml:

Channels:
  global: "987654321111111111"
  resources: "987654321777777777"
  local: "987654321888888888"

DiscordChatChannelVentureChatNeedsToBeSetForChat: true

VentureChat транслирует сообщение с префиксом канала, DiscordSRV ловит его через хук и кладёт в нужный текстовый канал. То же работает с CarbonChat и CMI, нужны соответствующие хуки в DiscordSRV.

Альтернатива без сторонних чат-плагинов: повесить мир-роутинг через DiscordSRV Channel Linking и кастомный фильтр на формат сообщения, но это уже хак.

Death-канал и achievement-канал

Спам про смерти в общем чате надоедает уже через час. Выносим в отдельный канал.

В messages.yml редактируете формат:

MinecraftPlayerDeathMessage:
  Channel: "deaths"
  Embed:
    Color: "#ef4444"
    Author:
      Name: "%player%"
      ImageUrl: "https://crafatar.com/avatars/%uuid%?size=64"
    Description: "%death_message%"
    Footer:
      Text: "%world%"

MinecraftPlayerAchievementMessage:
  Channel: "achievements"
  Embed:
    Color: "#fbbf24"
    Author:
      Name: "%player%"
      ImageUrl: "https://crafatar.com/avatars/%uuid%?size=64"
    Description: ":trophy: получил **%achievement_name%**"

Crafatar отдаёт скин игрока по UUID в виде PNG, embed получает аватар-головой соответствующего игрока. Альтернатива - MineSkin (https://api.mineskin.org/render/head?uuid=...) или mc-heads.net (https://mc-heads.net/avatar/%uuid%/64).

Channel: ссылается на ключ из Channels: блока в config.yml. Если ключа нет, сообщение упадёт в global.

Console-канал: безопасный приватный лог

Зеркалирование консоли в Discord удобно, но это серьёзная дыра если канал не закрыт. Любой игрок с доступом увидит токены, IP, ошибки с путями к конфигам.

Базовая настройка в config.yml:

DiscordConsoleChannelId: "987654321333333333"
DiscordConsoleChannelUsable: true
DiscordConsoleChannelBlacklistActsAsWhitelist: false
DiscordConsoleChannelBlacklistedCommands:
  - "?"
  - "op"
  - "deop"
  - "whitelist"
  - "stop"
  - "save-all"
  - "luckperms user * permission set *"
DiscordConsoleChannelDoNotSendCommandsList:
  - "lp user * info"
DiscordConsoleChannelTruncateLength: 1900

Канал #server-console должен быть в категории Admin, видим только админ-роли, у @everyone снято всё, у бота есть View Channel, Send Messages, Read Message History.

DiscordConsoleChannelBlacklistedCommands блокирует команды которые нельзя выполнять из Discord: даже если кто-то получит доступ к каналу, /op или /stop не пройдут. Если хотите пускать только белый список, переключите BlacklistActsAsWhitelist: true и оставьте в списке ровно те команды что разрешены.

Я бы дополнительно ставил 2FA для Discord-аккаунтов всех админов, и снимал у бота Administrator после первоначальной настройки. Для функционала достаточно: View Channels, Manage Roles, Send Messages, Embed Links, Manage Webhooks, Read Message History, Mention @everyone (если используете broadcast в общий канал).

Custom commands: выполнение из Discord

Через мост можно выполнять команды на сервере из Discord-канала. Полезно для модераторов когда они не в игре: /tempban Player1 1d спам, /broadcast рестарт через 5 минут.

Через слэш-команды бота это надёжнее обычных текстовых, потому что Discord сам валидирует аргументы и видит права роли:

DiscordCommandsExecutableViaConsoleChannel: true

DiscordCommandRolesRequiredToExecute:
  "tempban":
    - "987654321moderator"
    - "987654321admin"
  "broadcast":
    - "987654321admin"
  "kick":
    - "987654321moderator"

DiscordCommandConfirmation: true
DiscordCommandConfirmationTimeout: 10

DiscordCommandConfirmation добавляет реакцию-подтверждение, чтобы случайно не уронить сервер /stop. Для /op и подобного крайне рекомендую вообще не давать выполнения, держать в blacklist.

Webhooks: тизеры сезона и анонсы

DiscordSRV умеет слать кастомные сообщения через webhook URL. Полезно для скриптов которые публикуют апдейт сезона, ссылку на Patreon или превью карты.

Создайте webhook в настройках канала Discord (Edit Channel -> Integrations -> Webhooks -> New Webhook), сохраните URL.

Слать из плагина или внешним скриптом:

curl -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "username": "Season 4 Announcer",
    "avatar_url": "https://i.imgur.com/your.png",
    "embeds": [{
      "title": "Сезон 4 стартует 1 мая",
      "description": "Новый мир, новые ивенты, x2 опыт первую неделю",
      "color": 1099334,
      "image": {"url": "https://example.com/season4.png"}
    }]
  }' \
  "https://discord.com/api/webhooks/123456789/abcdef..."

Удобно повесить такой curl на cron или на Laravel-задачу из админки сервера.

Mention parsing и форматирование

По умолчанию упоминания в Discord (@admin) приходят в Minecraft-чат сырым ID. Включаем красивую отрисовку:

DiscordChatChannelTranslateMentions: true
DiscordChatChannelMinecraftMentionFormat: "&9@%name%"
DiscordChatChannelEmojiBehavior: "name"
DiscordChatChannelDiscordToMinecraft:
  - "&7&o[DISCORD] &r%name%&7: &f%message%"

TranslateMentions: true парсит <@123456> в @nickname. EmojiBehavior: name превращает :diamond: в [diamond] вместо мусора в чате (если на клиентах нет шрифта emoji). Есть значения unicode (как есть, ломается на старых клиентах) и hide (вырезать).

В обратную сторону, Minecraft to Discord, регулируйте через DiscordChatChannelMinecraftToDiscord шаблон.

DiscordSRV-Voice и proximity voice

Голосовой чат через Discord интегрируется через DiscordSRV-Voice аддон или связку с Plasmo Voice. Аддон создаёт временные voice-каналы привязанные к зонам в мире: подошёл ближе - слышишь, отошёл - перестал.

Установка коротко: в plugins/ кидаете DiscordSRV-Voice.jar, в Discord создаёте категорию Voice Proximity, бот сам создаёт каналы по мере необходимости. В конфиге указываете категорию:

Voice:
  Enabled: true
  CategoryId: "987654321999999999"
  LobbyChannelId: "987654321aaaaaaaaa"
  HorizontalRadius: 50
  VerticalRadius: 30
  AllowVoiceCrossWorlds: false

Альтернатива - Plasmo Voice с собственным сервером, без зависимости от Discord. Это когда нужен низкий пинг, поддержка radio-моделей, шифрование. DiscordSRV в этом сценарии остаётся для текстового чата и role-sync, голос идёт через Plasmo.

Производительность при 200+ онлайна

На больших SMP DiscordSRV начинает упираться в Discord rate limits: 5 сообщений в 5 секунд на канал, 50 запросов в секунду на бота. Базовая настройка под нагрузку:

ChatChannelHookTimeout: 1500
DiscordChatChannelMessageBatching: true
DiscordChatChannelMessageBatchSize: 8
DiscordChatChannelMessageBatchDelay: 1500
RestActionAsyncSubmit: true

MessageBatching: true склеивает несколько чат-сообщений в одно сообщение Discord за 1.5 секунды, что снижает RPS в 5-10 раз. Видно как небольшое отставание, но в общем потоке это незаметно.

RestActionAsyncSubmit: true обязательно: иначе синхронные запросы к Discord API блокируют main thread сервера и роняют TPS при сетевых задержках.

Метрики проверяйте через /discord debug: бот печатает текущий ping до Discord, длину очереди, количество задач JDA. Если ping регулярно выше 300 мс - стоит проверить сеть до европейских дата-центров Discord.

Бэкап конфига и привязок

Все привязки игроков к Discord хранятся в файле plugins/DiscordSRV/linkedaccounts.json (или в MySQL если включили). Это критичные данные: потеря означает что 200 игроков должны заново линковаться, role-sync сбрасывается.

Минимум - cron на ежедневный архив:

#!/bin/bash
DATE=$(date +%Y-%m-%d)
tar czf /backup/discordsrv-$DATE.tar.gz \
  /opt/minecraft/plugins/DiscordSRV/linkedaccounts.json \
  /opt/minecraft/plugins/DiscordSRV/config.yml \
  /opt/minecraft/plugins/DiscordSRV/synchronization.yml \
  /opt/minecraft/plugins/DiscordSRV/messages.yml
find /backup -name "discordsrv-*.tar.gz" -mtime +30 -delete

Если перешли на MySQL-хранилище привязок - добавьте дамп таблицы discordsrv_accountlinks в общий mysqldump-скрипт сервера. И не забудьте про BotToken: если выложите конфиг на github как пример, токен утечёт и кто-то получит контроль над ботом.

FAQ

DiscordSRV работает на Folia?

Частично. На текущий момент DiscordSRV не имеет официальной поддержки Folia, потому что многие хуки используют main thread API. Чат-бридж работает, но события вроде смертей и achievements ловятся нестабильно. Если у вас Folia - смотрите в сторону EssentialsXDiscord или DiscordIntegration как альтернативы, либо ждите официального патча.

Как защитить console-канал от утечки?

Канал в категории Admin, у @everyone снято View Channel, Read Message History отдельно проверьте. Включите 2FA у всех админов с доступом, иначе скомпрометированный аккаунт = доступ к консоли. В DiscordSRV держите BlacklistedCommands со списком опасных команд, чтобы даже при утечке нельзя было выполнить /op или /stop. Webhook URL никогда не светите в публичных репозиториях.

Можно ли несколько Minecraft серверов в один Discord?

Да, есть два подхода. Первый: ставите DiscordSRV на каждый сервер, выделяете под каждый отдельный канал (#smp-chat, #anarchy-chat, #creative-chat), один Discord-сервер. Второй: один прокси (BungeeCord/Velocity) с глобальным чатом, и DiscordSRV висит на прокси-плагине типа DiscordSRV-Bridge или используете Velocitab + общий чат-плагин. Для SMP-сети первый вариант чаще удобнее: каждый сервер изолирован, проще роутить.

Что делать если бот rate-limited?

Включите MessageBatching как описано выше, это решает 90% случаев. Если не помогло - смотрите в /discord debug какие именно запросы упираются. Часто виноват частый role-sync (CycleTime: 1 минута на 500 игроках = тысячи API-запросов). Поднимите до 10-15 минут. Для пиковых событий типа массового присоединения по линку - временно отключите автосинк на старте сервера, синхронизируйте порциями вручную через /discord resync.

Можно ли менять формат сообщений на лету без рестарта?

messages.yml и config.yml перечитываются командой /discord reload. Изменения в linking.yml и synchronization.yml обычно применяются после перезапуска плагина: /discord debug покажет текущие настройки, и если они не подхватились - перезагрузка сервера обязательна.

Бот не выдаёт роли несмотря на правильный конфиг

Девять из десяти случаев: иерархия ролей. Откройте Discord -> Server Settings -> Roles. Роль вашего бота должна быть выше всех ролей которыми он управляет. Перетащите её наверх, рестарт не нужен. Десятый случай: у бота нет permission Manage Roles на уровне сервера или конкретной категории/канала. Проверяйте в Server Settings -> Roles -> ваш бот -> Permissions.

Как сделать чтобы аватар в Discord был скином игрока?

В messages.yml для embed-сообщений ставьте ImageUrl: "https://crafatar.com/avatars/%uuid%?size=128&overlay". Параметр overlay накладывает второй слой шапки. Для full-body рендера - https://crafatar.com/renders/body/%uuid%?overlay. Сервис кэширует результаты, проблем с rate limit не будет.

Что дальше

Если только запускаетесь, делайте по шагам: сначала чат-бридж, потом account linking с RequireLinkedAccountToPlay, после этого role-sync. Не тащите всё сразу, отлаживайте по одной фиче. И обязательно прогоните бэкап скрипта на тестовом окружении перед продакшеном: восстановление линков из tar архива должно занимать секунды, иначе при инциденте потеряете больше времени чем сэкономили.

И сразу ограничьте права бота. Administrator нужен только в момент первой настройки, дальше держите минимально необходимый набор. Плюс 2FA на все Discord-аккаунты с доступом к админ-каналам. Это ровно тот случай когда дополнительные пять минут настройки экономят пол ночи восстановления после инцидента.


Protégez votre serveur contre les attaques DDoS

Protection gratuite avec configuration en 5 minutes. 1 To de bande passante inclus.

Essayer gratuitement


Articles connexes