# Linker (linker.)

The Linker is an association link of all of Garry's Mod's functions, this doesn't include builtins.
This allows cross-state communication.


# How it works

Garry's Mod runs two distinct Lua states: the menu state and the client state.
While they exist within the same process, their Lua states are completely isolated.
You cannot directly call a function from one state inside the other.

The bridging mechanism works by:

  • Retrieving the raw C function pointer behind a client-side Lua function. This pointer is taken from the game's internal LuaClasses and LuaLibraries structures, not from Lua itself.
  • Then, it wraps that pointer in a transfer closure on the menu state.
  • When you call the wrapper, it sends your arguments from the menu state to the client state, invokes the actual C function there, and returns the results back to the menu state.

A visual example:


# Functions

# table linker.Meta(string szClass)

  • Returns a table of all methods for a given class.

Example:

local meta = linker.Meta("Entity")
-- The entire Entity metatable, so:
-- meta.GetPos, meta.GetAngles, meta.Health, etc.

# function linker.Meta(string szClass, string szMethod)

  • Returns a single wrapped method by name.

Example:

local GetPos = linker.Meta("Entity", "GetPos")
local pos = GetPos(pSomeEntity)

# table linker.Global(string szLibrary)

  • Returns a table of all functions in a global library.
  • Passing in "GLOBAL" will return a table of every function that isn't in any libary. (e.x: MsgC)

Example:

local render = linker.Global("render")
render.SetColorModulation(1, 0, 0)

# function linker.Global(string szLibrary, string szFunction)

  • Returns a single wrapped function by name.
  • You can also pass in "GLOBAL" as the library to retrieve functions that aren't in any libaries. (e.x: MsgC)

Example:

local SetColor = linker.Global("render", "SetColorModulation")
SetColor(1, 0, 0)

# Proxies

When a client-side value can't be directly copied (entities, panels, userdata, Lua functions), the Linker creates a LinkerProxy on the menu state.
A proxy is a lightweight userdata that holds a reference ID to the real object on the client state.

When you index a proxy (e.g. proxy:GetPos()), the Linker resolves the method name from the engine's LuaClasses based on the object's type, wraps the underlying C function, and calls it on the client state.
Only C-defined methods are accessible. Lua-defined methods on the client are not.

local ents = linker.Global("ents")

for _, pEntity in ipairs(ents.GetAll()) do
    print(pEntity:GetClass())  -- looks up "Entity.GetClass" in LuaClasses → WrapperTransfer → result
end

Proxies also support tostring(), the # operator, == comparison, and can be called directly if the underlying object is a function.


# Value Transfer

When arguments and return values cross the state boundary, they are handled by type:

Type Transfer method
nil, bool, number, string Copied directly (value types)
Vector, Angle Copied natively via Push/Get
table Deep-copied recursively, nested userdata/functions become proxies
CFunction Wrapped via transfer closure
Lua function (menu → client) Wrapped in a reverse callback, a client-side closure that, when called, transfers args back to the menu and calls the original
Userdata / other Becomes a LinkerProxy (reference-based)

# Lifecycle

The Linker initializes on the menu state at startup, before the Client state exists.
It registers the linker global table and the LinkerProxy metatable.

When the Client state is created, the Linker is notified and cross-state calls become functional.
When the client disconnects, the client pointer is cleared, all proxy __index calls will return nil until the Client state isn't NULL.


# Security

The Linker never touches _G at runtime. All function lookups go through:

  1. LuaClasses, the games's internal C++ struct of registered metatables and their CFunctions.
  2. LuaLibraries, the game's internal C++ struct of registered global libraries and their CFunctions.
  3. Registry, this is only used if all fails. (This shouldn't really happen. It will check if it's actually a CFunction though.)

This means if someone tries to detour anything, it will have no effect on the Linker.
It always calls the true original CFunction.