Более года я занимаюсь техническим сопровождением сервера Selectel в Minecraft. За этот период разработчики выпустили шесть апдейтов, существенно расширяющих игровой опыт. Первые релизы внедрялись без каких-либо сложностей, однако версии 26.2 и 26.3 затронули алгоритмы генерации виртуального пространства.
Специфика игры такова, что свежие биомы и объекты появляются исключительно на неисследованных территориях. Это побудило меня провести ревизию игровой вселенной и подготовить ее к патчу: удалить регионы, куда игроки заглядывали лишь мимоходом. Отсутствие вайпов (полных стираний игрового прогресса) за всё время существования сервера в сочетании с впечатляющими масштабами карты превратили эту задачу в увлекательный инженерный квест.
В данном материале я расскажу об инструментах для работы с картами мира, поделюсь визуализацией наших владений и разберу ключевые геймплейные механики.
Эволюция обновлений Minecraft
На старте проекта актуальной ревизией выступала 1.21.5. В дальнейшем разработчики регулярно выпускали контентные патчи:
1.21.6 «В погоне за небесами» — привнесла уникального моба «счастливый гаст», новый рецепт создания седла и усовершенствованную механику поводков.
1.21.9 «Медный век» — добавила медную экипировку, боевые копья и свежие элементы декора.
1.21.11 «Скакуны Хаоса» — пополнила бестиарий водным обитателем наутилусом, а также агрессивными всадниками с копьями.
26.1 «Крошечный захват» — обновила внешность всех детенышей мобов, ввела крафт именных бирок и золотой одуванчик.
Эти нововведения органично вплелись в уже существующий ландшафт:
медь присутствовала в игре и раньше, патч лишь расширил спектр ее применения;
«счастливый гаст» добывается в схватке с классическим мобом, знакомым игрокам с 2010 года;
появление враждебных и дружелюбных существ регулируется текущим лимитом активных сущностей на сервере и близостью пользователя.
А вот версии 26.2 («Хаос в Кубе») и 26.3 («Зов дикой природы») привнесли новые биомы — серные пещеры и пестрый лес, а также уникальную структуру: заброшенный лагерь. Уже сформированные участки карты остаются неизменными — свежий контент генерируется исключительно при открытии новых территорий.
Для того чтобы новые биомы и структуры появились в игре, их нужно сгенерировать.
Биом «Пестрый лес», первое появление в 26.3.
За год работы почти 3 000 уникальных пользователей успели раздвинуть границы мира на десятки тысяч блоков от спавна, что существенно осложнило бы поиски новых биомов. Первым этапом стал аудит: потребовалось выяснить степень исследованности карты, основные векторы перемещений и общую плотность застройки.
Архитектура игрового пространства
Прежде чем переходить к специализированному софту, полезно вспомнить базовые принципы устройства мира Minecraft.
Пространство игры трехмерно и состоит из блоков размером ровно 1х1х1 метр. Ось Y ориентирована вертикально. Каждый блок обладает строго целочисленными координатами.
По вертикали строительство возможно в диапазоне от -64 до 319 блока (до апдейта 1.18 ограничения составляли 0–255), по горизонтали границы удалены на 59 999 968 блоков в любую сторону.
Визуализация чанка.
Для оптимизации производительности блоки объединяются в чанки — сегменты размером 16х16 клеток по горизонтали на всю высоту мира. Таким образом, один чанк включает 16x16x384 = 98 304 блока. Движок оперирует именно чанками: стоит игроку приблизиться хотя бы к одному блоку, как сервер подгружает всю область целиком.
В свою очередь, чанки группируются в регионы размером 32х32 чанка (512х512 блоков). Они используются для эффективного сохранения данных на накопитель. У каждого из этих уровней (блоки, чанки, регионы) своя система координат, но к этому мы еще вернемся.
Измерения: Верхний мир, Нижний мир и Край
По умолчанию в Minecraft предусмотрено три измерения, переходы между которыми осуществляются через порталы.
Верхний мир (Overworld) — базовая локация, с которой начинается путешествие.
Нижний мир (Nether) — экстремальное и опасное пространство, наполненное лавой.
Край (The End) — мрачное космическое измерение, где обитает дракон.
Оверворлд и Незер тесно взаимосвязаны: игроки могут возводить порталы для быстрой миграции. Здесь кроется важная геймплейная особенность — масштаб расстояний между мирами соотносятся как 1 к 8. Пройдя тысячу блоков в Нижнем мире и вернувшись обратно, вы окажетесь на расстоянии 8 000 блоков от исходной точки в Верхнем мире. Этот механизм позволяет стремительно преодолевать колоссальные дистанции, хотя и сопряжен с рисками.
Управленческий функционал самой игры крайне ограничен в этом плане. Во-первых, сервер хранит в оперативной памяти лишь локальные области вокруг игроков. во-вторых, движок не маркирует пустые зоны и молча достраивает их по мере необходимости.
Описанные ниже утилиты требуют прямого доступа к файлам сервера. Категорически не рекомендуется применять их на работающем проекте, так как это чревато безвозвратной порчей мира.
Всегда делайте бэкапы.
Верхний мир нашего сервера ограничен радиусом 100 000 блоков в каждом направлении. По предварительным оценкам, полная генерация такой территории потребовала бы около 400 ГБ без учета пользовательских построек. На текущий момент объем папки с миром составляет 190 ГБ.
Пришло время заглянуть внутрь.
uNmINed
Вид сверху на мир от -35 000 до 35 000 по оси X
Для внешнего анализа карты я использовал uNmINeD — специализированный просмотрщик миров для Java и Bedrock версий (а также Hytale). Для комфортной работы утилите необходим быстрый SSD, иначе рендеринг затянется.
Программа строит детальный план местности в масштабе до 1:32 и отображает общие контуры вплоть до 1:4096, позволяя детально изучить логистику игроков.
Контуры мира в масштабе 1:4096. Черное — сгенерированные чанки, серое — регионы, в которых есть сгенерированные чанки.
Глобальная карта подтвердила правильность выбранного курса: активная зона вокруг спавна простирается примерно на 7 000 блоков, причем пользователи явно предпочитают осваивать западное полушарие.
Этой утилите я уже посвящал пост в своем Telegram-канале. Присоединяйтесь: там я публикую инсайды сороковых черновиков, познавательные заметки и традиционные пятничные мемы.
Кроме того, на карте заметно множество изолированных мелких фрагментов. Некоторые выглядят неестественно: одиночный полностью прорисованный чанк посреди «сырой» области размером 5х5.
Три выхода игроков из Нижнего мира и четыре искусственные области.
При телепортации из Нижнего мира сервер автоматически генерирует область 16х16 блоков вокруг точки прибытия. Осмотревшись на месте, игрок невольно расширяет эту зону в произвольном направлении.
Помимо визуализации, uNmINed предоставляет доступ к сырым метаданным каждого чанка. Особый интерес для нас представляет параметр InhabitedTime.
Он отражает суммарное время активности в чанке: каждый игровой такт значение увеличивается пропорционально количеству находящихся там персонажей. На основе этого показателя сервер рассчитывает локальную сложность — чем дольше игроки развивают базу, тем сильнее монстры вокруг нее спавнятся.
Для администратора это идеальный маркер «населенности». Нулевое значение InhabitedTime может означать несколько вещей.
Также в структуре чанков присутствует атрибут Status, фиксирующий стадию генерации. Для завершения некоторых этапов движку требуются полностью готовые соседние чанки, поэтому часть областей создается исключительно ради вспомогательных расчетов и никогда не посещалась живыми игроками.
Свою роль играют и сторонние плагины, которые при опросе координат могут случайно инициировать генерацию пустотелых участков вроде упомянутых блоков 5х5.
Выбранный чанк «жил» всего 1,25 секунды.
Для сравнения: проверка чанка на точке спавна показала значение InhabitedTime, равное 69 687 111 тактам. Учитывая, что в одной секунде содержится 20 тактов, начальная зона была загружена суммарно почти 81 человеко-день.
В то же время анализ чанка, оказавшегося частью случайного маршрута перемещения, выявил параметр InhabitedTime всего в 25 тактов (1,25 секунды реального времени). Метка LastUpdate указала, что этот участок создавался на 181 023 855-м такте с момента запуска мира.
Учитывая, что текущий возраст вселенной составляет 726 463 872 такта (при длительности игровых суток в 24 000 тактов прошло уже 22 726 внутриигровых дней, или около 62 лет), очевидно, что никто здесь не селился и ценностей не оставлял. Такой участок можно смело удалять. Вопрос лишь в том, насколько гармонично свежесгенерированный ландшафт состыкуется со старым окружением.
Игровой сервер с криперами и порталом в Незер.
Добывайте ресурсы, стройте объекты, исследуйте мир Selectel в Minecraft и получайте призы.
Фундаментом любого мира Minecraft служит seed (зерно генерации). Он определяет конфигурацию рельефа, распределение биомов и расположение структур. Две карты, созданные с одинаковым сидом, окажутся практически идентичными, и это правило сохраняется при смене версий.
Процесс генерации многоступенчатый: сначала выстраивается базовый ландшафт, затем накладываются температурные карты и карты влажности, определяющие тип биома, после чего в них внедряются специфические структуры.
Патчи 26.2 и 26.3 лишь расширили библиотеку доступных биомов и объектов, не ломая базовую математику мира. По-настоящему масштабные тектонические сдвиги в генерации происходили лишь дважды: в версии 1.7.2 (2013 год) с появлением температурных карт и в версии 1.18 (2021 год), когда увеличили предельную высоту мира.
Это доказывает, что удаление пустых чанков абсолютно безопасно для целостности карты.
Холм, сгенерированный в 1.21.5.
На иллюстрации выше запечатлен скалистый массив по соседству с вишневой рощей и ледяными пиками (участок обрыва относится к биому равнин).
Тот же холм, сгенерированный в 26.3.
При пересоздании того же сида в свежей версии геометрия холма полностью сохраняется, однако примыкающие равнины трансформируются в пестрый лес. При наложении кадров видно, что мелкие детали вроде растительности или шапок снега слегка разнятся, но в игровых реалиях заметить это без покадрового сравнения невозможно.
Следовательно, очистка брошенных чанков не принесет вреда. Оставалось выбрать правильный инструмент для этой операции.
MCASelector
MCASelector. Вид на спавн.
Если uNmINed оптимален для визуальной оценки, то MCASelector создан для глубокого администрирования. Программа располагает удобным графическим интерфейсом и мощным CLI-модулем.
Утилита позволяет массово обрабатывать регионы мира, фильтруя их по заданным предикатам. Самым логичным стартовым шагом казалось удаление всех чанков с параметром InhabitedTime = 0. Это позволило бы избавиться от технических «хвостов» генерации. Выполняем команды:
Объем таких мусорных областей оказался колоссальным. На домашней машине с SSD процесс сканирования занял около часа, а само удаление — еще тридцать минут. В результате размер Верхнего мира уменьшился со 190 ГБ до 74 ГБ. Следующей задачей было найти баланс времени: отсечь случайные маршруты перемещения игроков, но гарантированно защитить их базы от стирания.
Я начал с порога в 5 минут, постепенно снижая планку. На скрининге выше показан срез чанков со временем жизни менее 15 секунд — видно, что пешеходная тропа стирается лишь фрагментарно, поскольку игрок преодолевал перепады рельефа неравномерно. Эмпирическим путем был определен оптимальный порог в 60 секунд (1 200 тактов), при котором большинство транзитных путей стиралось полностью.
План выглядел надежным, но оставались опасения. С одной стороны, минута активности — ничтожно мало (за это время едва ли успеешь построить даже землянку). С другой стороны, пользователь мог намеренно заприметить живописную локацию для будущего строительства, и потерять ее из-за изменения биома было бы обидно.
Поэтому перед финальной чисткой я решил перестраховаться и сопоставить временные метки с реальной активностью игроков.
Дополнительная валидация данных
На нашем сервере установлены стандартные плагины для мирного выживания: Essentials отвечает за расширенный набор команд, а WorldGuard позволяет игрокам защищать свои территории («приваты») от несанкционированного доступа.
Следовательно, если игрок нашел перспективное место, он либо ставит там регион WorldGuard, либо фиксирует точку командой /sethome для быстрой телепортации в будущем.
Все региональные приваты хранятся в файле regions.yml в виде координат диагоналей параллелепипеда. Точки домов Essentials записываются в YAML в виде единичных координат. Оставалось лишь соотнести эти точки с выборкой чанков в MCASelector.
Сгенерированный утилитой CSV-файл содержит четыре столбца:
region_x;region_z;chunk_x;chunk_z
Первые два определяют координаты регионального файла (например, r.190.70.mca), а два последних — абсолютные координаты чанка внутри него. Если указаны только параметры региона без чанков, это означает операцию со всеми данными файла.
Загвоздка заключалась в том, что MCASelector оперирует чанками и регионами, а плагины оперируют поблочными координатами. К счастью, простая математика позволяет конвертировать координаты блоков в чанки и регионы через деление с округлением вниз:
Алгоритм прост: точки домов пересчитываются в чанки, а приваты превращаются в массивы охватываемых ими чанков с добавлением небольшой защитной зоны (буфера) в 10–15 чанков по периметру.
Затем скрипт проходится по CSV-файлу и сверяет каждую строку с полученным белым списком охраняемых зон. Если совпадений нет, строка отправляется в итоговый файл на удаление. После финального визуального контроля запускается процедура очистки.
Полный исходный код скрипта автоматизации опубликован на GitHub Gist.
Итоги оптимизации
Слева — до очистки, справа — после.
Прямой запуск скрипта на серверном железе с NVMe-накопителем дал впечатляющие результаты. Поиск чанков с InhabitedTime = 0 занял всего 13 минут, а их удаление — 4 минуты. Второй этап (фильтрация чанков со временем жизни до 60 секунд) потребовал шесть минут: пять ушло на формирование выборки, меньше минуты на валидацию и 5 секунд на физическое стирание. В результате всех процедур объем мира сократился со 190 ГБ до скромных 13 ГБ.
Порог в 60 секунд оказался компромиссным: он сохранил те редкие тропы, где игроки пережидали ночь или добывали ресурсы, но вычистил мусорный хвост.
Профит очевиден для всех: значимые постройки игроков остались нетронуты, пользователи получили быстрый доступ к новому контенту в шаговой доступности от баз, а администрация существенно снизила нагрузку на дисковую подсистему и сократила время создания резервных копий.
Пожалуй, освоить этот метод стоил гораздо раньше.
Заключение
Комментарий к новости о «переоткрытии» игрового сервера. Источник.