summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMichael Smith <mikesmiffy128@gmail.com>2022-03-23 02:38:21 +0000
committerMichael Smith <mikesmiffy128@gmail.com>2022-03-23 02:38:21 +0000
commitb18326a75078530df7712667f41b4ea354e1da3e (patch)
treeae5612f71f3f7da3ad669f21fc84cbb42568061d
parent437b9952b29ba42dbb1036b29fb431a635b05a60 (diff)
Work around plugin unloading bug in old branches
This also introduces some stuff for interacting with the current plugin list. Other plugin management utilies are Coming Soon...
-rw-r--r--compile.bat2
-rw-r--r--src/sst.c71
2 files changed, 71 insertions, 2 deletions
diff --git a/compile.bat b/compile.bat
index b2a880a..da1e2d2 100644
--- a/compile.bat
+++ b/compile.bat
@@ -49,7 +49,7 @@ call :cc src/kv.c || exit /b
call :cc src/rinput.c || exit /b
call :cc src/sst.c || exit /b
call :cc src/udis86.c || exit /b
-clang -m32 -shared -O2 -flto -fuse-ld=lld -Wl,/implib:.build/sst.lib,/Brepro ^
+clang -m32 -fuse-ld=lld -shared -O2 -flto -Wl,/IMPLIB:.build/sst.lib,/Brepro ^
-L.build -luser32 -ladvapi32 -lshlwapi -ltier0 -lvstdlib -o sst.dll%objs% .build/dll.res || exit /b
:: get rid of another useless file (can we just not create this???)
del .build\sst.lib
diff --git a/src/sst.c b/src/sst.c
index 5c1568d..aeb7f4c 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -39,6 +39,7 @@ static int plugin_ver;
// this is where we start dynamically adding virtual functions, see vtable[]
// array below
static const void **vtable_firstdiff;
+static const void *const *const plugin_obj;
// most plugin callbacks are unused - define dummy functions for each signature
static void VCALLCONV nop_v_v(void *this) {}
@@ -91,6 +92,56 @@ static const char *VCALLCONV GetStringForSymbol_hook(void *this, int s) {
// vstdlib symbol, only currently used in l4d2 but exists everywhere so oh well
IMPORT void *KeyValuesSystem(void);
+// XXX: not sure if all this stuff should, like, go somewhere?
+
+struct CUtlMemory {
+ void *mem;
+ int alloccnt;
+ int growsz;
+};
+
+struct CUtlVector {
+ struct CUtlMemory m;
+ int sz;
+ void *mem_again_for_some_reason;
+};
+
+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;
+};
+
+#ifdef _WIN32
+extern long __ImageBase; // this is actually the PE header struct but don't care
+#define ownhandle() ((void *)&__ImageBase)
+#else
+// sigh, _GNU_SOURCE crap. define here instead >:(
+typedef struct {
+ const char *dli_fname;
+ void *dli_fbase;
+ const char *dli_sname;
+ void *dli_saddr;
+} Dl_info;
+int dladdr1(const void *addr, Dl_info *info, void **extra_info, int flags);
+static inline void *ownhandle(void) {
+ Dl_info dontcare;
+ void *dl;
+ dladdr1((void *)&ownhandle, &dontcare, &dl, /*RTLD_DL_LINKMAP*/ 2);
+ return dl;
+}
+#endif
+
static bool do_load(ifacefactory enginef, ifacefactory serverf) {
factory_engine = enginef; factory_server = serverf;
#ifndef __linux__
@@ -162,6 +213,24 @@ e: con_colourmsg(RGBA(64, 255, 64, 255),
}
static void do_unload(void) {
+ 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 unload us instead of just doing nothing.
+ // in newer branches that don't have this bug, this is still
+ // correct anyway so no need to bother checking.
+ (*pp)->module = ownhandle();
+ break;
+ }
+ }
+ }
+
if (has_autojump) autojump_end();
if (has_demorec) demorec_end();
#ifdef _WIN32
@@ -196,7 +265,7 @@ static bool VCALLCONV Load(void *this, ifacefactory enginef,
return already_loaded;
}
-static void Unload(void *this) {
+static void VCALLCONV Unload(void *this) {
// the game tries to unload on a failed load, for some reason
if (skip_unload) {
skip_unload = false;