diff options
author | Michael Smith <mikesmiffy128@gmail.com> | 2023-07-29 14:32:06 +0100 |
---|---|---|
committer | Michael Smith <mikesmiffy128@gmail.com> | 2023-08-02 21:02:31 +0100 |
commit | 9a0d8730fa977f666b5c12e4c5901e7d0391e245 (patch) | |
tree | 87eebcdcef04ae1e7348ef80e972c08aa4783649 /src/sst.c | |
parent | d337b09936ecd90bad07b28b48b7103395d97ce5 (diff) |
Make various preparations for upcoming features
A lot of this is random WIP from a while back, at least a month ago, and
is being committed now to get it out of the way so that other patches
can be brought in and integrated against it without causing headaches.
Also rolled into this commit is a way to distinguish plugin_unload from
exiting the game. This is required for another soon-to-be-integrated
feature to avoid crashing on exit, and could in theory also be used to
speed up unloading on exit in future. While we're at it, this also
avoids the need to linearly scan through the plugin list to do the
old branch unloading fix, because we can.
Rough summary of the other smaller stuff I can remember doing:
- Rework bitbuf a bit
- Add some cryptographic nonsense in ac.c (not final at all)
- Introduce the first couple of "chunklets" libraries as a sort-of
subproject of this one
- Tidy up random small bits and bobs
- Add source for a small keypair generation tool
- Rework democustom to be very marginally more useful
Diffstat (limited to 'src/sst.c')
-rw-r--r-- | src/sst.c | 109 |
1 files changed, 70 insertions, 39 deletions
@@ -30,6 +30,7 @@ #include "gametype.h" #include "hook.h" #include "os.h" +#include "sst.h" #include "vcall.h" #include "version.h" @@ -46,10 +47,13 @@ static int ifacever; void *clientlib = 0; bool sst_earlyloaded = false; // see deferinit() below +bool sst_userunloaded = false; // see hook_plugin_unload_cb() below + +#define VDFBASENAME "SourceSpeedrunTools" #ifdef _WIN32 extern long __ImageBase; // this is actually the PE header struct but don't care -#define ownhandle() ((void *)&__ImageBase) +static inline void *ownhandle(void) { return &__ImageBase; } #else // sigh, _GNU_SOURCE crap. define here instead >:( typedef struct { @@ -59,16 +63,16 @@ typedef struct { void *dli_saddr; } Dl_info; int dladdr1(const void *addr, Dl_info *info, void **extra_info, int flags); -static inline void *ownhandle(void) { +static void *ownhandle(void) { + static void *cached = 0; Dl_info dontcare; - void *dl; - dladdr1((void *)&ownhandle, &dontcare, &dl, /*RTLD_DL_LINKMAP*/ 2); - return dl; + if (!cached) { + dladdr1((void *)&ownhandle, &dontcare, &cached, /*RTLD_DL_LINKMAP*/ 2); + } + return cached; } #endif -#define VDFBASENAME "SourceSpeedrunTools" - #ifdef _WIN32 // not a proper check, just a short-circuit check to avoid doing more work. static inline bool checksamedrive(const ushort *restrict path1, @@ -305,6 +309,45 @@ e: con_warn("!!! SOME FEATURES MAY BE BROKEN !!!\n"); return false; } +DEF_PREDICATE(AllowPluginLoading, bool) +DEF_EVENT(PluginLoaded, void) +DEF_EVENT(PluginUnloaded, void) + +static struct con_cmd *cmd_plugin_load, *cmd_plugin_unload; +static con_cmdcb orig_plugin_load_cb, orig_plugin_unload_cb; + +static int ownidx; // XXX: super hacky way of getting this to do_unload() + +static void hook_plugin_load_cb(const struct con_cmdargs *args) { + if (args->argc == 1) return; + if (!CHECK_AllowPluginLoading(true)) return; + orig_plugin_load_cb(args); + EMIT_PluginLoaded(); +} +static void hook_plugin_unload_cb(const struct con_cmdargs *args) { + if (args->argc == 1) return; + if (!CHECK_AllowPluginLoading(false)) return; + int idx = atoi(args->argv[1]); + struct CPlugin **plugins = pluginhandler->plugins.m.mem; + if (idx >= 0 && idx < pluginhandler->plugins.sz && + plugins[idx]->theplugin == &plugin_obj) { + sst_userunloaded = true; + ownidx = idx; +#ifdef __clang__ + // thanks clang for forcing use of return here and THEN warning about it +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpedantic" + __attribute__((musttail)) return orig_plugin_unload_cb(args); +#pragma clang diagnostic pop +#else +#error We are tied to clang without an assembly solution for this! +#endif + } + // if it's some other plugin being unloaded, we can keep doing stuff after + orig_plugin_unload_cb(args); + EMIT_PluginUnloaded(); +} + static bool do_load(ifacefactory enginef, ifacefactory serverf) { if (!hook_init()) { errmsg_warnsys("couldn't set up memory for function hooking"); @@ -326,46 +369,34 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) { *p++ = (void *)&nop_p_v; // OnEdictAllocated *p = (void *)&nop_p_v; // OnEdictFreed if (!deferinit()) { do_featureinit(); fixes_apply(); } + if (pluginhandler) { + cmd_plugin_load = con_findcmd("plugin_load"); + orig_plugin_load_cb = cmd_plugin_load->cb; + cmd_plugin_load->cb = &hook_plugin_load_cb; + cmd_plugin_unload = con_findcmd("plugin_unload"); + orig_plugin_unload_cb = cmd_plugin_unload->cb; + cmd_plugin_unload->cb = &hook_plugin_unload_cb; + } return true; } -struct CServerPlugin /* : IServerPluginHelpers */ { - void **vtable; - struct CUtlVector plugins; - /*IPluginHelpersCheck*/ void *pluginhlpchk; -}; -struct CPlugin { - char description[128]; - bool paused; - void *theplugin; // our own "this" pointer (or whichever other plugin it is) - int ifacever; - // should be the plugin library, but in old Source branches it's just null, - // because CServerPlugin::Load() erroneously shadows this field with a local - void *module; -}; - static void do_unload(void) { #ifdef _WIN32 // this is only relevant in builds that predate linux support - struct CServerPlugin *pluginhandler = - factory_engine("ISERVERPLUGINHELPERS001", 0); - if (pluginhandler) { // if not, oh well too bad we tried :^) - struct CPlugin **plugins = pluginhandler->plugins.m.mem; - int n = pluginhandler->plugins.sz; - for (struct CPlugin **pp = plugins; pp - plugins < n; ++pp) { - if ((*pp)->theplugin == (void *)&plugin_obj) { - // see comment in CPlugin above. setting this to the real handle - // right before the engine tries to unload us allows it to - // actually do so. in newer branches this is redundant but - // doesn't do any harm so it's just unconditional. - // NOTE: old engines ALSO just leak the handle and never call - // Unload() if Load() fails; can't really do anything about that - (*pp)->module = ownhandle(); - break; - } + if (pluginhandler) { // if not, oh well too bad :^) + cmd_plugin_load->cb = orig_plugin_load_cb; + cmd_plugin_unload->cb = orig_plugin_unload_cb; + if (sst_userunloaded) { + struct CPlugin **plugins = pluginhandler->plugins.m.mem; + // see comment in CPlugin above. setting this to the real handle + // right before the engine tries to unload us allows it to actually + // do so. in newer branches this is redundant but doesn't do any + // harm so it's just unconditional. NOTE: old engines ALSO just leak + // the handle and never call Unload() if Load() fails; can't really + // do anything about that. + plugins[ownidx]->module = ownhandle(); } } #endif - endfeatures(); #ifdef __linux__ if (clientlib) dlclose(clientlib); |