API плагина

Для тех, кто пишет свой плагин и хочет дружить с LastMines, а не парсить его YAML руками.

Если коротко: API у LastMines маленький и без затей. Один интерфейс на десяток методов, набор обычных Bukkit-событий — и всё. Никакого Kotlin, никаких CompletableFuture — вызовы дешёвые (там просто чтение из памяти плагина), поэтому всё синхронно и сразу возвращает результат. Магии нет.

Как достучаться

Через ServicesManager API не регистрируется — вместо этого простой статический провайдер:

LastMinesAPI api = LastMinesProvider.getApi();

Выставляется один раз в onEnable() самого LastMines. Если дёрнуть getApi() раньше, чем LastMines включился, — получите null. На практике это значит: ставьте softdepend: [LastMines] себе в plugin.yml и не трогайте API до собственного onEnable().

Один нюанс setApi() кидает исключение, если вызвать его дважды — так что даже не пытайтесь подменить провайдер сами, это защищено от повторной инициализации.

Что умеет LastMinesAPI

Вот все методы интерфейса как есть, без причёсывания:

Mine getMine(String id);
Collection<Mine> getMines();
void openGui(Player player, String id);
boolean isWithinRadius(Mine mine, Location location, double radius);
Collection<Mine> getMinesInRadius(Location location, double radius);
void resetMine(String id);
Mine getMineAt(Location location);
Mine getMineAt(Player player);

getMine/getMines — это прямой доступ к внутренней карте шахт плагина, ничего не копируется. Объект Mine, который вы получаете, — тот же самый, что живёт внутри LastMines, а не его снимок. Долго держать ссылку на него в памяти не стоит — если шахту удалят, объект просто «протухнет». Надёжнее хранить у себя ID шахты и каждый раз получать актуальный Mine заново.

getMineAt(Location) — линейный перебор всех загруженных шахт с проверкой попадания в bounding box. Если у вас на сервере пара сотен шахт и вы дёргаете этот метод каждый тик на каждого игрока — сами себе злобный буратино, лучше закешируйте результат хотя бы на несколько тиков.

Тут можно споткнуться isWithinRadius и getMinesInRadius считают расстояние от центра шахты по прямой — обычная евклидова дистанция до середины между pos1 и pos2. Это не тот же алгоритм, что используется внутри для [radius:N] в действиях — там расстояние считается до ближайшей грани параллелепипеда (см. Геометрия и позиции). Так исторически сложилось: публичный API писался отдельно и проще. Если нужна логика «у стенки шахты» — считайте её сами по pos1/pos2.

События

Обычные Bukkit-события, все лежат в пакете ru.last.mines.api.events. Слушаются как угодно, через @EventHandler — ничего специфичного для LastMines тут нет.

СобытиеОтменяемоеКогда стреляет
MineCreateEventнетшахта создана через /lastmines create
MineDeleteEventдапрямо перед удалением — отмените, и шахта останется жить
MineLoadEvent / MineUnloadEventнетпри загрузке/выгрузке (рестарт, /reload, ручной анлоад)
MinePreResetEventдапрямо перед началом заполнения блоков
MineResetEventнеткогда заполнение реально закончилось, не сразу — см. ниже
MineUpdateRarityEventнетпри смене текущей/следующей редкости, до самого сброса блоков
MineTeleportEventдателепорт к шахте по команде/кнопке (не автосброс)
MineBlockBreakEventдаигрок ломает блок в шахте, до всех внутренних проверок плагина

MineResetEvent прилетает не там, где кажется

Сброс шахты — не мгновенная операция. Если шахта большая, заполнение размазывается по нескольким тикам: плагин расставляет блоки в цикле, но следит за временем — как только на один тик уходит больше ~10мс или уже поставлено больше 10000 блоков, он останавливается и продолжает на следующем. MinePreResetEvent стреляет один раз и сразу, а вот MineResetEvent — только когда всё реально расставлено, то есть может прилететь через несколько тиков после вызова сброса. Для «шахта обновилась, скажи игрокам» — самое то. А если ждать его синхронно сразу после resetMine() в том же методе — не дождётесь.

MineBlockBreakEvent идёт раньше прав и зачарований

Порядок в обработчике поломки блока такой: сначала летит ваш MineBlockBreakEvent, и только потом плагин сам проверяет право на шахту (см. права доступа) и требования к зачарованиям кирки. Отмените событие на своей стороне — и плагин даже не дойдёт до своих проверок. Удобно, если хотите добавить свою причину отказа поверх встроенных, не трогая конфиги LastMines.

Подключаем как зависимость

LastMines публикуется в собственный maven-репозиторий LastStudio, JitPack не нужен:

repositories {
    maven("https://repo.laststudio.space/releases")
}

dependencies {
    compileOnly("ru.last.mines:lastmines:0.2")
}

И в своём plugin.yml:

softdepend: [LastMines]

Пример: своя причина отказа на поломку блока

@EventHandler
public void onMineBreak(MineBlockBreakEvent event) {
    Player player = event.getPlayer();
    if (!player.hasPermission("myplugin.mine.access")) {
        event.setCancelled(true);
        player.sendMessage("У тебя нет доступа к этой шахте!");
    }
}

Просто, без сюрпризов — как и весь остальной API, собственно.

Сделано с Last