Palforge
Guide

Hooks

Intercept native engine functions before or after they run.

A hook lets you run your own code around a native Unreal function — inspect or change its arguments, cancel it, or read and replace its return value. Palforge discovers [PreHook] and [PostHook] methods in your plugin assembly and binds them automatically; each is revoked when the plugin stops.

Declaring a hook

using Palforge.Hooks.Attributes;
using Palforge.Sdk.Script.Pal;

public sealed class ChatHooks
{
    [PreHook("PalPlayerController:EnterChat_Receive")]
    public void BeforeChat(PalPlayerController self)
    {
        // runs before the engine handles the chat RPC
    }
}

A hook method may be static or an instance method (the declaring type is resolved from DI, or default-constructed). A single method can carry multiple hook attributes.

Targeting a function

There are four equivalent ways to name the target Class:Function:

[PreHook("PalPlayerController:EnterChat_Receive")]           // "Class:Function"
[PreHook("PalPlayerController", "EnterChat_Receive")]        // class + function
[PreHook(typeof(PalPlayerController), "EnterChat_Receive")]  // typed wrapper
[PreHook<PalPlayerController>("EnterChat_Receive")]          // generic typed wrapper

The typed forms translate the generated SDK type to its engine class name, so they only work for SDK types; the string forms work for any class in the running game. The class and function must exist at bind time, or the hook throws with a helpful message.

Including overrides

Set IncludeOverrides = true to also hook subclasses that override the function:

[PreHook<PalPlayerController>("EnterChat_Receive", IncludeOverrides = true)]
public bool BeforeChat(PalPlayerController self) => true;

Hook method parameters

The binder fills your parameters from the live call. You only declare the ones you care about:

ParameterBound to
First by-value param assignable to UObjectcontext.Self — the instance the function was called on. Use the SDK wrapper type (e.g. PalPlayerController self).
A parameter named resultThe function's return value (value types only). Use ref/out to overwrite it.
Any other parameterMatched by name against the function's parameters.

Matched parameters can be UObject-derived (read-only), string (writable with ref), a generated UStructValue struct (read-only), or a primitive/enum (writable with ref). A ref parameter is written back into the call frame after your method returns, so you can rewrite an argument:

// Suppose the engine function is  TakeDamage(float amount, AActor* instigator)
[PreHook("PalPlayerCharacter:TakeDamage")]
public void OnTakeDamage(PalPlayerCharacter self, ref float amount)
{
    amount *= 0.5f;   // halve all incoming damage
}

If the target instance isn't actually of the type you declared for self, the hook is skipped for that call — so a hook typed to PalPlayerCharacter won't fire for unrelated objects sharing the function.

Cancelling the original

A pre-hook that returns bool votes on whether the original runs: return false to skip it, true to let it proceed.

[PreHook<PalPlayerController>("EnterChat_Receive")]
public bool GateChat(PalPlayerController self)
{
    return self.BAdmin;   // non-admins: original EnterChat_Receive is skipped
}

You can also cancel explicitly by taking a HookContext and calling context.SkipOriginal().

Reading the return value

In a post-hook, take a result parameter, or use the HookContext to read and replace the return:

using Palforge.Unreal.Hooks;

[PostHook("SomeClass:ComputeScore")]
public static void ClampScore(HookContext context)
{
    if (context.GetReturnValue<int>() < 0)
        context.SetReturnValue(0);
}

HookContext exposes Self, Function, Get<T>/Set<T>, GetObject/SetObject, GetString/SetString, GetStruct, GetReturnValue<T>/SetReturnValue<T>, SkipOriginal() and OriginalSkipped.

Manual hooks

Inside OnStart you can register a hook directly through the plugin scope, which returns an IDisposable auto-revoked on stop. The callback receives a HookContext:

protected override void OnStart()
{
    Scope.Hook("PalPlayerController", "EnterChat_Receive",
        prefix: context =>
        {
            // inspect context.Self, args, call context.SkipOriginal() to cancel
        },
        includeOverrides: true);
}

HookByName(functionName, prefix, postfix) hooks by function name across classes when you don't have a specific class in hand.

Hooks run on the game thread, in the middle of engine execution. Keep them fast, don't block, and let exceptions be — a throw is caught and logged rather than crashing the server, but heavy work on the tick will stutter the game.

Next steps

On this page