From 94d33a4ef121ce0d05329275362d4e0ded3a9d73 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 22 Aug 2013 14:05:44 -0700 Subject: [PATCH] Fix various problems with unloading ClientPrefs and SourceMod (bug 5874, r=ds). --HG-- extra : rebase_source : 0a35f8380d651ca65fac9dd402c5cd3625e3105c --- core/logic/ExtensionSys.cpp | 27 ++---- core/logic/common_logic.cpp | 3 +- core/logic/intercom.h | 3 +- core/logic_bridge.cpp | 3 +- core/sourcemod.cpp | 113 ++++++++++++---------- core/sourcemod.h | 4 +- extensions/clientprefs/cookie.cpp | 30 +----- extensions/clientprefs/cookie.h | 11 --- extensions/clientprefs/extension.cpp | 64 +++++------- extensions/clientprefs/extension.h | 13 ++- extensions/clientprefs/query.cpp | 17 ---- extensions/clientprefs/sdk/smsdk_config.h | 2 +- extensions/clientprefs/sdk/smsdk_ext.cpp | 23 ++++- extensions/clientprefs/sdk/smsdk_ext.h | 25 ++++- public/IExtensionSys.h | 16 ++- public/sample_ext/sdk/smsdk_ext.cpp | 11 ++- public/sample_ext/sdk/smsdk_ext.h | 13 ++- 17 files changed, 195 insertions(+), 183 deletions(-) diff --git a/core/logic/ExtensionSys.cpp b/core/logic/ExtensionSys.cpp index 05b46296..87fe1c44 100644 --- a/core/logic/ExtensionSys.cpp +++ b/core/logic/ExtensionSys.cpp @@ -775,25 +775,21 @@ CExtension *CExtensionManager::FindByOrder(unsigned int num) bool CExtensionManager::UnloadExtension(IExtension *_pExt) { if (!_pExt) - { return false; - } CExtension *pExt = (CExtension *)_pExt; if (m_Libs.find(pExt) == m_Libs.end()) - { return false; - } /* Tell it to unload */ if (pExt->IsLoaded()) - { - IExtensionInterface *pAPI = pExt->GetAPI(); - pAPI->OnExtensionUnload(); - } + pExt->GetAPI()->OnExtensionUnload(); - /* First remove us from internal lists */ + // Remove us from internal lists. Note that because we do this, it's + // possible that our extension could be added back if another plugin + // tries to load during this process. If we ever find this to happen, + // we can just block plugin loading. g_ShareSys.RemoveInterfaces(_pExt); m_Libs.remove(pExt); @@ -827,13 +823,9 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) { pDep = (*c_iter); if ((pAPI=pDep->GetAPI()) == NULL) - { continue; - } if (pDep == pExt) - { continue; - } /* Now, get its dependency list */ bool dropped = false; List::iterator i_iter = pDep->m_Deps.begin(); @@ -862,13 +854,9 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) while (i_iter != pDep->m_ChildDeps.end()) { if ((*i_iter).owner == pExt) - { i_iter = pDep->m_ChildDeps.erase(i_iter); - } else - { i_iter++; - } } } @@ -887,6 +875,11 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt) } } + // Everything has been informed that we're unloading, so give the + // extension one last notification. + if (pExt->IsLoaded() && pExt->GetAPI()->GetExtensionVersion() >= 7) + pExt->GetAPI()->OnDependenciesDropped(); + pExt->Unload(); delete pExt; diff --git a/core/logic/common_logic.cpp b/core/logic/common_logic.cpp index c859ac25..8a076e0e 100644 --- a/core/logic/common_logic.cpp +++ b/core/logic/common_logic.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 sw=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -147,6 +147,7 @@ static void logic_init(const sm_core_t* core, sm_logic_t* _logic) gamehelpers = core->gamehelpers; g_pSourcePawn = *core->spe1; g_pSourcePawn2 = *core->spe2; + SMGlobalClass::head = core->listeners; g_ShareSys.Initialize(); g_pCoreIdent = g_ShareSys.CreateCoreIdentity(); diff --git a/core/logic/intercom.h b/core/logic/intercom.h index c744a023..006ea9e3 100644 --- a/core/logic/intercom.h +++ b/core/logic/intercom.h @@ -49,7 +49,7 @@ using namespace SourceHook; * Add 1 to the RHS of this expression to bump the intercom file * This is to prevent mismatching core/logic binaries */ -#define SM_LOGIC_MAGIC (0x0F47C0DE - 22) +#define SM_LOGIC_MAGIC (0x0F47C0DE - 23) #if defined SM_LOGIC class IVEngineServer @@ -270,6 +270,7 @@ struct sm_core_t void * serverFactory; void * engineFactory; void * matchmakingDSFactory; + SMGlobalClass * listeners; }; struct sm_logic_t diff --git a/core/logic_bridge.cpp b/core/logic_bridge.cpp index fc633024..3fd372f0 100644 --- a/core/logic_bridge.cpp +++ b/core/logic_bridge.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 sw=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. @@ -406,6 +406,7 @@ void InitLogicBridge() core_bridge.engineFactory = (void *)g_SMAPI->GetEngineFactory(false); core_bridge.serverFactory = (void *)g_SMAPI->GetServerFactory(false); + core_bridge.listeners = SMGlobalClass::head; ILibrary *mmlib; char path[PLATFORM_MAX_PATH]; diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 7e8cf3b2..db525627 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -129,6 +129,8 @@ ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key, return ConfigResult_Ignore; } +static bool sSourceModInitialized = false; + bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late) { const char *gamepath = g_SMAPI->GetBaseDir(); @@ -240,6 +242,8 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late g_pSourcePawn2->SetDebugListener(logicore.debugger); + sSourceModInitialized = true; + /* Hook this now so we can detect startup without calling StartSourceMod() */ SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false); @@ -475,69 +479,74 @@ size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, c void SourceModBase::CloseSourceMod() { - /* Force a level end */ - LevelShutdown(); + if (!sSourceModInitialized) + return; + SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false); + + if (g_Loaded) + { + /* Force a level end */ + LevelShutdown(); + ShutdownServices(); + } + + /* Rest In Peace */ + ShutdownLogicBridge(); + ShutdownJIT(); +} + +void SourceModBase::ShutdownServices() +{ /* Unload plugins */ scripts->Shutdown(); /* Unload extensions */ extsys->Shutdown(); - SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SourceModBase::LevelInit), false); + if (g_pOnMapEnd) + g_Forwards.ReleaseForward(g_pOnMapEnd); - if (g_Loaded) + /* Notify! */ + SMGlobalClass *pBase = SMGlobalClass::head; + while (pBase) { - if (g_pOnMapEnd) - { - g_Forwards.ReleaseForward(g_pOnMapEnd); - } - - /* Notify! */ - SMGlobalClass *pBase = SMGlobalClass::head; - while (pBase) - { - pBase->OnSourceModShutdown(); - pBase = pBase->m_pGlobalClassNext; - } - - /* Delete all data packs */ - CStack::iterator iter; - CDataPack *pd; - for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++) - { - pd = (*iter); - delete pd; - } - m_freepacks.popall(); - - /* Notify! */ - pBase = SMGlobalClass::head; - while (pBase) - { - pBase->OnSourceModAllShutdown(); - pBase = pBase->m_pGlobalClassNext; - } - - if (enginePatch) - { - SH_RELEASE_CALLCLASS(enginePatch); - enginePatch = NULL; - } - - if (gamedllPatch) - { - SH_RELEASE_CALLCLASS(gamedllPatch); - gamedllPatch = NULL; - } - - SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false); - SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false); + pBase->OnSourceModShutdown(); + pBase = pBase->m_pGlobalClassNext; } - /* Rest In Peace */ - ShutdownLogicBridge(); - ShutdownJIT(); + /* Delete all data packs */ + CStack::iterator iter; + CDataPack *pd; + for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++) + { + pd = (*iter); + delete pd; + } + m_freepacks.popall(); + + /* Notify! */ + pBase = SMGlobalClass::head; + while (pBase) + { + pBase->OnSourceModAllShutdown(); + pBase = pBase->m_pGlobalClassNext; + } + + if (enginePatch) + { + SH_RELEASE_CALLCLASS(enginePatch); + enginePatch = NULL; + } + + if (gamedllPatch) + { + SH_RELEASE_CALLCLASS(gamedllPatch); + gamedllPatch = NULL; + } + + SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false); + SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false); } void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...) diff --git a/core/sourcemod.h b/core/sourcemod.h index 420ba8a6..cf847c93 100644 --- a/core/sourcemod.h +++ b/core/sourcemod.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. @@ -136,6 +136,8 @@ public: // ISourceMod int GetPluginId(); int GetShApiVersion(); bool IsMapRunning(); +private: + void ShutdownServices(); private: CStack m_freepacks; char m_SMBaseDir[PLATFORM_MAX_PATH]; diff --git a/extensions/clientprefs/cookie.cpp b/extensions/clientprefs/cookie.cpp index b9f7abbb..ca38de5b 100644 --- a/extensions/clientprefs/cookie.cpp +++ b/extensions/clientprefs/cookie.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Client Preferences Extension * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -55,35 +55,12 @@ void CookieManager::Unload() for (int i = playerhelpers->GetMaxClients()+1; --i > 0;) { if (connected[i]) - { OnClientDisconnecting(i); - } } /* Find all cookies and delete them */ - Cookie *current; - for (SourceHook::List::iterator _iter = cookieList.begin(); _iter != cookieList.end(); _iter++) - { - current = (Cookie *)*_iter; - - if (current == NULL) - { - continue; - } - - g_ClientPrefs.cookieMutex->Lock(); - if (current->usedInQuery) - { - current->shouldDelete = true; - g_ClientPrefs.cookieMutex->Unlock(); - } - else - { - g_ClientPrefs.cookieMutex->Unlock(); - delete current; - } - } + delete (*_iter); cookieList.clear(); } @@ -93,9 +70,7 @@ Cookie *CookieManager::FindCookie(const char *name) Cookie **pCookie = cookieTrie.retrieve(name); if (pCookie == NULL) - { return NULL; - } return *pCookie; } @@ -116,7 +91,6 @@ Cookie *CookieManager::CreateCookie(const char *name, const char *description, C /* First time cookie - Create from scratch */ pCookie = new Cookie(name, description, access); - pCookie->usedInQuery++; /* Attempt to insert cookie into the db and get its ID num */ TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie); diff --git a/extensions/clientprefs/cookie.h b/extensions/clientprefs/cookie.h index 8da6ad6b..c678d392 100644 --- a/extensions/clientprefs/cookie.h +++ b/extensions/clientprefs/cookie.h @@ -75,12 +75,7 @@ struct Cookie dbid = -1; for (int i=0; i<=MAXCLIENTS; i++) - { data[i] = NULL; - } - - shouldDelete = false; - usedInQuery = 0; } ~Cookie() @@ -88,9 +83,7 @@ struct Cookie for (int i=0; i<=MAXCLIENTS; i++) { if (data[i] != NULL) - { delete data[i]; - } } } @@ -99,10 +92,6 @@ struct Cookie int dbid; CookieData *data[MAXCLIENTS+1]; CookieAccess access; - - /* Reference counting stuff */ - bool shouldDelete; - int usedInQuery; }; class CookieManager : public IClientListener, public IPluginsListener diff --git a/extensions/clientprefs/extension.cpp b/extensions/clientprefs/extension.cpp index f907ed5c..ea2c3b40 100644 --- a/extensions/clientprefs/extension.cpp +++ b/extensions/clientprefs/extension.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Client Preferences Extension * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -50,9 +50,6 @@ DbDriver g_DriverType; bool ClientPrefs::SDK_OnLoad(char *error, size_t maxlength, bool late) { - queryMutex = threader->MakeMutex(); - cookieMutex = threader->MakeMutex(); - DBInfo = dbi->FindDatabaseConf("clientprefs"); if (DBInfo == NULL) @@ -155,13 +152,15 @@ void ClientPrefs::NotifyInterfaceDrop(SMInterface *pInterface) } } -void ClientPrefs::SDK_OnUnload() +void ClientPrefs::SDK_OnDependenciesDropped() { + // At this point, we're guaranteed that DBI has flushed the worker thread + // for us, so no cookies should have outstanding queries. + g_CookieManager.Unload(); + handlesys->RemoveType(g_CookieType, myself->GetIdentity()); handlesys->RemoveType(g_CookieIterator, myself->GetIdentity()); - g_CookieManager.Unload(); - if (Database != NULL) { Database->Close(); @@ -199,9 +198,6 @@ void ClientPrefs::SDK_OnUnload() plsys->RemovePluginsListener(&g_CookieManager); playerhelpers->RemoveClientListener(&g_CookieManager); - - queryMutex->DestroyThis(); - cookieMutex->DestroyThis(); } void ClientPrefs::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) @@ -312,7 +308,11 @@ void ClientPrefs::DatabaseConnect() databaseLoading = false; - this->ProcessQueryCache(); + // Need a new scope because of the goto above. + { + ke::AutoLock lock(&queryLock); + this->ProcessQueryCache(); + } return; fatal_fail: @@ -321,24 +321,18 @@ fatal_fail: databaseLoading = false; } -bool ClientPrefs::AddQueryToQueue( TQueryOp *query ) +bool ClientPrefs::AddQueryToQueue(TQueryOp *query) { - queryMutex->Lock(); - if (Database == NULL) { - cachedQueries.push_back(query); - queryMutex->Unlock(); - return false; - } - - if (!cachedQueries.empty()) - { - queryMutex->Unlock(); - this->ProcessQueryCache(); - } - else - { - queryMutex->Unlock(); + ke::AutoLock lock(&queryLock); + if (Database == NULL) + { + cachedQueries.push_back(query); + return false; + } + + if (!cachedQueries.empty()) + this->ProcessQueryCache(); } query->SetDatabase(Database); @@ -348,12 +342,11 @@ bool ClientPrefs::AddQueryToQueue( TQueryOp *query ) void ClientPrefs::ProcessQueryCache() { - if (Database == NULL) - { - return; - } + queryLock.AssertCurrentThreadOwns(); + + if (Database == NULL) + return; - queryMutex->Lock(); TQueryOp *op; for (SourceHook::List::iterator iter = cachedQueries.begin(); iter != cachedQueries.end(); iter++) { @@ -363,7 +356,6 @@ void ClientPrefs::ProcessQueryCache() } cachedQueries.clear(); - queryMutex->Unlock(); } size_t IsAuthIdConnected(char *authID) @@ -417,8 +409,7 @@ void ClientPrefs::CatchLateLoadClients() void ClientPrefs::ClearQueryCache(int serial) { - queryMutex->Lock(); - + ke::AutoLock lock(&queryLock); for (SourceHook::List::iterator iter = cachedQueries.begin(); iter != cachedQueries.end();) { TQueryOp *op = *iter; @@ -432,7 +423,6 @@ void ClientPrefs::ClearQueryCache(int serial) iter++; } } - queryMutex->Unlock(); } bool Translate(char *buffer, @@ -530,7 +520,5 @@ ClientPrefs::ClientPrefs() phrases = NULL; DBInfo = NULL; - cookieMutex = NULL; - queryMutex = NULL; identity = NULL; } diff --git a/extensions/clientprefs/extension.h b/extensions/clientprefs/extension.h index 1d104926..dc5a2358 100644 --- a/extensions/clientprefs/extension.h +++ b/extensions/clientprefs/extension.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Client Preferences Extension * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -39,6 +39,8 @@ #include "smsdk_ext.h" #include "sh_list.h" +#include + char * UTIL_strncpy(char * destination, const char * source, size_t num); #include "cookie.h" @@ -73,11 +75,8 @@ public: * @return True to succeed loading, false to fail. */ virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); - - /** - * @brief This is called right before the extension is unloaded. - */ - virtual void SDK_OnUnload(); + + virtual void SDK_OnDependenciesDropped(); /** * @brief This is called once all known extensions have been loaded. @@ -162,7 +161,7 @@ public: private: SourceHook::List cachedQueries; - IMutex *queryMutex; + ke::Mutex queryLock; IdentityToken_t *identity; }; diff --git a/extensions/clientprefs/query.cpp b/extensions/clientprefs/query.cpp index db463df7..8ad3d58c 100644 --- a/extensions/clientprefs/query.cpp +++ b/extensions/clientprefs/query.cpp @@ -303,23 +303,6 @@ int TQueryOp::PullQuerySerial() ParamData::~ParamData() { - if (cookie) - { - g_ClientPrefs.cookieMutex->Lock(); - cookie->usedInQuery--; - - if (cookie->shouldDelete && cookie->usedInQuery <= 0) - { - g_ClientPrefs.cookieMutex->Unlock(); - delete cookie; - cookie = NULL; - } - else - { - g_ClientPrefs.cookieMutex->Unlock(); - } - } - if (data) { /* Data is only ever passed in a client disconnect query and always needs to be deleted */ diff --git a/extensions/clientprefs/sdk/smsdk_config.h b/extensions/clientprefs/sdk/smsdk_config.h index dcee6314..9a7f0c63 100644 --- a/extensions/clientprefs/sdk/smsdk_config.h +++ b/extensions/clientprefs/sdk/smsdk_config.h @@ -67,7 +67,7 @@ //#define SMEXT_ENABLE_MEMUTILS #define SMEXT_ENABLE_GAMEHELPERS //#define SMEXT_ENABLE_TIMERSYS -#define SMEXT_ENABLE_THREADER +//#define SMEXT_ENABLE_THREADER //#define SMEXT_ENABLE_LIBSYS #define SMEXT_ENABLE_MENUS //#define SMEXT_ENABLE_ADTFACTORY diff --git a/extensions/clientprefs/sdk/smsdk_ext.cpp b/extensions/clientprefs/sdk/smsdk_ext.cpp index 6404a919..d4e9b4e3 100644 --- a/extensions/clientprefs/sdk/smsdk_ext.cpp +++ b/extensions/clientprefs/sdk/smsdk_ext.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Base Extension Code * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -97,6 +97,12 @@ IUserMessages *usermsgs = NULL; #if defined SMEXT_ENABLE_TRANSLATOR ITranslator *translator = NULL; #endif +#if defined SMEXT_ENABLE_NINVOKE +INativeInterface *ninvoke = NULL; +#endif +#if defined SMEXT_ENABLE_ROOTCONSOLEMENU +IRootConsole *rootconsole = NULL; +#endif /** Exports the main interface */ PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI() @@ -185,6 +191,12 @@ bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, #if defined SMEXT_ENABLE_TRANSLATOR SM_GET_IFACE(TRANSLATOR, translator); #endif +#if defined SMEXT_ENABLE_NINVOKE + SM_GET_IFACE(NINVOKE, ninvoke); +#endif +#if defined SMEXT_ENABLE_ROOTCONSOLEMENU + SM_GET_IFACE(ROOTCONSOLE, rootconsole); +#endif if (SDK_OnLoad(error, maxlength, late)) { @@ -227,6 +239,11 @@ void SDKExtension::OnExtensionUnload() SDK_OnUnload(); } +void SDKExtension::OnDependenciesDropped() +{ + SDK_OnDependenciesDropped(); +} + const char *SDKExtension::GetExtensionAuthor() { return SMEXT_CONF_AUTHOR; @@ -279,6 +296,10 @@ void SDKExtension::SDK_OnAllLoaded() { } +void SDKExtension::SDK_OnDependenciesDropped() +{ +} + #if defined SMEXT_CONF_METAMOD PluginId g_PLID = 0; /**< Metamod plugin ID */ diff --git a/extensions/clientprefs/sdk/smsdk_ext.h b/extensions/clientprefs/sdk/smsdk_ext.h index 115fc800..e73e5078 100644 --- a/extensions/clientprefs/sdk/smsdk_ext.h +++ b/extensions/clientprefs/sdk/smsdk_ext.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Base Extension Code * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -91,6 +91,12 @@ #if defined SMEXT_ENABLE_TRANSLATOR #include #endif +#if defined SMEXT_ENABLE_NINVOKE +#include +#endif +#if defined SMEXT_ENABLE_ROOTCONSOLEMENU +#include +#endif #if defined SMEXT_CONF_METAMOD #include @@ -121,7 +127,7 @@ public: virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); /** - * @brief This is called right before the extension is unloaded. + * @brief This is called once the extension unloading process begins. */ virtual void SDK_OnUnload(); @@ -135,6 +141,12 @@ public: */ virtual void SDK_OnPauseChange(bool paused); + /** + * @brief Called after SDK_OnUnload, once all dependencies have been + * removed, and the extension is about to be removed from memory. + */ + virtual void SDK_OnDependenciesDropped(); + #if defined SMEXT_CONF_METAMOD /** * @brief Called when Metamod is attached, before the extension version is called. @@ -197,6 +209,9 @@ public: //IExtensionInterface virtual const char *GetExtensionDescription(); /** Returns date string */ virtual const char *GetExtensionDateString(); + + /** Called after OnExtensionUnload, once dependencies have been dropped. */ + virtual void OnDependenciesDropped(); #if defined SMEXT_CONF_METAMOD public: //ISmmPlugin /** Called when the extension is attached to Metamod. */ @@ -289,6 +304,12 @@ extern IUserMessages *usermsgs; #if defined SMEXT_ENABLE_TRANSLATOR extern ITranslator *translator; #endif +#if defined SMEXT_ENABLE_NINVOKE +extern INativeInterface *ninvoke; +#endif +#if defined SMEXT_ENABLE_ROOTCONSOLEMENU +extern IRootConsole *rootconsole; +#endif #if defined SMEXT_CONF_METAMOD PLUGIN_GLOBALVARS(); diff --git a/public/IExtensionSys.h b/public/IExtensionSys.h index e71833f8..3c63e7e3 100644 --- a/public/IExtensionSys.h +++ b/public/IExtensionSys.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -134,8 +134,9 @@ namespace SourceMod * itself is not versioned. * * V6 - added TestFeature() to IShareSys. + * V7 - added OnDependenciesDropped() to IExtensionInterface. */ - #define SMINTERFACE_EXTENSIONAPI_VERSION 6 + #define SMINTERFACE_EXTENSIONAPI_VERSION 7 /** * @brief The interface an extension must expose. @@ -313,6 +314,17 @@ namespace SourceMod virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) { } + + /** + * @brief Called once all dependencies have been unloaded. This is + * called AFTER OnExtensionUnload(), but before the extension library + * has been unloaded. It can be used as an alternate unload hook for + * cases where having no dependent plugins would make shutdown much + * simplier. + */ + virtual void OnDependenciesDropped() + { + } }; /** diff --git a/public/sample_ext/sdk/smsdk_ext.cpp b/public/sample_ext/sdk/smsdk_ext.cpp index f5a9ec71..d4e9b4e3 100644 --- a/public/sample_ext/sdk/smsdk_ext.cpp +++ b/public/sample_ext/sdk/smsdk_ext.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Base Extension Code * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -239,6 +239,11 @@ void SDKExtension::OnExtensionUnload() SDK_OnUnload(); } +void SDKExtension::OnDependenciesDropped() +{ + SDK_OnDependenciesDropped(); +} + const char *SDKExtension::GetExtensionAuthor() { return SMEXT_CONF_AUTHOR; @@ -291,6 +296,10 @@ void SDKExtension::SDK_OnAllLoaded() { } +void SDKExtension::SDK_OnDependenciesDropped() +{ +} + #if defined SMEXT_CONF_METAMOD PluginId g_PLID = 0; /**< Metamod plugin ID */ diff --git a/public/sample_ext/sdk/smsdk_ext.h b/public/sample_ext/sdk/smsdk_ext.h index 34261bf1..e73e5078 100644 --- a/public/sample_ext/sdk/smsdk_ext.h +++ b/public/sample_ext/sdk/smsdk_ext.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet: * ============================================================================= * SourceMod Base Extension Code * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -127,7 +127,7 @@ public: virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); /** - * @brief This is called right before the extension is unloaded. + * @brief This is called once the extension unloading process begins. */ virtual void SDK_OnUnload(); @@ -141,6 +141,12 @@ public: */ virtual void SDK_OnPauseChange(bool paused); + /** + * @brief Called after SDK_OnUnload, once all dependencies have been + * removed, and the extension is about to be removed from memory. + */ + virtual void SDK_OnDependenciesDropped(); + #if defined SMEXT_CONF_METAMOD /** * @brief Called when Metamod is attached, before the extension version is called. @@ -203,6 +209,9 @@ public: //IExtensionInterface virtual const char *GetExtensionDescription(); /** Returns date string */ virtual const char *GetExtensionDateString(); + + /** Called after OnExtensionUnload, once dependencies have been dropped. */ + virtual void OnDependenciesDropped(); #if defined SMEXT_CONF_METAMOD public: //ISmmPlugin /** Called when the extension is attached to Metamod. */