ISource2ModKHook

KHook's virtual hook, with the one thing Source2Mod needs to see that KHook keeps protected: the slot the hook is on.

Header source2mod/ISource2ModKHook.h

Types

ResolvedType

template <typename RESOLVER> using ResolvedType = std::decay_t<decltype(Evaluate(RESOLVER{}()))>;

VirtualHookFor

template <typename PRE, typename POST> using VirtualHookFor = typename MemberTraits<typename Callback<PRE, POST>::type>::Virtual;

MemberHookFor

template <typename PRE, typename POST> using MemberHookFor = typename MemberTraits<typename Callback<PRE, POST>::type>::Member;

FunctionHookFor

template <typename PRE, typename POST> using FunctionHookFor = typename FunctionTraits<typename Callback<PRE, POST>::type>::Function;

Virtual

Inherits KHook::Virtual<CLASS, RETURN, ARGS...>

GetIndex

std::int32_t GetIndex() const;

The vtable slot this hook is on: what the member function pointer or the index it was made with came to, or what Configure() set.

ISource2ModHook

A hook the plugin declared through one of the KHOOK_* macros.

Every instance registers itself on construction; KHOOK_INIT() and KHOOK_DESTRUCT() walk that list.

Init

virtual bool Init() = 0;

Resolves the target given at construction and installs the hook.

KHOOK_INIT() calls this on every hook that exists at that point; a hook constructed later is installed by calling it directly. Once installed it does nothing, and after a failure (an instance still null, say) it can simply be called again.

Returns bool — false when the target could not be resolved. A hook with no target (nullptr) is left alone and counts as success.

Destruct

virtual void Destruct() = 0;

Detaches and deletes the underlying KHook object.

Deleting is what takes the detour down. Safe to call more than once, and final: the hook cannot be installed again afterwards. The wrapper's destructor calls it too, so destroying the owning object takes its hooks down.

IsInstalled

virtual bool IsInstalled() const = 0;

Whether the hook is currently installed.

InitAll

static bool InitAll();

Init() on every hook declared in this plugin.

Returns bool — false when any of them failed; the rest are installed anyway.

DestructAll

static void DestructAll();

Destruct() on every hook declared in this plugin, last first.

Count

static int Count();

How many hooks this plugin declared -- ISource2ModPlugin::GetRawHookCount().

CSource2ModVTableName

The vtable a global virtual hook attaches to: an RTTI class name inside a module ("server", "engine2", ...).

Fields

FieldType
pszModuleconst char*
ppModuleISource2ModModule**
pszClassconst char*
pszBaseconst char*

Constructors

CSource2ModVTableName() = default;
CSource2ModVTableName(const char* module, const char* name);
CSource2ModVTableName(ISource2ModModule** module, const char* name);
CSource2ModVTableName(const char* module, const char* name, const char* base);
CSource2ModVTableName(ISource2ModModule** module, const char* name, const char* base);

The module by name -- loaded through ISource2ModModule::New at Init.

The module through a pointer to an ISource2ModModule* the plugin fills in at load (read at Init, so it may still be null now).

The table of a base class sub-object inside the class -- for an interface that is not its first base (ISource2ModModule::GetVirtualTableByBase).

CSource2ModVirtualIndex

A virtual hook's slot given both ways: the member function pointer the interface header compiles to, and the name of a gamedata offset for the same function. Gamedata wins at KHOOK_INIT(); a difference between the two is reported (one of them is stale), and a missing entry for this platform leaves the interface's index in place. KHOOK_GAMEDATA() makes one.

Fields

FieldType
mfpMFP
pszNameconst char*

CSource2ModVirtualHook

Inherits ISource2ModHook

Constructors

template <typename FUNCTION, typename TARGET, typename CONTEXT, typename PRE, typename POST> CSource2ModVirtualHook(FUNCTION function, TARGET target, CONTEXT* pContext, PRE pre, POST post);

Init

bool Init() override;
bool Init(CLASS* pInstance);

Installs the hook on one instance.

ParameterType
pInstanceCLASS*

InitGlobal

