RE:NODE

Конфликты модов Valheim: зависимости, патчи и поиск половинным делением

Автор Команда RE:NODE
13 мин чтения


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

Четыре вида конфликтов#

Проблему полезно назвать до того, как её искать: у каждого вида свой признак и своё решение.

ВидТипичный симптомГде проявляетсяРешение
Нет зависимости или она устарелаМод просто не загружаетсяLogOutput.log при запускеУстановить или обновить библиотеку
Конфликт патчейОшибки при использовании функции, странное поведениеИсключения в логе во время игрыУбрать один мод или найти патч совместимости
Два мода - одна задачаДвойные эффекты, сломанный интерфейс, рассинхронЧасто в логе ничего нетОставить один из двух
Несовпадение версийКик при входеСообщение о подключении, лог сервераПривести наборы клиента и сервера в соответствие

Четвёртый случай - на самом деле конфликт не между модами, а между машинами, и ему посвящено отдельное руководство: кик из-за несовпадения версий модов подробно разбирает сообщения Jotunn и ServerSync. Здесь мы сосредоточимся на первых трёх: они возникают на одной машине и ломают сервер или игру независимо от того, кто подключается.

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

Цепочки зависимостей и что о них говорит BepInEx#

Очень немногие моды Valheim работают сами по себе. Контентные моды опираются на Jotunn, общую библиотеку для моддинга; многие популярные моды включают ServerSync для передачи конфигурации клиентам; сам Jotunn требует HookGenPatcher. Пакет мода на Thunderstore объявляет их в manifest.json как строки зависимостей вида Author-Name-Version:

manifest.json
{  "name": "ExampleMod",  "version_number": "1.4.0",  "dependencies": [    "denikson-BepInExPack_Valheim-5.4.2202",    "ValheimModding-Jotunn-2.20.0"  ]}

Версия в этой строке - та, под которую собирал мод автор, а не жёсткая привязка. r2modman и другие менеджеры модов читают её и ставят зависимость за вас, обычно в последней версии. Ручная установка этого не делает: если скопировать один .dll в BepInEx/plugins без его библиотеки, BepInEx откажется его загружать. На сервере это самый частый способ получить мод, который у всех игроков работает, а на сервере молча ничего не делает: сервер настраивали вручную, а клиентов - через менеджер. О том, как держать их в одном состоянии, рассказывает материал Thunderstore и r2modman для групп.

Внутри .dll плагин объявляет те же связи для BepInEx с помощью атрибутов, и загрузчик BepInEx проверяет их при запуске:

  • Жёсткая зависимость означает "не загружай меня без этого". Если её нет, мод пропускается.
  • Мягкая зависимость означает "если это есть, загрузи его раньше". Без неё ничего не ломается, мод лишь теряет часть интеграции.
  • Несовместимость означает "не загружай меня, если это есть". Авторы добавляют её, когда знают, что два мода вместе работать не могут.
  • Минимальная версия у жёсткой зависимости означает, что библиотека должна быть не старше указанной, поэтому старый Jotunn не пройдёт проверку, хотя и установлен.

Каждый из этих случаев даёт в логе свою строку. В BepInEx 5, который поставляется с пакетом для Valheim, они выглядят так (названия приведены для примера):

BepInEx/LogOutput.log
[Info   :   BepInEx] Loading [Jotunn 2.20.0][Error  :   BepInEx] Could not load [ExampleMod 1.4.0] because it has missing dependencies: com.jotunn.jotunn (v2.21.0 or newer)[Error  :   BepInEx] Could not load [OtherMod 2.0.1] because it is incompatible with: some.other.mod[Warning:   BepInEx] Skipping [ExampleMod 1.3.0] because a newer version exists (ExampleMod 1.4.0)

