FOnline Engine
Current master GitHub
Документация Docs/ru/how-to/native-extensions.md

Нативные расширения

Нативные расширения позволяют подключающему проекту добавлять код на C++ в FOnline, не перенося игровую логику в репозиторий переиспользуемого движка. Они компилируются из исходного кода в составе общей сборки, проходят через тот же конвейер метаданных и кодогенерации и линкуются с библиотеками выбранных ролей движка.

Это руководство описывает архитектуру, разработку и проверку. Точные актуальные объявления приведены в сгенерированных справочниках ролей, хуков, правил биндинга и в канонической JSON-модели.

Статус контракта

Интерфейс нативных расширений имеет статус experimental и привязан к ревизии. Движок документирует компоновку исходников и поведение сгенерированных биндингов для зафиксированной ревизии, но не обещает бинарную совместимость расширения, собранного для одной ревизии, с библиотеками среды выполнения от другой.

Движок отвечает за:

  • распределение исходников по ролям через AddEngineSources и их обнаружение;
  • участие каждого зарегистрированного исходника в обработке метаданных и кодогенерации;
  • поддерживаемые хуки движка и сгенерированные реализации по умолчанию;
  • соглашения о пространстве имён движка, указателях, nullable-типах и экспорте в скрипты;
  • связи базовых библиотек и линковки для пяти ролей.

Подключающий проект отвечает за:

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

FO_NATIVE_SCRIPTING выбирает скриптовый бэкенд. Это не переключатель проектных нативных расширений, и проверять по нему доступность расширения нельзя.

Проверенные исходные пути

  • BuildTools/NativeExtensionInterface.json
  • BuildTools/Init.cmake
  • BuildTools/cmake/ProjectInterface.json
  • BuildTools/cmake/helpers/Build.cmake
  • BuildTools/cmake/helpers/Options.cmake
  • BuildTools/cmake/stages/EngineSources.cmake
  • BuildTools/cmake/stages/Codegen.cmake
  • BuildTools/cmake/stages/CoreLibs.cmake
  • BuildTools/codegen.py
  • места вызова хуков движка в Source/Applications/, Source/Frontend/, Source/Common/, Source/Client/, Source/Server/ и Source/Tools/
  • Examples/MinimalProject/CMakeLists.txt
  • Examples/MinimalProject/StarterServerExtension.cpp
  • Examples/NativeExtensionSample/CMakeLists.txt
  • Examples/NativeExtensionSample/SourceExt/ServerExtension.cpp

Компоновка сборки

Регистрируйте проектные исходники после стадии ThirdParty и до точки входа EngineSources:

StartProjectGeneration()
RegisterProjectOptions()
AddThirdPartyLibraries()

AddEngineSources(
    COMMON SourceExt/CommonExtension.cpp
    SERVER SourceExt/ServerExtension.cpp
    CLIENT SourceExt/ClientExtension.cpp
    MAPPER SourceExt/MapperExtension.cpp
    BAKER SourceExt/BakerExtension.cpp
    TESTS SourceExt/Test_ProjectExtension.cpp)

RegisterEngineSources()
SetupCodeGeneration()

Пути и шаблоны разрешаются относительно корня вклада подключающего проекта. Аргументы задаются парами «роль/путь»; нечётное число аргументов приводит к ошибке конфигурации. Текущий helper не отклоняет неизвестный token роли: он создаёт список FO_<ROLE>_SOURCE и всё равно добавляет файл во входы metadata, но ни одна цель Engine не использует этот список, если роль не входит в шесть описанных ниже. Используемых ролей исходников EDITOR, ANIMATION_VIEWER и PARTICLE_VIEWER нет. Mapper, оба специализированных просмотрщика, Baker и ASCompiler линкуются с BakerLib, поэтому переиспользуемая поддержка авторинга и запекания обычно относится к BAKER; код, действительно общий для всех приложений, относится к COMMON. Проектные модули трансляции с тестами Catch2 относятся к TESTS: эта роль добавляет их непосредственно во включённые исполняемые файлы unit-тестов и покрытия, но не в runtime-библиотеки.

Каждый найденный файл добавляется как в список исходников своей роли, так и в FO_SOURCE_META_FILES. Заголовок, зарегистрированный как COMMON, также попадает во входные данные генерации общих заголовков. Регистрируйте только файлы, которые должна анализировать кодогенерация: вендорное дерево исходников должно быть отдельной библиотечной целью, а не широким шаблоном расширения.

Выбор роли

Выбирайте самую узкую роль, которой принадлежит поведение:

