Первое нативное расширение Windows x64
Собрать DLL, проверить класс, константы и выгрузку.
Пример рассчитан на Windows x64, Reckfell 1.0.0 и MSVC с инструментами C++. Он регистрирует абстрактный DocsNativeProbe : Entity, ProductVersion=10000 и EntityPathVariant=22. Создать такой класс как персонажа нельзя: это проверка сборки и ABI.
1. Подготовьте исходник и дескриптор
Создайте отдельный тестовый проект. Сохраните native_probe.cpp и native_probe.reckfellextension:
#include "reckfellextension_interface.h"
// This example is deliberately Windows x64 only, not a general C++ binding.
struct NativeStringName { void *opaque; };
static_assert(sizeof(NativeStringName) == 8, "Check StringName size against extension_api.json");
static_assert(RECKFELLEXTENSION_VARIANT_TYPE_ENTITY_PATH == 22, "Unexpected EntityPath ABI");
static ReckfellExtensionClassLibraryPtr library;
static ReckfellExtensionInterfaceStringNameNewWithUtf8Chars name_new;
static ReckfellExtensionPtrDestructor name_destroy;
static ReckfellExtensionInterfaceClassdbRegisterExtensionClass6 register_class;
static ReckfellExtensionInterfaceClassdbUnregisterExtensionClass unregister_class;
static ReckfellExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant register_constant;
static void free_instance(void *, ReckfellExtensionClassInstancePtr) {}
static void initialize(void *, ReckfellExtensionInitializationLevel level) {
if (level != RECKFELLEXTENSION_INITIALIZATION_SCENE) return;
NativeStringName name, parent, enum_name, constant;
name_new(&name, "DocsNativeProbe");
name_new(&parent, "Entity");
name_new(&enum_name, "");
ReckfellExtensionClassCreationInfo6 info = {};
info.is_abstract = 1;
info.is_exposed = 1;
info.free_instance_func = free_instance;
register_class(library, &name, &parent, &info);
name_new(&constant, "ProductVersion");
register_constant(library, &name, &enum_name, &constant, 10000, 0);
name_destroy(&constant);
name_new(&constant, "EntityPathVariant");
register_constant(library, &name, &enum_name, &constant, 22, 0);
name_destroy(&constant);
name_destroy(&enum_name);
name_destroy(&parent);
name_destroy(&name);
}
static void deinitialize(void *, ReckfellExtensionInitializationLevel level) {
if (level != RECKFELLEXTENSION_INITIALIZATION_SCENE) return;
NativeStringName name;
name_new(&name, "DocsNativeProbe");
unregister_class(library, &name);
name_destroy(&name);
}
extern "C" __declspec(dllexport) ReckfellExtensionBool reckfell_docs_init(
ReckfellExtensionInterfaceGetProcAddress get_proc,
ReckfellExtensionClassLibraryPtr native_library,
ReckfellExtensionInitialization *initialization) {
library = native_library;
name_new = (ReckfellExtensionInterfaceStringNameNewWithUtf8Chars)get_proc("string_name_new_with_utf8_chars");
register_class = (ReckfellExtensionInterfaceClassdbRegisterExtensionClass6)get_proc("classdb_register_extension_class6");
unregister_class = (ReckfellExtensionInterfaceClassdbUnregisterExtensionClass)get_proc("classdb_unregister_extension_class");
register_constant = (ReckfellExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant)get_proc("classdb_register_extension_class_integer_constant");
auto get_destructor = (ReckfellExtensionInterfaceVariantGetPtrDestructor)get_proc("variant_get_ptr_destructor");
if (!name_new || !register_class || !unregister_class || !register_constant || !get_destructor) return 0;
name_destroy = get_destructor(RECKFELLEXTENSION_VARIANT_TYPE_STRING_NAME);
if (!name_destroy) return 0;
initialization->minimum_initialization_level = RECKFELLEXTENSION_INITIALIZATION_SCENE;
initialization->userdata = nullptr;
initialization->initialize = initialize;
initialization->deinitialize = deinitialize;
return 1;
}[configuration]
entry_symbol = "reckfell_docs_init"
compatibility_minimum = "1.0.0"
reloadable = false
[libraries]
windows.x86_64 = "./native_probe.windows.x86_64.dll"extern "C" сохраняет имя reckfell_docs_init; оно должно совпадать с entry_symbol. Если обязательная функция get_proc отсутствует, инициализация возвращает 0 вместо вызова пустого указателя. Размер буфера StringName 8 байт выбран только для проверенного Windows x64; ABI и время жизни.
2. Соберите DLL
Откройте x64 Developer PowerShell для MSVC. Перейдите в каталог исходника, задайте $Sdk абсолютным путём к установленному ExtensionSDK и выполните:
cl /nologo /std:c++17 /EHsc /LD /W3 /WX /I"$Sdk/include" native_probe.cpp /Fe:native_probe.windows.x86_64.dllПроверьте код завершения. Нужный результат — native_probe.windows.x86_64.dll; .lib, .exp и .obj не являются загружаемой библиотекой. Другие проверенные сборщики: CMake, SCons.
3. Проверьте загрузку
Положите DLL и дескриптор рядом в asset://addons/native_probe/. Путь ./native_probe.windows.x86_64.dll считается относительно дескриптора. ОС и архитектура должны совпадать; переименование файла не исправляет другую архитектуру DLL.
В C# вызовите ReckfellExtensionManager.LoadExtension("asset://addons/native_probe/native_probe.reckfellextension") и проверьте LoadStatus.Ok. Затем ClassDB.ClassExists("DocsNativeProbe") должно быть true, а ClassDB.ClassGetIntegerConstant("DocsNativeProbe", "ProductVersion") — 10000. Автоматическое обнаружение проектом выполняет сканирование редактора; действия мышью здесь не проверялись.
Повторная загрузка того же пути дала AlreadyLoaded. После UnloadExtension() класс и путь исчезли из реестров. Не заменяйте DLL, используемую запущенным процессом. Пример отключает горячую перезагрузку через reloadable=false.
Если не работает
Файл не найден — проверьте оба пути. Точка входа отсутствует — проверьте экспорт, extern "C" и entry_symbol. Библиотека не открывается — проверьте архитектуру и зависимости среды выполнения. Инициализация отказала — сверяйте обязательные функции и версию SDK. Класс зарегистрирован, создать нельзя — ожидаемо, он абстрактный.
Все три сборки загрузились, передали константы через ABI и выгрузились. Создаваемые native-instance объекты, другие платформы и перезагрузка живых объектов не проверялись. Изменять или пересобирать движок не требуется.