diff --git a/core-legacy/gamedll_bridge.cpp b/core-legacy/gamedll_bridge.cpp index b3af4c0..c264717 100644 --- a/core-legacy/gamedll_bridge.cpp +++ b/core-legacy/gamedll_bridge.cpp @@ -34,8 +34,17 @@ public: LoadAsGameDLL(info); return true; } - virtual void DLLInit_Post() + virtual void DLLInit_Post(int *isgdUnload) { + SourceHook::MemFuncInfo mfi; + + mfi.isVirtual = false; + SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfi); + assert(mfi.isVirtual); + assert(mfi.vtbloffs == 0); + assert(mfi.thisptroffs == 0); + *isgdUnload = mfi.vtblindex; + g_PluginMngr.SetAllLoaded(); } virtual void *QueryInterface(const char *iface, int *ret) @@ -53,6 +62,7 @@ public: } virtual void Unload() { + UnloadMetamod(true); } }; diff --git a/core-legacy/sourcemm.cpp b/core-legacy/sourcemm.cpp index 97cd4ba..b61db9a 100644 --- a/core-legacy/sourcemm.cpp +++ b/core-legacy/sourcemm.cpp @@ -32,13 +32,11 @@ using namespace SourceMM; #undef CommandLine DLL_IMPORT ICommandLine *CommandLine(); -SH_DECL_HOOK0_void(IServerGameDLL, DLLShutdown, SH_NOATTRIB, false); SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *); SH_DECL_HOOK0(IServerGameDLL, GameInit, SH_NOATTRIB, false, bool); -void DLLShutdown_handler(); void LevelShutdown_handler(); bool LevelInit_handler(char const *pMapName, char const *pMapEntities, @@ -101,7 +99,6 @@ void InitMainStates() abspath(smm_path, game_dir); g_ModPath.assign(smm_path); - SH_ADD_HOOK_STATICFUNC(IServerGameDLL, DLLShutdown, g_GameDll.pGameDLL, DLLShutdown_handler, false); SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelShutdown, g_GameDll.pGameDLL, LevelShutdown_handler, true); SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelInit, g_GameDll.pGameDLL, LevelInit_handler, true); SH_ADD_HOOK_STATICFUNC(IServerGameDLL, GameInit, g_GameDll.pGameDLL, GameInit_handler, false); @@ -304,8 +301,6 @@ void UnloadMetamod(bool shutting_down) /* Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly */ g_SMConVarAccessor.MarkCommandsAsGameDLL(); g_Engine.icvar->UnlinkVariables(FCVAR_GAMEDLL); - - SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)(); } SH_RELEASE_CALLCLASS(g_GameDllPatch); @@ -316,12 +311,6 @@ void UnloadMetamod(bool shutting_down) g_SourceHook.CompleteShutdown(); } -void DLLShutdown_handler() -{ - UnloadMetamod(true); - RETURN_META(MRES_SUPERCEDE); -} - void LoadFromVDF(const char *file) { PluginId id; diff --git a/loader/gamedll.cpp b/loader/gamedll.cpp index a411794..4b9a89a 100644 --- a/loader/gamedll.cpp +++ b/loader/gamedll.cpp @@ -25,6 +25,7 @@ static void *gamedll_lib = NULL; static IServerGameDLL *gamedll_iface = NULL; static QueryValveInterface gamedll_qvi = NULL; static int gamedll_version = 0; +static int isgd_shutdown_index = -1; #if defined _WIN32 #define TIER0_NAME "bin\\tier0.dll" @@ -213,7 +214,11 @@ mm_FreeCachedLibraries() static void mm_PatchDllInit(bool patch); -static void *isgd_orig_call = NULL; +static void +mm_PatchDllShutdown(); + +static void *isgd_orig_init = NULL; +static void *isgd_orig_shutdown = NULL; class VEmptyClass { @@ -286,7 +291,7 @@ public: #if defined _WIN32 void *addr; } u; - u.addr = isgd_orig_call; + u.addr = isgd_orig_init; #else struct { @@ -294,7 +299,7 @@ public: intptr_t adjustor; } s; } u; - u.s.addr = isgd_orig_call; + u.s.addr = isgd_orig_init; u.s.adjustor = 0; #endif result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(engineFactory, @@ -316,13 +321,47 @@ public: } else if (gamedll_bridge != NULL) { - gamedll_bridge->DLLInit_Post(); + gamedll_bridge->DLLInit_Post(&isgd_shutdown_index); + assert(isgd_shutdown_index != -1); + mm_PatchDllShutdown(); } mm_PatchDllInit(false); return result; } + + virtual void DLLShutdown() + { + gamedll_bridge->Unload(); + gamedll_bridge = NULL; + mm_UnloadMetamodLibrary(); + + /* Call original function */ + { + union + { + void (VEmptyClass::*mfpnew)(); +#if defined _WIN32 + void *addr; + } u; + u.addr = isgd_orig_shutdown; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = isgd_orig_shutdown; + u.s.adjustor = 0; +#endif + (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(); + } + + mm_UnloadLibrary(gamedll_lib); + gamedll_lib = NULL; + } }; static IServerGameDLL isgd_thunk; @@ -349,18 +388,38 @@ mm_PatchDllInit(bool patch) if (patch) { - assert(isgd_orig_call == NULL); - isgd_orig_call = vtable_dest[mfp.vtblindex]; + assert(isgd_orig_init == NULL); + isgd_orig_init = vtable_dest[mfp.vtblindex]; vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex]; } else { - assert(isgd_orig_call != NULL); - vtable_dest[mfp.vtblindex] = isgd_orig_call; - isgd_orig_call = NULL; + assert(isgd_orig_init != NULL); + vtable_dest[mfp.vtblindex] = isgd_orig_init; + isgd_orig_init = NULL; } } +static void +mm_PatchDllShutdown() +{ + void **vtable_src; + void **vtable_dest; + SourceHook::MemFuncInfo mfp; + + mfp.isVirtual = false; + SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfp); + assert(mfp.isVirtual); + assert(mfp.thisptroffs == 0); + assert(mfp.vtbloffs == 0); + + vtable_src = (void **)*(void **)&isgd_thunk; + vtable_dest = (void **)*(void **)gamedll_iface; + + isgd_orig_shutdown = vtable_dest[isgd_shutdown_index]; + vtable_dest[isgd_shutdown_index] = vtable_src[mfp.vtblindex]; +} + static void mm_PrepForGameLoad() { diff --git a/loader/loader_bridge.h b/loader/loader_bridge.h index f15a301..6dc2dd6 100644 --- a/loader/loader_bridge.h +++ b/loader/loader_bridge.h @@ -35,7 +35,7 @@ class IGameDllBridge { public: virtual bool DLLInit_Pre(const gamedll_bridge_info *info, char *buffer, size_t maxlength) = 0; - virtual void DLLInit_Post() = 0; + virtual void DLLInit_Post(int *isgdUnload) = 0; virtual void *QueryInterface(const char *name, int *ret) = 0; virtual void Unload() = 0; };