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
This commit is contained in:
David Anderson 2007-01-17 03:01:38 +00:00
parent b7e10b111f
commit 1857f29efc
11 changed files with 305 additions and 92 deletions

View File

@ -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.
*

View File

@ -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;
/**

View File

@ -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);
}

View File

@ -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();

View File

@ -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<IfaceInfo>::iterator iter = m_Deps.begin();
if (iter == m_Deps.end())
{
return NULL;
}
if (pOwner)
{
*pOwner = (*iter).owner;
}
if (pInterface)
{
*pInterface = (*iter).iface;
}
List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
return (ITERATOR *)pIter;
}
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
{
List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
List<IfaceInfo>::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<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::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<CExtension *> UnloadQueue;
/* Handle dependencies */
if (pExt->IsLoaded())
{
/* Notify and/or unload all dependencies */
List<CExtension *>::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<IfaceInfo>::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<CExtension *>::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;
}

View File

@ -6,12 +6,15 @@
#include <sh_list.h>
#include <sh_string.h>
#include "sm_globals.h"
#include "ShareSys.h"
#include <ISmmAPI.h>
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<IfaceInfo> m_Deps;
List<SMInterface *> 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<CExtension *> m_Libs;
};

View File

@ -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....
* <b>Punish them.</b>
*/
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<IfaceInfo>::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<IfaceInfo>::iterator iter = m_Interfaces.begin();
while (iter != m_Interfaces.end())
{
if ((*iter).owner == pExtension)
{
iter = m_Interfaces.erase(iter);
} else {
iter++;
}
}
}

View File

@ -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()
{

View File

@ -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 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\sample.ext.dll"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="2"
@ -116,7 +117,7 @@
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="2"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
@ -133,6 +134,7 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\sample.ext.dll"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
@ -193,7 +195,7 @@
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SMEXT_CONF_METAMOD"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
@ -211,6 +213,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="tier0.lib"
OutputFile="$(OutDir)\sample.ext.dll"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="2"
@ -267,7 +270,7 @@
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SMEXT_CONF_METAMOD"
RuntimeLibrary="2"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
@ -284,6 +287,7 @@
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)\sample.ext.dll"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"

View File

@ -2,7 +2,7 @@
#include "smsdk_ext.h"
IShareSys *g_pShareSys = NULL;
IdentityToken_t *myself = NULL;
IExtension *myself = NULL;
IHandleSys *g_pHandleSys = NULL;
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
@ -22,7 +22,9 @@ SDKExtension::SDKExtension()
bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t err_max, bool late)
{
g_pShareSys = sys;
myself = me->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();
}

View File

@ -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