MAATRIX GAMES / Блог / Кастомные команды через плагины

Кастомные команды через плагины

MAATRIX GAMES

Рано или поздно встроенных команд игры перестаёт хватать. Игроки просят /tpahome, донатеры хотят /vip, новичкам нужен /rules с приветствием — а движок сервера из коробки такого не умеет. Хорошая новость: почти в любой игре с плагин-экосистемой (Minecraft на Bukkit/Spigot/Paper, Rust на Oxide/uMod и десятки других) кастомные команды — это рутинная задача, которая решается либо парой строк в конфиге, либо небольшим самописным плагином. Разберём оба пути и как не положить боевой сервер во время эксперимента.

Зачем вообще свои команды

Список типичных сценариев, ради которых админы городят кастомные команды:

  • Телепорт к точке или дому/home, /spawn, /warp shop. Без этого игроки тратят по 10 минут на пешие переходы и в итоге уходят на другой сервер.
  • Магазин/shop, /buy diamond 5, /sell all. Экономика держит игроков дольше, чем чистый PvP или выживание.
  • Статистика игрока/stats, /playtime, /kills — простая обратная связь, которая мотивирует возвращаться.
  • Приветствие новичка/rules, /discord, автосообщение при первом входе с ссылками на правила и комьюнити.
  • Служебные команды админа/vanish, /heal, /fly для модерации, которых нет в ванильном сервере.

Все эти команды объединяет одно: они не входят в базовую логику игры, значит их либо добавляет плагин, либо их вообще не будет.

No-code: командные конфиги без единой строчки кода

Самый быстрый путь — плагины, которые умеют создавать команды прямо из YAML/JSON-конфига, без компиляции и Java/C#. На Minecraft (Paper/Spigot) для этого чаще всего берут CommandPanels или связку EssentialsX + CMILib, где часть команд уже готова (/home, /warp, /tpa), а недостающие докручиваются через алиасы и command blocks. Ещё один рабочий вариант — Skript, который формально «код», но синтаксис настолько близок к обычному английскому, что порог входа минимальный.

Пример простой команды на Skript — приветствие новичка с телепортом на спавн:

command /welcome:
    trigger:
        send "&aДобро пожаловать на сервер! Правила — /rules" to player
        teleport player to {spawn}

Для Rust на Oxide/uMod похожая история: плагины вроде Kits или Backpacks уже дают готовые команды из коробки, а свои алиасы под конкретные нужды (например /tphome для дома игрока) настраиваются через oxide/config/PluginName.json — без единой строчки C#.

Плюс no-code подхода — скорость и отсутствие риска сломать что-то программной ошибкой. Минус — вы ограничены тем, что придумал автор плагина: сложную логику (например, команду с кулдауном, зависящим от VIP-статуса и текущей карты) через конфиг не всегда выразишь.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Когда конфига не хватает: свой плагин

Если нужна логика, которой нет в готовых решениях — берём минимальный самописный плагин. Для Minecraft это Java-плагин под Paper API (Bukkit/Spigot API совместим), для Rust — C#-плагин под Oxide/uMod. Здесь не будем разворачивать полноценный гайд по каждой из этих плагин-платформ — подробный старт по Bukkit/Spigot плагинам и установка Oxide/uMod для Rust уже разобраны отдельно. Здесь — именно про структуру команды.

Базовая структура обработчика команды в Paper-плагине (onCommand):

public class HomeCommand implements CommandExecutor {
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage("Команда доступна только игрокам.");
            return true;
        }
        if (!player.hasPermission("myplugin.home")) {
            player.sendMessage("У тебя нет прав на эту команду.");
            return true;
        }
        Location home = loadHomeLocation(player.getUniqueId());
        if (home == null) {
            player.sendMessage("Дом ещё не установлен. Используй /sethome.");
            return true;
        }
        player.teleport(home);
        player.sendMessage("Телепортация домой.");
        return true;
    }
}

Регистрация команды в plugin.yml:

name: MyHomePlugin
version: '1.0'
main: com.example.myhome.MyHomePlugin
api-version: '1.21'
commands:
  home:
    description: Телепорт к дому игрока
    usage: /home
permissions:
  myplugin.home:
    description: Доступ к команде /home
    default: true

Для Rust на Oxide структура похожая, но короче — плагин это один C#-файл в oxide/plugins/:

using Oxide.Core.Plugins;

namespace Oxide.Plugins
{
    [Info("HomeCommand", "YourName", "1.0.0")]
    class HomeCommand : RustPlugin
    {
        [ChatCommand("home")]
        void HomeCmd(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, "homecommand.use"))
            {
                player.ChatMessage("У тебя нет доступа к этой команде.");
                return;
            }
            // логика телепорта
        }

        void Init()
        {
            permission.RegisterPermission("homecommand.use", this);
        }
    }
}

