Хостинг серверов Minecraft playvds.com
  1. Вы находитесь в русском сообществе Bukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на русский язык плагины наших собратьев из других стран.
    Dismiss Notice

Туториал Выполнение комманд

Discussion in 'Руководства, инструкции, утилиты' started by DmitriyMX, Jun 19, 2012.

  1. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Продолжаю новый (улучшенный) цикл статей по написанию плагинов к Bukkit. Сегодня научимся делать обработчики комманд. Заодно отвечу на вопрос, который был задан очень давно в коммантариях, но так и получил ответа.

    Наш следующий простой плагин будет выводить в чат игрока сообщение, которое написано в отдельном текстовом файле. Все ссылки и сходники как всегда продублирую в конце статьи.

    Полагаю, что вы уже знаете, как в эклипсе создавать новый проект и делать заготовку плагина, так что перейдем сразу к делу.

    Когда я говорю о коммандах в майне, я имею ввиду такие комманды
    Code:
    /help Bukkit 2
    где «help» – это сама комманда, а «Bukkit» и «2» – ее аргументы(или параметры, как хотите).

    Каждой комманде можно задать псевдоним(alias) для упрощения работы. Например, принято давать короткие псевдонимы длинным коммандым. Псевдонимов к одной комманде может быть сколько угодно.

    Для обработки комманд, существует метод onCommand() c четыремя аргументами:
    • sender
      Источник выполнения комманды. Здесь хранится информация о том, кто вызвал комманду;
    • command
      Комманда, которую выполнили;
    • label
      Псевдоним этой комманды;
    • args
      Аргументы комманды.
    Возвращает этот метод true или false, в зависимости от того, выполнилась комманда или нет(что, кстати, решать вам).

    В виде кода, это выглядит следующим образом:
    Code:
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if(command.getName().equalsIgnoreCase("cmdplugin")){
            // что-то выполняется
            return true;
        }else if(command.getName().equalsIgnoreCase("helpme")){
            // что-то выполняется
            return true;
        }else if(command.getName().equalsIgnoreCase("dmitriymx")){
            if(args.length > 0){
                if(args[0].equalsIgnoreCase("blog")){
                    // DmitriyMX.ru
                    return true;
                }
            }
        }
        return false;
    }
    Как видите, тоже ничего сложного нету и при знании английского языка на уровне пятого класса(ну или с Google-Переводчиком в соседней вкладке), “магическое заклинание” command.getName().equalsIgnoreCase(«helpme») становится понятным и без дополнительных разъяснений.

    Но комманды не будут вот так сразу работать в нашем плагине, если мы их не опишем в plugin.yml. Делается это так:
    Code:
    name: Dmx Command Plugin
    main: ru.dmitriymx.bukkit.tutorial.CmdPlugin
    description: Пример плагина по работе с коммандами с сайта dmitriymx.ru
    version: 1.0
    commands:
      cmdplugin:
          description: This is a demo command.
          usage: /<command>
          aliases: [cmd1, cmd2]
      helpme:
          description: Demo help
          usage: /<command>
      dmitriymx:
          description: Copyright
          usage: /<command> blog
    Все эти описания(description) и примеры использования(usage) нужны лишь для грамотного вывода комманды /help. Если плагин делаете для себя, а не для других, то описание и примеры использования можете опустить. Главное, что бы сами комманды были перечислены.

    Так, с этим, думаю. разобрались. Теперь по поводу чтения текста из файла.

    Для начала скажу, что у каждого плагина есть(хоть и не обязательно) своя папка, имя которой определяется из параметра “name” в plugin.yml. Чтобы получить эту папку в коде плагина, понадобится метод getDataFolder(). Он возвращает объект File, который является ссылкой на папку плагина. Итого, чтобы нам получить ссылку на текстовый файл в такой папке, надо написать вот такой код:
    Code:
    File helpText = new File(getDataFolder(), “message.txt”);
    Но иметь тупо ссылку не достаточно для чтения из файла. Тем более, если он многострочный. Для этого нам понадобится вот такая конструкция:
    Code:
    File helpText = new File(getDataFolder(), "message.txt");
    StringBuilder fullText = new StringBuilder();
    try {
      BufferedReader buffer = new BufferedReader(new FileReader(helpText));
      String line;
      while((line = buffer.readLine()) != null){
          fullText.append(line).append(“\n”);
      }
      buffer.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    sender.sendMessage(fullText.toString());
    Теперь в переменной fullText у нас будет хранится весь текст из файла message.txt. Да, код кажется большим для такой маленькой задачи, но я сейчас объясню, что к чему.

    Сначала объявляется переменная fullText типа StringBuilder. О StringBuilder можете прочитать вот в этих ссылках: [1], [2]. Ну а я вам для понимания происходящего скажу так: StringBuilder в основном нужен, чтобы “склеить” две строки в одну.

    «- Нафига? Можно же простыми String обойтись и склеивать их через плюс! Вот так: fullText = fullText + line;»

    Да, так тоже можно. Но если почитаете спецификацию Java (ну или просто почитаете умные форумы на эту тему), то станет извесно, что метод склеивания строк через “плюс” более ресурсоёмкий, чем через append(). А я хочу чтобы вы писали хороший код, а не «быдло-индуский-код», как это иногда делают. Именно по этому я не перестаю вставлять в статьи ссылки на умные темы по Яве.

    Так, мы отвлеклись от темы.

    После объявления переменной, следует «открытие» файла в объект-буффер, через который и будет производится чтение самого файла. Да, можно было бы обойтись и просто FileReader-ом, но там не было бы замечательного метода readLine(). Тогда пришлось бы читать файл по-байтово, «вручную».

    Далее следует цикл, в котором мы присваиваем переменной line прочтенную строку и сразу же проверяем, не равна ли она «пустоте»(null). Цикл будет работать до тех пор, пока line не станет равным null, что будет означать, что файл прочтен до конца. В самом цикле, все прочтенные строки склеиваются в переменную fullText. Последней строкой в коде, мы отправляем sender-у то, что сумели прочесть.

    Вот собственно и всё. Теперь осталось только создать в папке plugins папку «Dmx Command Plugin», создать там файлик message.txt и нополнить его содержимым. Весь код плагина выглядит вот так:

    Code:
    package ru.dmitriymx.bukkit.tutorial;
     
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
     
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.event.Listener;
    import org.bukkit.plugin.java.JavaPlugin;
     
    public class CmdPlugin extends JavaPlugin implements Listener {
     
        @Override
        public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
            if(command.getName().equalsIgnoreCase("cmdplugin")){
                sender.sendMessage("this is \"cmdplugin\" command");
                return true;
            }else if(command.getName().equalsIgnoreCase("helpme")){
                File helpText = new File(getDataFolder(), "message.txt");
                StringBuilder fullText = new StringBuilder();
                try {
                    BufferedReader buffer = new BufferedReader(new FileReader(helpText));
                    String line;
                    while((line = buffer.readLine()) != null){
                        fullText.append(line).append("\n");
                    }
                    buffer.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                sender.sendMessage(fullText.toString());
                return true;
            }else if(command.getName().equalsIgnoreCase("dmitriymx")){
                if(args.length > 0){
                    if(args[0].equalsIgnoreCase("blog")){
                        sender.sendMessage("http://DmitriyMX.ru");
                        return true;
                    }
                }
            } 
            return false;
        }
    }

    Думаю, вы заметили отсутствие «точки входа» onEnable(). Тем самым, я хочу показать, что объявление onEnable не обязательно, если не требуется что-то проверить или сделать до того, как будет работать. Так мы могли бы в этом методе проверить, существует ли папка плагина, если в этой папке файлик message.txt и так далее. Но пусть это будет для вас «Домашним заданием»))


    Ссылки
    Следующая статья:
    Документация:
    Полезное:
    ________________________________________​
     
  2. Хостинг MineCraft
    <
  3. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Кстати да, в блоге я поправил, а тут забыл.

    Короче, есть еще один способ обработки комманд: CommandExecutor. Суть состоит в том, что вместо одного большого метода onCommand(), мы его “дробим” на несколько маленьких. Такой прием будет полезен только если обработчик действительно выходит большим, а не в 10 строчек.

    Так вот, преобразуем выше написанный метод в несколько мелких его аналогов. Вместо того, чтобы писать onCommand(), cоздаем 3 новых класса с интерфейсом CommandExecutor:
    Code:
    /* привожу пример одного класса. Остальные делаются по аналогии */
    package ru.dmitriymx.bukkit.tutorial;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
     
    public class HelpmeCommand implements CommandExecutor {
       @Override
       public boolean onCommand(CommandSender arg0, Command arg1, String arg2, String[] arg3) {
           // какие-то действия
           return true;
       }
    }
    Теперь в методе onEnable() пишем следующее:
    Code:
    getCommand("cmdplugin").setExecutor(new CmdPlgCommand());
    getCommand("helpme").setExecutor(new HelpmeCommand());
    getCommand("dmitriymx").setExecutor(new CopyrightAuthor());
     
  4. Boya

    Boya Активный участник

    Trophy Points:
    63
    Имя в Minecraft:
    Boya
    Сделал по рецепту. Чот выходит как-то так...

    04:22:51 [SEVERE] java.io.FileNotFoundException: message.txt (═х єфрхЄё эрщЄш є
    ърчрээ√щ Їрщы)
    04:22:51 [SEVERE] at java.io.FileInputStream.open(Native Method)
    04:22:51 [SEVERE] at java.io.FileInputStream.<init>(Unknown Source)
    04:22:51 [SEVERE] at java.io.FileReader.<init>(Unknown Source)
    04:22:51 [SEVERE] at ru.boya.bukkit.commander.Commander.<init>(Commander.j
    ava:23)
    04:22:51 [SEVERE] at sun.reflect.NativeConstructorAccessorImpl.newInstance
    0(Native Method)
    04:22:51 [SEVERE] at sun.reflect.NativeConstructorAccessorImpl.newInstance
    (Unknown Source)
    04:22:51 [SEVERE] at sun.reflect.DelegatingConstructorAccessorImpl.newInst
    ance(Unknown Source)
    04:22:51 [SEVERE] at java.lang.reflect.Constructor.newInstance(Unknown Sou
    rce)
    04:22:51 [SEVERE] at org.bukkit.plugin.java.JavaPluginLoader.loadPlugin(Ja
    vaPluginLoader.java:145)
    04:22:51 [SEVERE] at org.bukkit.plugin.SimplePluginManager.loadPlugin(Simp
    lePluginManager.java:305)
    04:22:51 [SEVERE] at org.bukkit.plugin.SimplePluginManager.loadPlugins(Sim
    plePluginManager.java:230)
    04:22:51 [SEVERE] at org.bukkit.craftbukkit.CraftServer.loadPlugins(CraftS
    erver.java:213)
    04:22:51 [SEVERE] at org.bukkit.craftbukkit.CraftServer.<init>(CraftServer
    .java:189)
    04:22:51 [SEVERE] at net.minecraft.server.ServerConfigurationManager.<init
    >(ServerConfigurationManager.java:53)
    04:22:51 [SEVERE] at net.minecraft.server.MinecraftServer.init(MinecraftSe
    rver.java:166)
    04:22:51 [SEVERE] at net.minecraft.server.MinecraftServer.run(MinecraftSer
    ver.java:432)
    04:22:51 [SEVERE] at net.minecraft.server.ThreadServerApplication.run(Sour
    ceFile:492)

    Что не так?
     
  5. Boya

    Boya Активный участник

    Trophy Points:
    63
    Имя в Minecraft:
    Boya
    Все. Разобрался.
    Спосибо за урок :)
     
  6. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    А как узнать, есть ли более 1 аргумента?
     
  7. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Code:
    if(args.length > 1){
        //введено больше 1 аргумента
    }
     
    kaban1997 likes this.
  8. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    А как всё что больше 1 аргумента выволить чат.
    Например
    if(args.length > 1){
    sender.sendMessage(args[0] + " причина: " + args[1] + args[2] + args[3]) ;
    }
    То есть, как это сократить?
     
  9. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Code:
    if(args.length > 1){
        StringBuilder sb = new StringBuilder(args[0] + " причина: ");
        for(int i = 1; i < args.length; i++){
            sb.append(args[i]);
        }
        sender.sendMessage(sb.toString());
    }
     
    kaban1997 and fromgate like this.
  10. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    Спасибо, помогло
     
  11. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    А как узнать совершилось ли действие, и если не совершалось то выполнялось другое?
     
  12. Dj Arktic

    Dj Arktic Активный участник Пользователь

    Trophy Points:
    93
    Skype:
    dj_arktic
    Имя в Minecraft:
    Dj_Arktic
    Пригодилось.
     
  13. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    ResultSet res = stat.executeQuery("SELECT * FROM `table` WHERE Player='"+args[0]+"'");
    while(res.next()){
    sRes5 = res.getString("123");
    }
    Надо чтобы если, он не нашёл в таблице то выдавал Сообщение
    If(res==nul) не помогает
     
  14. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    Нету такого
     
  15. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Если ничего не найдено, то res.next() вернет false
     
    kaban1997 likes this.
  16. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    Если написать так. то не робит
    Code:
    if(res.next()==false) sender.sendMessage("§cИгрок ненайден");
    while(res.next()) {
    sRes5 = res.getString("123");
    yaz=true;
    }
    Если написать так. то робит
    Code:
     while(res.next()) {
    sRes5 = res.getString("123");
    yaz=true;
    } 
     
  17. Автор темы
    DmitriyMX

    DmitriyMX Старожил Пользователь

    Trophy Points:
    153
    Skype:
    dmn550
    Потому что res.next() возвращает не какое-то статическое значение, а пытается "продвинуться" на следующий пункт выдачи. По этому первый код не корректен: в блоке while{...} тебе будет выдана вторая строчка найденой выдачи. И если её не будет (т.е. найдено только 1 значение), то результат res.next() будет false, следовательно while{...} не выполнится. И первая строка if{...} тоже не выполнится, т.к. res.next() попытается взять первую строку выдачи, которая будет. Следовательно, результат уже true, что не соответствует условию.

    Правильнее тут лучше так:
    Code:
    if(res.next()){
        do{
            sRes5 = res.getString("123");
            yaz=true;
        }while(res.next());
    }
     
    kaban1997 likes this.
  18. kaban1997

    kaban1997 Старожил Переводчик Пользователь

    Trophy Points:
    173
    Во, спс. норм объяснил + код рабочий
     
  19. countersem

    countersem Активный участник Пользователь

    Trophy Points:
    68
    А можно сделать что то типа звука оповещения?
    Т.е. например, если игрок написал в чат, то проиграется какой нибудь звук из игры.
     

Share This Page