Одна из самых загадочных ошибок на сервере выглядит так: плагин отлично работает у разработчика и на чистом тестовом сервере, но стоит поставить его рядом с другими плагинами, и в консоли появляется ошибка про отсутствующий метод или несовместимый класс. Причина почти всегда в зависимостях - библиотеках, которые плагин использует внутри. Разберёмся, как устроены зависимости в Gradle, что такое shade и relocate и как не устроить конфликт библиотек.
Что такое зависимости
Почти ни один плагин не пишется полностью с нуля. Для работы с базой данных подключают одну библиотеку, для JSON - другую, для красивых сообщений - третью. Это и есть зависимости: чужой код, который ваш плагин использует.
Gradle - система сборки, которая скачивает нужные библиотеки и собирает из них и вашего кода готовый jar. В файле сборки вы указываете, какие библиотеки нужны и каких версий, а Gradle делает остальное.
Два вида зависимостей
Важно различать зависимости, которые уже есть на сервере, и те, которых нет. Paper API, Vault, PlaceholderAPI и другие плагины уже установлены на сервере. Их не нужно класть внутрь своего плагина: достаточно подключить их только для компиляции. В Gradle это обычно называется compileOnly.
Библиотеки, которых на сервере нет, нужно либо положить внутрь своего jar, либо загрузить при запуске. Если забыть об этом, плагин соберётся, но на сервере упадёт с ошибкой «класс не найден».
Что такое shade
Shade - это упаковка библиотек внутрь вашего jar-файла. Для этого в Gradle используют плагин Shadow: он собирает «толстый» jar, в котором лежит и ваш код, и код всех нужных библиотек. Такой плагин можно положить на любой сервер, и он будет работать без дополнительных файлов.
Минус в размере: если тянуть внутрь всё подряд, jar может весить десятки мегабайт. Поэтому внутрь кладут только то, чего нет на сервере.
Почему возникают конфликты
Проблема начинается, когда два плагина кладут внутрь себя одну и ту же библиотеку, но разных версий. Java загружает классы по имени, и сервер может использовать версию библиотеки из одного плагина для другого. Если версии несовместимы, второй плагин падает с ошибкой про отсутствующий метод - хотя у его разработчика всё работало.
Особенно часто это происходит с популярными библиотеками для баз данных, JSON и HTTP-запросов: их использует половина плагинов.
Relocate - главное лекарство
Relocate - это переименование пакета библиотеки при упаковке. Плагин Shadow переносит классы библиотеки из стандартного пакета в пакет внутри вашего плагина, например из «com.zaxxer.hikari» в «su.lumex.myplugin.libs.hikari», и исправляет все ссылки в вашем коде.
После этого ваша копия библиотеки больше не пересекается с копиями в других плагинах: у каждого своя, под своим именем. Это стандартная практика для всех публичных плагинов, и её стоит делать всегда, когда вы упаковываете библиотеку внутрь.
Загрузка библиотек при запуске
У Paper есть ещё один способ: указать библиотеки в описании плагина, и сервер сам скачает их при запуске из центрального репозитория. Тогда jar остаётся маленьким, а библиотеки загружаются отдельно для каждого плагина и не пересекаются с чужими.
Этот способ удобен, но требует доступа сервера к интернету при первом запуске. На некоторых хостингах внешние запросы ограничены, и тогда плагин не запустится. Поэтому для публичных плагинов часто всё равно выбирают shade с relocate.
Как найти виновника конфликта
Если на сервере появилась ошибка вроде NoSuchMethodError или ClassCastException, посмотрите в ней название пакета. Если там пакет популярной библиотеки, а не вашего плагина, скорее всего, это конфликт. Дальше можно найти, какие ещё плагины на сервере содержат эту библиотеку без relocate: обычно это видно, если открыть их jar-файлы как архив.
Для разработчика самое надёжное решение - сделать relocate в своём плагине. Для владельца сервера - написать авторам проблемных плагинов и, если нужно, временно убрать один из конфликтующих плагинов.
Пример
Владелец заказал плагин статистики, который на тестовом сервере работал идеально, а на основном падал при подключении к базе. Ошибка указывала на метод из библиотеки пула соединений. Оказалось, что другой плагин на сервере упаковал внутрь старую версию той же библиотеки без relocate, и сервер использовал именно её. Разработчик добавил relocate в сборку, выпустил новую версию - и плагин заработал рядом со всеми остальными.
Как проверять зависимости до релиза
Перед выпуском плагина откройте получившийся jar как архив и посмотрите, что лежит внутри. Если там есть пакеты популярных библиотек под их исходными именами, значит, relocate не настроен. Если внутри оказались классы самого Paper или других плагинов, значит, какая-то зависимость подключена не как compileOnly, и jar раздулся без причины.
Хорошо также запустить плагин на тестовом сервере вместе с самыми популярными плагинами, которые обычно стоят у ваших пользователей: системой прав, экономикой, PlaceholderAPI. Так конфликты обнаруживаются до того, как о них напишут покупатели. Эти десять минут проверки экономят дни на разбор жалоб после релиза.
Наш взгляд
Зависимости - невидимая часть плагина, которая вспоминается только когда что-то ломается. Подключайте серверные API только для компиляции, упаковывайте внутрь лишь то, чего на сервере нет, и всегда делайте relocate. Эти три привычки избавляют от большей части загадочных ошибок «у меня работает, а у вас нет». А если вы владелец сервера, а не разработчик, знание этих слов помогает быстро объяснить автору плагина, что пошло не так, и получить исправление, а не ответ «у меня всё работает».
Встречали конфликты библиотек между плагинами? Как удалось найти виновника?



