Developer guide · SDK v1
Build a native UNBSE plugin
Create a 64-bit Windows UE4SS C++ mod that registers with the UNBSE host and uses only the versioned capabilities it needs.
UNBSE 0.14.5 supports Steam
1.512.105.0 only. Check runtime identity before selecting
version-specific addresses or behavior.
Step 1
Include the SDK
Add the five public SDK headers
to your project. Begin with UNBSEAddonHostV1.h; it also
includes the script-service declarations. Query the other services only
when your feature needs them.
| Header | Use |
|---|---|
UNBSEAddonHostV1.h | Registration, capabilities, ownership, and retirement |
UNBSEMessagingV1.h | In-process messages and lifecycle events |
UNBSEScriptServiceV1.h | Native functions exposed to supported script runtimes |
UNBSERuntimeInfoV1.h | Loaded executable and foundation identity |
UNBSERelocationV1.h | Owner-checked executable RVA resolution |
Step 2
Find and register with the host
Locate UNBSE_QueryAddonHostV1 among modules already loaded
in the game process, request ABI version 1, then submit a descriptor.
Use a stable namespaced add-on ID and declare every effect your add-on
can perform. The declaration records effects; it is not a security sandbox.
#include <UNBSEAddonHostV1.h>
#include <cstdint>
#include <cstring>
bool RegisterAddon(UNBSEQueryAddonHostV1Function query,
UNBSEAddonHostV1& host,
std::uint32_t& owner)
{
host = {};
host.structSize = sizeof(host);
if (!query || !query(UNBSE_ADDON_HOST_ABI_VERSION, &host) ||
host.apiVersion != UNBSE_ADDON_HOST_ABI_VERSION ||
host.structSize < sizeof(host) ||
!host.registerAddon || !host.retireAddon) {
return false;
}
UNBSEAddonDescriptorV1 descriptor{};
descriptor.structSize = sizeof(descriptor);
descriptor.apiVersion = UNBSE_ADDON_HOST_ABI_VERSION;
descriptor.declaredEffects = UNBSE_ADDON_EFFECT_RUNTIME_READ;
descriptor.requiredHostCapabilities =
UNBSE_ADDON_HOST_CAPABILITY_RUNTIME_INFO_V1;
std::memcpy(descriptor.addonId, "example.weather", 16);
std::memcpy(descriptor.addonVersion, "1.0.0", 6);
UNBSEAddonRegistrationV1 registration{};
registration.structSize = sizeof(registration);
registration.apiVersion = UNBSE_ADDON_HOST_ABI_VERSION;
const auto result = host.registerAddon(&descriptor, ®istration);
if (result != UNBSE_ADDON_RESULT_OK) {
return false;
}
if (registration.missingHostCapabilities != 0) {
host.retireAddon(registration.ownerHandle,
UNBSE_SCRIPT_DEFAULT_DEADLINE_MS);
return false;
}
owner = registration.ownerHandle;
return true;
}
Keep the host table and owner handle for the add-on lifetime. Validate
missingHostCapabilities before using optional features, and
log both numeric results and resultName when diagnosing failures.
Step 3
Query only the services you use
Each service has its own query export and ABI version. Zero the destination,
initialize structSize, request the matching version, and verify
every returned function pointer before calling it.
| Goal | Query | First check |
|---|---|---|
| Publish script functions | host.queryScriptService | getCapabilities() |
| Exchange add-on messages | UNBSE_QueryMessagingV1 | Listener and message limits |
| Read the loaded runtime | UNBSE_QueryRuntimeInfoV1 | Identity flags and exact game version |
| Resolve a known RVA | UNBSE_QueryRelocationV1 | Runtime identity, then bounds result |
A successful script-service query does not promise that every VM or specialized integration is available. Test the matching capability bit and provide a clean disabled path for unsupported hosts.
See the complete native SDK reference for every query signature, service-table function, result code, structure size, capability value, and current limit.
Step 4
Retire cleanly
Stop starting new work, then retire service callbacks and the owner before
your DLL unloads. Use retireOwner for registered script
functions and retireAddon for the add-on. Treat message
payloads, invocation arguments, and other call pointers as borrowed for
the synchronous call unless a header explicitly says otherwise.
Do not unload code that the host may still call. Log the result and keep the module loaded when retirement cannot complete safely.
Step 5
Package and declare compatibility
Ship the DLL as an enabled UE4SS C++ mod with this layout. The empty
enabled.txt marker tells UE4SS to load the mod.
ue4ss/Mods/<YourPlugin>/
├── enabled.txt
└── dlls/
└── main.dll
Do not put a native UNBSE add-on in OBSE/Plugins; that
directory is for plugins using the OBSE64 compatibility ABI. In your
release notes, state the exact UNBSE and game versions you tested and
describe optional features that disable themselves when capabilities
are unavailable.
Next steps