Skip to content

Часто задаваемые вопросы

Главная | ЧаВо


Начало работы

В: Что мне нужно, чтобы начать моддинг DayZ?

О: Вам нужны Steam, DayZ (розничная копия), DayZ Tools (бесплатно в Steam в разделе Tools) и текстовый редактор (рекомендуется VS Code). Опыт программирования не является обязательным --- начните с Главы 8.1: Ваш первый мод. DayZ Tools включает Object Builder, Addon Builder, TexView2 и IDE Workbench.

В: Какой язык программирования использует DayZ?

О: DayZ использует Enforce Script --- проприетарный язык от Bohemia Interactive. Он имеет C-подобный синтаксис, похожий на C#, но со своими правилами и ограничениями (нет тернарного оператора, нет try/catch, нет лямбд). Полное руководство по языку см. в Части 1: Enforce Script.

В: Как настроить диск P:?

О: Откройте DayZ Tools из Steam, нажмите "Workdrive" или "Setup Workdrive" для монтирования диска P:. Это создает виртуальный диск, указывающий на ваше рабочее пространство моддинга, где движок ищет исходные файлы во время разработки. Также можно использовать subst P: "C:\Your\Path" из командной строки. См. Главу 4.5.

В: Могу ли я тестировать мод без выделенного сервера?

О: Да. Запустите DayZ с параметром -filePatching и загруженным модом. Для быстрого тестирования используйте Listen Server (хостинг из внутриигрового меню). Для продакшн-тестирования всегда проверяйте на выделенном сервере, так как некоторые пути кода различаются. См. Главу 8.1.

В: Где найти ванильные скрипты DayZ для изучения?

О: После монтирования диска P: через DayZ Tools ванильные скрипты находятся в P:\DZ\scripts\, организованные по слоям (3_Game, 4_World, 5_Mission). Это авторитетный справочник по каждому классу, методу и событию движка. Также см. Шпаргалку и Краткий справочник API.


Распространенные ошибки и исправления

В: Мой мод загружается, но ничего не происходит. Ошибок в логе нет.

О: Скорее всего, в вашем config.cpp некорректная запись requiredAddons[], поэтому ваши скрипты загружаются слишком рано или вообще не загружаются. Убедитесь, что каждое имя аддона в requiredAddons точно совпадает с существующим именем класса CfgPatches (с учетом регистра). Проверьте лог скриптов в %localappdata%/DayZ/ на наличие скрытых предупреждений. См. Главу 2.2.

В: Я получаю ошибки "Cannot find variable" или "Undefined variable".

О: Обычно это означает, что вы ссылаетесь на класс или переменную из более высокого слоя скриптов. Нижние слои (3_Game) не видят типы, определенные в верхних слоях (4_World, 5_Mission). Переместите определение класса в правильный слой или используйте рефлексию typename для слабой связанности. См. Главу 2.1.

В: Почему JsonFileLoader<T>.JsonLoadFile() не возвращает мои данные?

О: JsonLoadFile() возвращает void, а не загруженный объект. Необходимо предварительно создать объект и передать его как параметр-ссылку: ref MyConfig cfg = new MyConfig(); JsonFileLoader<MyConfig>.JsonLoadFile(path, cfg);. Присвоение возвращаемого значения молча дает null. См. Главу 6.8.

В: Мой RPC отправлен, но никогда не получен на другой стороне.

О: Проверьте частые причины: (1) ID RPC не совпадает между отправителем и получателем. (2) Вы отправляете с клиента, но слушаете на клиенте (или сервер-серверу). (3) Вы забыли зарегистрировать обработчик RPC в OnRPC() или вашем пользовательском обработчике. (4) Целевая сущность null или не синхронизирована по сети. См. Главу 6.9 и Главу 7.3.

В: Я получаю ошибку "Error: Member already defined" в блоке else-if.