Зависимость указана по GUID плагина, а не по названию на Thunderstore, поэтому в логе стоит com.jotunn.jotunn, а не ValheimModding-Jotunn. GUID обычно легко угадать; если не получается, поищите его на странице мода на Thunderstore или в README. Строка "Skipping" означает, что установлены две копии одного мода - как правило, одна в plugins, а другая в подпапке, оставшейся от прежней ручной установки. BepInEx загружает более новую, и всё хорошо, пока вы не удалите новую: тогда устаревшая копия оживёт. Удаляйте дубликаты, как только видите эту строку.

Конфликты патчей#

Почти каждый мод Valheim работает так: во время выполнения патчит методы самой игры через Harmony (BepInEx поставляется с форком HarmonyX). Патч может выполнить код до метода игры (prefix), после него (postfix) или напрямую переписать инструкции метода (transpiler). Многие моды могут патчить один и тот же метод и обычно уживаются, поскольку каждый postfix просто добавляет немного поведения сверху. Проблемы начинаются в трёх ситуациях:

  1. Prefix, пропускающий оригинал. Prefix может сказать Harmony вообще не вызывать метод игры. Если мод A делает так, чтобы заменить проверку крафта, а postfix мода B рассчитывал, что оригинал выполнился, мод B начинает работать со значениями, которые никто не задавал.
  2. Два transpiler на одном методе. Transpiler ищет определённую последовательность инструкций и переписывает её. Если другой transpiler уже переписал эту последовательность, второй не находит того, что искал, и либо не применяется, либо применяется не туда.
  3. Патч к изменившемуся коду. После обновления игры метод могли переименовать, удалить или переписать. Патчи к нему перестают работать, и каждый мод, который на них опирался, теряет соответствующую функцию.

Сбои патчей при запуске HarmonyX записывает в лог с указанием метода, который не удалось пропатчить, и стектрейсом. Проблемы, возникающие во время игры, проявляются исключениями, в стектрейсе которых есть пространство имён мода: NullReferenceException, выброшенный внутри SomeAuthor.SomeMod.Patches.InventoryGui_Patch, показывает, какой мод работал в момент сбоя, даже если виноват не он. Читайте трассировку целиком: мод наверху - тот, что упал, но кадр ниже от другого мода часто принадлежит тому, кто изменил состояние, на котором тот споткнулся.

Встроенного отчёта о том, какие моды патчат какой метод, нет. На практике такие конфликты находят так: замечают, что неисправная функция относится к области, которой касаются два ваших мода (инвентарь, крафт, расстановка построек, карта), и проверяют без одного из них.

Два мода с одной задачей#

Самый тихий конфликт - два мода, каждый из которых прекрасно работает и которые не стоит ставить вместе. Ошибок нет; вы просто получаете двойное или противоречивое поведение. Такие пересечения снова и снова встречаются в списках модов у групп:

ОбластьЧто пересекаетсяЧто видно
Размер инвентаряДва мода расширения инвентаря или один плюс "всё в одном"Пропавшие строки, предметы исчезают в скрытых слотах
Работа с контейнерамиМоды quick stack, сортировки и корзиныДвойные кнопки, предметы перемещаются дважды
Общие сундукиМультипользовательский сундук плюс мод стопок без его поддержкиРассинхрон, отклонённые перемещения
Крафт из сундуковДва мода крафта из контейнеровРесурсы учитываются дважды или не учитываются вовсе
Размеры стопок, грузоподъёмностьМод "всё в одном" плюс отдельный модОдин конфиг молча перекрывает другой
КартаНесколько модов меток или миникартыДублирующиеся метки, перекрывающиеся панели

Некоторые авторы описывают такие конфликты. Страница MultiUserChest сообщает, что QuickStore, QuickStack и SimpleSort вызывают рассинхрон и не запустятся вместе с ним, и рекомендует вместо них Quick Stack Store Sort Trash Restock; тот, в свою очередь, объявляет себя несовместимым с Trash Items, поскольку уже включает корзину. Перед добавлением любого мода читайте раздел "Incompatible" или "Known issues" на его странице и сверяйте с ним свой список. В материале моды для комфорта игры в группе разобраны популярные моды контейнеров и карты и то, какие из них хорошо сочетаются.

