Представьте обычный вечер после обновления. Администратор положил на сервер свежую версию модпака, запустил его, дождался строки о готовности и зашёл проверить. Всё на месте: спавн, дома игроков, склад. Через час кто-то замечает, что в хранилище исчезла часть предметов из одного мода. Администратор решает: «Верну старые файлы модов, и всё будет как раньше». Он возвращает файлы, запускает сервер, и тот отказывается загружать мир или, что хуже, загружает его с пустотами. Новая версия уже успела пересохранить данные мира в своём формате, а старая версия этого формата не понимает.

Эта история не про неудачливого администратора. Она про то, что у модпака есть несколько слоёв, а привычный «откат» возвращает только один из них. Дальше разберём, как обновлять сборку так, чтобы прежний мир оставался нетронутым и вернуться к нему можно было по-настоящему, а не в надежде на удачу.

Четыре слоя, которые обновляются вместе, но возвращаются по-разному

Когда говорят «версия модпака», обычно имеют в виду одну цифру на странице загрузки. За ней стоит четыре разных вещи.

  • Версия игры. Определяет базовые блоки, предметы, генерацию и внутренний формат хранения мира.
  • Версия загрузчика. В нашем разборе это NeoForge: он подключает моды к игре и задаёт набор требований к окружению, включая версию Java. Документация NeoForge подчёркивает, что Java выбирают под конкретную сборку, а не произвольно (документация NeoForge по серверу).
  • Версии модов. Каждый мод развивается по своему графику: добавляет предметы, меняет рецепты, переименовывает внутренние идентификаторы, переделывает хранение данных у своих механизмов.
  • Данные мира. Это не только блоки и постройки. В каталоге мира лежат инвентари, содержимое контейнеров, состояния машин, сведения о размещённых модовых блоках и дополнительные записи, которые ведут сами моды.

Первые три слоя — это программа. Её можно сохранить как набор файлов и вернуть одним движением. Четвёртый слой — это результат работы программы. Как только новая версия открыла мир и сохранила его, четвёртый слой принадлежит уже новой версии, и «вернуть программу» не означает «вернуть данные».

Почему старые файлы модов не возвращают старый мир

Полезная аналогия — текстовый редактор. Вы открыли документ в новой версии редактора, поправили одну строчку и сохранили. Документ мог остаться внешне тем же, но внутри он записан по правилам новой версии: в нём появились поля, которых старая версия не знает. Старый редактор либо откажется открывать файл, либо откроет, но молча проигнорирует то, чего не понимает, и при следующем сохранении выбросит это насовсем.

С миром Minecraft происходит похожее, только на другом масштабе. Несколько сценариев, как это проявляется:

  • Мод переименовал предмет или блок. Новая версия мода при загрузке сопоставляет старое имя с новым и записывает в мир уже новое. Старая версия мода такого имени не знает и может заменить предмет пустотой или отбросить его.
  • Мод поменял формат данных своей машины. Например, иначе хранит содержимое буфера или состояние рецепта. После пересохранения старая версия прочитает запись как повреждённую.
  • Игра сменила внутренний формат чанков. Загрузчик и игра обычно не умеют работать в обратную сторону: мир, пересохранённый новой версией игры, старая версия может отказаться открывать.
  • Мод удалён из сборки. Если разработчики вынули мод, его блоки и предметы в мире остаются записями без владельца. Что с ними будет, зависит от версии игры и загрузчика, и далеко не всегда это «остались как были».

Обратной совместимости не обещает никто, и рассчитывать на неё нельзя: даже если один мод аккуратно читает свои старые данные, это ничего не говорит о соседнем. Поэтому единственный честный способ вернуться к прежнему миру — вернуть прежний мир, то есть его копию, снятую до первого запуска новой сборки. Возврат файлов модов вторичен: он нужен лишь затем, чтобы старый мир открывался той программой, с которой он жил.

Из этого вытекает правило, на котором держится вся остальная статья: новая сборка никогда не должна открывать единственный экземпляр рабочего мира. Сначала она открывает копию.

Что входит в копию прежнего состояния

Документация NeoForge прямо требует делать резервную копию мира перед обновлением загрузчика или модов (NeoForge). Но одной папки мира для настоящего отката мало. Нужна согласованная копия: такая, в которой все части относятся к одному моменту и друг другу не противоречат. Если вместо NeoForge у вас сервер на Paper с плагинами, его документация перечисляет тот же набор по духу: сохранить миры, серверную конфигурацию, настройки и JAR плагинов (Paper: обновление). Для модпака на NeoForge перенесём этот принцип на его состав.

