Skip to main content

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

signature
string
required
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:
  1. Checks if the function pointer is null
  2. Automatically passes the this pointer as the first argument
  3. 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

Build docs developers (and LLMs) love