API, TDB & VM — Core Static Classes

These three static classes are the primary entry points for REFramework.NET plugins. They provide logging, singleton access, type database queries, and managed string allocation.

API (REFrameworkNET.API)

All members are static. This is your main interface to the REFramework runtime.

Logging

API.LogInfo("Player spawned");
API.LogWarning("Config file missing, using defaults");
API.LogError("Failed to resolve singleton");
MethodDescription
LogInfo(string message)Log at Info level
LogWarning(string message)Log at Warning level
LogError(string message)Log at Error level

Two static properties control log behavior:

PropertyTypeDefaultDescription
LogLevelLogLevel enum (Info, Warning, Error)InfoMinimum severity that gets emitted. Set to Warning to suppress info-level noise.
LogToConsolebooltrueWhen true, log messages are also printed to the REFramework console window.
// Suppress info logs during a hot loop
API.LogLevel = LogLevel.Warning;

// Disable console mirroring
API.LogToConsole = false;

Singleton access

Singletons are how you reach into the running game. Most game managers (app.PlayerManager, app.EnemyManager, etc.) are managed singletons; engine subsystems (via.SceneManager, via.Application) are native singletons.

Untyped access

ManagedObject playerMgr = API.GetManagedSingleton("app.PlayerManager");
NativeObject sceneMgr = API.GetNativeSingleton("via.SceneManager");
MethodReturnsDescription
GetManagedSingleton(string name)ManagedObjectRetrieve a managed singleton by its full type name. Returns null if not found.
GetNativeSingleton(string name)NativeObjectRetrieve a native singleton by its full type name. Returns null if not found.

Generic typed access

The generic variants call GetManagedSingleton / GetNativeSingleton under the hood and then cast to a typed proxy via .As<T>(). This is the preferred approach when you have generated proxy types:

var playerMgr = API.GetManagedSingletonT<app.PlayerManager>();
var sceneMgr = API.GetNativeSingletonT<via.SceneManager>();

// playerMgr is already typed — direct property/method access:
var player = playerMgr.CurrentPlayer;
MethodReturnsConstraint
GetManagedSingletonT<T>()TT : ref class (proxy type)
GetNativeSingletonT<T>()TT : ref class (proxy type)

Both return null (default of T) if the singleton is not found.

Caveat: NativeSingleton does not carry a TypeDefinition — only a TypeInfo handle. This means GetNativeSingleton(name) returns a NativeObject that lacks the typed proxy path. Use GetNativeSingletonT<T>() when you need typed access to native singletons.

Enumerating all singletons

List<ManagedSingleton> managed = API.GetManagedSingletons();
List<NativeSingleton> native = API.GetNativeSingletons();

foreach (var s in managed) {
    API.LogInfo($"Managed: {s.Name} @ 0x{s.Instance.GetAddress():X}");
}
MethodReturns
GetManagedSingletons()List<ManagedSingleton> — all currently registered managed singletons
GetNativeSingletons()List<NativeSingleton> — all currently registered native singletons

GC — LocalFrameGC()

API.LocalFrameGC();

Flushes the RE Engine VM's local reference frame. Call this on custom threads (not the main game thread) after performing bulk managed allocations or method invocations. Without it, local references accumulate and can crash the managed heap.

On the main thread, the engine handles this automatically each frame. You only need to call it manually in background workers or long-running loops.

UI — IsDrawingUI()

if (API.IsDrawingUI()) {
    ImGui.Text("Overlay is visible");
}

Returns true while REFramework's ImGui overlay is actively rendering. Use this to conditionally draw ImGui elements inside your render callbacks — avoids drawing when the overlay is hidden.

Plugin directory — GetPluginDirectory(Assembly)

string dir = API.GetPluginDirectory(typeof(MyPlugin).Assembly);
string configPath = Path.Combine(dir, "config.json");

Returns the directory containing the calling plugin's .cs source file or .dll. Useful for loading configuration files, assets, or data relative to your plugin without hardcoding paths.

TDB shortcut — GetTDB()

TDB tdb = API.GetTDB();

Equivalent to TDB.Get(). Convenience accessor when you already have API in scope.

ResourceManager — GetResourceManager()

ResourceManager mgr = API.GetResourceManager();

Returns the engine's ResourceManager, used to create resources and userdata objects. See the ResourceManager section below.


TDB (REFrameworkNET.TDB)

The Type Database — REFramework's reflection system over the RE Engine's type metadata. Access it via TDB.Get() or API.GetTDB().

Type lookup

MethodReturnsDescription
FindType(string name)TypeDefinitionLook up a type by full name (e.g. "app.PlayerManager"). Not cached — avoid in hot paths.
GetType(uint index)TypeDefinitionLook up by numeric TDB index.
GetType(string name)TypeDefinitionAlias for FindType.
FindTypeByFqn(uint fqn)TypeDefinitionLook up by FQN hash.
GetTypeT<T>()TypeDefinitionCached lookup using the proxy type's compile-time metadata. Prefer this on hot paths.
var tdb = TDB.Get();

// One-off lookup (fine in init code):
TypeDefinition td = tdb.FindType("app.PlayerManager");

// Hot-path lookup (cached, no string allocation):
TypeDefinition td2 = tdb.GetTypeT<app.PlayerManager>();

Method and field lookup

Method m = tdb.FindMethod("app.PlayerManager", "get_CurrentPlayer");
Field f = tdb.FindField("app.EnemyContext", "_ConditionDamageList");
MethodReturnsDescription
FindMethod(string typeName, string methodName)MethodFind a method by type name and method name.
FindField(string typeName, string fieldName)FieldFind a field by type name and field name.

Metadata counts