В копию прежнего состояния входит:

  1. Каталог мира целиком. Не отдельные регионы, а всё, что лежит рядом: данные игроков, данные модов, записи о достижениях и прочее.
  2. Каталог с модами, ровно в том виде, в каком он работал.
  3. Конфигурации модов и сервера. Новая версия мода часто переписывает или дополняет свои конфигурационные файлы. Если вернуть старый мод с новым конфигом, поведение может отличаться от прежнего.
  4. Файл с параметрами JVM — в NeoForge память задаётся в user_jvm_args.txt. Его стоит сохранить, потому что новая сборка может потребовать другого объёма памяти, и вы захотите видеть, с каким значением работало старое.
  5. Сам серверный пакет прежней версии: загрузчик и всё, чем он запускается.
  6. Короткая записка о версиях: версия игры, загрузчика, сборки, Java. Это не бюрократия: через два месяца по этой записке будет понятно, что вы хотите вернуть, а не угадывать по именам файлов.

Копировать нужно только при остановленном сервере. Работающий сервер пишет в мир, и копия, снятая на ходу, может оказаться «разорванной»: часть чанков от одного момента, часть от другого. Документация Paper в том же ключе не рекомендует подменять JAR у работающего сервера; для нас вывод общий: любые операции со сменой файлов делаются на остановленном экземпляре. Дождитесь штатного завершения, убедитесь по журналу, что мир сохранён, и только после этого копируйте.

Дальше копию стоит не трогать. Первая копия — архив с прежним состоянием, и у неё одна роль: лежать неизменной. Все репетиции делаются на второй копии, снятой с неё. Это немного неудобно, зато убирает целый класс ошибок: нельзя случайно «улучшить» эталон.

Заодно полезно один раз проверить, что копию вообще можно развернуть. Поднимите из неё прежний мир со старой сборкой и зайдите в него. Копия, которую никто никогда не открывал, остаётся предположением. Если она не открылась именно сейчас, это дёшево, а в ночь неудачного обновления дорого.

Отдельный экземпляр: где репетировать

Репетиция требует места, где новая сборка может сломать что угодно и никто этого не заметит. На VPS это обычно отдельный каталог со своим серверным пакетом, своей копией мира и своим портом. Игроки заходят на рабочий адрес, а репетиционный экземпляр слушает другой порт и для них закрыт.

Разберём несколько практических вопросов.

Серверный пакет вместо клиентской сборки. Документация NeoForge рекомендует для модпака использовать его server pack: клиентские визуальные моды (шейдеры, карты, интерфейсные украшения) на сервере могут быть не нужны. Если собрать серверную папку из клиентского архива, в неё попадут лишние моды, и репетиция будет проверять не то, что работает в проде. Берите серверный пакет, который предлагает автор модпака.

Версия Java. Новая версия сборки может требовать другую версию Java, чем прежняя. Документация подчёркивает, что Java выбирают под конкретную сборку. Значит, на репетиции нужно проверить не только моды, но и то, что нужная Java установлена и запускает именно этот экземпляр. Если старый сервер всё ещё должен работать на прежней, держите их раздельно и убедитесь, что каждый запускается своей.

Память. Параметры JVM в NeoForge лежат в user_jvm_args.txt. Для репетиции стоит начать с тех же значений, что у рабочего сервера, и менять их осознанно, а не потому, что «вдруг с большим объёмом будет лучше». Если новая сборка потребует больше памяти, это важный итог репетиции: вы узнаете об этом заранее и успеете решить вопрос с тарифом.

Здесь и появляется связь задачи с ресурсами VPS. На тарифах MAATRIX с NVMe-накопителем и root-доступом можно держать рядом несколько каталогов серверов и запускать ту копию, которая нужна, но ресурсы плана надо понимать как отправную точку для теста, а не как обещание ёмкости. Перечень планов: Ba (1 vCPU, 2 ГБ RAM, 40 ГБ NVMe), Uab (2 vCPU, 4 ГБ, 80 ГБ), Heka (4 vCPU, 8 ГБ, 160 ГБ), Maat (8 vCPU, 16 ГБ, 240 ГБ), Djed (16 vCPU, 32 ГБ, 320 ГБ). Для нашей задачи диск считается просто: на нём должны одновременно поместиться рабочий мир, эталонная копия, репетиционная копия и серверные пакеты обеих версий. Если мир разросся, проверьте это до обновления, а не когда копирование остановится на середине. Оперативной памяти хватит на две JVM одновременно только при достаточном запасе; если запаса нет, остановите рабочий сервер на время репетиции и проверьте, что готовый запуск укладывается в выбранный объём. Установку самого сервера и первичную настройку мы разбирали в отдельной статье: как установить и настроить Minecraft на VPS.

