Здравствуйте, SE7EN! Меня зовут Владимир Туров, я работаю разработчиком в Selectel. В этом году команда Paper объявила о том, что проект становиться полностью автономным. С каждым новым выпуском Minecraft API Spigot и Paper могут всё больше расходиться, поэтому настало время освоить разработку и отладку плагинов для ядра Paper.
На примере задачи по синхронизации погоды в лобби Minecraft с реальной погодой Санкт-Петербурга я покажу весь процесс: от создания базового прототипа до управления временем и погодой в игровом мире.
Введение
Среди альтернатив официальному серверу Minecraft наиболее популярным стал Paper — оптимизированная и более безопасная ветка Spigot. Spigot сам по себе представляет собой набор патчей над «ванильным» сервером Minecraft: лицензия игры разрешает дорабатывать серверное ПО, но запрещает распространять его в изменённом виде.
Накопление патчей заставляло команду Paper ждать выхода новых версий Spigot, чтобы применить собственные исправления. Недавний «hard fork» устранил эту зависимость: Paper перешёл в статус независимого проекта. При этом разработчики гарантируют сохранение совместимости с API Spigot и конфигурационными файлами по крайней мере на первых этапах, обещая при этом более частые релизы.
Постановка задачи
Для демонстрации работы я подготовил лобби в сказочном стиле и настроил в нём раздачу промокодов на услуги Selectel. Мне показалось забавным синхронизировать смену дня и ночи в лобби с фактическим циклом суток в Санкт-Петербурге.
Да, мы действительно выдаём промокоды просто за вход на наш сервер Minecraft.
Существуют готовые решения для синхронизации мира Minecraft с реальным временем: WorldSync, CustomDay и другие. Но мы пойдём своим путём и создадим собственный плагин.

Играйте в Minecraft и получайте бонусы в панели управления!
Накопленными бонусами можно оплатить облачный сервер для сайта, приложения или бота.
Исходные данные
В Minecraft реализованы циклы дня и ночи длительностью 20 минут (1 200 секунд или 24 000 тактов) и три типа погоды: ясная, дождь и гроза. Смена времени происходит плавно: Солнце и Луна движутся по небосводу, проходя через зенит в положенные моменты.

Вот ключевые точки игрового времени (в тактах от начала цикла):
- 0 — рассвет;
- 6 000 — Солнце в зените;
- 12 000 — начало заката;
- 13 000 — ночь вступает в силу;
- 13 702 — Солнце полностью ушло за горизонт;
- 18 000 — Луна в зените;
- 22 000 — рассвет начинается;
- 22 300 — Солнце появляется на востоке;
- 23 216 — рассвет завершён;
- 24 000 — начало нового цикла.

Для ручного вмешательства доступны команды:
/time set <такты>
gamerule doDaylightCycle false

Погода глобальная для всего мира, виды осадков управляются командами:
/weather <clear|rain|thunder>
gamerule doWeatherCycle false
В своём Telegram-канале я публикую заметки о феноменах Minecraft и занимательные мини-посты. По пятницам — мемы!
Итак, у нас есть базовый цикл дня, базовые команды для времени и погоды — время переходить к прототипированию плагина.
Прототип плагина
Для начала создайте в IDE (VS Code или IntelliJ IDEA) новый плагин по шаблону Bukkit/Spigot, добавьте в pom.xml зависимость Paper API и переименуйте plugin.yml в paper-plugin.yml:
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>

