diff --git a/bridge/include/IScriptManager.h b/bridge/include/IScriptManager.h index 2af39055..114d6de7 100644 --- a/bridge/include/IScriptManager.h +++ b/bridge/include/IScriptManager.h @@ -75,6 +75,8 @@ public: virtual IPluginIterator *GetPluginIterator() = 0; virtual void OnLibraryAction(const char *name, LibraryAction action) = 0; virtual bool LibraryExists(const char *name) = 0; + virtual bool HookPluginUnload(IPlugin *pl, IPluginFunction *fwd) = 0; + virtual bool UnhookPluginUnload(IPlugin *pl, IPluginFunction *fwd) = 0; virtual SMPlugin *FindPluginByOrder(unsigned num) = 0; virtual SMPlugin *FindPluginByIdentity(IdentityToken_t *ident) = 0; virtual SMPlugin *FindPluginByContext(IPluginContext *ctx) = 0; diff --git a/core/logic/PluginSys.cpp b/core/logic/PluginSys.cpp index 649e8429..c0807a7d 100644 --- a/core/logic/PluginSys.cpp +++ b/core/logic/PluginSys.cpp @@ -1505,6 +1505,8 @@ void CPluginManager::Purge(CPlugin *plugin) (*iter)->OnPluginUnloaded(plugin); } + CallUnloadHooks(plugin); + plugin->DropEverything(); for (ListenerIter iter(m_listeners); !iter.done(); iter.next()) @@ -1601,6 +1603,19 @@ void CPluginManager::OnSourceModShutdown() forwardsys->ReleaseForward(m_pOnLibraryAdded); forwardsys->ReleaseForward(m_pOnLibraryRemoved); + + { + std::list::iterator iter = m_PluginUnloadHooks.begin(); + + while (iter!=m_PluginUnloadHooks.end()) + { + forwardsys->ReleaseForward(iter->pForward); + + iter++; + } + + m_PluginUnloadHooks.clear(); + } } ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key, @@ -2202,6 +2217,72 @@ bool CPluginManager::LibraryExists(const char *lib) return false; } +bool CPluginManager::HookPluginUnload(IPlugin *pPluginToHook, IPluginFunction *pHookFunction) +{ + { + std::list::iterator iter = m_PluginUnloadHooks.begin(); + + while (iter!=m_PluginUnloadHooks.end()) + { + if(iter->pPluginTarget == pPluginToHook) + { + return iter->pForward->AddFunction(pHookFunction); + } + + iter++; + } + } + + IChangeableForward *pForward = forwardsys->CreateForwardEx(nullptr, ET_Hook, 1, nullptr, Param_Cell); + + m_PluginUnloadHooks.push_back({pPluginToHook, pForward}); + + return pForward->AddFunction(pHookFunction); +} + +bool CPluginManager::UnhookPluginUnload(IPlugin *pPluginToUnhook, IPluginFunction *pHookFunction) +{ + std::list::iterator iter = m_PluginUnloadHooks.begin(); + + while (iter!=m_PluginUnloadHooks.end()) + { + if(iter->pPluginTarget == pPluginToUnhook) + { + return iter->pForward->RemoveFunction(pHookFunction); + } + + iter++; + } + + return false; +} + +void CPluginManager::CallUnloadHooks(IPlugin *pTarget) +{ + std::list::iterator iter = m_PluginUnloadHooks.begin(); + + IChangeableForward *pForwardToCall; + + while (iter!=m_PluginUnloadHooks.end()) + { + // There can only be one unique + if(iter->pPluginTarget == pTarget) + { + pForwardToCall = iter->pForward; + + pForwardToCall->PushCell((cell_t)pTarget); + pForwardToCall->Execute(); + + pForwardToCall->Cancel(); + m_PluginUnloadHooks.erase(iter); + + return; + } + + iter++; + } +} + void CPluginManager::AllPluginsLoaded() { for (PluginIter iter(m_plugins); !iter.done(); iter.next()) diff --git a/core/logic/PluginSys.h b/core/logic/PluginSys.h index 0b250cda..b3e6dad6 100644 --- a/core/logic/PluginSys.h +++ b/core/logic/PluginSys.h @@ -425,6 +425,12 @@ public: bool LibraryExists(const char *lib); + bool HookPluginUnload(IPlugin *pPluginToHook, IPluginFunction *pHookFunction); + + bool UnhookPluginUnload(IPlugin *pPluginToUnhook, IPluginFunction *pHookFunction); + + void CallUnloadHooks(IPlugin *pTarget); + bool ReloadPlugin(CPlugin *pl, bool print=false); void UnloadAll(); @@ -523,6 +529,12 @@ private: // Forwards IForward *m_pOnLibraryAdded; IForward *m_pOnLibraryRemoved; + struct PluginUnloadHookInfo + { + IPlugin *pPluginTarget; + IChangeableForward *pForward; + }; + std::list m_PluginUnloadHooks; }; extern CPluginManager g_PluginSys; diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index b7512752..bcf33b97 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -637,6 +637,34 @@ static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params) return pPlugin->GetMyHandle(); } +static cell_t HookPluginUnload(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + + if (!pPlugin) + { + return false; + } + + IPluginFunction *pCallback = pContext->GetFunctionById(params[3]); + + return scripts->HookPluginUnload(pPlugin, pCallback); +} + +static cell_t UnhookPluginUnload(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + + if (!pPlugin) + { + return false; + } + + IPluginFunction *pCallback = pContext->GetFunctionById(params[3]); + + return scripts->UnhookPluginUnload(pPlugin, pCallback); +} + static cell_t VerifyCoreVersion(IPluginContext *pContext, const cell_t *params) { return 4; @@ -969,6 +997,8 @@ REGISTER_NATIVES(coreNatives) {"LogToFileEx", LogToFileEx}, {"GetExtensionFileStatus", GetExtensionFileStatus}, {"FindPluginByNumber", FindPluginByNumber}, + {"HookPluginUnload", HookPluginUnload}, + {"UnhookPluginUnload", UnhookPluginUnload}, {"VerifyCoreVersion", VerifyCoreVersion}, {"GetFeatureStatus", GetFeatureStatus}, {"RequireFeature", RequireFeature}, diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index d1fcd2a9..c784e253 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -84,6 +84,13 @@ enum APLRes APLRes_SilentFailure /**< Plugin shouldn't load but do so silently */ }; +/** + * Called when a plugin unloaded. + * + * @param plugin Plugin Handle who unloaded. + */ +typedef PluginUnloaded = function void (Handle plugin); + methodmap GameData < Handle { // Loads a game config file. @@ -319,6 +326,26 @@ native bool GetPluginInfo(Handle plugin, PluginInfo info, char[] buffer, int max */ native Handle FindPluginByNumber(int order_num); +/** + * Creates a hook for when a plugin unloaded. + * + * @param plugin Plugin Handle to hook. + * @param callback An PluginUnloaded function pointer. + * + * @return True on success, false otherwise. + */ +native bool HookPluginUnload(Handle plugin, PluginUnloaded callback); + +/** + * Removes a hook for when a plugin unloaded. + * + * @param plugin Plugin Handle to hook. + * @param callback An PluginUnloaded function pointer. + * + * @return True on success, false otherwise. + */ +native bool UnhookPluginUnload(Handle plugin, PluginUnloaded callback); + /** * Causes the plugin to enter a failed state. An error will be thrown and * the plugin will be paused until it is unloaded or reloaded.