О: Enforce Script не позволяет повторное объявление переменных в соседних блоках else if в одной области видимости. Объявите переменную один раз перед цепочкой if или используйте отдельные области видимости с фигурными скобками. См. Главу 1.12.

В: Мой UI-макет ничего не показывает / виджеты невидимы.

О: Частые причины: (1) Виджет имеет нулевой размер --- проверьте, что ширина/высота установлены корректно (без отрицательных значений). (2) Виджет не Show(true). (3) Альфа цвета текста равна 0 (полностью прозрачный). (4) Путь к макету в CreateWidgets() неверен (ошибка не выбрасывается, просто возвращается null). См. Главу 3.3.

В: Мой мод вызывает краш при запуске сервера.

О: Проверьте: (1) Вызов клиентских методов (GetGame().GetPlayer(), UI-код) на сервере. (2) Обращение к null в OnInit или OnMissionStart до готовности мира. (3) Бесконечная рекурсия в modded class, где забыли вызвать super. Всегда добавляйте guard-проверки, так как try/catch отсутствует. См. Главу 1.11.

В: Символы обратной косой черты или кавычки в строках вызывают ошибки парсинга.

О: Парсер Enforce Script (CParser) не поддерживает escape-последовательности \\ или \" в строковых литералах. Полностью избегайте обратных косых черт. Для путей к файлам используйте прямые косые черты ("my/path/file.json"). Для кавычек в строках используйте одинарные кавычки или конкатенацию строк. См. Главу 1.12.


Архитектурные решения

В: Что такое 5-слойная иерархия скриптов и почему она важна?

О: Скрипты DayZ компилируются в пяти пронумерованных слоях: 1_Core, 2_GameLib, 3_Game, 4_World, 5_Mission. Каждый слой может ссылаться только на типы из того же или нижнего слоя. Это обеспечивает архитектурные границы --- помещайте общие перечисления и константы в 3_Game, логику сущностей в 4_World, а UI/хуки миссий в 5_Mission. См. Главу 2.1.

В: Следует ли использовать modded class или создавать новые классы?

О: Используйте modded class, когда нужно изменить или расширить существующее ванильное поведение (добавление метода в PlayerBase, подключение к MissionServer). Создавайте новые классы для самостоятельных систем, которым не нужно ничего переопределять. Модифицированные классы автоматически образуют цепочку --- всегда вызывайте super, чтобы не сломать другие моды. См. Главу 1.4.

В: Как организовать клиентский и серверный код?

О: Используйте директивы препроцессора #ifdef SERVER и #ifdef CLIENT для кода, который должен выполняться только на одной стороне. Для крупных модов разделяйте на отдельные PBO: клиентский мод (UI, рендеринг, локальные эффекты) и серверный мод (спавн, логика, персистентность). Это предотвращает утечку серверной логики клиентам. См. Главу 2.5 и Главу 6.9.

В: Когда использовать Синглтон, а когда Модуль/Плагин?

О: Используйте Модуль (зарегистрированный через PluginManager CF или вашу собственную систему модулей), когда вам нужно управление жизненным циклом (OnInit, OnUpdate, OnMissionFinish). Используйте автономный Синглтон для утилитарных сервисов без состояния, которым нужен только глобальный доступ. Модули предпочтительны для всего, что имеет состояние или требует очистки. См. Главу 7.1 и Главу 7.2.

В: Как безопасно хранить данные игроков, сохраняющиеся между перезапусками сервера?

О: Сохраняйте JSON-файлы в каталог $profile: сервера с помощью JsonFileLoader. Используйте Steam UID игрока (из PlayerIdentity.GetId()) как имя файла. Загружайте при подключении игрока, сохраняйте при отключении и периодически. Всегда корректно обрабатывайте отсутствующие/поврежденные файлы с помощью guard-проверок. См. Главу 7.4 и Главу 6.8.


Публикация и распространение

В: Как упаковать мод в PBO?