bool InitGlobal(ISource2ModMemory vtable);
bool InitGlobal(ISource2ModModule* pModule, const char* pszClass, const char* pszBase = nullptr);
bool InitGlobal(const char* pszModule, const char* pszClass, const char* pszBase = nullptr);

Installs the hook on a whole vtable -- every object sharing it.

Installs the hook on the vtable of an RTTI class name in a module, or of its base pszBase.

Same, with the module loaded by name for the lookup.

ParameterTypeDefault
vtableISource2ModMemory
pModuleISource2ModModule*
pszClassconst char*
pszBaseconst char*nullptr
pszModuleconst char*

Destruct

void Destruct() override;

IsInstalled

bool IsInstalled() const override;

Get

HookType* Get() const;

The KHook object itself, for anything the wrapper does not cover.

CallOriginal

RETURN CallOriginal(CLASS* pThis, ARGS... args);

Runs the original on pThis, bypassing the hook chain.

ParameterType
pThisCLASS*
argsARGS...

HookType

using HookType = Source2ModKHook::Virtual<CLASS, RETURN, ARGS...>;

CSource2ModMemberHook

A KHook::Member (a function with a this, found by address) plus where its address comes from.

Inherits ISource2ModHook

Constructors

template <typename TARGET, typename CONTEXT, typename PRE, typename POST> CSource2ModMemberHook(TARGET target, CONTEXT* pContext, PRE pre, POST post);

Init

bool Init() override;
bool Init(ISource2ModMemory address);
template <typename R, typename... A> bool Init(R (*pfnFunction)(A...));

Places the detour at an address.

Same, from a typed function pointer (an ADDR_* getter, say).

ParameterType
addressISource2ModMemory
pfnFunctionR (*)(A...)

Destruct

void Destruct() override;

IsInstalled

bool IsInstalled() const override;

Get

HookType* Get() const;

CallOriginal

RETURN CallOriginal(CLASS* pThis, ARGS... args);
ParameterType
pThisCLASS*
argsARGS...

HookType

using HookType = KHook::Member<CLASS, RETURN, ARGS...>;

CSource2ModFunctionHook

A KHook::Function (a free function, found by address) plus where its address comes from.

Inherits ISource2ModHook

Constructors

template <typename TARGET, typename CONTEXT, typename PRE, typename POST> CSource2ModFunctionHook(TARGET target, CONTEXT* pContext, PRE pre, POST post);

Init

bool Init() override;
bool Init(ISource2ModMemory address);
template <typename R, typename... A> bool Init(R (*pfnFunction)(A...));
ParameterType
addressISource2ModMemory
pfnFunctionR (*)(A...)

Destruct

void Destruct() override;

IsInstalled

bool IsInstalled() const override;

Get

HookType* Get() const;

CallOriginal

RETURN CallOriginal(ARGS... args);
ParameterType
argsARGS...

HookType

using HookType = KHook::Function<RETURN, ARGS...>;

Functions

ToMemory

inline ISource2ModMemory ToMemory(ISource2ModMemory address);
template <typename R, typename... A> inline ISource2ModMemory ToMemory(R (*pfnFunction)(A...));
ParameterType
addressISource2ModMemory
pfnFunctionR (*)(A...)

Source2ModVirtualIndex

template <typename MFP> constexpr CSource2ModVirtualIndex<MFP> Source2ModVirtualIndex(MFP mfp, const char* pszName);
ParameterType
mfpMFP
pszNameconst char*

Source2ModRawHookCount

inline int Source2ModRawHookCount();

See ISource2ModPlugin::GetRawHookCount().

Macros

KHOOK_VTABLE

#define KHOOK_VTABLE(module, name) ::CSource2ModVTableName{ module, name }

A whole vtable as a virtual hook's target: an RTTI class name in a module -- the module's name ("server", "engine2", "tier0", ...) or a pointer to an ISource2ModModule* the plugin fills in at load (&modules::server).

KHOOK_VTABLE_BASE

#define KHOOK_VTABLE_BASE(module, name, base) ::CSource2ModVTableName{ module, name, base }