Оба примера — минимальный каркас, без обработки ошибок и без сохранения данных (для домов и статистики понадобится своя база или файл конфига — SQLite или YAML, в зависимости от объёма данных). Но структура «команда → проверка прав → проверка условий → действие → фидбек игроку» одинакова практически везде.

Права доступа: не выдавайте всё подряд

Любая кастомная команда — потенциальная дыра, если права выставлены неверно. По умолчанию Bukkit/Spigot/Paper используют permission-ноды вида plugin.command, а раздачей групп занимается отдельный permissions-плагин — обычно LuckPerms. Подробно про группы и права разобрано в статье про permissions-плагины — здесь коротко: команду /vanish или /fly даём только группе admin, /home — всем, /vip-kit — только группе vip.

Типовая ошибка новичков — оставить default: true в plugin.yml для команды, которая должна быть только у модераторов. Проверяйте дефолтные права каждой новой команды до того, как плагин уедет на боевой сервер:

/lp user Steve permission check myplugin.home
/lp group admin permission set myplugin.vanish true

Для Oxide права регистрируются через permission.RegisterPermission (как в примере выше) и выдаются командой:

oxide.grant group admin homecommand.use
oxide.grant user Steve homecommand.use

Именование и конфликты команд

Когда команд становится много, легко словить конфликт — два разных плагина регистрируют одинаковую команду, и сервер использует ту, что загрузилась последней (порядок обычно алфавитный или по времени установки, и это не всегда предсказуемо). Несколько правил, которые экономят нервы:

  • Используйте префикс плагина в названии команды (/myp-home вместо /home), если рискуете столкнуться с EssentialsX или другим крупным плагином.
  • Проверяйте занятые команды через /help (Bukkit/Paper) или список зарегистрированных чат-команд в консоли Oxide перед добавлением новой.
  • Если конфликт всё же случился — используйте алиасы в plugin.yml (aliases: [myhome]) вместо переименования основной команды, чтобы не ломать привычки игроков.

Тестирование перед боевым сервером

Главное правило, которое почему-то регулярно игнорируют даже опытные админы: новая команда сначала выкатывается на тестовый сервер, и только потом на боевой. Причины две — во-первых, ошибка в обработчике команды может уронить весь сервер (NullPointerException в Java или необработанное исключение в C# на популярных серверах иногда крашит процесс целиком), во-вторых, неправильные права могут случайно открыть админскую команду всем игрокам.

Практическая схема:

  1. Разверните второй, тестовый сервер — по конфигурации идентичный боевому (та же версия ядра, тот же набор плагинов). Для Minecraft/Rust это отдельный инстанс с теми же модами, но с тестовым миром — по сути, второй арендованный сервер.
  2. Скопируйте плагины и конфиги с боевого на тестовый, добавьте новый плагин туда же.
  3. Прогоните сценарий вручную: зайдите под обычным игроком без прав и под админом, проверьте обе ветки поведения команды.
  4. Проверьте лог сервера на ошибки (logs/latest.log для Paper, консоль Oxide для Rust) — если в логе есть stack trace при вызове команды, на боевой её выкатывать рано.
  5. Только после чистого прогона — копируйте плагин и обновлённый конфиг на боевой сервер, желательно на низкой нагрузке (не в прайм-тайм выходных).

Держать отдельный тестовый сервер для одной команды может показаться избыточным, но по факту это дешевле, чем чинить боевой сервер посреди вечера, когда там 40 онлайн-игроков и все видят ошибку.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Частые вопросы

Можно ли добавить команду без перезапуска сервера?

Да, если плагин поддерживает hot-reload (у Oxide это встроено — oxide.reload PluginName, у Bukkit/Paper зависит от плагина, но большинство командных фреймворков вроде CommandPanels тоже умеют перечитывать конфиг на лету через /reload внутриигровую команду плагина). Полный /restart сервера всё равно надёжнее для критичных изменений.

Как ограничить команду только определённым миром или картой?

В Bukkit/Paper проверяется через player.getWorld().getName() внутри обработчика команды. В Oxide — аналогично через текущую сцену/зону, если игра это поддерживает (например, зоны на карте Rust через отдельный zone-плагин).

Нужно ли писать свой плагин, если есть Skript?

Не обязательно — Skript закрывает большинство простых сценариев (телепорт, сообщения, базовые условия) без компиляции. Свой Java/C# плагин имеет смысл, когда логика сложная (своя база данных, интеграция с внешним API, производительность на большом онлайне).

Что если команда работает на тесте, но падает на боевом?

Чаще всего причина — расхождение версий плагинов или ядра между тестовым и боевым сервером. Держите версии синхронизированными и обновляйте оба сервера одновременно.

Куда сохранять данные команды (дома, статистику)?

Для небольших серверов достаточно YAML или JSON-файла в папке плагина. При росте онлайна за пару сотен игроков лучше переходить на SQLite или MySQL — плагин будет меньше нагружать диск частыми перезаписями.