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
| Field | Type |
|---|---|
pszModule | const char* |
ppModule | ISource2ModModule** |
pszClass | const char* |
pszBase | const 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
| Field | Type |
|---|---|
mfp | MFP |
pszName | const 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.
| Parameter | Type |
|---|---|
pInstance | CLASS* |
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.
| Parameter | Type | Default |
|---|---|---|
vtable | ISource2ModMemory | |
pModule | ISource2ModModule* | |
pszClass | const char* | |
pszBase | const char* | nullptr |
pszModule | const 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.
| Parameter | Type |
|---|---|
pThis | CLASS* |
args | ARGS... |
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).
| Parameter | Type |
|---|---|
address | ISource2ModMemory |
pfnFunction | R (*)(A...) |
Destruct
void Destruct() override;IsInstalled
bool IsInstalled() const override;Get
HookType* Get() const;CallOriginal
RETURN CallOriginal(CLASS* pThis, ARGS... args);| Parameter | Type |
|---|---|
pThis | CLASS* |
args | ARGS... |
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...));| Parameter | Type |
|---|---|
address | ISource2ModMemory |
pfnFunction | R (*)(A...) |
Destruct
void Destruct() override;IsInstalled
bool IsInstalled() const override;Get
HookType* Get() const;CallOriginal
RETURN CallOriginal(ARGS... args);| Parameter | Type |
|---|---|
args | ARGS... |
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...));| Parameter | Type |
|---|---|
address | ISource2ModMemory |
pfnFunction | R (*)(A...) |
Source2ModVirtualIndex
template <typename MFP> constexpr CSource2ModVirtualIndex<MFP> Source2ModVirtualIndex(MFP mfp, const char* pszName);| Parameter | Type |
|---|---|
mfp | MFP |
pszName | const 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
| Macro | Expands to |
|---|---|
KHOOK_INIT() | do { KHOOK_CHECK(); ::ISource2ModHook::InitAll(); } while (0) |