var tdb = TDB.Get();
API.LogInfo($"Types: {tdb.GetNumTypes()}, Methods: {tdb.GetNumMethods()}, " +
            $"Fields: {tdb.GetNumFields()}, Properties: {tdb.GetNumProperties()}");
MethodReturns
GetNumTypes()uint — total type count in the TDB
GetNumMethods()uint — total method count
GetNumFields()uint — total field count
GetNumProperties()uint — total property count

Iterating all types

The Types property returns an iterable collection of every TypeDefinition in the database:

foreach (var td in TDB.Get().Types) {
    if (td.GetFullName().Contains("Enemy")) {
        API.LogInfo($"Found: {td.GetFullName()} (index {td.GetIndex()})");
    }
}

Performance note: This iterates the entire TDB. Use FindType or GetTypeT<T> for targeted lookups.


VM (REFrameworkNET.VM)

Low-level access to the RE Engine's managed virtual machine.

CreateString(string)SystemString

Allocates a System.String on the RE Engine's managed GC heap. Use this when you need to pass a string argument to a game method via reflection:

var greeting = VM.CreateString("Hello, Hunter!");
method.Invoke(obj, new object[] { greeting });

The returned SystemString is a ManagedObject. It is subject to the engine's garbage collector — if you need to store it beyond the current frame, call Globalize() to prevent collection:

var persistent = VM.CreateString("Cached label");
persistent.Globalize();
// Safe to store in a static field now

When do you need this? Whenever you call a game method that expects a managed System.String parameter. Passing a raw C# string will not work — the engine expects its own heap-allocated string object.

SystemString extends ManagedObject and overrides ToString(), so you can read engine strings back to C#:

// Reading a string field from a game object
var nameField = someObj.GetField("_Name");
string name = nameField?.ToString(); // calls SystemString.ToString()

ResourceManager (REFrameworkNET.ResourceManager)

The engine's resource factory. Obtain via API.GetResourceManager().

CreateResource(string typeName, string name)Resource

Creates a new resource from a PAK path. The typeName is a via.typeinfo.TypeInfo name (the runtime type system name, not a TypeDefinition name). The name is the resource path inside the game's PAK archives.

var mgr = API.GetResourceManager();
var tex = mgr.CreateResource("via.render.Texture", "enemy/em0100/texture/body_BM.tex");

Returns null if the type is not found or creation fails.

CreateUserData(string typeName, string name)ManagedObject

Creates a userdata ManagedObject. Userdata objects are engine-managed data containers typically backed by a .user file in the PAK. Like CreateResource, the typeName is a via.typeinfo.TypeInfo name.

var mgr = API.GetResourceManager();
var userData = mgr.CreateUserData("app.ItemUserData", "data/app/item/item_data.user");

Returns null if the type is not found or creation fails.

TypeInfo names vs TypeDefinition names: Both methods take via.typeinfo.TypeInfo names, which are the runtime names the engine uses internally. These usually match TypeDefinition full names, but not always — some types have different runtime representations. If a call returns null unexpectedly, verify the name against the runtime type system rather than the TDB.


Resource (REFrameworkNET.Resource)

Wraps a native RE Engine resource handle. Returned by ResourceManager.CreateResource().

Reference counting

Resources are reference-counted by the engine. If you store a resource beyond the scope where it was created, you must manage its lifetime:

var resource = mgr.CreateResource("via.render.Texture", "path/to/texture.tex");
resource.AddRef();  // prevent engine from releasing it
// ... use resource ...
resource.Release(); // when done
MethodDescription
AddRef()Increment the native reference count
Release()Decrement the native reference count

CreateHolder(string typeName)ManagedObject

Creates a resource holder — a managed wrapper object the engine uses to reference a loaded resource. The typeName here is a TypeDefinition name (unlike ResourceManager methods which take TypeInfo names).

var tex = mgr.CreateResource("via.render.Texture", "path/to/texture.tex");
var holder = tex.CreateHolder("via.render.TextureResource");

Returns null if the type is not found or creation fails.


Performance Tips

Cache type/method/field lookups in hot paths

TDB.FindType(string), IObject.Call(string, ...), and IObject.GetField(string) all perform string-based hashmap lookups on every call. In init code this is fine, but in hooks or frame callbacks that fire hundreds of times per second, the overhead adds up.

Typed proxies handle this automatically — the generated code caches method resolution internally. This is one of their biggest advantages over reflection-style access.

For reflection paths, cache MethodDefinition and FieldDefinition objects in static fields:

// Cache at load time
static MethodDefinition s_getHealth = app.cHunterHealth.REFType.GetMethod("get_Health");
static FieldDefinition s_maxHp = app.cHunterHealth.REFType.GetField("_MaxHealth");

// Use in hot path
[Callback(typeof(app.SomeManager), nameof(app.SomeManager.update), CallbackType.Pre)]
static PreHookResult OnUpdate(Span<ulong> args) {
    var obj = ManagedObject.ToManagedObject(args[1]);
    // Fast: uses cached definitions
    float hp = (float)s_getHealth.Invoke(obj, null);
    float maxHp = (float)s_maxHp.GetDataBoxed(obj);
    return PreHookResult.Continue;
}

Quick reference

// Logging
API.LogInfo("message");
API.LogLevel = LogLevel.Warning;

// Singletons (typed)
var mgr = API.GetManagedSingletonT<app.PlayerManager>();

// Type database
var td = TDB.Get().FindType("app.EnemyManager");
var m  = TDB.Get().FindMethod("app.EnemyManager", "getEnemyCount");

// Resource creation
var resMgr = API.GetResourceManager();
var userdata = resMgr.CreateUserData("app.SomeData", "data/some_data.user");

// String allocation
var s = VM.CreateString("text");

// Plugin-relative paths
var dir = API.GetPluginDirectory(typeof(MyPlugin).Assembly);

// GC on custom threads
API.LocalFrameGC();