Соберите проект, скачайте сервер Paper с официального сайта, поместите .jar в папку plugins/ и запустите сервер. В списке плагинов вы увидите наш модуль как Paper-плагин.
Класс для отладки
Создадим singleton-класс WorldState, который будет хранить времена рассвета и заката, а также текущее время суток в секундах:
public class WorldState {
private static final WorldState INSTANCE = new WorldState();
private long sunriseOfDay;
private long sunsetOfDay;
private WorldState() { }
public static WorldState getInstance() {
return INSTANCE;
}
public long getSecondsOfDay() {
return LocalTime.now().toSecondOfDay();
}
public void setSunrise(String time) {
LocalTime t = LocalTime.parse(time, DateTimeFormatter.ofPattern("h:mm a"));
sunriseOfDay = t.toSecondOfDay();
}
public void setSunset(String time) {
LocalTime t = LocalTime.parse(time, DateTimeFormatter.ofPattern("h:mm a"));
sunsetOfDay = t.toSecondOfDay();
}
public long getSunriseOfDay() { return sunriseOfDay; }
public long getSunsetOfDay() { return sunsetOfDay; }
}
Далее добавим команду /weathersync с подкомандами version, show, setSunrise <time> и setSunset <time> через Command API Paper:
weathersync
├─ version — показать версию
├─ show — отобразить настройки
├─ setSunrise
Синхронизация солнца
Чтобы плавно изменять положение Солнца, заведём в методе onEnable() периодическую задачу, которая каждую игровую «тик» будет продвигать время мира:
BukkitScheduler scheduler = getServer().getScheduler();
sun = scheduler.runTaskTimer(this, () -> {
World w = getServer().getWorld("world");
if (w != null) w.setFullTime(w.getTime() + 20);
}, 0, 1);
Чтобы соотнести реальные секунды с игровыми тактами, используем времена рассвета и заката как опорные точки. Разделим сутки на участки «ночь—рассвет», «рассвет—закат» и «закат—полночь», вычислим линейные преобразования и назначим world.setFullTime(newWorldTime). Пример вычислений:
if (now <= sunrise) {
// ночь
newTime = midnightTick + (sunriseTick - midnightTick) * now / sunrise;
} else if (now <= sunset) {
// день
newTime = sunriseTick + (sunsetTick - sunriseTick) * (now - sunrise) / (sunset - sunrise);
} else {
// вечер
newTime = sunsetTick + (midnightTick - sunsetTick) * (now - sunset) / (86400 - sunset);
}
world.setFullTime(newTime);
Настройка поставщиков данных
Для получения времени рассвета, заката и текущей погоды я выбрал сервис weatherapi.com с бесплатным тарифом. Добавим в resources/config.yml параметры:
enabled: true
api-key: ""
city: ""
world: "world"
fetch-interval: 3600
В onEnable():
@Override
public void onEnable() {
saveDefaultConfig();
WeatherProvider.API_KEY = getConfig().getString("api-key");
WeatherProvider.CITY = getConfig().getString("city");
getServer().getScheduler().runTaskTimerAsynchronously(this, () -> {
if (WeatherProvider.fetchAstronomy()) {
WorldState st = WorldState.getInstance();
st.setSunrise(WeatherProvider.getSunrise());
st.setSunset(WeatherProvider.getSunset());
}
}, 0, getConfig().getInt("fetch-interval")); // по умолчанию раз в час
}
Управление погодой
В Minecraft есть три базовых погодных режима: ясно, дождь и гроза. API предоставляет методы:
world.setStorm(false);
world.setThundering(false);
Фактически погодных состояний четыре: ясно, дождь, гроза и дождь с грозой. Бонусом учтём «магические биомы»: снег вместо дождя в холодных зонах и пасмурную пустыню без осадков. Для смены биома используется world.setBiome(x, y, z, biome), но это стоит делать осторожно — массовая замена биомов может вызвать просадки TPS и не всегда корректно отображается клиентом.
- Изменение биома требует основного потока, большие объёмы влияют на производительность.
- Клиент не всегда обновляет текстуры сразу, иногда нужен повторный вход.
- Некоторые механики (замерзание воды, спавн мобов) завязаны на тип биома.
Добавьте в конфигурацию координаты региона для биомов и доработайте команды, обработав ошибки и уведомления об успехе.
Заключение
Хотя Command API Paper поначалу кажется громоздким по сравнению с Bukkit/Spigot, он открывает гибкие возможности регистрации деревьев команд и управления задачами. Плагин легко расширить: добавить фазы Луны, локальные осадки («местами дождь»), смешанные погодные эффекты и многое другое.
Исходный код плагина доступен на GitHub. Попробовать его работу можно на нашем сервере: подключиться →.
Авторы Arc Raiders опубликовали тизер крупного обновления Frozen Trail
Из Steam удаляют пошаговую тактику про футбол по мотивам тайских комиксов
The Blood of Dawnwalker получит режим New Game Plus
Для пародии на игры о Гарри Поттере под названием Secret Agent Wizard Boy and the International Crime Syndicate выпустили любительский русификатор
Для настольной игры Blood Bowl анонсировали команду Ночных гоблинов со сквигами
Финальный трейлер фильма «Ночь ведьм» покажут 30 сентября
Double Fine намерена пригласить актрису Эрику Исии из Ghost of Yōtei для озвучки своей следующей игры
Художники вручную воссоздали ошибки ИИ при создании «Квест Вижна» без применения нейросетей