1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-01-31 20:52:18 +01:00

Properly implementing GameDLL unloading.

This commit is contained in:
David Anderson 2008-11-23 21:50:17 -06:00
parent 97b323a583
commit 08f2df2d78
4 changed files with 80 additions and 22 deletions

View File

@ -34,8 +34,17 @@ public:
LoadAsGameDLL(info); LoadAsGameDLL(info);
return true; 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(); g_PluginMngr.SetAllLoaded();
} }
virtual void *QueryInterface(const char *iface, int *ret) virtual void *QueryInterface(const char *iface, int *ret)
@ -53,6 +62,7 @@ public:
} }
virtual void Unload() virtual void Unload()
{ {
UnloadMetamod(true);
} }
}; };

View File

@ -32,13 +32,11 @@ using namespace SourceMM;
#undef CommandLine #undef CommandLine
DLL_IMPORT ICommandLine *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_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_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_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
SH_DECL_HOOK0(IServerGameDLL, GameInit, SH_NOATTRIB, false, bool); SH_DECL_HOOK0(IServerGameDLL, GameInit, SH_NOATTRIB, false, bool);
void DLLShutdown_handler();
void LevelShutdown_handler(); void LevelShutdown_handler();
bool LevelInit_handler(char const *pMapName, bool LevelInit_handler(char const *pMapName,
char const *pMapEntities, char const *pMapEntities,
@ -101,7 +99,6 @@ void InitMainStates()
abspath(smm_path, game_dir); abspath(smm_path, game_dir);
g_ModPath.assign(smm_path); 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, LevelShutdown, g_GameDll.pGameDLL, LevelShutdown_handler, true);
SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelInit, g_GameDll.pGameDLL, LevelInit_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); 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 */ /* Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly */
g_SMConVarAccessor.MarkCommandsAsGameDLL(); g_SMConVarAccessor.MarkCommandsAsGameDLL();
g_Engine.icvar->UnlinkVariables(FCVAR_GAMEDLL); g_Engine.icvar->UnlinkVariables(FCVAR_GAMEDLL);
SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)();
} }
SH_RELEASE_CALLCLASS(g_GameDllPatch); SH_RELEASE_CALLCLASS(g_GameDllPatch);
@ -316,12 +311,6 @@ void UnloadMetamod(bool shutting_down)
g_SourceHook.CompleteShutdown(); g_SourceHook.CompleteShutdown();
} }
void DLLShutdown_handler()
{
UnloadMetamod(true);
RETURN_META(MRES_SUPERCEDE);
}
void LoadFromVDF(const char *file) void LoadFromVDF(const char *file)
{ {
PluginId id; PluginId id;

View File

@ -25,6 +25,7 @@ static void *gamedll_lib = NULL;
static IServerGameDLL *gamedll_iface = NULL; static IServerGameDLL *gamedll_iface = NULL;
static QueryValveInterface gamedll_qvi = NULL; static QueryValveInterface gamedll_qvi = NULL;
static int gamedll_version = 0; static int gamedll_version = 0;
static int isgd_shutdown_index = -1;
#if defined _WIN32 #if defined _WIN32
#define TIER0_NAME "bin\\tier0.dll" #define TIER0_NAME "bin\\tier0.dll"
@ -213,7 +214,11 @@ mm_FreeCachedLibraries()
static void static void
mm_PatchDllInit(bool patch); 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 class VEmptyClass
{ {
@ -286,7 +291,7 @@ public:
#if defined _WIN32 #if defined _WIN32
void *addr; void *addr;
} u; } u;
u.addr = isgd_orig_call; u.addr = isgd_orig_init;
#else #else
struct struct
{ {
@ -294,7 +299,7 @@ public:
intptr_t adjustor; intptr_t adjustor;
} s; } s;
} u; } u;
u.s.addr = isgd_orig_call; u.s.addr = isgd_orig_init;
u.s.adjustor = 0; u.s.adjustor = 0;
#endif #endif
result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(engineFactory, result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(engineFactory,
@ -316,13 +321,47 @@ public:
} }
else if (gamedll_bridge != NULL) 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); mm_PatchDllInit(false);
return result; 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; static IServerGameDLL isgd_thunk;
@ -349,18 +388,38 @@ mm_PatchDllInit(bool patch)
if (patch) if (patch)
{ {
assert(isgd_orig_call == NULL); assert(isgd_orig_init == NULL);
isgd_orig_call = vtable_dest[mfp.vtblindex]; isgd_orig_init = vtable_dest[mfp.vtblindex];
vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex]; vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex];
} }
else else
{ {
assert(isgd_orig_call != NULL); assert(isgd_orig_init != NULL);
vtable_dest[mfp.vtblindex] = isgd_orig_call; vtable_dest[mfp.vtblindex] = isgd_orig_init;
isgd_orig_call = NULL; 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 static void
mm_PrepForGameLoad() mm_PrepForGameLoad()
{ {

View File

@ -35,7 +35,7 @@ class IGameDllBridge
{ {
public: public:
virtual bool DLLInit_Pre(const gamedll_bridge_info *info, char *buffer, size_t maxlength) = 0; 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 *QueryInterface(const char *name, int *ret) = 0;
virtual void Unload() = 0; virtual void Unload() = 0;
}; };