Создайте Gradle-проект и два файла сборки
Скачайте IntelliJ IDEA и Temurin JDK 21. В IDEA выберите New Project → Java, Build system: Gradle, Gradle DSL: Kotlin, JDK 21 и имя TrailNotes. После создания дождитесь синхронизации Gradle. Структуру проверьте по Paper Project setup.
В корне проекта файл settings.gradle.kts содержит rootProject.name = "TrailNotes". Полностью замените build.gradle.kts следующим содержимым. API используется как compileOnly: сервер предоставляет его во время запуска. Версия 1.21.1 выбрана для воспроизводимого примера, а не как текущий выпуск публичного сервера.
plugins { java }
group = "ru.example"
version = "1.0.0"
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
}Создайте главный класс
В дереве IDEA создайте src/main/java/ru/example/trailnotes/TrailNotesPlugin.java, затем вставьте класс ниже. Если каталог src/main/java уже есть, создавайте только пакет и файл через New → Package / Java Class. Имя public-класса совпадает с именем .java.
При включении метод onEnable записывает одну строку. Это наблюдаемая проверка загрузки, без изменения инвентарей и мира. Если IDE не распознаёт JavaPlugin, сначала синхронизируйте Gradle и проверьте доступ к репозиторию; создание собственной пустой версии этого класса ошибку зависимости не исправляет.
package ru.example.trailnotes;
import org.bukkit.plugin.java.JavaPlugin;
public final class TrailNotesPlugin extends JavaPlugin {
@Override
public void onEnable() {
getLogger().info("TrailNotes загружен");
}
}Добавьте manifest в resources
Создайте src/main/resources/plugin.yml и вставьте текст ниже. main — полное имя только что созданного класса, включая регистр. api-version ограничен нашим примером 1.21.1; не заменяйте его номером Java. Назначения полей есть в Paper plugin.yml.
Файл должен оказаться в корне JAR. На Windows включите показ расширений в Проводнике и убедитесь, что имя не стало plugin.yml.txt. Для этой минимальной проверки команды и permissions в manifest не нужны: они появятся при добавлении обработчика.
name: TrailNotes
version: 1.0.0
main: ru.example.trailnotes.TrailNotesPlugin
api-version: "1.21"Соберите и проверьте JAR на тестовом Paper
В Terminal IDEA из корня проекта выполните в Linux/macOS ./gradlew clean build, в Windows PowerShell ./gradlew.bat clean build. Wrapper создан Gradle-проектом; если его нет, используйте Gradle-панель IDEA → Tasks → build → build либо создайте wrapper через установленный Gradle по документации Gradle.
Выполните jar tf build/libs/TrailNotes-1.0.0.jar: нужны plugin.yml и ru/example/trailnotes/TrailNotesPlugin.class. Остановите отдельный Paper 1.21.1, скопируйте JAR в его plugins, запустите с Java 21 и найдите TrailNotes загружен в logs/latest.log. После stop и нового запуска сообщение появляется снова один раз.
Настройте сборку так, чтобы API не попал внутрь JAR
Создайте Java-проект с Gradle или Maven и документированным репозиторием Paper. Зависимость paper-api подключайте как compileOnly в Gradle либо provided в Maven: сервер предоставляет API при запуске. Встраивание полного API в свой плагин создаёт лишний размер и риск конфликтующих классов. Сам код вашего проекта и необходимые собственные ресурсы при этом должны попасть в итоговый артефакт.
Для первого примера удобно явно выбрать Paper 1.21.1 и Java 21. Это ограниченная учебная цель, а не рекомендация устаревшей версии для нового публичного сервера. При выборе современного ядра сверяйте текущие требования runtime и API. Условие api-version в manifest сообщает совместимость, но не переводит байткод, собранный новой Java, в старую.
Разделите исходники и ресурсы
В src/main/java хранится класс, а в src/main/resources — plugin.yml. Не помещайте manifest рядом с Java-файлом: обычная сборка не обязана перенести его оттуда в корень JAR. Проверьте пакет класса и main посимвольно. Имя файла, имя public-класса и пакет должны соответствовать структуре проекта, включая регистр.
После сборки просмотрите содержимое JAR командой jar tf с именем вашего файла. В списке должны быть TrailNotesPlugin.class по пути пакета и plugin.yml в корне. Файл исходного .java не заменяет скомпилированный класс. Если нужных записей нет, исправляйте структуру сборки до копирования на сервер: серия рестартов не добавит отсутствующий ресурс.
После проверки архива выполните обычный цикл установки с чтением журналов. Проверка JAR на тестовом сервере.
Проверяйте точку включения отдельно от функций
Скопируйте один собранный JAR в plugins тестового Paper и запустите сервер штатно. Найдите имя плагина и сообщение из onEnable. Если оно отсутствует, сначала читайте ошибку загрузки, а не добавляйте игровые слушатели. Неправильный main, несовместимая Java и отсутствующая зависимость проявляются до того, как ваше действие сможет работать.
В начале достаточно одного сообщения в журнале. Следующая функция должна быть небольшой и наблюдаемой: например, команда справки либо подсказка при входе. Зарегистрируйте обработчик в onEnable и проверьте его обычным игроком. Это отделяет успешную загрузку проекта от успешной регистрации конкретного компонента.
- Выполните чистую сборку и найдите итоговый JAR.
- Проверьте класс и manifest внутри архива.
- Установите файл в отдельный тестовый сервер.
- Подтвердите одно сообщение включения.
- Добавьте одну функцию и её обработчик.
- Проверьте штатное отключение и повторный запуск.
Добавляйте конфигурацию без скрытой логики
Если нужен config.yml, разместите шаблон в resources и создайте пользовательскую копию поддерживаемым API. Читайте значения с явными единицами и диапазонами. Например, время ожидания должно называться wait-seconds или иметь такое же ясное описание. Отсутствующее значение и явно заданный ноль требуют разных решений, если ноль отключает функцию.
Главный класс собирает компоненты, но не должен содержать каждый клик, SQL-запрос и переход раунда. Выделите отдельный сервис для логики, слушатель для событий и хранилище для данных. Даже в маленьком проекте это позволяет проверить расчёт результата без запуска Minecraft и оставить работу с миром в нужном потоке.
Загруженный YAML ещё нужно проверить по смыслу до включения игровой механики. Проверка config.yml.
Типичные ошибки первого проекта
Если сервер пишет UnsupportedClassVersionError, сравните Java сборки и запуска. Если не найден главный класс, сопоставьте main с записью внутри JAR. Если plugin.yml отсутствует, проверьте каталог ресурсов. Если команда возвращает неизвестное имя, выясните, зарегистрирована ли она выбранным способом и успешно ли включился плагин.
Не подменяйте диагностику общим reload. После замены бинарника выполните штатную остановку и запуск. Сохраняйте номер своего выпуска и имя исходного коммита, чтобы тест относился к определённому файлу. Когда простая функция работает после рестарта с обычными правами, минимальная основа готова к расширению.



