alliedmodders / metamod-source

Metamod:Source - C++ Plugin Environment and Detour Library for the Source Engine
http://www.metamodsource.net/
Other
370 stars 84 forks source link

Templated SourceHook #176

Open Mooshua opened 3 months ago

Mooshua commented 3 months ago

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:

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);
}
GAMMACASE commented 3 months ago

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.

Mooshua commented 3 months ago

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?

dvander commented 3 months ago

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

Namespaces are good avoiding for name collisions.

Mooshua commented 3 months ago

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.

dvander commented 3 months ago

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

Mooshua commented 3 months ago

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)

dvander commented 3 months ago

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>.

Mooshua commented 2 months ago

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 :^)

MSWS commented 1 month ago

LGTM!