> ## Documentation Index
> Fetch the complete documentation index at: https://lua.starline.one/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks

> Create and manage native function hooks from LuaJIT FFI.

Hook a function by address.

```lua theme={"dark"}
ffi.cdef[[ typedef float (__fastcall *fnGetViewmodelFov)(void*); ]]

local addr = cheat.FindPattern("client.dll", "40 53 48 83 EC 20 ...")
local oGetViewmodelFov
oGetViewmodelFov = hooks.Add(addr, ffi.typeof("fnGetViewmodelFov"), function(self)
    return 120.0
end)
```

The callback runs on the calling thread. Lua execution is serialized across threads. Callback errors are logged instead of propagating into the game.

## hooks.Add

```lua theme={"dark"}
local original = hooks.Add(address, ctype, callback)
```

Installs an inline hook at `address`. Returns the original function as a callable FFI pointer of the same type.

| Param      | Type      |                                                                              |
| ---------- | --------- | ---------------------------------------------------------------------------- |
| `address`  | number    | target function address (from `cheat.FindPattern`, `cheat.FindExport`, etc.) |
| `ctype`    | ffi ctype | function pointer type created with `ffi.typeof`                              |
| `callback` | function  | your Lua function, receives the same args as the original                    |

The callback must match the function type. On x64, `__fastcall` is suitable for all calling conventions.

```lua theme={"dark"}
ffi.cdef[[ typedef void (__fastcall *fnFrameStageNotify)(void*, int); ]]

local oFrameStageNotify
oFrameStageNotify = hooks.Add(
    cheat.FindPattern("client.dll", "..."),
    ffi.typeof("fnFrameStageNotify"),
    function(self, stage)
        if stage == 6 then
            -- pre FRAME_RENDER_START
        end
        oFrameStageNotify(self, stage)
    end
)
```

## hooks.Remove

```lua theme={"dark"}
hooks.Remove(address)
```

Removes a hook by its target address. Restores the original function bytes. Unknown addresses are ignored silently.

Hooks are matched only by address. Do not remove a hook from inside its own detour.

## Calling the original

`hooks.Add` returns the original function as a typed FFI pointer. Call it like any other function:

```lua theme={"dark"}
local result = oGetViewmodelFov(self)
```

Omit the call to replace the original function.

## Lifecycle

* `hooks.Add` is load-time only, same as `events.On`
* Detours are uninstalled when a script is unloaded or disabled
* Maximum 32 hooks per script
* Cannot hook the address of a function inside Starline's own module
* The FFI callback stays alive until the hook is removed

## Low-level API

For advanced use, the raw primitives are available:

```lua theme={"dark"}
local orig_addr, err = hooks.Create(target_addr, detour_addr)
hooks.Remove(target_addr)
```

`hooks.Create` takes two raw addresses and returns the trampoline address. Null, duplicate, and protected targets error. Installation failure returns `nil, error`.

`hooks.Add` is built on top of these and handles FFI callback creation, type casting, and GC anchoring for you.
