
* * *
ПРЕДИСЛОВИЕ
* * *
Данная статья не является гайдом по изучению языка программирования Java, для получения необходимых знаний можете воспользоваться следующими ресурсами на форуме: "Изучение языка Java". Автор заранее предполагает, что вы обладаете необходимыми начальными знаниями синтаксиса языка, поэтому прошу воздержаться от дальнейших комментариев по этому поводу. В конце статьи будут продублированы используемые ссылки и ресурсы. * * *
НАЧАЛО
* * *
В данной статье мы разберём как создать свой первый плагин для ядра Spigot. Автор знает, что существуют форки данного ядра, но так-как гайд рассчитан для новичков, то мы будем брать конкретно это API.𝟙 - Установка среды разработки
Для того, чтобы работать на языке Java предполагается использование так называемых интегрированных сред разработки (IDE). Существует достаточно количество десктоп приложений для этого, приведу в пример некоторых из них:
- Eclipse, довольно популярная среди разработчиков, является open-source проектом, поэтому бесплатна. Из минусов хочется отметить немалую нагрузка на CPU, иногда медленно компилирует приложение, возникновение ошибок и вылетов из-за дополнительных плагинов и прочего.
- NetBeans, является официальной средой разработки для Java 8, так же является open-source проектом. Потребляет много ОЗУ во время работы.
- IntelliJ IDEA, мощная и эргономичная среда разработки с функциями для повышения производительности, нет загромождения пользовательского интерфейса. На данный момент представлена в двух версиях: Ultimate / Community, одна из которых является платной, а другая бесплатной. Из минусов хочется отметить большую нагрузку на системные ресурсы и так-же довольно сложна для новичков в освоении.
Из всего выше перечисленного мы будем использовать именно последний вариант - IntelliJ IDEA Community.
При переходе на страницу сайта мы увидим вот такое:

Здесь вам необходимо скачать Community версию, так-как она бесплатна и не имеет срока пользования. Нажимайте на кнопку Download и ожидайте загрузки.
После нам необходимо открыть установщик приложения, запустите .exe файл который только что скачался.
Далее следуйте инстукциям ниже:





На данном этапе установка нашего приложения завершена, поздравляю вас, если всё получилось.
𝟚 - Необходимые настройки IDE
При первом запуске, у нас запросят импорт настроек IDE, пропускаем это.

Теперь мы видим главный экран нашего приложения:

Посмотрите на небольшое меню слева. Тут необходимо перейти по вкладке Plugins, нажмите на неё.

В строке поиска, которая находится чуть ниже слова Marketplace, мы должны набрать следующее: Minecraft Development
Нажимаем кнопку Install, ожидаем загрузки и позже перезагружаем приложение.

𝟛 - Создание первого проекта
На главном экране нашего приложения нажимаем кнопку New Project

Теперь перед собой мы видим следующее:

Теперь нам необходимо в левом меню выбрать пункт Minecraft и отметить галочкой следующее:

Нажимаем кнопку Next и переходим на следующий этап создания проекта:

Что такое GroupId: В основном это что-то наподобие названия компании (com.google, org.apache и прочее). Можно использовать такой путь: me.{author_name} или же любой другой, как ваша душа пожелает, так-как это ни на что не влияет.
Что такое ArtifactId: Чаще всего представляет собой название проекта.
Что такое Version: Версия проекта, можно указать просто 1.0 или v1.0, тут так-же как и с GroupId, без разницы.

Теперь нам предстоит изменить название главное класса на Main и указать версию майнкрафта для которой будет плагин (в нашем случае 1.18), для своего же удобства. По желанию можно указать авторов и описание. Если вы пропустите этот шаг, то позже в любом случае, вы сможете всё это изменить.

Укажите название папки в которую будет сохранён ваш проект и нажмите Finish.