Потребность Роль Следствие
Общепроцессное поведение, конфигурация или приложение, используемые несколькими приложениями COMMON Компилируется в CommonLib; не допускайте зависимостей только для клиента или сервера.
Авторитетная логика, персистентность, серверная сеть, серверные скриптовые методы SERVER Компилируется в ServerLib; недоступно клиентским скриптам и бинарным файлам.
Рендеринг, ввод, клиентская сеть, клиентские скриптовые методы CLIENT Компилируется в ClientLib; Mapper также получает клиентские регистрации через ClientLib.
Автоматизация только для Mapper или его скриптовые методы MAPPER Компилируется в MapperLib.
Пользовательские запекатели ресурсов и средства авторинга для Mapper, просмотрщиков и ASCompiler BAKER Компилируется в BakerLib; BAKER не является целью экспорта в скрипты.
Проектные модули трансляции с регрессиями Catch2 TESTS Компилируется непосредственно во включённые исполняемые файлы unit-тестов и покрытия; runtime- и скриптовых целей нет.

Не используйте COMMON только ради устранения ошибки линковки символа. Перенесите зависимость в принадлежащую ей роль или отделите небольшой общий интерфейс от реализаций для конкретных ролей.

Форматы игровых систем, принадлежащие проекту

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

  • COMMON для парсера, общих записей, экспортируемых скриптовых типов или реестра;
  • BAKER для проверки синтаксиса, валидации с учётом метаданных и генерации ресурсов;
  • SERVER, CLIENT или проектные скрипты для авторитетного поведения среды выполнения;
  • проектный редактор, средство аудита, фикстуры и игровые тесты для подтверждения авторинга и поведения.

Регистрация через AddEngineSources даёт интеграцию со сборкой, метаданными, кодогенерацией и линковкой. Она не передаёт FOnline владение API, форматом, совместимостью, безопасностью или документацией. Подключающий проект обязан описать собственные парсер, запекатель, сгенерированные результаты, потребителей среды выполнения, уровни проверки и политику миграции.

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

Подключения и пространства имён

Начинайте с Common.h, затем подключайте минимальный заголовок роли, необходимый реализации:

#include "Common.h"
#include "Server.h"

FO_USING_NAMESPACE();

FO_BEGIN_NAMESPACE
///@ ExportMethod
FO_SCRIPT_API int32_t Server_Game_ProjectValue(ptr<ServerEngine> server);
FO_END_NAMESPACE

int32_t FO_NAMESPACE Server_Game_ProjectValue(ptr<ServerEngine> server)
{
    ignore_unused(server);
    return 1;
}

Объявления метаданных должны компилироваться как с включённым, так и с выключенным пространством имён движка. Размещайте объявления внутри FO_BEGIN_NAMESPACE / FO_END_NAMESPACE, а определения квалифицируйте через FO_NAMESPACE.

Экспорты FO_SCRIPT_API являются границей кодогенерации и намеренно не начинаются с FO_TRACE_ZONE(Script). Обычные неэкспортируемые проектные функции C++ следуют стандартному соглашению движка о трассировке стека.

Экспорт в скрипты и метаданные

Зарегистрированные файлы расширения разбираются вместе с метаданными движка. Проектный код может использовать поддерживаемые объявления ///@, включая ExportMethod, ExportEvent, ExportRefType, ExportSettings и EngineHook, соблюдая те же правила парсера и nullable-типов, что и объявления движка.

Роль исходника CMake и цель метаданных связаны, но не взаимозаменяемы:

  • файл SERVER обычно объявляет экспорты Server_*;
  • файл CLIENT обычно объявляет экспорты Client_*, и эти регистрации также доступны сборкам Mapper;
  • файл MAPPER объявляет экспорты только для Mapper;
  • экспорты COMMON регистрируются на каждой применимой стороне;
  • BAKER может реализовывать хуки запекателя, но не является целью ExportMethod.

Для заимствований дескрипторов движка используйте ptr<T> / nptr<T>. Кодогенерация отклоняет необёрнутые сырые указатели на дескрипторы. Значения аргументов по умолчанию, nullable-семантика, владение и сторона среды выполнения должны совпадать со сгенерированным скриптовым объявлением. После любого изменения нативных метаданных пересоберите проект и перезапеките ресурсы; не переносите сгенерированные файлы регистрации между ревизиями движка.

Удалённые вызовы проекта остаются проектными скриптовыми метаданными и используют запечённый каталог, описанный в разделе Удалённые вызовы. Они не являются символами нативного расширения.

Хуки движка

Хуки представляют собой необязательные именованные точки входа C++. Объявите хук с ///@ EngineHook и точной сигнатурой из сгенерированного справочника хуков в файле, зарегистрированном в принадлежащей ему роли. Обнаружив объявление, кодогенератор исключает реализацию этого хука по умолчанию из GenericCode-Common.gen.cpp.

Если проект не объявляет хук, кодогенерация создаёт его документированную реализацию по умолчанию. Наличие большинства хуков участвует в сгенерированном состоянии совместимости; текущее исключение — ApplicationShutdownHook. Набор имён хуков закрыт: неизвестное имя приводит к ошибке кодогенерации.

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

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

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

Состояние и время жизни

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

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

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