Теперь можно запускать. Первый запуск новой сборки на копии — это уже тест, причём первый его результат лежит не в игре, а в журнале.

Первое чтение журнала: что сервер говорит до того, как вы зашли

Документация Paper советует после запуска проверить журнал; для модпака на NeoForge эта привычка не менее полезна. Журнал первого запуска — самый честный отчёт о том, что загрузчик думает о вашей сборке. В нём стоит искать не просто слово «ошибка», а вопросы по существу:

  • Все ли моды загрузились, или какой-то отказался стартовать из-за несовместимой зависимости?
  • Были ли сообщения о преобразовании данных мира, о незнакомых или отсутствующих предметах и блоках?
  • Не повторяется ли одно и то же предупреждение каждую секунду? Это не фатально, но такой журнал быстро распухнет и спрячет реальные проблемы.
  • Корректно ли завершился запуск и открылся ли порт?

Фразы вроде «заменён пустотой» или «неизвестный идентификатор» — повод остановиться. Они означают, что новая сборка не нашла что-то из старого мира. Такие записи не значат, что всё пропало: возможно, проблема в сборке на репетиционном экземпляре, и именно для этого он существует. Но продолжать, пока вы не поняли причину, не стоит.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Проверки, которые понимает только ваш мир

Дальше начинается самое интересное. Сервер запустился, порт открыт, игра отвечает. На этом этапе слишком легко решить, что всё хорошо. Но «сервер отвечает» — это проверка для сервера вообще, а не для вашего сервера. Ваш мир уникален: у него свои машины, свои склады, своя инфраструктура. Проверять нужно именно то, что игроки считают своим.

Составьте список заранее, пока старый мир ещё работает, и сверяйте результаты на новой копии. Вот набор проверок, который можно взять за основу; каждый пункт нужно заменить своими конкретными объектами.

Игровые проверки после обновления модпака
ПроверкаЧто именно смотримЧто это может выявить
Склад с модовыми предметамиОткрыть самые заполненные сундуки и хранилища, сравнить содержимое со скриншотами или записями до обновленияПредметы, пропавшие или превратившиеся в другие из-за переименованных идентификаторов
Работающая машина или производственная линияЗапустить цепочку, где несколько механизмов зависят друг от друга, и дождаться первого готового результатаИзменившиеся рецепты, потерянное состояние машины, другая скорость работы
Энергосеть или трубопроводПроверить, что генераторы подключены, потребители получают питание, а жидкости и предметы идут по трубамСломанные связи между блоками, изменённая логика соединений
Постройка на стыке регионовПройти вдоль крупной базы, особенно там, где она пересекает границы чанковЧастично загруженные или повреждённые участки, пропавшие блоки
Инвентарь и экипировка игрокаЗайти тестовым или реальным персонажем и сверить снаряжение, зачарования, содержимое специальных слотовПотерянные модовые предметы, сброшенные данные игрока
Другое измерениеПройти через портал, посмотреть, как генерируется местность и работают постройки рядом с порталомСломанные порталы, ошибки генерации у мест, где игроки уже были
Рецепты и прогрессОткрыть книгу рецептов или аналог в сборке, проверить, какие рецепты доступны, сохранился ли прогресс достижений или квестовСброшенный прогресс, недоступные рецепты
Автоматическая фермаПроверить, что механизмы появления существ и сбора ресурсов продолжают работать как преждеИзменения в механике появления существ и поведении предметов

Главная идея таблицы — сравнение «до и после». Пока старый мир работает, сделайте короткие записи: сколько предметов лежало в каком хранилище, что выдавала линия за один цикл, какой набор рецептов был у игрока. Это не требует специальных инструментов, достаточно блокнота и нескольких скриншотов. Без «до» результат «после» не с чем сравнивать, и «вроде всё нормально» остаётся впечатлением.

