metamod-source icon indicating copy to clipboard operation
metamod-source copied to clipboard

Templated SourceHook

Open Mooshua opened this issue 1 year ago • 10 comments

Introduces one massive template for generating SourceHook managers. The goal is for this to simplify the definition of SourceHooks long-term by replacing macros with templates.

Draft until API is settled upon, but is most likely merge-able now.

Introducing SourceHook::HookImpl

SourceHook::HookImpl is a template that implements the previous functionality of the SH_DECL macros. It uses the following template parameters in it's implementation:

  • ISourceHook** SH - Pointer to SourceHook (eg &g_SHPtr)
  • Plugin* PL - Pointer to global PluginId value (eg &g_PLID)
  • typename Interface - The interface to hook
  • Result (Interface::Method)(Args...) - The argument to hook (eg &IEngine::DoThing
  • typename Result - the return type or void
  • typename ...Args - all arguments, specified as a varardic type pack.

The macro PLUGIN_GLOBALVARS() now exposes the Hook template with the SH and PL fields filled in from the global variables--so all plugin authors need to do to get started is specify `Hook<Interface, Method, Return, Args...>::Make().

Example usage:

//  Declares Hook<> template
PLUGIN_GLOBALVARS()

//                                 Interface       Method                     Result
//  Generic Hook                   vvvvv           vvvvv                      vvvv
auto OnGameInit = SourceHook::Hook<IServerGameDLL, &IServerGameDLL::GameInit, bool>::Make();
auto OnLevelInit = SourceHook::Hook<IServerGameDLL, &IServerGameDLL::LevelInit, bool, const char*, const char*, const char*, const char*, bool, bool>::Make();

//  Varfmt hook
//  virtual void ClientCommand(edict_t* pClient, const char* fmt, ...);
auto OnClientCommand = SourceHook::FmtHook<IVEngineServer, &IVEngineServer::ClientCommand, void, edict_t*>::Make();

void AddHooks()
{
	OnGameInit->Add(server, false, SH_MEMBER(this, &SourceProvider::Hook_GameInit));
 
        //  Alternative hook modes available in fourth argument (defaulting to Normal)
	OnLevelInit->Add(server, false, SH_MEMBER(this, &SourceProvider::Hook_LevelInit), ISourceHook::AddHookMode::Hook_VP);
}

Mooshua avatar Apr 24 '24 06:04 Mooshua

Very neat stuff, but would throw my 2 cents here, what do you think on renaming exposed struct Hook to something more verbose, like SH_Hook? Just to prevent any potential name collisions and further confusion due to it on the user end.

GAMMACASE avatar Apr 24 '24 13:04 GAMMACASE

Very neat stuff, but would throw my 2 cents here, what do you think on renaming exposed struct Hook to something more verbose, like SH_Hook? Just to prevent any potential name collisions and further confusion due to it on the user end.

How about "HookFactory" or "SourceHookFactory", since that's effectively what it is?

Mooshua avatar Apr 24 '24 22:04 Mooshua

Thanks for doing this! Will take a look this weekend hopefully.

Namespaces are good avoiding for name collisions.

dvander avatar Apr 24 '24 22:04 dvander

Added varardic hook macros, and I decided to just put the helpers into the SourceHook namespace. Hopefully this doesn't cause issues with plugins.

Split the original Hook template into four new templates: One for SourceHook -> Delegate APIs (HookHandlerImpl), one for Plugin -> SourceHook APIs (HookCoreImpl, and two handling the ABI for the raw hooks (HookImpl and FmtHookImpl). They're still a little rough around the edges and need some boundary refinement.

As a result of this, varfmt hooks are now supported, with similar handling to macro varfmt hooks:

auto OnClientCommand = SourceHook::FmtHook<IVEngineServer, &IVEngineServer::ClientCommand, void, edict_t*>::Make();

void SamplePlugin::Hook_SendClientCommand(edict_t* pEntity, const char* pszCommand)
{
    // pszCommand = printf(fmt, ...)
}

Still thinking about how I want to tackle manual hooks; open to suggestions.

Mooshua avatar Apr 25 '24 02:04 Mooshua

Not everything has to be solved in one PR! Happy to take incremental improvements. API changes don't get frozen until release.

dvander avatar Apr 25 '24 02:04 dvander

~~Don’t merge just yet—-just found some issues with reference return handling. Should be a quick fix.~~ FIXED.

On another note, are the test cases still maintained and regularly built? I’m getting some errors in the macros on MSVC and on GCC all of the tests fail. I was able to get it to build on windows by gutting some of the tests and all the tests passed there. Linux was WSL Debian x64 with G++ multilib (-m32)

Mooshua avatar Apr 25 '24 14:04 Mooshua

There's also a build error:

/home/runner/work/metamod-source/metamod-source/metamod-source/core/sourcehook/sourcehook.h:1279:5: error: use of undeclared identifier 'vsnprintf'
                                vsnprintf(buf, sizeof(buf), fmt, ap);

I'm not sure why this didn't get pulled in as part of <cstdarg>.

dvander avatar May 01 '24 03:05 dvander

I'm not sure why this didn't get pulled in as part of <cstdarg>.

Fixed. Hopefully. I think. Did a lot of the changes requested here! Working on tests & sample MM sometime in the future. Let me know if there's anything else that suits your fancy :^)

Mooshua avatar May 02 '24 22:05 Mooshua

LGTM!

MSWS avatar Jun 09 '24 20:06 MSWS