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

  • Автор темы Автор темы Ission
  • Дата начала Дата начала

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

  • Да

    Голосов: 446 74,7%
  • Нет

    Голосов: 8 1,3%
  • Я злой лосось, иду хлестать медведей

    Голосов: 143 24,0%

  • Всего проголосовало
    597

Ission

Старожил
Девелопер
Пользователь
Баллы
173
Это статья является некоей базой для написания плагинов. По ней Вы сможете написать свой первый и последующие плагины.
Для начала Вам понадобится 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 является кроссплатформенной, то регистр имеет значение.
Теперь открываем этот класс (если он не открыт) и видим нечто подобное:
Java:
package названиеВашегоПакета;

public class названиеВашегоКласса
{

}
Комментарии не влияют на код и пишутся так: между /* и */ или после // вся строка.
Класс - структурная единица языка, вторая по величине, после пакета.
Метод - структурная единица класса, которая может принимать, возвращать значения и выполнять различные действия.
Переменная - объект, который может хранить некоторую информацию.
Тела всех методов и классов находятся внутри фигурных скобок, после их объявления.
У всех классов, методов и переменных есть 3 модификатора доступа: public - доступна из любого места, protected - доступна только внутри пакета и private - доступна только внутри класса.
Расширим стандартный класс JavaPlugin - добавим после названия Вашего класса и до открывающейся фигурной скобки:
Java:
extends JavaPlugin
Компилятор должен среагировать и ввести нас в курс, что у нас в коде ошибка на слове JavaPlugin. Если в компиляторе есть структура, отвечающая за полуавтоматическое исправление ошибок, то она предложит добавить император импорта ...JavaPlugin. Разрешим ему. Впоследствии я не буду упоминать такие моменты.
Переопределяем стандартный метод JavaPlugin, вводим в теле класса:
Java:
@Override
public void onEnable()
{

}
Это метод, который вызывается при старте нашего плагина. Также существуют методы public void onDisable() и public void onLoad(), они соответственно запускаются при выключении и подгрузке плагина.
В данном методе желательно подгружать все нужные ресурсы для работы плагина, шерстить файл настроек и пр.
Давайте введём в теле класса пару переменных и произведём подгрузку файла настроек.
Создадим общедоступную переменную строкового типа:
Java:
public String название_переменной;
и булеву переменную, для доступа изнутри класса:
Java:
private boolean другое_название;
Чтобы удобнее взаимодействовать с кодом, создадим приватный метод для проверки и создания файла настроек. Метод не должен принимать никаких данных, и возвращать (void).
В теле метода инициализируем локальную переменную для работы с конфигом:
Java:
FileConfiguration config = this.getConfig();
Теперь начинаем шерстить настроечный файл. Проверим, есть ли в нём булево поле "enable", если нет, то создадим его и присвоим ему значение true:
Java:
if(!config.isBoolean("enable"))
    config.set("enable", true);
Теперь запишем значение этого поля в ранее определённую булеву переменную:
Java:
другое_название = c.getBoolean("enable");
Проделаем ту же операцию с строковым полем "какое-то_поле":
Java:
if(!config.isString("название_переменной"))
    config.set("какое-то_поле", "значение_по_умолчанию");
название_переменной = config.getString("какое-то_поле");
По окончании операций с файлом настроек, сохраним его:
Java:
this.saveConfig();
Вызываем этот метод в теле onEnable(). Если значение, которое мы получили из поля "enable" равно false, то выключаем плагин:
Java:
if(!другое_название)
{  getServer().getPluginManager().disablePlugin(this); return; }
Теперь создадим базу для обработки событий. Для этого нам понадобится класс-инструментарий Listener.
В качестве такого класса можно использовать основной класс, внутренний класс или новый класс.
После названия класса вводим:
Java:
implements Listener
Нам подойдёт одна из следующих конструкций, они применяются в зависимости от нужд и количества различных событий, предполагаемых к обработке:
Java:
Bukkit.getServer().getPluginManager().registerEvents(new название_класса_обработчика(), this);
//Вызываем в главном классе, желательно в методе void onEnable()

Bukkit.getServer().getPluginManager().registerEvents(переменная_объекта_класса_обработчика, this);
//Вызываем в главном классе, желательно в методе void onEnable(), но после инициализации  переменной

Bukkit.getServer().getPluginManager().registerEvents(this, переменная_содержащая_объект_главного_класса_плагина);
//Вызываем в самом классе-обработчике, если это основной класс, то не рекомендую использовать для большого количества различных событий
В качестве обработчика событий используются ничего не возвращающие методы класса-обработчика, принимающие только один объект-расширение стандартного класса bukkit - Event.
Также у таких методов должна быть аннотация @EventHandler.

