Разработка плагина синхронизации погодных условий между Minecraft-сервером и Санкт-Петербургом

Разработка плагина синхронизации погодных условий между Minecraft-сервером и Санкт-Петербургом

Здравствуйте, 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 и другие. Но мы пойдём своим путём и создадим собственный плагин.

Бонусы Selectel

Играйте в 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. Попробовать его работу можно на нашем сервере: подключиться →.

 

Источник

Читайте также