Привлеките игроков. Администратор знает свою сборку со стороны сервера, а игроки — со стороны игры. Один из них помнит, где у него стоит капризная машина, которая всегда ломается первой. Другой строил систему, зависящую от странного поведения конкретного мода. Просьба «зайдите на тестовый порт, проверьте свои базы» даёт больше, чем самая тщательная проверка в одиночку. Честно предупредите, что всё, сделанное на репетиционном экземпляре, будет стёрто.

Когда сервер жив, но мир ведёт себя иначе

Самые неприятные проблемы — не вылеты, а изменившаяся скорость и поведение. Для них у spark есть команды диагностики, и важно помнить, что они измеряют разные вещи. По документации, health, tps, ping и gc показывают разные стороны состояния: ресурсы, тики, сетевую задержку и сборку мусора (команды spark). То есть ping расскажет о задержке до игроков, но ничего не скажет о том, не тормозит ли новая сборка из-за какого-то мода в тяжёлой постройке. Для репетиции полезнее связка: сначала tps и health в спокойном состоянии, затем те же команды во время работы большой фермы или производства, и gc, если подозреваете проблемы с памятью.

Если ощущение «стало тяжелее» не проходит, можно собрать профиль. Команда profiler start с параметром --timeout ограничивает длительность замера, чтобы профайлер не работал бесконечно, а параметр --only-ticks-over позволяет собирать только длинные тики, те самые, из-за которых игроки видят «подвисание». Остановка командой profiler stop с параметром --save-to-file сохраняет профиль в файл, не загружая его на внешний сайт: это удобно, если вы не хотите выкладывать данные сервера наружу. Дамп памяти (heap dump), как отмечает та же документация, может занимать много места и содержать данные процесса, поэтому на небольшом диске его лучше не снимать без необходимости и не рассылать посторонним.

Сравнение имеет смысл только на одинаковых условиях: та же нагрузка на старой и новой сборке, тот же набор игроков или та же ферма. Если сравнивать пустой старый сервер с новым под нагрузкой, вывод получится случайным. Поэтому полезно заранее договориться, какую ситуацию вы воспроизводите, и повторить её на обоих экземплярах. Конкретных чисел «нормы» здесь нет и быть не может: важно, ухудшилось ли поведение именно вашего мира относительно вашего же прошлого.

Условный пример: две репетиции вместо одной

Чтобы всё это не выглядело абстрактно, разберём придуманную ситуацию. Это иллюстрация, а не рассказ о реальном сервере и не результат замеров.

На сервере живёт сообщество из нескольких друзей. Мир существует давно: огромный склад, автоматизированная линия по производству материалов, пара баз в разных измерениях. Автор модпака выпустил обновление, в списке изменений — обновление нескольких модов и смена версии Java.

Администратор останавливает сервер, делает эталонную копию, разворачивает из неё вторую и запускает новую сборку на отдельном порту. Журнал чистый, сервер стартует. Администратор заходит и открывает склад. В одном из сундуков вместо стопки модовых деталей лежит пустота. Он возвращается к записям «до» и видит, что в этом сундуке было несколько сотен предметов одного типа. Проблема найдена до того, как кто-либо из игроков что-то потерял.

Дальше два пути. Первый: выяснить, что произошло с этим предметом. Возможно, в списке изменений мода есть пометка о переименовании, и достаточно ещё раз пройти обновление в правильном порядке или прочитать инструкцию по миграции. Второй: отложить обновление и подождать исправления. Оба пути возможны только потому, что репетиция проходила на копии. Если бы то же самое случилось в рабочем мире, оставалось бы лишь восстанавливать из архива и объяснять людям, почему они потеряли час-два игрового времени.

Вторая репетиция после выяснения причин нужна обязательно. Новая копия мира снимается заново из эталона, новая сборка запускается с учётом найденного решения, и проверки проходят заново по тому же списку. Одна успешная репетиция после одной исправленной проблемы не гарантирует, что вторая проблема не прячется глубже. Лучше повторить цикл, чем довериться первому «вроде работает».

Момент, после которого мир считается новым

Репетиция заканчивается, и приходит момент, который чаще всего оставляют без внимания: где та черта, после которой возвращаться уже дорого?

Пока на рабочий адрес заходят игроки на старой сборке, откат бесплатен: достаточно вернуть сервер в прежнее состояние. Как только вы переключили рабочий адрес на новую сборку, и игроки начали строить, торговать, крафтить, они меняют уже новую копию. Каждый час игры увеличивает расстояние между новым миром и эталоном. Если на третий день обнаружится проблема, возврат к эталону означает потерю всего, что игроки сделали за эти дни. Откат остаётся технически возможным, но становится социальной проблемой.