Вот так выглядит обработка простого события входа игрока на сервер и оповещение лично всех игроков, которые онлайн, с проверкой наличия разрешения:
Java:
@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.
В качестве такого класса можно использовать основной класс, внутренний класс или новый класс.
Один класс может обрабатывать несколько различных команд.

После названия класса вводим:
Java:
implements CommandExecutor
Нам подойдёт одна из следущих конструкций для определения такого класса для сервера. Но, в отличие от событий, такой метод вызывается от имени плагина:
Java:
getCommand("название_команды").setExecutor(new название_класса_обработчик());
//Вызываем в главном классе, желательно в методе void onEnable()

getCommand("название_команды").setExecutor(переменная_объекта_класса_обработчика);
//Вызываем в главном классе, желательно в методе void onEnable(), но после инициализации  переменной

переменная_содержащая_объект_главного_класса_плагина.getCommand("название_команды").setExecutor(this);
//Вызываем в самом классе-обработчике
Метод, обрабатывающий команды - переопределённый метод
Java:
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
Данный метод принимает несколько значений:
CommandSender sender - отправляющий сообщение, может быть объектом ConsoleCommandSender, при отправке из консоли, и Player, при отправке игроком.
Command command - объект отправленной команды.
String label - название команды без символа '/' и аргументов.
String[] args - список аргументов команды, если их нет - то равен null.
Как Вы должны были заметить, метод возвращает boolean, означающий выполнена команда верно или нет.
Вот так выглядит обработка простой команды, приветствующей определённого игрока с запретом на использование из консоли/без ника игрока/на себя:
Java:
@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;
}
Я уверен, что данных знаний достаточно для написания своего собственного плагина. Можно приступать!
 
Последнее редактирование:
Cпасиб попробую что-то написать)
 
Может быть Вы знаете, как изменить поведение крипера? Например, сделать дружелюбным?
 
Нашёл. Вот только, как прикрутить?
Java:
    public void onEntityTarget(EntityTargetEvent event)
    {
        if (((event.getTarget() instanceof Player)) &&
        ((event.getEntity() instanceof Creeper)))
        {
        event.setCancelled(true);
        }
    }
 
А почему ничего не сказано про перезагрузку конфига, думаю, что тоже полезно
 
Мда, знаю ошибок много, но чего-то не так я сделал. Я полный нуб в программировании вот учится начинаю... Если кто-то свободен, и есть желание помогите плиз. Посмотрите что у меня не так.... подправьте, и расскажите мне об моих ошибках... Опять же - если не трудно конечно...
Мой плУгин.
Смысл плагина - тупо как в туториале выводить сообщение "Игрок зашел", и скрывать эту надпись если есть permissions.
 
Мда, знаю ошибок много, но чего-то не так я сделал. Я полный нуб в программировании вот учится начинаю... Если кто-то свободен, и есть желание помогите плиз. Посмотрите что у меня не так.... подправьте, и расскажите мне об моих ошибках... Опять же - если не трудно конечно...
Мой плУгин.
Смысл плагина - тупо как в туториале выводить сообщение "Игрок зашел", и скрывать эту надпись если есть permissions.
А нет. Все получилось. Просто мимо класса функции совал :). Ну теперь я и вправду смог написать хотя бы такой плагин. Признаюсь - это для меня достижение.:D
 
А нет. Все получилось. Просто мимо класса функции совал :). Ну теперь я и вправду смог написать хотя бы такой плагин. Признаюсь - это для меня достижение.:D
Можете скинуть исходный код? ;)
 
Я чё-то не понял :(

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

/*
* 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
}
}

Как понять?
 
Можно пример кода простого плагина? И как добавить плагину permissions?
 
А что делать ели я хочу просто добавить команду для всех людей, ну например /trade ***** и в чате отображается [Торг][Hubrid] ***** как добавить команду и пермы к ней?
 
А что делать ели я хочу просто добавить команду для всех людей, ну например /trade ***** и в чате отображается [Торг][Hubrid] ***** как добавить команду и пермы к ней?
Команды сам узнаешь:), а пермы можно сделать так:
Код:
if(sender.hasPermission("test.testperm")){
//какой-то код
}else{
//Если у игрока нет этого перма сверху
sender.sendMessage(ChatColor.RED + "У тебя нет прав!"); 
}
 
А что делать ели я хочу просто добавить команду для всех людей, ну например /trade ***** и в чате отображается [Торг][Hubrid] ***** как добавить команду и пермы к ней?

Код:
@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;
 
Ну а что на счёт onUpdate()? Ведь иногда что-то же надо проверять для проверки в каждый тик.
 
Назад
Верх