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

Туториал [Урок] Пишем первый плагин

Тема в разделе "Руководства, инструкции, утилиты", создана пользователем Ission, 31 окт 2012.

?

Продолжать писать ещё туториалы?

  1. Да

    354 голосов
    76,3%
  2. Нет

    6 голосов
    1,3%
  3. Я злой лосось, иду хлестать медведей

    104 голосов
    22,4%
  1. Автор темы
    Ission

    Ission Старожил Девелопер Пользователь

    Баллы:
    173
    Skype:
    lokivava
    Это статья является некоей базой для написания плагинов. По ней Вы сможете написать свой первый и последующие плагины.
    Для начала Вам понадобится jar архив с сервером bukkit/craftbukkit,среда для программирования(мне более удобна NetBeans), Java SDK, описание API bukkit'а. Помните, что совместимость версий идёт по нарастающей: первые версии-1.8.1 и 1.0.0 - ... Это два разряда, совместимость между которыми обеспечивать нужно отдельно.
    Главные места я постараюсь выделить так, а места на которые нужно обратить внимание так.
    Мы будем писать на 1.0.0+.
    Если у Вас есть среда, сервер, мы можем приступать.
    Для начала создайте новый проект java: иблиотека классов java.
    После этого мы должны внутрь нашего проекта подключить в качестве библиотеки наш сервер.

    В пакете по умолчанию нам нужно создать файл YAML plugin.yml.
    Как наполнять его описано в http://wiki.bukkit.org/Plugin_YAML/ru .

    Далее создадим пакет. Название пишем любое, но оно не должно содержать пробелы и любые не буквенные символы, кроме точек нижнего подчёркивания, цифр, но он не должен на них начинаться(подчёркивания не касается).
    1. Несовпадения имен пакетов гарантированы при использовании имени домена в обратном порядке: com.javasoft.jag - "com" и "edu" - обычно в верхнем регистре, но теперь - рекомендуется в нижнем.
    2. Имена классов - описательные существительные, с заглавной буквой в начале каждого слова: PolarCoords (ПолярныеКоординаты). Часто (но не всегда) интерфейсы называются "что-то-able", например, "Runnable" (запускаемый), "Sortable" (упорядочиваемый). Предупреждение: java.util.Observable не интерфейс, а java.util.Observer - интерфейс. Эти два имени плохо продуманы.
    3. Переменные и поля - существительное/фраза с существительным, с первой буквой в нижнем регистре и первой буквой подслова - в верхнем: currentLimit (текущийПредел).
    4. Имена методов - глагол/фраза с глаголом, с маленькой буквы, каждое подслово - с большой: calculateCurrentLimit (вычислитьТекущийПредел).
    5. Имена констант (с модификатором final) пишутся большими буквами: UPPER_LIMIT
    Теперь мы начнём наполнять пакет.
    Нам нужен главный класс. Создаём в нашем классе новый класс java и назовём его любым именем (может содержать буквы и цифры (но не сначала)). Рекомендую его назвать main, дабы не путаться.
    Помните: т.к. java является кроссплатформенной, то регистр имеет значение.
    Теперь открываем этот класс (если он не открыт) и видим нечто подобное:
    Код:
    package названиеВашегоПакета;
    
    public class названиеВашегоКласса
    {
    
    }
    Комментарии не влияют на код и пишутся так: между /* и */ или после // вся строка.
    Класс - структурная единица языка, вторая по величине, после пакета.
    Метод - структурная единица класса, которая может принимать, возвращать значения и выполнять различные действия.
    Переменная - объект, который может хранить некоторую информацию.
    Тела всех методов и классов находятся внутри фигурных скобок, после их объявления.
    У всех классов, методов и переменных есть 3 модификатора доступа: public - доступна из любого места, protected - доступна только внутри пакета и private - доступна только внутри класса.
    Расширим стандартный класс JavaPlugin - добавим после названия Вашего класса и до открывающейся фигурной скобки:
    Код:
    extends JavaPlugin
    Компилятор должен среагировать и ввести нас в курс, что у нас в коде ошибка на слове JavaPlugin. Если в компиляторе есть структура, отвечающая за полуавтоматическое исправление ошибок, то она предложит добавить император импорта ...JavaPlugin. Разрешим ему. Впоследствии я не буду упоминать такие моменты.
    Переопределяем стандартный метод JavaPlugin, вводим в теле класса:
    Код:
    @Override
    public void onEnable()
    {
    
    }
    Это метод, который вызывается при старте нашего плагина. Также существуют методы public void onDisable() и public void onLoad(), они соответственно запускаются при выключении и подгрузке плагина.
    В данном методе желательно подгружать все нужные ресурсы для работы плагина, шерстить файл настроек и пр.
    Давайте введём в теле класса пару переменных и произведём подгрузку файла настроек.
    Создадим общедоступную переменную строкового типа:
    Код:
    public String название_переменной;
    и булеву переменную, для доступа изнутри класса:
    Код:
    private boolean другое_название;
    Чтобы удобнее взаимодействовать с кодом, создадим приватный метод для проверки и создания файла настроек. Метод не должен принимать никаких данных, и возвращать (void).
    В теле метода инициализируем локальную переменную для работы с конфигом:
    Код:
    FileConfiguration config = this.getConfig();
    Теперь начинаем шерстить настроечный файл. Проверим, есть ли в нём булево поле "enable", если нет, то создадим его и присвоим ему значение true:
    Код:
    if(!config.isBoolean("enable"))
        config.set("enable", true);
    Теперь запишем значение этого поля в ранее определённую булеву переменную:
    Код:
    другое_название = c.getBoolean("enable");
    Проделаем ту же операцию с строковым полем "какое-то_поле":
    Код:
    if(!config.isString("название_переменной"))
        config.set("какое-то_поле", "значение_по_умолчанию");
    название_переменной = config.getString("какое-то_поле");
    По окончании операций с файлом настроек, сохраним его:
    Код:
    this.saveConfig();
    Вызываем этот метод в теле onEnable(). Если значение, которое мы получили из поля "enable" равно false, то выключаем плагин:
    Код:
    if(!другое_название)
    {  getServer().getPluginManager().disablePlugin(this); return; }
    Теперь создадим базу для обработки событий. Для этого нам понадобится класс-инструментарий Listener.
    В качестве такого класса можно использовать основной класс, внутренний класс или новый класс.
    После названия класса вводим:
    Код:
    implements Listener
    Нам подойдёт одна из следующих конструкций, они применяются в зависимости от нужд и количества различных событий, предполагаемых к обработке:
    Код:
    Bukkit.getServer().getPluginManager().registerEvents(new название_класса_обработчика(), this);
    //Вызываем в главном классе, желательно в методе void onEnable()
    
    Bukkit.getServer().getPluginManager().registerEvents(переменная_объекта_класса_обработчика, this);
    //Вызываем в главном классе, желательно в методе void onEnable(), но после инициализации  переменной
    
    Bukkit.getServer().getPluginManager().registerEvents(this, переменная_содержащая_объект_главного_класса_плагина);
    //Вызываем в самом классе-обработчике, если это основной класс, то не рекомендую использовать для большого количества различных событий
    В качестве обработчика событий используются ничего не возвращающие методы класса-обработчика, принимающие только один объект-расширение стандартного класса bukkit - Event.
    Также у таких методов должна быть аннотация @EventHandler.

    Вот так выглядит обработка простого события входа игрока на сервер и оповещение лично всех игроков, которые онлайн, с проверкой наличия разрешения:
    Код:
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent e)
    {
        if(e.getPlayer().hasPermission("не_показывать_что_я_вошёл"))
            return;
        String name = e.getPlayer().getName();
        for(Player p:Bukkit.getServer().getOnlinePlayers())
            if(!p.getName().equals(name))
                p.sendMessage("Игрок "+name+" вошёл на сервер!");
    }
    Переходим к обработчикам команд.
    Каждая команда, которая отправляется в плагин, должна быть заранее описана в plugin.yml.
    В качестве класса-обработчика выступает класс-инструментарий CommandExecutor.
    В качестве такого класса можно использовать основной класс, внутренний класс или новый класс.
    Один класс может обрабатывать несколько различных команд.

    После названия класса вводим:
    Код:
    implements CommandExecutor
    Нам подойдёт одна из следущих конструкций для определения такого класса для сервера. Но, в отличие от событий, такой метод вызывается от имени плагина:
    Код:
    getCommand("название_команды").setExecutor(new название_класса_обработчик());
    //Вызываем в главном классе, желательно в методе void onEnable()
    
    getCommand("название_команды").setExecutor(переменная_объекта_класса_обработчика);
    //Вызываем в главном классе, желательно в методе void onEnable(), но после инициализации  переменной
    
    переменная_содержащая_объект_главного_класса_плагина.getCommand("название_команды").setExecutor(this);
    //Вызываем в самом классе-обработчике
    Метод, обрабатывающий команды - переопределённый метод
    Код:
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
    Данный метод принимает несколько значений:
    CommandSender sender - отправляющий сообщение, может быть объектом ConsoleCommandSender, при отправке из консоли, и Player, при отправке игроком.
    Command command - объект отправленной команды.
    String label - название команды без символа '/' и аргументов.
    String[] args - список аргументов команды, если их нет - то равен null.
    Как Вы должны были заметить, метод возвращает boolean, означающий выполнена команда верно или нет.
    Вот так выглядит обработка простой команды, приветствующей определённого игрока с запретом на использование из консоли/без ника игрока/на себя:
    Код:
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
    {
        if(sender instanceof ConsoleCommandSender)
        {  sender.sendMessage("Запрещено использовать из консоли!"); return true; }
        if(args==null||args.length!=1||Bukkit.getServer().getPlayer(strings[0])==null)
        {  sender.sendMessage("Ник игрока указан неверно!"); return false; }
        if(Bukkit.getServer().getPlayer(strings[0]).getName().equals(sender.getName()))
        {  sender.sendMessage("Запрещено использовать на себя!"); return false; }
        Bukkit.getServer().getPlayer(strings[0]).sendMessage("Вас приветствует "+sender.getName()+"!");
        return true;
    }
    Я уверен, что данных знаний достаточно для написания своего собственного плагина. Можно приступать!
     
    Последнее редактирование: 13 окт 2015
    Lassiter, Левый, AngryLogic и 32 другим нравится это.
  2. Хостинг MineCraft
    <
  3. dimavv

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

    Баллы:
    88
    Имя в Minecraft:
    dimavv
    Cпасиб попробую что-то написать)
     
  4. Kachalov

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

    Баллы:
    103
    Skype:
    alex-kachalov-01
    Имя в Minecraft:
    Kachalov
    Может быть Вы знаете, как изменить поведение крипера? Например, сделать дружелюбным?
     
  5. Kachalov

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

    Баллы:
    103
    Skype:
    alex-kachalov-01
    Имя в Minecraft:
    Kachalov
    Нашёл. Вот только, как прикрутить?
    Код:
        public void onEntityTarget(EntityTargetEvent event)
        {
            if (((event.getTarget() instanceof Player)) &&
            ((event.getEntity() instanceof Creeper)))
            {
            event.setCancelled(true);
            }
        }
     
  6. Den_Abr

    Den_Abr Старожил Девелопер Пользователь

    Баллы:
    173
    Skype:
    Den_Abr
    Имя в Minecraft:
    Den_Abr
    А почему ничего не сказано про перезагрузку конфига, думаю, что тоже полезно
     
  7. valexv14

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

    Баллы:
    123
    Skype:
    valexv13
    Имя в Minecraft:
    valexv12
    Мда, знаю ошибок много, но чего-то не так я сделал. Я полный нуб в программировании вот учится начинаю... Если кто-то свободен, и есть желание помогите плиз. Посмотрите что у меня не так.... подправьте, и расскажите мне об моих ошибках... Опять же - если не трудно конечно...
    Мой плУгин.
    Смысл плагина - тупо как в туториале выводить сообщение "Игрок зашел", и скрывать эту надпись если есть permissions.
     
  8. valexv14

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

    Баллы:
    123
    Skype:
    valexv13
    Имя в Minecraft:
    valexv12
    А нет. Все получилось. Просто мимо класса функции совал :). Ну теперь я и вправду смог написать хотя бы такой плагин. Признаюсь - это для меня достижение.:D
     
    tOshKa нравится это.
  9. tOshKa

    tOshKa Активный участник Заблокирован

    Баллы:
    88
    Skype:
    antloginov
    Имя в Minecraft:
    MegaFlary
    Можете скинуть исходный код? ;)
     
    valexv14 нравится это.
  10. DragonX

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

    Баллы:
    173
    Skype:
    dx3756
    Имя в Minecraft:
    DragonX256
    Я злой лосось, иду хлестать медведей xD
     
    ZloYCRipeeR, eshkin и valexv14 нравится это.
  11. ujkbktj

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

    Баллы:
    123
    lssion,мой стол шатается когда я читаю твои шутки)))
     
  12. Сергей-minecrafter

    Сергей-minecrafter Активный участник

    Баллы:
    68
    Имя в Minecraft:
    Fighter
    Я чё-то не понял :(

    Я открываю класс, а там написано:

    /*
    * To change this template, choose Tools | Templates
    * and open the template in the editor.
    */
    package ru.smallpresent.smallpresentplugin;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import org.openide.cookies.EditorCookie;
    import org.openide.awt.ActionID;
    import org.openide.awt.ActionReference;
    import org.openide.awt.ActionReferences;
    import org.openide.awt.ActionRegistration;
    import org.openide.util.NbBundle.Messages;

    @ActionID(
    category = "Edit",
    id = "ru.smallpresent.smallpresentplugin.SmallpresentAction")
    @ActionRegistration(
    iconBase = "ru/smallpresent/smallpresentplugin/0537.png",
    displayName = "#CTL_SmallpresentAction")
    @ActionReferences({
    @ActionReference(path = "Menu/File", position = -100),
    @ActionReference(path = "Toolbars/File", position = -100)
    })
    @Messages("CTL_SmallpresentAction=SmallPresent")
    public final class SmallpresentAction implements ActionListener {

    private final EditorCookie context;

    public SmallpresentAction(EditorCookie context) {
    this.context = context;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
    // TODO use context
    }
    }

    Как понять?
     
  13. ZloYCRipeeR

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

    Баллы:
    153
    Skype:
    cry_zloy
    Имя в Minecraft:
    ZloYCRipeeR
    Можно пример кода простого плагина? И как добавить плагину permissions?
     
  14. Сергей-minecrafter

    Сергей-minecrafter Активный участник

    Баллы:
    68
    Имя в Minecraft:
    Fighter
    с кодом у меня у самого проблемы... :(
    А про пермишены тут.
     
  15. hubridos

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

    Баллы:
    103
    Skype:
    hubridos
    Имя в Minecraft:
    Hubrid
    А что делать ели я хочу просто добавить команду для всех людей, ну например /trade ***** и в чате отображается [Торг][Hubrid] ***** как добавить команду и пермы к ней?
     
    ВремяПриключений нравится это.
  16. BeYkeR

    BeYkeR Старожил Девелопер Пользователь

    Баллы:
    173
    Команды сам узнаешь:), а пермы можно сделать так:
    Код:
    if(sender.hasPermission("test.testperm")){
    //какой-то код
    }else{
    //Если у игрока нет этого перма сверху
    sender.sendMessage(ChatColor.RED + "У тебя нет прав!"); 
    }
    
     
    ВремяПриключений нравится это.
  17. unlimited

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

    Баллы:
    123
    Имя в Minecraft:
    infinity
    Код:
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    if(command.getName().equalsIgnoreCase("test")){
    //Если ввели команду /test - выполняем код ниже
    Player player = (Player)sender;//переводим сендера в плеера, так удобнее
    if(player.hasPermission("пермишионы")) {
    код если есть пермишионы
    player.SendMessage("текст");//отправит тому кто ввел команду "текст"
    return true;//просто чтоб было
    } else { код если нету }
    }
    return false;
     
  18. hubridos

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

    Баллы:
    103
    Skype:
    hubridos
    Имя в Minecraft:
    Hubrid
    А если писать под MCPC+
    http://ci.md-5.net/job/MCPC-Plus/503/
    я хочу апи добавить, мне нужно просто серв взять или там где приписка api :?
     
  19. Den_Abr

    Den_Abr Старожил Девелопер Пользователь

    Баллы:
    173
    Skype:
    Den_Abr
    Имя в Minecraft:
    Den_Abr
    Значения не имеет, подойдёт обычный буккит
     
  20. kaban1997

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

    Баллы:
    173
    Ну а что на счёт onUpdate()? Ведь иногда что-то же надо проверять для проверки в каждый тик.
     
  21. unlimited

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

    Баллы:
    123
    Имя в Minecraft:
    infinity
    Создаешь доп. поток, в нем цикл с задержкой
     

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