Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Помогите Command Handler

Быдло код?

  • 100%

  • 80%

  • 60%

  • 40%

  • 20%

  • Идеал = 0%


Результаты видны после голосования.
Проверяю вот так, а логика в другом классе
Код:
        if (args.length == 1) {
            if (args[0].equals("cmdone")) {
                return new OnCmdOne().onCommand(cd, args);
            } else if (args[0].equals("cmdtwo")) {
                return new OnCmdTwo().onCommand(cd, args);
            } else {
                data.sendMessage("bla bla bla");
                return true;
            }
        }

public abstract boolean onCommand(ConfigData cd, String[] args)
ОЗУ не жалеешь.
Понятно, ситуация похоже у всех, у меня с утра появилось некоторое соображение, что можно сделать, я попробую реализовать идею и покажу вам. Без аннотаций, другой вариант немного, более табличный :)

Пока я пишу код, частично опишу задумку для обсуждения:
  • Предлагается интерфейс с методом, реализация тела которого будет выполнять код нужной команды:
    Java:
    public abstract interface FairyCommand
    {
        public abstract void execute(CommandSender sender, FairyArgument[] args) throws CommandAnswerException;
    }
    . Почему так?: 1. Тип результата void — большая краткость кода. Возвращаемые текстовые строки должны быть выкинуты через исключение, а оно уже имеет несколько конструкторов для удобства. 2. Количество аргументов в массиве args всегда будет равно числу заявленных при регистрации аргументов — уходят проверки на null и .length.
  • Класс аргумента FairyArgument имеет методы isPresent (проверка на наличие, если он не обязательный), getType (вернёт один из типов аргумента, которые перечислены в отдельном enum), и методы get### для опередлённых типов, которые заранее распарсены. Например, для аргумента типа FairyArgumentType.PLAYER нужен вызов Player getPlayer(). Список аргументов передаётся при регистрации команды с помощью отдельного описания FairyArgumentDesc, в котором можно указать его название, необходимость (если необходимый аргумент не был введён, команда не будет выполнена, а sender получит корректное описание синтаксиса команды), и т.п.
Второй пункт порадовал, а вот первая фигня не удобная. ИМХО.
 
Убегал на семинар по работе, вернулся, продолжаю.
а вот первая фигня не удобная. ИМХО.
Исключение весьма простое.
В моём случае одна строка с throw выгоднее, чем три действия: замена всяких {DARKBLUE} на нормальные цветовые коды §1, отсылка сообщения sender-у, и return. Плюс конструкторы есть для List<String>, String[] и просто String. Я придумал, я пользуюсь, я радуюсь :)

Пока выходит такой выкидыш:
Java:
public final FairyCommandExecutor commands = new FairyCommandExecutor(this);
...
@Override
public void onLoad() {
    commands.register(
        "rscp",
        new CommandHub(this),
        new FairyArgumentDesc[]
        {
            // Конструктор для регистрации субкоманды: название, обработчик, её аргументы
            new FairyArgumentDesc(
                "user", new CommandHubUserList(this), new FairyArgumentDesc[]
                {
                    // Конструктор с указанием только типа аргумента
                    new FairyArgumentDesc(FairyArgumentType.PLAYER).setCaption("").setRequired(true),
                }
            ).setCaption("Player section").setHint("pex-like commands with users"),
            // Конструктор с аргументами: type, required
            new FairyArgumentDesc(FairyArgumentType.KEYWORD, false),
        });
    ...
}
@Override
public void onEnable() {
    commands.onEnable();
    ...
}
@Override
public void onDisable() {
    commands.onDisable();
    ...
}
Жозенько выглядит.
Обработчики, тем не менее, попроще:
Java:
/* /rscp user <user> list (permissions|groups) */
public class CommandHubUserList implements IFairyCommand
{
    private final JavaPlugin plugin;
    public CommandHubUserList(BukkitPluginMain plugin)
    {
        this.plugin= plugin;
    }
    @Override
    public void execute(CommandSender sender, FairyArgument[] args) throws CommandAnswerException
    {
        switch(args[0].getKeyword()) // Я уверен, что аргумент 0 есть и его тип KEYWORD
        {
            case "permissions":
                // DO HERE
                break;
            case "groups":
                // DO HERE
                break;
            default:
                throw new CommandAnswerException("{_LR}Only `permissions` or `groups`, please.");
        }
        // Ничуть не сложнее, чем sender.sendMessage() и потом return true!
        throw new CommandAnswerException("{_LG}Done.");
    }
}
Пока есть проблема для суб-команд: если до субкоманды были аргументы простых типов (PLAYER, INTEGER), как получить к ним доступ... В примере для продолжения работы нужно получить Player из аргумента user родительской команды CommandHub.
И ещё ничего нет про пермишены :) Но тут тоже задумка такая, что норм.
В общем, пока творю... =\
Закоммитил текущие разработки :)
https://github.com/.../Bukkit/Commands
Прошу ознакомиться и остановить меня, пока не поздно (либо помочь советом).
 