Поэтому момент перехода стоит назначить заранее и сделать его событием, а не побочным эффектом. Несколько соображений о том, как это устроить:

  • Объявить окно. Игроки должны знать, когда сервер недоступен и что после включения мир станет «новым». Тем, кто хочет перед обновлением что-то закончить, это даёт время.
  • Финальная копия перед переключением. Если после репетиции игроки ещё играли на старой сборке, эталон устарел. Остановите рабочий сервер и снимите свежую копию прямо перед переходом. Тогда откат вернёт не вчерашний мир, а мир до самого момента обновления.
  • Короткий период наблюдения. Первые часы после запуска новая сборка работает на реальных людях. Договоритесь, что в этот период вы и несколько игроков активно проверяете самое важное, а любые странности сообщаются сразу. Чем раньше найдена проблема, тем меньше прогресса придётся жертвовать при откате.
  • Заранее решить, что считается поводом для отката. Потерянные предметы в складе, сломанная критичная машина, повреждённая база — да. Косметический недочёт, который можно исправить без отката, — нет. Без этой договорённости в напряжённый момент спор «откатываем или чиним» съест больше времени, чем сама проблема.
  • Определить срок, после которого эталон перестаёт быть планом отката. Это не значит удалить его. Это значит честно сказать себе и игрокам: теперь мы чиним вперёд, а не возвращаемся назад.

Отдельно о том, что именно вы возвращаете при откате. Откат — это комплект: эталонная копия мира, прежние файлы модов, прежние конфигурации, прежний серверный пакет и прежняя версия Java. Если вернуть только часть, получится смесь, которую никто не тестировал. Записка о версиях, которую мы упоминали ранее, здесь окупается: по ней видно, какой комплект считался рабочим.

Если откат всё же понадобился, делайте его так же аккуратно, как и обновление. Остановите сервер, сохраните проблемный новый мир отдельно, а не поверх чего-либо (он может пригодиться, чтобы разобраться в причине или вытащить из него отдельные постройки), разверните эталон и сервер прежней версии, проверьте журнал и зайдите в мир теми же проверками из таблицы. Откат, как и обновление, нужно подтверждать игровыми проверками, а не уверенностью.

Что остаётся после принятия нового мира

Допустим, репетиция пройдена, переход состоялся, период наблюдения закончился спокойно. Что делать с эталоном?

Не удалять сразу. Эталонная копия прежнего мира — это не просто резерв на случай аварии, а ещё и историческая точка. Через месяц может выясниться, что одна из старых построек испорчена, и выручит именно копия до обновления: из неё можно аккуратно забрать нужный участок. Хранить её дольше разумно, пока хватает места на диске; решение о сроке принимайте, исходя из размера мира и объёма накопителя. Слово «разумно» тут не случайно: если места не хватает, лучше сжать и перенести копию в другое место, чем потерять возможность вернуться к ней. Обратите внимание, что автоматические резервные копии как свойство тарифа мы не предполагаем: об их наличии нужно договариваться отдельно или организовать самим, поэтому копии мира остаются вашей ответственностью.

Вместе с эталоном храните записку о версиях. Через полгода вы будете благодарны себе за короткий файл, где написано, на какой версии игры, загрузчика, Java и модпака этот мир жил до обновления. Без неё эталон превращается в архив, который страшно открывать.

И наконец, перенесите в следующее обновление то, что узнали сейчас. Список игровых проверок, который вы составили, не одноразовый: с каждой итерацией он дополняется. Появилась новая машина — добавьте её. Игрок построил хитрую ферму — добавьте. Через несколько циклов у вас окажется собственный сценарий приёмки, написанный под ваш мир, и он будет надёжнее любого универсального списка.

Такой порядок действий не гарантирует, что обновление пройдёт без сюрпризов: моды меняются независимо, и у вас нет гарантий, что любое из изменений безопасно. Он гарантирует другое: что вы узнаете о сюрпризе на копии, а не на мире, который игроки строили месяцами. Всё сводится к двум договорённостям. Первая: новая версия сначала открывает копию, и нового мира не существует, пока копия не прошла проверки. Вторая: есть назначенный момент, когда сообщество принимает новый мир, и до него исходная копия лежит нетронутой. Прежний мир остаётся неизменным, пока вы не решили иначе.