О: Используйте Addon Builder (из DayZ Tools) или сторонние инструменты, такие как PBO Manager. Укажите исходную папку мода, установите правильный префикс (совпадающий с префиксом аддона в config.cpp) и соберите. Выходной файл .pbo помещается в папку Addons/ вашего мода. См. Главу 4.6.

В: Как подписать мод для использования на сервере?

О: Сгенерируйте пару ключей с помощью DSSignFile или DSCreateKey из DayZ Tools: это создаст .biprivatekey и .bikey. Подпишите каждый PBO закрытым ключом (создаются файлы .bisign рядом с каждым PBO). Передайте .bikey администраторам серверов для их папки keys/. Никогда не делитесь .biprivatekey. См. Главу 4.6.

В: Как опубликовать в Steam Workshop?

О: Используйте Publisher из DayZ Tools или загрузчик Steam Workshop. Вам нужен файл mod.cpp в корне мода, определяющий имя, автора и описание. Загрузчик загружает ваши упакованные PBO, и Steam присваивает ID Workshop. Обновляйте повторной публикацией с того же аккаунта. См. Главу 2.3 и Главу 8.7.

В: Может ли мой мод требовать другие моды в качестве зависимостей?

О: Да. В config.cpp добавьте имя класса CfgPatches мода-зависимости в массив requiredAddons[]. В mod.cpp формальной системы зависимостей нет --- документируйте необходимые моды в описании Workshop. Игроки должны подписаться на все необходимые моды и загрузить их. См. Главу 2.2.


Продвинутые темы

В: Как создать пользовательские действия игрока (взаимодействия)?

О: Наследуйте от ActionBase (или подкласса вроде ActionInteractBase), определите CreateConditionComponents() для предусловий, переопределите OnStart/OnExecute/OnEnd для логики и зарегистрируйте в SetActions() на целевой сущности. Действия поддерживают непрерывный (удержание) и мгновенный (клик) режимы. См. Главу 6.12.

В: Как работает система урона для пользовательских предметов?

О: Определите класс DamageSystem в config.cpp вашего предмета с DamageZones (именованные регионы) и значениями ArmorType. Каждая зона отслеживает собственное здоровье. Переопределите EEHitBy() и EEKilled() в скрипте для пользовательских реакций на урон. Движок сопоставляет компоненты Fire Geometry модели с именами зон. См. Главу 6.1.

В: Как добавить пользовательские назначения клавиш в мод?

О: Создайте файл inputs.xml, определяющий ваши действия ввода с привязками клавиш по умолчанию. Зарегистрируйте их в скрипте через GetUApi().RegisterInput(). Запрашивайте состояние с помощью GetUApi().GetInputByName("your_action").LocalPress(). Добавьте локализованные имена в stringtable.csv. См. Главу 5.2 и Главу 6.13.

В: Как сделать мод совместимым с другими модами?

О: Следуйте принципам: (1) Всегда вызывайте super в переопределениях modded-классов. (2) Используйте уникальные имена классов с префиксом мода (например, MyMod_Manager). (3) Используйте уникальные ID RPC. (4) Не переопределяйте ванильные методы без вызова super. (5) Используйте #ifdef для обнаружения необязательных зависимостей. (6) Тестируйте с популярными комбинациями модов (CF, Expansion и т.д.). См. Главу 7.2.

В: Как оптимизировать мод для производительности сервера?

О: Ключевые стратегии: (1) Избегайте покадровой (OnUpdate) логики --- используйте таймеры или событийную архитектуру. (2) Кешируйте ссылки вместо повторных вызовов GetGame().GetPlayer(). (3) Используйте guard-проверки GetGame().IsServer() / GetGame().IsClient() для пропуска ненужного кода. (4) Профилируйте с помощью замеров int start = TickCount(0);. (5) Ограничивайте сетевой трафик --- группируйте RPC и используйте Net Sync Variables для частых мелких обновлений. См. Главу 7.7.


Есть вопрос, не освещенный здесь? Создайте issue в репозитории.

Released under CC BY-SA 4.0 | Code examples under MIT License