Последнее редактирование:
Да, очень удобно, если есть очень много подкоманд
Я имел в виду не удобство, а оптимальность. Я тоже для каждой команды делаю класс обработчик, но не создаю его при каждой команде, а просто регистрирую его в OnEnable.
 
https://github.com/RuBukkit/FairyCommands/
Решил сделать прослойку небольшой библиотечкой.
Что хочется:
  • Чтобы аргументы, приходящие в IFairyCommand, не было необходимости проверять на количество и null.
    • Все аргументы, помеченные при регистрации как required, действительно имеются.
  • Автоматическая проверка прав с подстановками. Поясняю на примере: команда "/dothat limit abc". Первый аргумент опустим, второй является каким-то идентификатором у предполагаемого плагина. Прописываем пермишен обработчику команды в таком виде: "dothat.perm.{$1}" или "dothat.perm.{$id}". Соответсвенно, для значения аргумента "abc" перед выполнением команды произойдёт проверка CommandSender-а на пермишен "dothat.perm.abc".
  • Парсинг интервалов времени вида "1y 12 weeks 1 hour" в кол-во int секунд.
  • Тип аргумента TEXT: /dothat fuck that "and here we have long text". P.S. А сейчас это просто поделится на несколько args?
  • Автоматическая генерация справки (хотя бы выдача списка доступных команд и их правильного синтаксиса).
    • Я даже подумал, что можно сделать каждому аргументы хинт (onCover), который описывал бы его в тексте справки.
  • Субкоманды. Обработчики более глубокого уровня. Например, "/dothat kill ololonka" делает одно, а "/dothat reload" другое :)
  • Ну и само собой — простота использования.
P.S. do that, а не dot hat.
Помогите хотя бы составить требования в библиотечке.
Вообще у ТС-а это всё есть по ссылке в первом посту :D Ну почти всё, и почти красиво. Чуть попозже попизжу код и у него.
 
Последнее редактирование:
Я имел в виду не удобство, а оптимальность. Я тоже для каждой команды делаю класс обработчик, но не создаю его при каждой команде, а просто регистрирую его в OnEnable.
для настройки чего-либо такие команды приемлемы, т.к. вызываются, один раз, при настройке, делать в таких случаях оптимизацию нет необходимости, с моей точки зрения, а читабельность в разы повышается (пропорционально деоптимизации xD)
 
Вс
https://github.com/RuBukkit/FairyCommands/
Решил сделать прослойку небольшой библиотечкой.
Что хочется:
  • Чтобы аргументы, приходящие в IFairyCommand, не было необходимости проверять на количество и null.
    • Все аргументы, помеченные при регистрации как required, действительно имеются.
  • Автоматическая проверка прав с подстановками. Поясняю на примере: команда "/dothat limit abc". Первый аргумент опустим, второй является каким-то идентификатором у предполагаемого плагина. Прописываем пермишен обработчику команды в таком виде: "dothat.perm.{$1}" или "dothat.perm.{$id}". Соответсвенно, для значения аргумента "abc" перед выполнением команды произойдёт проверка CommandSender-а на пермишен "dothat.perm.abc".
  • Парсинг интервалов времени вида "1y 12 weeks 1 hour" в кол-во int секунд.
  • Тип аргумента TEXT: /dothat fuck that "and here we have long text". P.S. А сейчас это просто поделится на несколько args?
  • Автоматическая генерация справки (хотя бы выдача списка доступных команд и их правильного синтаксиса).
    • Я даже подумал, что можно сделать каждому аргументы хинт (onCover), который описывал бы его в тексте справки.
  • Субкоманды. Обработчики более глубокого уровня. Например, "/dothat kill ololonka" делает одно, а "/dothat reload" другое :)
  • Ну и само собой — простота использования.
