Overview
The [MemberFunction] attribute is used to define wrappers for non-virtual member functions and static functions in native FFXIV classes. The source generator creates function pointer wrappers that automatically handle the this pointer and null safety checks.
Attribute Definition
The signature pattern to locate the function in the game binary. Must use ?? for wildcard bytes and include 2 characters per byte.
Basic Usage
Define a partial method with the [MemberFunction] attribute and the signature pattern:
[MemberFunction("E8 ?? ?? ?? ?? C1 E7 0C")]
public partial void AddEvent(
AtkEventType eventType,
uint eventParam,
AtkEventListener* listener,
AtkResNode* nodeParam,
bool isGlobalEvent);
Generated Code
The source generator creates a wrapper that:
- Checks if the function pointer is null
- Automatically passes the
this pointer as the first argument
- Forwards all other arguments to the native function
public partial void AddEvent(
AtkEventType eventType,
uint eventParam,
AtkEventListener* listener,
AtkResNode* nodeParam,
bool isGlobalEvent)
{
if (MemberFunctionPointers.AddEvent is null)
{
ThrowHelper.ThrowNullAddress(
"MemberFunctionPointers.AddEvent",
"E8 ?? ?? ?? ?? C1 E7 0C");
}
MemberFunctionPointers.AddEvent(
(AtkResNode*)Unsafe.AsPointer(ref this),
eventType,
eventParam,
listener,
nodeParam,
isGlobalEvent);
}
Calling Member Functions
Call member functions just like regular C# methods:
// Get a pointer to a native object
var resNode = someAtkComponent->GetRootNode();
// Call member function - the 'this' pointer is passed automatically
resNode->AddEvent(
AtkEventType.MouseClick,
1,
listener,
nodeParam,
false);
Static Functions
Static functions work the same way, but don’t pass a this pointer:
[MemberFunction("E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 74 51")]
public static partial hkReferencedObject* LoadFromStream(
hkIstream* stream,
hkSerializeUtil.ErrorDetails* errorDetails);
The generator detects the static modifier and omits the instance pointer:
// Usage
var result = hkSerializeUtil.LoadFromStream(stream, errorDetails);
Return Values
Member functions can return any unmanaged type:
[MemberFunction("E8 ?? ?? ?? ?? 4C 8B E0 48 8B 4F")]
public partial GameObjectId GetTargetId();
// Usage
var character = GetLocalPlayerCharacter();
var targetId = character->GetTargetId();
Examples from the Codebase
UI Component Methods
From AtkResNode.cs:176-205:
[MemberFunction("E8 ?? ?? ?? ?? 49 89 86 ?? ?? ?? ?? 45 33 C9")]
public partial AtkComponentTreeList* GetAsAtkComponentTreeList();
[MemberFunction("E8 ?? ?? ?? ?? BA FB 01 00 00 48 89 83")]
public partial AtkComponentScrollBar* GetAsAtkComponentScrollBar();
[MemberFunction("E8 ?? ?? ?? ?? 44 8D 7E")]
public partial AtkComponentIcon* GetAsAtkComponentIcon();
Character Methods
From Character.cs:98:
[MemberFunction("E8 ?? ?? ?? ?? 4C 8B E0 48 8B 4F")]
public partial GameObjectId GetTargetId();
Havok Animation
From hkaPose.cs:33-84:
[MemberFunction("40 53 48 83 EC 20 4C 89 01 33 C0")]
public partial void Ctor1(hkaSkeleton* skeleton);
[MemberFunction("40 53 48 83 EC 30 4C 89 01")]
public partial void Ctor2(hkaSkeleton* skeleton, byte* buffer);
[MemberFunction("E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 8B D0 E8 ?? ?? ?? ?? 48 83 C4 20")]
public partial hkaPose* Clone();
Best Practices
Always mark the struct unsafe and partial when using member function attributes.
Use descriptive method names that match the game’s internal naming when known from RTTI data.
Signature Patterns
- Use specific bytes where possible to ensure unique matches
- Wildcard patterns (
??) should be minimal but sufficient
- Test signatures across game versions when possible
Method Declarations
// Good - clear parameter names and types
[MemberFunction("E8 ?? ?? ?? ?? C1 E7 0C")]
public partial void AddEvent(
AtkEventType eventType,
uint eventParam,
AtkEventListener* listener);
// Avoid - unclear parameter names
[MemberFunction("E8 ?? ?? ?? ?? C1 E7 0C")]
public partial void DoThing(int a, uint b, void* c);
See Also