#
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:
#
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:
- LuaClasses, the games's internal C++ struct of registered metatables and their CFunctions.
- LuaLibraries, the game's internal C++ struct of registered global libraries and their CFunctions.
- 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.