P.S. do that, а не dot hat.
Помогите хотя бы составить требования в библиотечке.
Вообще у ТС-а это всё есть по ссылке в первом посту :D Ну почти всё, и почти красиво. Чуть попозже попизжу код и у него.
Все, до корочки - есть в моем коде. И пермишены, и фиксед аргументы, и тексты в скобках, и любой парсер типа "text" или "time" создать проще некуда, в switch дописать. Справка тоже есть, и ошибки в комманде указывает если совпадают все fixed аргументы. И просто. Смысла от этой всей бредятины с выкидышами..)) Имхо.
 
Возможно, я не разобрался дрстаточно в том, что ты предложил.
А ты плохо разрекламировал ))
Я до пнд офф, потом подключусь к происходящему заново...
 
Возможно, я не разобрался дрстаточно в том, что ты предложил.
А ты плохо разрекламировал ))
Я до пнд офф, потом подключусь к происходящему заново...
Ну смотри:

Создаешь класс с командами. К каждой команде, на каждый набор фиксированых аргументов создаешь функцию.
@CommandExecutor(
cmd="тут команда",
args="тут аргументы, и если ввели именно их то вызовется функция внизу",
desc="Описание для вывода их в cmd+" help"",
perm="пермишен")
public void (sender, cmd, label, args) { }

В args он возвращает уже "пофикшеные" аргументы, где " some string blablabla" уже занимает не три разные ячейки в массиве а одну, причем скобки удаляются.

Дальше. Аргументы бывают разных типов: string, numeric, player, onplayer, location, time и тд, их можно легко добавлять/удалять/изменять. Все проверки для них тоже легко меняются. Каждый аргумент проверяется отдельно.

Если совпали все фиксед аргументы (допустим, что нужные это "command arg1 [onplayer:hahhahaSomePlayer] arg2", а введенные это "command arg1 NicknameOfOnlinePlayerThere arg2"), но игрок hahhahaSomePlayer оффлайн (а onplayer означает, что он должен быть онлайн) то система напишет в чате, что игрок такой-то - оффлайн. И так со всеми аргументами.

В ближайшее время сделаю код читабельным и добавлю систему не обезательных аргументов типа "--тутбуква"
 
Как ты смотришь на то, чтобы сделвть из этого куска кода минибиблиотеку, и её могли подключать все, кому она нужна?. Заготовка твоя baseplugin тоже хороша, но подходит для интеграции чуть менее, особенно в готовые плагины.
Плюс нужно лучше задокументировать. Гоу совместно?
 
Как ты смотришь на то, чтобы сделвть из этого куска кода минибиблиотеку, и её могли подключать все, кому она нужна?. Заготовка твоя baseplugin тоже хороша, но подходит для интеграции чуть менее, особенно в готовые плагины.
Плюс нужно лучше задокументировать. Гоу совместно?

Столько мусора для простой работы, вы серьёзно? У меня ощущение, что человек стянул большой кусок с другого места, куски комментов как бы намекают.

Сделать плагин, чтобы его использовали для написания плагинов? У каждого собственный путь, я лично большого удобства в данной плагине не вижу, у меня своя маленькая библиотечка для быстрого написания небольших плагинов, куча различных реализация многих вещей, начиная от простой и изящной реализации построения функционального интерфейса с действиями на основе сундуков, и заканчивая заготовкой по работе с orm.
 
Как ты смотришь на то, чтобы сделвть из этого куска кода минибиблиотеку, и её могли подключать все, кому она нужна?. Заготовка твоя baseplugin тоже хороша, но подходит для интеграции чуть менее, особенно в готовые плагины.
Плюс нужно лучше задокументировать. Гоу совместно?
Оно сейчас в таком херовом состоянии, забагованом и тд что выкладывать его смысла нет. Как доделаю, код отформатирую и тп - напишу.
 
