Hooks on the game functions plugins reach for most often, held by the core.
One detour per function, placed by the core from its gamedata the first time a plugin asks for it and taken out again once the last listener is gone -- a function nobody listens to is not hooked at all. A plugin registers a handler and never touches KHook, a signature or a prototype. When Valve changes one -- an argument added, the order swapped between platforms, as GroundAccelerate's is -- the core's gamedata and its one call site change, and the plugins keep running. Anything not in this set is still a KHOOK_MEMBER / KHOOK_VIRTUAL of the plugin's own.
Every hook hands its handler a context: the hooked object, the arguments, and result where the function returns something. What the handler returns is the usual Action:
Ignore -- nothing changed.
Override -- pre: the original still runs but the function returns ctx.result; post: the function returns ctx.result instead of what the original produced.
Supersede -- pre only: the original does not run, the function returns ctx.result. Stops the remaining pre handlers.
Arguments passed by pointer (the damage info, the move data, the weapon) can be changed in place and the original sees the change; arguments passed by value (a speed, a flag) cannot -- a handler that needs that keeps a raw KHOOK_MEMBER of its own. Handlers run in registration order, pre before the original and post after it; in post, result holds what the original returned.
HookX() returns a Source2ModGameHookId for UnhookX(); a plugin may have several handlers on one function. The handlers are the plugin's until it unhooks them or unloads, whichever comes first; nothing to undo in Unload().
Whose a handler is, the core reads off the handler (see Source2ModCallback): no call takes a plugin id. What a plugin still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
What HookX() returns and UnhookX() takes: one registered handler. A plugin may register several handlers on the same function and drop any one of them; whatever it still holds at unload the core drops for it.
Drops a handler: the one HookX() returned the id for, or the one that is this function or object and method (a lambda goes by its id). True when it was found.
The CCSPlayer_MovementServices functions that take the move data alone: AirMove, CheckFalling, CheckParameters, Duck, Friction, PlayerMove, ProcessMovement, WalkMove, WaterMove (void), and CanUnduck, CheckWater, LadderMove, MoveInit (bool, in result).
CCSPlayer_MovementServices::GroundAccelerate(move, wishDirection, frameTime, wishSpeed, acceleration). The engine's argument order differs between Linux and Windows; here it is always this one.
What a game hook handler answers -- KHook's { action, value }.
return { Action::Supersede, 0 }; // skip the original, it returns 0return { Action::Override, AcquireResult::NotAllowedByMode };return { Action::Supersede, ctx.CallOriginal() };return Action::Ignore; // an action alone
An action alone takes its value from ctx.result, so a handler that sets the field and returns Override / Supersede keeps working. For a function without a return value there is only the action.
Several handlers on one function: Supersede stops the chain, otherwise the strongest action wins; each value a handler returns lands in ctx.result right away, so the last one to return a value decides it and the next handler sees it there.
#define SOURCE2MOD_GAMEHOOK_CALL_ORIGINAL(RET) \ using Return = RET; RET CallOriginal() const { return callOriginal_(callOriginalData_); } RET (*callOriginal_)(const void*) = nullptr; const void* callOriginalData_ = nullptr;
Every context ends with CallOriginal(): the game's function, run now with the arguments the game passed, past every hook on it -- SourceHook's SH_CALL. What it returns is what the game would have answered.
In a pre handler the function really runs (with all its side effects), so a handler that calls it should answer Supersede with ctx.result set to the value, or the game runs it a second time after the handlers:
const AcquireResult game = ctx.CallOriginal(); ctx.result = game == AcquireResult::Allowed && Blocked(ctx) ? AcquireResult::NotAllowedByMode : game; return Action::Supersede;
A changed pointed-to object (the damage info, the move data) counts; a scalar field changed in the context does not, the call takes what the game passed. In a post handler ctx.result already holds the game's answer.
The two members after it belong to the core; the movement contexts shared by void and bool functions answer false for the void ones.