𝟜 - Создание плагина
Теперь я представлю несколько вещей, которые будут являться примерами для разработки дальнейших плагинов. И конечно же, как всё это сделать, будет представлено в статье для общего понимания вещей;)
𝟭 - Форматированный (красивый) и удобный вывод цветных сообщений в консоль.
𝟮 - Выдача начального набора вещей при вызове команды.
Начнём с уже созданного нами до этого проекта:

Давайте создадим новый Package и назовём его utils для нашего же удобства и разместим там наш вспомогательный класс.

Создаём класс с простым названием Console

Выбираем обычное создание класса, не интерфейса и прочего.

Так-как данный класс является утилити-классом, то конструктор соответственно должен иметь модификатор private. Так-же укажем заранее некоторые поля, которые позже нам пригодятся.
Java:
package me.notch.demoplugin.utils;
import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender;
public class Console {
private static final ConsoleCommandSender SENDER = Bukkit.getConsoleSender();
private static final String PREFIX = "[DemoPlugin]";
private Console() { }
}
Теперь нам необходимо создать метод отправки сообщений в консоль. Делается это довольно просто.
Java:
public static void log(String message) {
SENDER.sendMessage(message);
}
Но, что если нам необходимо отправить составное сообщение? Которое состоит не только из строк, а например из чисел или объектов классов. Тут в дело приходят любимые generics. Однако метод sendMessage позволяет принимать в виде аргумента только строку, что тогда делать? Привести обратно к строке? Но каким образом? Тут нам спешит на помощь класс StringBuilder. С его помощью, мы сможем соединить все элементы массива (переданные ввиде аргументов объекты) в единую строку.
Java:
public static <T> void log(T[] messages) {
StringBuilder builder = new StringBuilder(PREFIX);
for (T msg : messages)
builder.append(" ").append(msg);
SENDER.sendMessage(builder.toString().trim() );
}
Однако, вот незадача, постоянно инициализировать перед этим массив из разно-типовых объектов неудобно, очень даже неудобно, поэтому мы будем использовать замечательные Variable Arguments. Они упростят нашу жизнь, я уверен в этом.. по крайней мере в этом году.. И давайте тогда вынесем всю сборку строки в один метод, чтобы всё смотрелось гармонично.
Java:
public static <T> void log(T... messages) {
SENDER.sendMessage(compressArgs(messages) );
}
@SafeVarargs
private static <T> String compressArgs(T... args) {
StringBuilder builder = new StringBuilder(PREFIX);
for (T arg : args)
builder.append(" ").append(arg);
return builder.toString().trim();
}
Теперь нам стало скучно и мы бы хотели увидеть разноцветные сообщения в консоли, но как? Довольно таки просто, используем метод ChatColor#translateAlternateColorCodes. Теперь наш класс имеет вот такой код:
Java:
package me.notch.demoplugin.utils;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender;
public class Console {
private static final ConsoleCommandSender SENDER = Bukkit.getConsoleSender();
private static final String PREFIX = "[DemoPlugin]";
private Console() { }
@SafeVarargs
public static <T> void log(T... messages) {
SENDER.sendMessage(colored(compressArgs(messages) ) );
}
private static String colored(String message) {
return ChatColor.translateAlternateColorCodes('&', message);
}
@SafeVarargs
private static <T> String compressArgs(T... args) {
StringBuilder builder = new StringBuilder(PREFIX);
for (T arg : args)
builder.append(" ").append(arg);
return builder.toString().trim();
}
}
Раз уж мы сделали этот класс, то давайте попробуем вывести несколько сообщений при включении плагина:
Java:
package me.notch.demoplugin;
import me.notch.demoplugin.utils.Console;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Date;
public final class Main extends JavaPlugin {
@Override
public void onEnable() {
Console.log("&9Plugin's Author:", getDescription().getAuthors().get(0) );
Console.log("&9Current date:", new Date(System.currentTimeMillis() ) );
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
}
Для того, чтобы собрать весь наш плагин в единый .jar пакет, нам необходимо нажать на функцию package в окне Maven

После этого в папке target мы сможем увидеть скомпилированный нами плагин:

Теперь приступим к созданию команды. Первоначально нам необходимо указать в файле plugin.yml название нашей команды, указать правильное использование/описание по желанию. Делаем это вот таким образом:

Теперь нам необходимо создать новый пакейдж commands и разместить там класс, который будет являться исполнителем команды. Вот такой класс получился у меня:
Java:
package me.notch.demoplugin.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class KitCommandExecutor implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return false;
}
}
Давайте создадим единый список стартовых вещей и накидаем туда чего-нибудь. Это будет выглядеть примерно вот так:
Java:
private final static List<ItemStack> STARTER_KIT = new LinkedList<>();
static {
// Броня
ItemStack helmet = new ItemStack(Material.CHAINMAIL_HELMET); STARTER_KIT.add(helmet);
ItemStack chestPlate = new ItemStack(Material.CHAINMAIL_CHESTPLATE); STARTER_KIT.add(chestPlate);
ItemStack leggings = new ItemStack(Material.CHAINMAIL_LEGGINGS); STARTER_KIT.add(leggings);
ItemStack boots = new ItemStack(Material.CHAINMAIL_BOOTS); STARTER_KIT.add(boots);
// Инструменты
ItemStack sword = new ItemStack(Material.IRON_SWORD); STARTER_KIT.add(sword);
ItemStack pickaxe = new ItemStack(Material.IRON_PICKAXE); STARTER_KIT.add(pickaxe);
ItemStack axe = new ItemStack(Material.IRON_AXE); STARTER_KIT.add(axe);
ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); STARTER_KIT.add(shovel);
// Еда
ItemStack apple = new ItemStack(Material.APPLE, 32); STARTER_KIT.add(apple);
ItemStack pork = new ItemStack(Material.PORKCHOP, 32); STARTER_KIT.add(pork);
// Предметы
ItemStack oakWood = new ItemStack(Material.OAK_WOOD, 48); STARTER_KIT.add(oakWood);
ItemStack glass = new ItemStack(Material.GLASS, 48); STARTER_KIT.add(glass);
}
Теперь при вызове команды нам необходимо проверить, что она вызвана не через консоль, в ином случае вывести ошибку. Так-же скопируем все предметы которые были добавлены в стартовый набор и присвоим им название, что предмет принадлежит определённому игроку. Конечный код будет выглядеть следующим образом:
Java:
package me.notch.demoplugin.commands;
import me.notch.demoplugin.utils.Console;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedList;
import java.util.List;
public class KitCommandExecutor implements CommandExecutor {
private final static List<ItemStack> STARTER_KIT = new LinkedList<>();
static {
// Броня
ItemStack helmet = new ItemStack(Material.CHAINMAIL_HELMET); STARTER_KIT.add(helmet);
ItemStack chestPlate = new ItemStack(Material.CHAINMAIL_CHESTPLATE); STARTER_KIT.add(chestPlate);
ItemStack leggings = new ItemStack(Material.CHAINMAIL_LEGGINGS); STARTER_KIT.add(leggings);
ItemStack boots = new ItemStack(Material.CHAINMAIL_BOOTS); STARTER_KIT.add(boots);
// Инструменты
ItemStack sword = new ItemStack(Material.IRON_SWORD); STARTER_KIT.add(sword);
ItemStack pickaxe = new ItemStack(Material.IRON_PICKAXE); STARTER_KIT.add(pickaxe);
ItemStack axe = new ItemStack(Material.IRON_AXE); STARTER_KIT.add(axe);
ItemStack shovel = new ItemStack(Material.IRON_SHOVEL); STARTER_KIT.add(shovel);
// Еда
ItemStack apple = new ItemStack(Material.APPLE, 32); STARTER_KIT.add(apple);
ItemStack pork = new ItemStack(Material.PORKCHOP, 32); STARTER_KIT.add(pork);
// Предметы
ItemStack oakWood = new ItemStack(Material.OAK_WOOD, 48); STARTER_KIT.add(oakWood);
ItemStack glass = new ItemStack(Material.GLASS, 48); STARTER_KIT.add(glass);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!(sender instanceof Player p)) {
Console.log("&4Console cannot use this command!");
return true;
}
if (args.length > 0) {
// Смотрим какой набор хочет получить человек
if (args[0].equalsIgnoreCase("starter") || args[0].equalsIgnoreCase("start") ) {
List<ItemStack> copied = new LinkedList<>(STARTER_KIT);
for (ItemStack item : copied) {
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(
ChatColor.translateAlternateColorCodes('&',
"&dПринадлежит игроку &c" + p.getName())
);
item.setItemMeta(meta);
}
p.getInventory().addItem(copied.toArray(ItemStack[]::new));
} else {
// В ином случае выводим ошибку
p.sendMessage(ChatColor.
translateAlternateColorCodes('&',"&cТакого набора не существует!") );
}
}
return true;
}
}
Нам так-же необходимо зарегистрировать команду на сервере, это делается следующим образом:
Java:
package me.notch.demoplugin;
import me.notch.demoplugin.commands.KitCommandExecutor;
import me.notch.demoplugin.utils.Console;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Date;
public final class Main extends JavaPlugin {
@Override
public void onEnable() {
Console.log("&9Plugin's Author:", getDescription().getAuthors().get(0) );
Console.log("&9Current date:", new Date(System.currentTimeMillis() ) );
getCommand("kit").setExecutor(new KitCommandExecutor() );
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
}
Теперь при вызове команды /kit starter или /kit start нам будет выдаваться именной набор вещей.
ЗАКЛЮЧЕНИЕ
* * *
В заключение, хочу сказать, что я старался расписать, как можно больше в плане объяснений тех или иных моментов по написанию кода. Признаю, не хватает опыта в написании таких статей, но не судите слишком строго, я попытался - я сделал. И считаю что сделал довольно не плохо, если будет много запросов по наиболее детализированному объяснению кода, то я сделаю несколько апдейтов в этой же статье.
Так-же в дальнейшем, в этом же разделе, буду постить некоторые гайды по созданию интересных плагинов, с полным код и объяснениями. Хочу так-же упомянуть, то что писалось в самом начале: я категорически не собираюсь разъяснять те или иные синтаксические моменты языка программирования, это уже должно быть в понимании человека, который собирается писать плагины, другого выбора быть не может.
Отдельное спасибо тем, кто дочитал с начало до самого конца - вы лучшие 🆒 Успехов вам всем в ваших начинаниях!
Материалы, которые использовались в статье:
ЯП Java -

Java (programming language) - Wikipedia

SpigotMC - High Performance Minecraft
The home of Spigot a high performance, no lag customized CraftBukkit Minecraft server API, and BungeeCord, the cloud server proxy.

Что такое форк в программировании — Журнал «Код»
У разработчиков есть понятие «форк» — оно часто встречается в командной разработке и в проектах с открытым кодом. Посмотрите, что это, и когда вам предложат сделать форк, вы будете во всеоружии.

Open source — Википедия

Eclipse Foundation | Powering Open Innovation
The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …

The Leading IDE for Professional Java and Kotlin Development
IntelliJ IDEA is the JetBrains IDE for pro development in Java and Kotlin. Built for your comfort, it unlocks productivity, ensures quality code, supports cutting-edge tech, and protects your privacy.
Generics in Java - Wikipedia

Variable Arguments (Varargs) in Java - GeeksforGeeks
Your All-in-One Learning Portal: GeeksforGeeks is a comprehensive educational platform that empowers learners across domains-spanning computer science and programming, school education, upskilling, commerce, software tools, competitive exams, and more.

Подключенные зависимости файла pom.xml:
Код:
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Вложения
Последнее редактирование: