Разработка Java-серверов на Paper · 6 мин чтения

Команды плагина: синтаксис, права и один проверяемый сервис

Создадим одну команду /trailinfo, которая показывает UUID игрока, принимает только help и корректно отвечает консоли. Полный manifest и класс позволяют проверить права, аргументы и alias до добавления сложной игровой логики.

Редакция LonelyCraft · Обновлено

Схема: Синтаксис → Доступ → Ответ
Синтаксис приводит к проверке доступа и сообщению о подтверждённом результате. Авторская схема LonelyCraft.

Объявите команду и разрешение

Команда /trailinfo рассчитана на Paper 1.21.1 и JDK 21. Если проекта ещё нет, выполните шаги создания первого плагина. Замените главный класс ru.example.trailnotes.TrailNotesPlugin приведённым кодом и объявите команду в src/main/resources/plugin.yml.

Замените src/main/resources/plugin.yml содержимым ниже. Команду /trailinfo и alias /ti обслуживает один обработчик. Реальное permission нашего примера — trailnotes.info, по умолчанию оно доступно всем; административные права здесь не требуются. Поля описаны в Paper plugin.yml.

name: TrailNotes
version: 1.0.0
main: ru.example.trailnotes.TrailNotesPlugin
api-version: "1.21"
commands:
  trailinfo:
    description: Показать UUID или справку
    aliases: [ti]
    usage: /trailinfo [help]
    permission: trailnotes.info
permissions:
  trailnotes.info:
    default: true

Проверьте sender и аргументы до выполнения

Сохраните этот полный класс в основной Java-файл. Отказ и справка возвращают true, потому что сообщение уже отправлено. Возврат false запускает usage из manifest и может создать вторую подсказку. Проверка permission внутри обработчика остаётся явной.

package ru.example.trailnotes;

import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

public final class TrailNotesPlugin extends JavaPlugin {
    @Override
    public boolean onCommand(CommandSender sender, Command command,
                             String label, String[] args) {
        if (!sender.hasPermission("trailnotes.info")) {
            sender.sendMessage("Нет права trailnotes.info.");
            return true;
        }
        if (args.length == 1 && args[0].equalsIgnoreCase("help")) {
            sender.sendMessage("/trailinfo — ваш UUID; /trailinfo help — справка.");
            return true;
        }
        if (args.length != 0) {
            sender.sendMessage("Использование: /trailinfo [help]");
            return true;
        }
        if (!(sender instanceof Player player)) {
            sender.sendMessage("UUID показывает только игроку. Используйте trailinfo help.");
            return true;
        }
        player.sendMessage("Ваш UUID: " + player.getUniqueId());
        return true;
    }
}

Соберите и выполните матрицу команд

Соберите и установите JAR на отдельный Paper 1.21.1. От обычного игрока выполните /trailinfo, /trailinfo help, /trailinfo wrong и /trailinfo help extra. У первых двух есть результат, у остальных — usage без исключения. /ti должен показывать тот же UUID.

В консоли выполните trailinfo help, затем trailinfo без слеша. Первая форма показывает справку, вторая объясняет необходимость игрока. Для проверки запрета через LuckPerms задайте lp user ВашНик permission set trailnotes.info false, испытайте обе команды и затем удалите персональный запрет через permission unset.

Расширяйте контракт по одному аргументу

Если добавляете число, сначала разберите его через Integer.parseInt, обработайте NumberFormatException и проверьте допустимый диапазон до изменения игры. Для именованного режима сравнивайте с закрытым списком; автодополнение лишь помогает вводу и не заменяет проверку исполнения.

Для покупки команда передаёт намерение одному сервису, а тот проверяет текущие деньги, право и повтор операции. GUI и alias обращаются к тому же правилу. Успешное сообщение показывается после фактического результата; при фоновой операции сначала сообщите о принятии запроса, а ответ отправьте после подтверждения.

Сначала напишите три пользовательских сценария

Для мини-игры достаточно описать начало, выход и просмотр результата. Например, /trail открывает меню, /trail join начинает выбранный режим, /trail leave завершает участие, /trail top показывает рекорды. Это условные команды учебного проекта. У каждой формы определите допустимого исполнителя, право и результат при текущем состоянии игрока.

Команда без аргументов должна помогать, а не выдавать техническую ошибку. Для обычного игрока откройте меню или покажите короткую справку. Для консоли верните текстовый вариант. Если функция требует местоположение Player, отказ консоли должен объяснять это условие. Не приводите CommandSender к Player до проверки типа.

Аргументы имеют контракт