Сделать плагин, чтобы его использовали для написания плагинов? У каждого собственный путь, я лично большого удобства в данной плагине не вижу, у меня своя маленькая библиотечка для быстрого написания небольших плагинов, куча различных реализация многих вещей, начиная от простой и изящной реализации построения функционального интерфейса с действиями на основе сундуков, и заканчивая заготовкой по работе с orm.
Я веду речь не про плагин, а тоже про библиотеку. Небольшую, начиная с обработки команд и заканчивая произвольными удобными плюшками. Всё равно каждый второй пишет для себя её родную — готового материала по приватным кодовым базам уже более чем достаточно.
 
Я веду речь не про плагин, а тоже про библиотеку. Небольшую, начиная с обработки команд и заканчивая произвольными удобными плюшками. Всё равно каждый второй пишет для себя её родную — готового материала по приватным кодовым базам уже более чем достаточно.
Есть идея сделать один базовый плагин, который нужен был бы для каждого написаного с ним плагина. То есть наследовать специальный класс в библиотеке, где будут все методы, включая систему сообщений (с автопереводом под язык игрока), подключение к БД (либо вообще без нее, либо MySQL либо SQLite - по выбору, с автоматическим подключением из конфига), работа с конфигами и тп.. еще парочку "статических" классов типа утилсов и тп..

Проблема только в моем времени, и в моем "хорошем" знании Java =)
 
Твоя идея называется "сделать библиотеку". Нет никакого смысла делать всё в одном классе и затем наследовать плагин от него. Код должен быть логично структурирован. Все плюшки — понятны, но опциональны.
 
Твоя идея называется "сделать библиотеку". Нет никакого смысла делать всё в одном классе и затем наследовать плагин от него. Код должен быть логично структурирован. Все плюшки — понятны, но опциональны.
Ты не понял - в новом плагине наследовать (extend'ить) некий интерфейс BasePlugin. В нем будут методы типа getDatabase, getMessages и тп (понятное дело, что все будет не так просто).. А библиотека будет подключатся как плагин, точно так-же, как тот-же SQLibrary. В этой либе будет и хандлер комманд, и логгер, и утилсы, и работа с БД, и с конфигами/месседжами и что только не будет.. Каждый класс будет работать на main Class того самого плагина, дабы упростить код.

То-есть эта либа это как большой хандлер всего.
 
Я всё понял, делай, посмотрим как будет получаться.
Если просветится что-то толковое — буду помогать.
Баланс.png
 
Ты не понял - в новом плагине наследовать (extend'ить) некий интерфейс BasePlugin. В нем будут методы типа getDatabase, getMessages и тп (понятное дело, что все будет не так просто).. А библиотека будет подключатся как плагин, точно так-же, как тот-же SQLibrary. В этой либе будет и хандлер комманд, и логгер, и утилсы, и работа с БД, и с конфигами/месседжами и что только не будет.. Каждый класс будет работать на main Class того самого плагина, дабы упростить код.

То-есть эта либа это как большой хандлер всего.

Ты не поверишь, но многие вещи делаются очень просто за минимальное количество времени и с минимальным количеством строк кода. Ты пользуешься jdbc, я пользуюсь jpa и orm, ты пользуешься читым yaml и думаешь, что он удобен, а я использую gson, т.к. его возможности по сериализации позволяют делать более простые конфиги не заморачиваясь на создание методов read/write. Ты пишешь какой-то непонятный логгер, а до тебя уже придумали log4j,slf4j и прочие, ты думаешь от куда взять хандлер комманд и как работать с рефлексией - а уменя простой и понятный код, который в понимание осилишь даже ты:
http://pastebin.com/vimiALNw .

Ты будешь думать об удобной основе плагинов, динамической перезагрузке плагинов, а это достигается с помощью скриптовых языков и сделано в VarScript (groovy мне нравится).

Посему я могу сказать, что ты делаешь велосипед, в качетсве собственного развития, в качестве собственных граблей он пойдёт, а в качестве профессиональной основы для других плагинов - без обид, но гавно.
 
А что тут такого сложного в абстрактном классе, который реализует CommandExecutor?
Даже даун поймет твой код. Уж поверь.

PS. Насчет дауна может быть получилось уже слишком, но человек понимающий основы Java сможет понять представленный выше код.
 

Поделиться этой страницей

Назад
Верх