Используйте словарь указателей движка:

  • ptr<T> / nptr<T> для заимствованных объектов движка;
  • unique_* / refcount_* и вспомогательные средства shared_ptr движка для владения;
  • сырые указатели только на документированных границах ABI ОС или SDK с немедленным оборачиванием при входе.

Зависимости и платформы

AddEngineSources не выводит зависимости автоматически. Подключающий проект должен добавить библиотеки, каталоги включения, определения компилятора, сгенерированные заголовки и платформенные фреймворки до построения базовых библиотек и приложений. Держите сторонние исходники в отдельной цели и направляйте её только через самый узкий потребляемый список FO_*_LIBS текущей ревизии; проектные зависимости описывают полный процесс выбора, происхождения, CMake-интеграции, ABI, упаковки и обновления.

Для платформенно-зависимых расширений нужен явный контракт доступности:

  1. оградите настоящую реализацию платформенными макросами движка или проекта;
  2. предоставьте компилируемую заглушку неподдерживаемого режима, если общий скриптовый или нативный символ должен сохраняться;
  3. явно отклоняйте неподдерживаемое использование во время выполнения вместо имитации успеха;
  4. синхронизируйте содержимое пакетов и внешние библиотеки времени выполнения со скомпилированной возможностью;
  5. проверяйте как минимум по одному пути сборки с включённой и выключенной возможностью.

Никогда не помещайте учётные данные, ключи API, материалы подписания или закрытые URL сервисов в исходники расширения, сгенерированные метаданные, примеры, журналы или документацию.

Стратегия тестирования

Используйте самый узкий путь, доказывающий затронутую границу:

  • изменения регистрации CMake или ролей: cmake -P BuildTools/tests/validate_native_extension_interface.cmake;
  • изменения хуков, метаданных или кодогенерации: перегенерируйте и проверьте справочники нативных расширений и API, затем выполните BakeResources в настоящем подключающем проекте;
  • переиспользуемый минимальный серверный хук: запустите python validate.py из Examples/MinimalProject;
  • полный путь lifecycle, role-link, script export и focused-теста: запустите python validate.py из Examples/NativeExtensionSample;
  • экспорт в скрипты: добавьте проверку компиляции или запекания скриптов и сфокусированный тест среды выполнения, вызывающий сгенерированный метод на правильной стороне;
  • видимое клиенту расширение: соберите и запустите реальный путь отдельного клиента; серверная или headless-проверка не доказывает рендеринг, ввод, динамические библиотеки или поведение пакета;
  • внешний SDK или платформенный мост: проверьте включённый, выключенный и упакованный пути среды выполнения на целевой платформе.

Минимальный проект движка является нормативным starter-примером. Examples/NativeExtensionSample показывает полный сфокусированный native-путь: хранит состояние отдельного сервера в ServerEngine.UserData, подключает небольшую библиотеку через текущее integration state FO_SERVER_LIBS, экспортирует один серверный метод и выполняет native unit- и runtime smoke-проверки. Большой игровой проект даёт ценное интеграционное свидетельство, но не определяет переиспользуемый контракт.

Обновление ревизии движка

Считайте изменение gitlink движка событием совместимости расширения:

  1. сравните старую и новую канонические модели нативного расширения через управление изменениями сгенерированных контрактов;
  2. проверьте изменения сигнатур, значений по умолчанию и мест вызова хуков, ролей и библиотек, указателей и nullable-типов, сгенерированных метаданных и маркеров совместимости;
  3. повторно сконфигурируйте проект, чтобы заново проверить роли и разрешить шаблоны исходников;
  4. пересоберите каждую затронутую нативную роль и перезапеките проектные метаданные и ресурсы;
  5. обновите документацию и тесты проектного расширения, а при изменении поведения — заметки о миграции и релизе;
  6. никогда не используйте повторно нативные бинарные файлы или сгенерированные файлы регистрации от предыдущей ревизии движка.

Контрольный список проверки

  1. Каждый исходник зарегистрирован в самой узкой допустимой роли до RegisterEngineSources().
  2. У каждого объявления метаданных правильны цель, макросы пространства имён, словарь указателей и nullable-типов, а для хука — точная сигнатура.
  3. Состояние на экземпляр движка имеет владельца экземпляра; для общепроцессных глобальных данных явно обоснован общепроцессный жизненный цикл.
  4. Зависимости, платформенные условия, отключённые заглушки и содержимое пакета соответствуют скомпилированной возможности.
  5. Сгенерированные справочники API и нативных расширений, а также сводная разница контрактов актуальны.
  6. Структурные проверки CMake, кодогенерация и запекание, сфокусированные нативные и скриптовые тесты и минимальный реальный путь среды выполнения проходят без предупреждений.
  7. Проектная документация фиксирует настройки, персистентность, сервисы, безопасность и релизное поведение, которые руководство движка намеренно не охватывает.

См. также

Введите запрос.