Для режима задайте закрытый набор standard, combat, peaceful, если именно эти варианты существуют в механике. Для количества — целое число и диапазон. Для UUID — корректный формат и проверку существования записи, когда она нужна. Сообщение ошибки показывает допустимый пример, но не раскрывает внутренние пароли, SQL и подробный stack trace.

Не принимайте произвольную командную строку как параметр награды. Пользовательский текст нельзя вставлять в shell или SQL конкатенацией. Даже административная команда выигрывает от строгого типа значения: ограниченный идентификатор режима понятнее универсального «выполнить всё». Для внешнего поиска задайте предел длины и допустимую обработку отсутствующего результата.

Значения команд и настройки механики должны иметь согласованные единицы и границы. Проверка параметров.

Одна операция для команды, алиаса и меню

Обработчик команды разбирает синтаксис и вызывает общий сервис. Кнопка GUI вызывает тот же сервис с уже выбранными параметрами. Алиас не должен обходить право, кулдаун или проверку текущей сессии. Если приходится копировать одинаковые проверки в три места, вынесите их туда, где выполняется само действие.

Перед стартом раунда сервис проверяет, что игрок авторизован, не состоит в другой игре и может получить доступный слот. Перед покупкой — баланс, товар и идентификатор операции. Между автодополнением и выполнением состояние меняется, поэтому скрытие недоступного аргумента в подсказках является удобством, а не защитой.

Тестируйте основную форму, алиас и подкоманду аккаунтом без OP. Эффективные права игрока.

Регистрация зависит от выбранного API

Для обычного Bukkit-style проекта команды могут описываться в plugin.yml и связываться с исполнителем в коде. Paper также предоставляет документированный API команд с деревом аргументов. Выберите один понятный способ для целевого выпуска и не смешивайте примеры разных версий без проверки. Регистрация строки в manifest не реализует её игровое действие.

Для подсказок не выполняйте тяжёлый запрос к БД на каждый символ. Используйте небольшой допустимый список или ограниченный кэш. Если подсказка содержит имя другого игрока, проверьте, разрешено ли его показывать этому пользователю. Консоль и клиент имеют разное представление текста, поэтому протестируйте справку обоими способами.

  1. Введите команду без аргументов.
  2. Выберите верный и неверный режим.
  3. Проверьте слишком большое, отрицательное и нечисловое значение.
  4. Повторите запуск в уже активной сессии.
  5. Выполните алиас без нужного права.
  6. Запустите поддерживаемую форму из консоли.

Подтверждение и сообщения

Разрушительная операция, расход валюты или окончательный выход должны показывать конкретный итог до подтверждения. Свяжите подтверждение с параметрами и коротким сроком: нельзя подтверждать старую цену после изменения товара. У повторного запроса должен быть идентификатор, позволяющий найти уже выполненную операцию.

Сообщение «успешно» отправляйте после подтверждения результата. При фоновой работе сначала покажите ожидание, затем выполненный итог либо понятную ошибку. Если игрок отключился, запись результата может завершиться без отправки чата. На повторном входе данные должны отражать реальное состояние, а не последний текст, который успели показать.

Проверяйте поведение после обновления

Установленный сторонний плагин способен занять тот же короткий alias. Проверьте, какой обработчик вызывается, и предусмотрите ясную основную команду. Не заставляйте игрока угадывать namespace. При переименовании опубликованной команды сохраняйте осмысленный переход или обновляйте все кнопки, справку и сообщения вместе.

Для диагностики логируйте тип операции, безопасные параметры и итог, связывая их с идентификатором. Не записывайте пароль регистрации или секретный токен как обычный аргумент. Команда хороша, когда её можно повторить из понятной инструкции и определить, почему конкретная попытка была разрешена или отклонена.

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

Где проверять право: в GUI или команде?

В фактической операции сервиса, а интерфейсы могут дополнительно показывать доступность. Так ручная команда и алиас не обходят политику.

Можно ли считать автодополнение защитой?

Нет. Игрок может ввести значение вручную, а состояние измениться после подсказки. Исполнение повторно проверяет тип, диапазон и доступ.

Почему консоль вызывает ClassCastException?

Обработчик, вероятно, приводит исполнителя к Player без проверки. Либо поддержите текстовую форму консоли, либо объясните, что нужен игрок.

Когда отправлять успешное сообщение?

После подтверждённой записи или выполнения операции. Начало асинхронного запроса ещё не означает завершённую покупку или награду.

Просмотры: … · Один просмотр с браузера за день

Было полезно?Общий счёт: …

Одна реакция с браузера. Нажмите повторно, чтобы снять её, или выберите другую.

Источники и документация

Minecraftразработка плагиновPaper

Читайте дальше