Отдельное предупреждение - моды "всё в одном". Форки Valheim Plus и подобные пакеты меняют десятки систем сразу: размеры стопок, строительство, крафт, обмен картой, инвентарь. Добавлять к такому моду любой узкий мод - лотерея, потому что "всё в одном", скорее всего, уже патчит эту область. Если ваша группа использует такой мод, считайте его основой списка и проверяйте каждое добавление по списку его функций.

Как правильно читать LogOutput.log#

Всё сказанное выше зависит от умения читать один файл. BepInEx записывает BepInEx/LogOutput.log при каждом запуске, перезаписывая предыдущий, поэтому скопируйте его перед перезапуском, если хотите сравнивать запуски. На сервере те же строки можно наблюдать и в консоли, но по файлу искать удобнее.

Нормальный запуск идёт примерно в таком порядке:

  1. Строки preloader и Doorstop. Если лог обрывается здесь, сломан сам BepInEx - это разобрано в материале сервер падает при запуске.
  2. Строка Loading [Name Version] для каждого плагина, в порядке зависимостей.
  3. Chainloader startup complete, после чего управление берёт игра.
  4. Строки игры: загрузка мира, регистрация сервера, код присоединения на сервере с кроссплеем.

Всё между шагами 2 и 3 - собственный запуск модов. Мод, который загрузился, но затем выбросил исключение в собственном Awake, будет значиться загруженным и всё равно не будет работать, так что не останавливайтесь на поиске Could not load: ищите ещё Exception и [Error. Предупреждения в основном шум; ошибки стоит читать всегда. Подробнее о сбоях preloader и Doorstop - в материале чтение логов BepInEx.

На клиенте тот же файл находится в папке профиля r2modman, а не в папке игры, что многих сбивает с толку. В настройках r2modman есть кнопка открытия папки профиля; лог лежит в её подпапке BepInEx.

Поиск виновника делением набора пополам#

Когда лог не называет виновника, делите список пополам. Это кажется медленным, но это самый быстрый метод: из 40 модов за шесть раундов остаётся один подозреваемый. Порядок действий:

  1. Работайте с копией. Сначала сделайте резервную копию мира и всей папки BepInEx. Проверка с удалённой половиной контентных модов может при сохранении стереть из мира их предметы и постройки, поэтому никогда не делите на боевом мире. Подходит тестовый сервер или локальная копия мира.
  2. Сделайте проблему воспроизводимой. Запишите точное действие, которое ломается: "крафт у кузницы выбрасывает ошибку", "сервер останавливается на chain loader". Если вызвать проблему по требованию нельзя, делить набор бесполезно.
  3. Оставьте библиотеки. Jotunn, HookGenPatcher и другие библиотеки остаются в каждом раунде. Вы проверяете моды, которые ими пользуются.
  4. Уберите половину. Переместите половину оставшихся модов из BepInEx/plugins в папку вне её, например plugins-disabled рядом с BepInEx. В r2modman их достаточно отключить.
  5. Проверьте. Если проблема исчезла, виновник в убранной половине: верните её и уберите другую. Если проблема осталась, она в половине, которая всё ещё установлена.
  6. Повторяйте с подозрительной половиной, пока не останется один мод.
bash
# On a Linux server, park half the mods outside the plugins folder$ mkdir -p ~/server/plugins-disabled$ mv ~/server/BepInEx/plugins/{ModA,ModB,ModC,ModD}* ~/server/plugins-disabled/$ ls ~/server/BepInEx/plugins

Есть два осложнения. Во-первых, для конфликта нужны два мода, поэтому проблема может исчезнуть в обеих половинах. Тогда по одному подозреваемому в каждой половине: оставьте одну половину установленной полностью и делите другую, пока проблема не вернётся, - так вы найдёте первый мод; затем повторите в обратную сторону, чтобы найти его пару. Во-вторых, убранный мод, от которого зависят другие, уносит с собой и зависимые. После каждого раунда проверяйте лог на новые строки Could not load, чтобы знать, что на самом деле работало.

На RE:NODE файловый менеджер умеет перемещать и переименовывать файлы в BepInEx/plugins без SFTP-клиента, а консоль показывает строки BepInEx по мере их появления, поэтому раунд деления сводится к перемещению, перезапуску и просмотру лога. Создайте резервную копию на вкладке Backups до первого раунда и заблокируйте её, чтобы ротация не удалила её во время тестов - как работает блокировка, см. в разделе резервные копии.

Конфликты конфигов и синхронизируемые настройки#

Некоторые конфликты живут в конфигурации, а не в коде. Два мода, которые оба предоставляют настройку размера стопки или грузоподъёмности, применят каждый свою, и победит тот, что сработает последним. Решение - задать значение только в одном из них, а в другом оставить значение по умолчанию или отключить.

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

При делении набора файлы конфигурации - ловушка. .cfg убранного мода остаётся в BepInEx/config, что безвредно, но мод, читающий настройки другого мода, может вести себя по-разному в зависимости от того, какие конфиги существуют. Если проблема переживает все раунды деления, попробуйте целиком убрать в сторону BepInEx/config, чтобы каждый мод стартовал со своими значениями по умолчанию. Если это помогло, виновник - настройка, а не мод.

Как не допустить конфликтов на сервере для группы#

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

  • Одно изменение за перезапуск. Добавьте или обновите один мод, перезапустите, проверьте, затем следующий. Если что-то сломается, вы уже знаете, какое изменение виновато.
  • Читайте список изменений перед обновлением. Авторы отмечают там новые зависимости и отказ от совместимости. Новая жёсткая зависимость от библиотеки, которой у вас нет, сломает мод на сервере, который обслуживается вручную.
  • Храните заведомо рабочую копию. После каждого рабочего состояния архивируйте BepInEx/plugins и BepInEx/config. Откат тогда сводится к копированию папки, а не к попыткам вспомнить сорок номеров версий.
  • Предпочитайте узкие моды перекрывающимся. Один мод на одну задачу. Если два мода предлагают одну функцию, отключите её в одном из них.
  • Не обновляйтесь в день выхода патча. Патч игры может одновременно сломать патчи во многих модах, и это выглядит как сотня конфликтов, хотя причина одна - о том, как пережить ожидание, рассказывает материал что делать, если обновление мода всё сломало.

Если вы собираете список с нуля, основы самой установки изложены в руководстве по серверу с BepInEx.

FAQ#

Как понять, какой мод вызывает падение моего сервера Valheim?

Откройте BepInEx/LogOutput.log и поищите Could not load, затем Exception. В стектрейсе исключения обычно есть пространство имён мода, который работал в тот момент. Если лог не называет ничего полезного, делите набор: уберите половину модов, проверьте и продолжайте делить ту половину, в которой осталась проблема.

Нужно ли устанавливать зависимости на сервере вручную?

Да, если вы не ставили моды сервера менеджером, который сам разрешает зависимости. Каждая жёсткая зависимость, указанная на странице мода на Thunderstore, должна быть в BepInEx/plugins в требуемой версии или новее, иначе BepInEx пропустит мод при запуске.

Могут ли два мода, патчащие одно и то же, работать вместе?

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

Почему мод работает у моих друзей, но не на сервере?

Обычно потому, что сервер настраивали вручную и на нём нет библиотеки, которую менеджер модов поставил друзьям, либо на сервере осталась старая копия мода в подпапке. Поищите в логе сервера строки Could not load и Skipping.

Повредит ли мой мир удаление мода при тестировании?

Может. Когда контентного мода нет, мир не может загрузить его предметы и постройки, и при сохранении они пропадают. Всегда делите набор на копии мира и восстанавливайте резервную копию после этого, а не тестируйте на мире, в котором играет группа.