From 1857f29efc92d2c2f72538f1ba50286fc4bac382 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 17 Jan 2007 03:01:38 +0000 Subject: [PATCH] Added extension loading/unloading Extended SDK for interface sharing Completed Metamod extension support --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40309 --- core/interfaces/IExtensionSys.h | 72 +++++++++---- core/interfaces/IShareSys.h | 9 +- core/sourcemm_api.cpp | 10 ++ core/sourcemm_api.h | 1 + core/systems/ExtensionSys.cpp | 179 ++++++++++++++++++++++++++++++-- core/systems/ExtensionSys.h | 15 ++- core/systems/ShareSys.cpp | 71 ++++++------- core/systems/ShareSys.h | 8 +- extensions/sdk/sdk.vcproj | 12 ++- extensions/sdk/smsdk_ext.cpp | 18 +++- extensions/sdk/smsdk_ext.h | 2 +- 11 files changed, 305 insertions(+), 92 deletions(-) diff --git a/core/interfaces/IExtensionSys.h b/core/interfaces/IExtensionSys.h index 16f25474..9982832e 100644 --- a/core/interfaces/IExtensionSys.h +++ b/core/interfaces/IExtensionSys.h @@ -7,6 +7,7 @@ namespace SourceMod { class IExtensionInterface; + typedef void * ITERATOR; /** * @brief Encapsulates an IExtension. @@ -40,6 +41,32 @@ namespace SourceMod * @return An IdentityToken_t pointer. */ virtual IdentityToken_t *GetIdentity() =0; + + /** + * @brief Retrieves the extension dependency list for this extension. + * + * @param pOwner Optional pointer to store the first interface's owner. + * @param pInterface Optional pointer to store the first interface. + * @return An ITERATOR pointer for the results, or NULL if no results at all. + */ + virtual ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) =0; + + /** + * @brief Finds the next dependency in the dependency list. + * + * @param iter Pointer to iterator from FindFirstDependency. + * @param pOwner Optional pointer to store the interface's owner. + * @param pInterface Optional pointer to store the interface. + * @return True if there are more results after this, false otherwise. + */ + virtual bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) =0; + + /** + * @brief Frees an ITERATOR handle from FindFirstDependency. + * + * @param iter Pointer to iterator to free. + */ + virtual void FreeDependencyIterator(ITERATOR *iter) =0; }; #define SMINTERFACE_EXTENSIONAPI_VERSION 1 @@ -49,6 +76,11 @@ namespace SourceMod */ class IExtensionInterface { + public: + virtual unsigned int GetExtensionVersion() + { + return SMINTERFACE_EXTENSIONAPI_VERSION; + } public: /** * @brief Called when the extension is loaded. @@ -84,16 +116,29 @@ namespace SourceMod */ virtual void OnExtensionPauseChange(bool pause) =0; - virtual unsigned int GetExtensionVersion() + /** + * @brief Asks the extension whether it's safe to remove an external interface it's using. + * If it's not safe, return false, and the extension will be unloaded afterwards. + * NOTE: It is important to also hook NotifyInterfaceDrop() in order to clean up resources. + * + * @param pInterface Pointer to interface being dropped. + * @return True to continue, false to unload this extension afterwards. + */ + virtual bool QueryInterfaceDrop(SMInterface *pInterface) { - return SMINTERFACE_EXTENSIONAPI_VERSION; + return true; } /** - * @brief Returns true if the extension is Metamod-dependent. + * @brief Notifies the extension that an external interface it uses is being removed. + * + * @param pInterface Pointer to interface being dropped. */ - virtual bool IsMetamodExtension() =0; + virtual void NotifyInterfaceDrop(SMInterface *pInterface) + { + } public: + virtual bool IsMetamodExtension() =0; virtual const char *GetExtensionName() =0; virtual const char *GetExtensionURL() =0; virtual const char *GetExtensionTag() =0; @@ -138,25 +183,6 @@ namespace SourceMod char *error, size_t err_max) =0; - /** - * @brief Returns the number of plugins that will be unloaded when this - * module is unloaded. - * - * @param pExt IExtension pointer. - * @param optional Optional pointer to be filled with # of plugins that - * are dependent, but will continue safely. NOT YET USED. - * @return Total number of dependent plugins. - */ - virtual unsigned int NumberOfPluginDependents(IExtension *pExt, unsigned int *optional) =0; - - /** - * @brief Returns whether or not the extension can be unloaded. - * - * @param pExt IExtension pointer. - * @return True if unloading is possible, false otherwise. - */ - virtual bool IsExtensionUnloadable(IExtension *pExtension) =0; - /** * @brief Attempts to unload a module. * diff --git a/core/interfaces/IShareSys.h b/core/interfaces/IShareSys.h index d4f6db0f..9569eb5c 100644 --- a/core/interfaces/IShareSys.h +++ b/core/interfaces/IShareSys.h @@ -7,6 +7,7 @@ namespace SourceMod { + class IExtension; struct IdentityToken_t; typedef unsigned int HandleType_t; typedef HandleType_t IdentityType_t; @@ -53,11 +54,11 @@ namespace SourceMod /** * @brief Adds an interface to the global interface system. * + * @param myself Object adding this interface, in order to track dependencies. * @param iface Interface pointer (must be unique). - * @param token Parent token of the module/interface. * @return True on success, false otherwise. */ - virtual bool AddInterface(SMInterface *iface, IdentityToken_t *token) =0; + virtual bool AddInterface(IExtension *myself, SMInterface *iface) =0; /** * @brief Requests an interface from the global interface system. @@ -65,12 +66,12 @@ namespace SourceMod * * @param iface_name Interface name. * @param iface_vers Interface version to attempt to match. - * @param token Object requesting this interface, in order to track dependencies. + * @param myself Object requesting this interface, in order to track dependencies. * @param pIface Pointer to store the return value in. */ virtual bool RequestInterface(const char *iface_name, unsigned int iface_vers, - IdentityToken_t *token, + IExtension *myself, SMInterface **pIface) =0; /** diff --git a/core/sourcemm_api.cpp b/core/sourcemm_api.cpp index bf2f884c..e81cf40f 100644 --- a/core/sourcemm_api.cpp +++ b/core/sourcemm_api.cpp @@ -7,6 +7,7 @@ SourceMod_Core g_SourceMod_Core; IVEngineServer *engine = NULL; IServerGameDLL *gamedll = NULL; IServerGameClients *serverClients = NULL; +ISmmPluginManager *g_pMMPlugins = NULL; PLUGIN_EXPOSE(SourceMod, g_SourceMod_Core); @@ -18,6 +19,15 @@ bool SourceMod_Core::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen GET_V_IFACE_CURRENT(engineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); GET_V_IFACE_CURRENT(serverFactory, serverClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS); + if ((g_pMMPlugins = (ISmmPluginManager *)g_SMAPI->MetaFactory(MMIFACE_PLMANAGER, NULL, NULL)) == NULL) + { + if (error) + { + snprintf(error, maxlen, "Unable to find interface %s", MMIFACE_PLMANAGER); + } + return false; + } + return g_SourceMod.InitializeSourceMod(error, maxlen, late); } diff --git a/core/sourcemm_api.h b/core/sourcemm_api.h index 8dea8b0b..7e753363 100644 --- a/core/sourcemm_api.h +++ b/core/sourcemm_api.h @@ -31,6 +31,7 @@ extern SourceMod_Core g_SourceMod_Core; extern IVEngineServer *engine; extern IServerGameDLL *gamedll; extern IServerGameClients *serverClients; +extern ISmmPluginManager *g_pMMPlugins; PLUGIN_GLOBALVARS(); diff --git a/core/systems/ExtensionSys.cpp b/core/systems/ExtensionSys.cpp index f7287315..3d9375ce 100644 --- a/core/systems/ExtensionSys.cpp +++ b/core/systems/ExtensionSys.cpp @@ -2,6 +2,7 @@ #include "LibrarySys.h" #include "ShareSys.h" #include "CLogger.h" +#include "sourcemm_api.h" CExtensionManager g_Extensions; IdentityType_t g_ExtType; @@ -11,6 +12,7 @@ CExtension::CExtension(const char *filename, char *error, size_t err_max) m_File.assign(filename); m_pAPI = NULL; m_pIdentToken = NULL; + m_PlId = 0; char path[PLATFORM_MAX_PATH+1]; g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename); @@ -44,7 +46,8 @@ CExtension::CExtension(const char *filename, char *error, size_t err_max) if (m_pAPI->IsMetamodExtension()) { - /* :TODO: STUFF */ + bool already; + m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max); } m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType); @@ -53,7 +56,12 @@ CExtension::CExtension(const char *filename, char *error, size_t err_max) { if (m_pAPI->IsMetamodExtension()) { - /* :TODO: stuff */ + if (m_PlId) + { + char dummy[255]; + g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy)); + m_PlId = 0; + } } m_pAPI = NULL; m_pLib->CloseLibrary(); @@ -69,6 +77,10 @@ CExtension::~CExtension() if (m_pAPI) { m_pAPI->OnExtensionUnload(); + if (m_PlId) + { + g_pMMPlugins->Unload(m_PlId, true, NULL, 0); + } } if (m_pIdentToken) @@ -107,6 +119,81 @@ bool CExtension::IsLoaded() return (m_pLib != NULL); } +void CExtension::AddDependency(IfaceInfo *pInfo) +{ + m_Deps.push_back(*pInfo); +} + +ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) +{ + List::iterator iter = m_Deps.begin(); + + if (iter == m_Deps.end()) + { + return NULL; + } + + if (pOwner) + { + *pOwner = (*iter).owner; + } + if (pInterface) + { + *pInterface = (*iter).iface; + } + + List::iterator *pIter = new List::iterator(iter); + + return (ITERATOR *)pIter; +} + +bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) +{ + List::iterator *pIter = (List::iterator *)iter; + List::iterator _iter; + + if (_iter == m_Deps.end()) + { + return false; + } + + _iter++; + + if (pOwner) + { + *pOwner = (*_iter).owner; + } + if (pInterface) + { + *pInterface = (*_iter).iface; + } + + *pIter = _iter; + + if (_iter == m_Deps.end()) + { + return false; + } + + return true; +} + +void CExtension::FreeDependencyIterator(ITERATOR *iter) +{ + List::iterator *pIter = (List::iterator *)iter; + + delete pIter; +} + +void CExtension::AddInterface(SMInterface *pInterface) +{ + m_Interfaces.push_back(pInterface); +} + +/********************* + * EXTENSION MANAGER * + *********************/ + void CExtensionManager::OnSourceModAllInitialized() { g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); @@ -119,6 +206,12 @@ void CExtensionManager::OnSourceModShutdown() IExtension *CExtensionManager::LoadAutoExtension(const char *path) { + IExtension *pAlready; + if ((pAlready=FindExtensionByFile(path)) != NULL) + { + return pAlready; + } + char error[256]; CExtension *p = new CExtension(path, error, sizeof(error)); @@ -188,6 +281,12 @@ IExtension *CExtensionManager::FindExtensionByName(const char *ext) IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max) { + IExtension *pAlready; + if ((pAlready=FindExtensionByFile(file)) != NULL) + { + return pAlready; + } + CExtension *pExt = new CExtension(file, error, err_max); /* :NOTE: lifetime is currently ignored */ @@ -203,20 +302,80 @@ IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime return pExt; } -bool CExtensionManager::UnloadExtension(IExtension *pExt) +void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo) { - /* :TODO: implement */ - return true; + CExtension *pExt = (CExtension *)pOwner; + + pExt->AddDependency(pInfo); } -unsigned int CExtensionManager::NumberOfPluginDependents(IExtension *pExt, unsigned int *optional) +void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) { - /* :TODO: implement */ - return 0; + CExtension *pExt = (CExtension *)pOwner; + + pExt->AddInterface(pInterface); } -bool CExtensionManager::IsExtensionUnloadable(IExtension *pExtension) +bool CExtensionManager::UnloadExtension(IExtension *_pExt) { - /* :TODO: implement */ + if (!_pExt) + { + return false; + } + + CExtension *pExt = (CExtension *)_pExt; + + if (m_Libs.find(pExt) == m_Libs.end()) + { + return false; + } + + /* First remove us from internal lists */ + g_ShareSys.RemoveInterfaces(_pExt); + m_Libs.remove(pExt); + + List UnloadQueue; + + /* Handle dependencies */ + if (pExt->IsLoaded()) + { + /* Notify and/or unload all dependencies */ + List::iterator c_iter; + CExtension *pDep; + IExtensionInterface *pAPI; + for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) + { + pDep = (*c_iter); + if ((pAPI=pDep->GetAPI()) == NULL) + { + continue; + } + /* Now, get its dependency list */ + bool dropped = false; + List::iterator i_iter = pDep->m_Deps.begin(); + while (i_iter != pDep->m_Deps.end()) + { + if ((*i_iter).owner == _pExt) + { + if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface)) + { + dropped = true; + } + pAPI->NotifyInterfaceDrop((*i_iter).iface); + i_iter = pDep->m_Deps.erase(i_iter); + } else { + i_iter++; + } + } + } + } + + List::iterator iter; + for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) + { + /* NOTE: This is safe because the unload function backs out of anything not present */ + UnloadExtension((*iter)); + } + return true; } diff --git a/core/systems/ExtensionSys.h b/core/systems/ExtensionSys.h index 8d7a69cf..7bf05f1c 100644 --- a/core/systems/ExtensionSys.h +++ b/core/systems/ExtensionSys.h @@ -6,12 +6,15 @@ #include #include #include "sm_globals.h" +#include "ShareSys.h" +#include using namespace SourceMod; using namespace SourceHook; class CExtension : public IExtension { + friend class CExtensionManager; public: CExtension(const char *filename, char *error, size_t maxlen); ~CExtension(); @@ -20,14 +23,22 @@ public: //IExtension const char *GetFilename(); IdentityToken_t *GetIdentity(); bool IsLoaded(); + ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface); + bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface); + void FreeDependencyIterator(ITERATOR *iter); public: void SetError(const char *error); + void AddDependency(IfaceInfo *pInfo); + void AddInterface(SMInterface *pInterface); private: IdentityToken_t *m_pIdentToken; IExtensionInterface *m_pAPI; String m_File; ILibrary *m_pLib; String m_Error; + List m_Deps; + List m_Interfaces; + PluginId m_PlId; }; class CExtensionManager : @@ -42,13 +53,13 @@ public: //IExtensionManager ExtensionLifetime lifetime, char *error, size_t err_max); - unsigned int NumberOfPluginDependents(IExtension *pExt, unsigned int *optional); - bool IsExtensionUnloadable(IExtension *pExtension); bool UnloadExtension(IExtension *pExt); IExtension *FindExtensionByFile(const char *file); IExtension *FindExtensionByName(const char *ext); public: IExtension *LoadAutoExtension(const char *path); + void BindDependency(IExtension *pOwner, IfaceInfo *pInfo); + void AddInterface(IExtension *pOwner, SMInterface *pInterface); private: List m_Libs; }; diff --git a/core/systems/ShareSys.cpp b/core/systems/ShareSys.cpp index ef10e505..bf91c851 100644 --- a/core/systems/ShareSys.cpp +++ b/core/systems/ShareSys.cpp @@ -1,5 +1,6 @@ #include "ShareSys.h" #include "HandleSys.h" +#include "ExtensionSys.h" ShareSystem g_ShareSys; @@ -90,7 +91,7 @@ IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type) return pToken; } -bool ShareSystem::AddInterface(SMInterface *iface, IdentityToken_t *token) +bool ShareSystem::AddInterface(IExtension *myself, SMInterface *iface) { if (!iface) { @@ -99,17 +100,9 @@ bool ShareSystem::AddInterface(SMInterface *iface, IdentityToken_t *token) IfaceInfo info; + info.owner = myself; info.iface = iface; - info.token = token; - if (token) - { - /* If we're an external object, we have to do this */ - info.handle = g_HandleSys.CreateHandle(m_IfaceType, iface, token, GetIdentRoot(), NULL); - } else { - info.handle = 0; - } - m_Interfaces.push_back(info); return true; @@ -117,28 +110,13 @@ bool ShareSystem::AddInterface(SMInterface *iface, IdentityToken_t *token) bool ShareSystem::RequestInterface(const char *iface_name, unsigned int iface_vers, - IdentityToken_t *token, + IExtension *mysql, SMInterface **pIface) { - /* If Some yahoo.... SOME HOOLIGAN... some NO GOOD DIRTY - * HORRIBLE PERSON passed in a token that we don't recognize.... - * Punish them. - */ - HandleSecurity sec; - - sec.pIdentity = GetIdentRoot(); - sec.pOwner = NULL; - - if (!g_HandleSys.ReadHandle(token->ident, m_TypeRoot, &sec, NULL)) - { - return false; - } - /* See if the interface exists */ List::iterator iter; SMInterface *iface; - IdentityToken_t *iface_owner; - Handle_t iface_handle; + IExtension *iface_owner; bool found = false; for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++) { @@ -149,8 +127,7 @@ bool ShareSystem::RequestInterface(const char *iface_name, if (iface->GetInterfaceVersion() == iface_vers || iface->IsVersionCompatible(iface_vers)) { - iface_owner = info.token; - iface_handle = info.handle; + iface_owner = info.owner; found = true; break; } @@ -162,23 +139,21 @@ bool ShareSystem::RequestInterface(const char *iface_name, return false; } - /* If something external owns this, we need to track it. */ + /* Add a dependency node */ if (iface_owner) { - Handle_t newhandle; - if (g_HandleSys.CloneHandle(iface_handle, &newhandle, token, &sec) - != HandleError_None) - { - return false; - } - /** - * Now we can deny module loads based on dependencies. - */ + IfaceInfo info; + info.iface = iface; + info.owner = iface_owner; + g_Extensions.BindDependency(iface_owner, &info); } - /* :TODO: finish */ + if (pIface) + { + *pIface = iface; + } - return NULL; + return true; } void ShareSystem::AddNatives(IdentityToken_t *token, const sp_nativeinfo_t *natives[]) @@ -202,3 +177,17 @@ void ShareSystem::DestroyIdentType(IdentityType_t type) g_HandleSys.RemoveType(type, GetIdentRoot()); } +void ShareSystem::RemoveInterfaces(IExtension *pExtension) +{ + List::iterator iter = m_Interfaces.begin(); + + while (iter != m_Interfaces.end()) + { + if ((*iter).owner == pExtension) + { + iter = m_Interfaces.erase(iter); + } else { + iter++; + } + } +} diff --git a/core/systems/ShareSys.h b/core/systems/ShareSys.h index c8b8aecf..2aa86f82 100644 --- a/core/systems/ShareSys.h +++ b/core/systems/ShareSys.h @@ -20,8 +20,7 @@ namespace SourceMod struct IfaceInfo { SMInterface *iface; - IdentityToken_t *token; - Handle_t handle; + IExtension *owner; }; class ShareSystem : @@ -32,10 +31,10 @@ class ShareSystem : public: ShareSystem(); public: //IShareSys - bool AddInterface(SMInterface *iface, IdentityToken_t *token); + bool AddInterface(IExtension *myself, SMInterface *pIface); bool RequestInterface(const char *iface_name, unsigned int iface_vers, - IdentityToken_t *token, + IExtension *mysql, SMInterface **pIface); void AddNatives(IdentityToken_t *token, const sp_nativeinfo_t *natives[]); IdentityType_t CreateIdentType(const char *name); @@ -51,6 +50,7 @@ public: //IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); public: IdentityToken_t *CreateCoreIdentity(); + void RemoveInterfaces(IExtension *pExtension); public: inline IdentityToken_t *GetIdentRoot() { diff --git a/extensions/sdk/sdk.vcproj b/extensions/sdk/sdk.vcproj index d4ccdd57..fb4199c5 100644 --- a/extensions/sdk/sdk.vcproj +++ b/extensions/sdk/sdk.vcproj @@ -43,7 +43,7 @@ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE" MinimalRebuild="true" BasicRuntimeChecks="3" - RuntimeLibrary="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="true" @@ -60,6 +60,7 @@ /> GetIdentity(); + myself = me; + + m_WeAreUnloaded = true; #if defined SMEXT_CONF_METAMOD if (!m_SourceMMLoaded) @@ -37,7 +39,13 @@ bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys); - return SDK_OnLoad(error, err_max, late); + if (SDK_OnLoad(error, err_max, late)) + { + m_WeAreUnloaded = true; + return true; + } + + return false; } bool SDKExtension::IsMetamodExtension() @@ -51,7 +59,9 @@ bool SDKExtension::IsMetamodExtension() void SDKExtension::OnExtensionPauseChange(bool state) { +#if defined SMEXT_CONF_METAMOD m_WeGotPauseChange = true; +#endif SDK_OnPauseChange(state); } @@ -62,7 +72,9 @@ void SDKExtension::OnExtensionsAllLoaded() void SDKExtension::OnExtensionUnload() { +#if defined SMEXT_CONF_METAMOD m_WeAreUnloaded = true; +#endif SDK_OnUnload(); } diff --git a/extensions/sdk/smsdk_ext.h b/extensions/sdk/smsdk_ext.h index 420e7e93..7060cb1c 100644 --- a/extensions/sdk/smsdk_ext.h +++ b/extensions/sdk/smsdk_ext.h @@ -118,7 +118,7 @@ private: extern SDKExtension *g_pExtensionIface; extern IShareSys *g_pShareSys; -extern IdentityToken_t *myself; +extern IExtension *myself; extern IHandleSys *g_pHandleSys; #if defined SMEXT_CONF_METAMOD