The vtable of a base class sub-object inside an RTTI class: for an interface that is not the class's first base, whose table KHOOK_VTABLE cannot reach. The base's position comes from the RTTI. Needs a core with ISource2ModModule::GetVirtualTableByBase.

KHOOK_VIRTUAL

#define KHOOK_VIRTUAL(member, function, target, pre, post) ::source2modhook::VirtualHookFor<decltype(pre), decltype(post)> member { function, target, this, pre, post }

Declares a virtual hook as a class member.

Which form to pick: a gamedata name (alone or with KHOOK_GAMEDATA) follows the core's gamedata, so an engine update that moves the slot is fixed by updating the core; a bare &Class::Method compiles the slot in from the SDK's header and needs a rebuild when it moves.

KHOOK_VIRTUAL(m_hGameFrame, &ISource2Server::GameFrame, &g_pSource2Server, nullptr, &Plugin::Hook_GameFrame);
KHOOK_VIRTUAL(m_hGameFrame, KHOOK_GAMEDATA(&ISource2Server::GameFrame, "ISource2Server::GameFrame"), &g_pSource2Server, nullptr, &Plugin::Hook_GameFrame);

KHOOK_GAMEDATA

#define KHOOK_GAMEDATA(function, name) ::Source2ModVirtualIndex(function, name)

A virtual hook's slot from the interface header and from gamedata at once, for KHOOK_VIRTUAL's function. See there.

KHOOK_MEMBER

#define KHOOK_MEMBER(member, target, pre, post) ::source2modhook::MemberHookFor<decltype(pre), decltype(post)> member { [] { return (target); }, this, pre, post }

Declares a hook on a member function found by address.

The target is evaluated at KHOOK_INIT(), not here, so an expression that needs Source2Mod -- ADDR_TAKE_DAMAGE_OLD(), a GAMECONFIG_RESOLVE() -- is fine even though the owner is usually a global constructed long before Load().

KHOOK_MEMBER(m_hPostThink, "CCSPlayerPawn::PostThink", &Plugin::Hook_PostThink, nullptr);
KHOOK_MEMBER(m_hTakeDamageOld, ADDR_TAKE_DAMAGE_OLD(), &Plugin::Hook_TakeDamageOld, nullptr);

KHOOK_FUNCTION

#define KHOOK_FUNCTION(member, target, pre, post) ::source2modhook::FunctionHookFor<decltype(pre), decltype(post)> member { [] { return (target); }, this, pre, post }

Declares a hook on a free function found by address. Same parameters as KHOOK_MEMBER; the handler takes no hooked object.

KHOOK_CHECK

#define KHOOK_CHECK() \
    do { 
        const char* source2modKHookNote = nullptr; 
        if (!::Source2ModKHookVersionMatches(g_Source2ModCoreKHookHeader, g_Source2ModCoreKHookCommit, error, maxlen, &source2modKHookNote)) 
            return false; 
        if (source2modKHookNote) 
            g_pSource2ModAPI->Log(g_pPluginAPI, "%s\n", source2modKHookNote); 
    } while (0)

Installs every hook declared with the macros above. Call in Load(), after SOURCE2MOD_SAVEVARS() and once the instance pointers hooks read are set.

This is also where the KHook check happens: a plugin that hooks through KHook shares the detour engine with the core and must have been built against the same khook.hpp (IKHook and the template layout; a KHook update confined to its src/ does not count), so a mismatch returns false out of Load() with the reason in error. A plugin that never calls this is not checked -- it does not hook, so it does not care. A plugin using raw KHook objects (KHOOK_NEW) without any KHOOK_* macro must call KHOOK_CHECK() itself before the first one.

Each hook that could not be resolved is logged and skipped.

KHOOK_DESTRUCT

#define KHOOK_DESTRUCT() ::ISource2ModHook::DestructAll()

Takes every hook down. Call in Unload().

Other macros

MacroExpands to
KHOOK_INIT()do { KHOOK_CHECK(); ::ISource2ModHook::InitAll(); } while (0)

On this page