added experimental extension interface for loading external extensions from mm:s
authorDavid Anderson <dvander@alliedmods.net>
Sun Oct 28 08:02:24 2007 +0000 (2007-10-28)
changeset 16483c8256da48b9
parent 1647 91d53c3f8a5e
child 1649 37373aea4dfe
added experimental extension interface for loading external extensions from mm:s
hard-bumped the extension api
core/systems/ExtensionSys.cpp
core/systems/ExtensionSys.h
public/IExtensionSys.h
     1.1 --- a/core/systems/ExtensionSys.cpp	Sun Oct 28 05:51:10 2007 +0000
     1.2 +++ b/core/systems/ExtensionSys.cpp	Sun Oct 28 08:02:24 2007 +0000
     1.3 @@ -41,14 +41,26 @@
     1.4  CExtensionManager g_Extensions;
     1.5  IdentityType_t g_ExtType;
     1.6  
     1.7 -CExtension::CExtension(const char *filename, char *error, size_t maxlength)
     1.8 +void CExtension::Initialize(const char *filename, const char *path)
     1.9  {
    1.10 -	m_File.assign(filename);
    1.11  	m_pAPI = NULL;
    1.12  	m_pIdentToken = NULL;
    1.13 +	unload_code = 0;
    1.14 +	m_bFullyLoaded = false;
    1.15 +	m_File.assign(filename);
    1.16 +	m_Path.assign(path);
    1.17 +}
    1.18 +
    1.19 +CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path)
    1.20 +{
    1.21 +	m_pAPI = pAPI;
    1.22 +	Initialize(filename, path);
    1.23 +}
    1.24 +
    1.25 +CLocalExtension::CLocalExtension(const char *filename)
    1.26 +{
    1.27  	m_PlId = 0;
    1.28 -	unload_code = 0;
    1.29 -	m_FullyLoaded = false;
    1.30 +	m_pLib = NULL;
    1.31  
    1.32  	char path[PLATFORM_MAX_PATH];
    1.33  
    1.34 @@ -73,12 +85,27 @@
    1.35  		g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
    1.36  	}
    1.37  
    1.38 -	m_Path.assign(path);
    1.39 -
    1.40 -	m_pLib = NULL;
    1.41 +	Initialize(filename, path);
    1.42  }
    1.43  
    1.44 -bool CExtension::Load(char *error, size_t maxlength)
    1.45 +bool CRemoteExtension::Load(char *error, size_t maxlength)
    1.46 +{
    1.47 +	if (!PerformAPICheck(error, maxlength))
    1.48 +	{
    1.49 +		m_pAPI = NULL;
    1.50 +		return false;
    1.51 +	}
    1.52 +
    1.53 +	if (!CExtension::Load(error, maxlength))
    1.54 +	{
    1.55 +		m_pAPI = NULL;
    1.56 +		return false;
    1.57 +	}
    1.58 +
    1.59 +	return true;
    1.60 +}
    1.61 +
    1.62 +bool CLocalExtension::Load(char *error, size_t maxlength)
    1.63  {
    1.64  	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
    1.65  
    1.66 @@ -99,23 +126,24 @@
    1.67  	}
    1.68  
    1.69  	m_pAPI = pfnGetAPI();
    1.70 -	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
    1.71 +
    1.72 +	/* Check pointer and version */
    1.73 +	if (!PerformAPICheck(error, maxlength))
    1.74  	{
    1.75  		m_pLib->CloseLibrary();
    1.76  		m_pLib = NULL;
    1.77 -		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
    1.78 +		m_pAPI = NULL;
    1.79  		return false;
    1.80  	}
    1.81  
    1.82 +	/* Load as MM:S */
    1.83  	if (m_pAPI->IsMetamodExtension())
    1.84  	{
    1.85  		bool already;
    1.86  		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
    1.87  	}
    1.88  
    1.89 -	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
    1.90 -
    1.91 -	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
    1.92 +	if (!CExtension::Load(error, maxlength))
    1.93  	{
    1.94  		if (m_pAPI->IsMetamodExtension())
    1.95  		{
    1.96 @@ -126,13 +154,71 @@
    1.97  				m_PlId = 0;
    1.98  			}
    1.99  		}
   1.100 +		m_pLib->CloseLibrary();
   1.101 +		m_pLib = NULL;
   1.102  		m_pAPI = NULL;
   1.103 +		return false;
   1.104 +	}
   1.105 +
   1.106 +	return true;
   1.107 +}
   1.108 +
   1.109 +void CRemoteExtension::Unload()
   1.110 +{
   1.111 +}
   1.112 +
   1.113 +void CLocalExtension::Unload()
   1.114 +{
   1.115 +	if (m_pAPI != NULL && m_PlId)
   1.116 +	{
   1.117 +		char temp_buffer[255];
   1.118 +		g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
   1.119 +		m_PlId = 0;
   1.120 +	}
   1.121 +
   1.122 +	if (m_pLib != NULL)
   1.123 +	{
   1.124  		m_pLib->CloseLibrary();
   1.125  		m_pLib = NULL;
   1.126 -		g_ShareSys.DestroyIdentity(m_pIdentToken);
   1.127 -		m_pIdentToken = NULL;
   1.128 +	}
   1.129 +}
   1.130 +
   1.131 +bool CRemoteExtension::IsExternal()
   1.132 +{
   1.133 +	return true;
   1.134 +}
   1.135 +
   1.136 +bool CLocalExtension::IsExternal()
   1.137 +{
   1.138 +	return false;
   1.139 +}
   1.140 +
   1.141 +CExtension::~CExtension()
   1.142 +{
   1.143 +	DestroyIdentity();
   1.144 +}
   1.145 +
   1.146 +bool CExtension::PerformAPICheck(char *error, size_t maxlength)
   1.147 +{
   1.148 +	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
   1.149 +	{
   1.150 +		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
   1.151  		return false;
   1.152 -	} else {
   1.153 +	}
   1.154 +
   1.155 +	return true;
   1.156 +}
   1.157 +
   1.158 +bool CExtension::Load(char *error, size_t maxlength)
   1.159 +{
   1.160 +	CreateIdentity();
   1.161 +	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
   1.162 +	{
   1.163 +		DestroyIdentity();
   1.164 +		return false;
   1.165 +	}
   1.166 +	else
   1.167 +	{
   1.168  		/* Check if we're past load time */
   1.169  		if (!g_SourceMod.IsMapLoading())
   1.170  		{
   1.171 @@ -143,35 +229,33 @@
   1.172  	return true;
   1.173  }
   1.174  
   1.175 -CExtension::~CExtension()
   1.176 +void CExtension::CreateIdentity()
   1.177  {
   1.178 -	char temp_buffer[255];
   1.179 -
   1.180 -	if (m_pAPI)
   1.181 +	if (m_pIdentToken != NULL)
   1.182  	{
   1.183 -		if (m_PlId)
   1.184 -		{
   1.185 -			g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
   1.186 -		}
   1.187 +		return;
   1.188  	}
   1.189  
   1.190 -	if (m_pIdentToken)
   1.191 +	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
   1.192 +}
   1.193 +
   1.194 +void CExtension::DestroyIdentity()
   1.195 +{
   1.196 +	if (m_pIdentToken == NULL)
   1.197  	{
   1.198 -		g_ShareSys.DestroyIdentity(m_pIdentToken);
   1.199 +		return;
   1.200  	}
   1.201  
   1.202 -	if (m_pLib)
   1.203 -	{
   1.204 -		m_pLib->CloseLibrary();
   1.205 -	}
   1.206 +	g_ShareSys.DestroyIdentity(m_pIdentToken);
   1.207 +	m_pIdentToken = NULL;
   1.208  }
   1.209  
   1.210  void CExtension::MarkAllLoaded()
   1.211  {
   1.212 -	assert(m_FullyLoaded == false);
   1.213 -	if (!m_FullyLoaded)
   1.214 +	assert(m_bFullyLoaded == false);
   1.215 +	if (!m_bFullyLoaded)
   1.216  	{
   1.217 -		m_FullyLoaded = true;
   1.218 +		m_bFullyLoaded = true;
   1.219  		m_pAPI->OnExtensionsAllLoaded();
   1.220  	}
   1.221  }
   1.222 @@ -221,7 +305,12 @@
   1.223  	return m_pIdentToken;
   1.224  }
   1.225  
   1.226 -bool CExtension::IsLoaded()
   1.227 +bool CRemoteExtension::IsLoaded()
   1.228 +{
   1.229 +	return (m_pAPI != NULL);
   1.230 +}
   1.231 +
   1.232 +bool CLocalExtension::IsLoaded()
   1.233  {
   1.234  	return (m_pLib != NULL);
   1.235  }
   1.236 @@ -435,7 +524,7 @@
   1.237  	}
   1.238  
   1.239  	char error[256];
   1.240 -	CExtension *p = new CExtension(path, error, sizeof(error));
   1.241 +	CExtension *p = new CLocalExtension(path);
   1.242  
   1.243  	/* We put us in the list beforehand so extensions that check for each other
   1.244  	 * won't recursively load each other.
   1.245 @@ -456,13 +545,6 @@
   1.246  	List<CExtension *>::iterator iter;
   1.247  	CExtension *pExt;
   1.248  
   1.249 -	if (!strstr(file, "." PLATFORM_LIB_EXT))
   1.250 -	{
   1.251 -		char path[PLATFORM_MAX_PATH];
   1.252 -		UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT);
   1.253 -		return FindExtensionByFile(path);
   1.254 -	}
   1.255 -
   1.256  	/* Chomp off the path */
   1.257  	char lookup[PLATFORM_MAX_PATH];
   1.258  	g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
   1.259 @@ -478,6 +560,16 @@
   1.260  		}
   1.261  	}
   1.262  
   1.263 +	/* If we got no results, test if there was a platform extension.
   1.264 +	 * If not, add one.
   1.265 +	 */
   1.266 +	if (!strstr(file, "." PLATFORM_LIB_EXT))
   1.267 +	{
   1.268 +		char path[PLATFORM_MAX_PATH];
   1.269 +		UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT);
   1.270 +		return FindExtensionByFile(path);
   1.271 +	}
   1.272 +
   1.273  	return NULL;
   1.274  }
   1.275  
   1.276 @@ -513,7 +605,7 @@
   1.277  	return NULL;
   1.278  }
   1.279  
   1.280 -IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t maxlength)
   1.281 +IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
   1.282  {
   1.283  	IExtension *pAlready;
   1.284  	if ((pAlready=FindExtensionByFile(file)) != NULL)
   1.285 @@ -521,12 +613,11 @@
   1.286  		return pAlready;
   1.287  	}
   1.288  
   1.289 -	CExtension *pExt = new CExtension(file, error, maxlength);
   1.290 -
   1.291 -	/* :NOTE: lifetime is currently ignored */
   1.292 +	CExtension *pExt = new CLocalExtension(file);
   1.293  
   1.294  	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   1.295  	{
   1.296 +		pExt->Unload();
   1.297  		delete pExt;
   1.298  		return NULL;
   1.299  	}
   1.300 @@ -790,6 +881,7 @@
   1.301  		}
   1.302  	}
   1.303  
   1.304 +	pExt->Unload();
   1.305  	delete pExt;
   1.306  
   1.307  	List<CExtension *>::iterator iter;
   1.308 @@ -833,7 +925,7 @@
   1.309  		{
   1.310  			continue;
   1.311  		}
   1.312 -		if (pExt->m_FullyLoaded)
   1.313 +		if (pExt->m_bFullyLoaded)
   1.314  		{
   1.315  			continue;
   1.316  		}
   1.317 @@ -891,7 +983,9 @@
   1.318  					if (!pExt->IsRunning(error, sizeof(error)))
   1.319  					{
   1.320  						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
   1.321 -					} else {
   1.322 +					}
   1.323 +					else
   1.324 +					{
   1.325  						IExtensionInterface *pAPI = pExt->GetAPI();
   1.326  						const char *name = pAPI->GetExtensionName();
   1.327  						const char *version = pAPI->GetExtensionVerString();
   1.328 @@ -903,7 +997,9 @@
   1.329  				}
   1.330  			}
   1.331  			return;
   1.332 -		} else if (strcmp(cmd, "info") == 0) {
   1.333 +		}
   1.334 +		else if (strcmp(cmd, "info") == 0)
   1.335 +		{
   1.336  			if (argcount < 4)
   1.337  			{
   1.338  				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   1.339 @@ -952,25 +1048,43 @@
   1.340  			{
   1.341  				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   1.342  				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
   1.343 -			} else {
   1.344 +			}
   1.345 +			else
   1.346 +			{
   1.347  				char error[255];
   1.348  				if (!pExt->IsRunning(error, sizeof(error)))
   1.349  				{
   1.350  					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   1.351  					g_RootMenu.ConsolePrint(" Loaded: Yes");
   1.352  					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
   1.353 -				} else {
   1.354 +				}
   1.355 +				else
   1.356 +				{
   1.357  					IExtensionInterface *pAPI = pExt->GetAPI();
   1.358  					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   1.359  					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
   1.360  					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
   1.361  					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
   1.362  					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
   1.363 -					g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no");
   1.364 +
   1.365 +					if (pExt->IsExternal())
   1.366 +					{
   1.367 +						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
   1.368 +					}
   1.369 +					else if (pAPI->IsMetamodExtension())
   1.370 +					{
   1.371 +						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
   1.372 +					}
   1.373 +					else
   1.374 +					{
   1.375 +						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
   1.376 +					}
   1.377  				}
   1.378  			}
   1.379  			return;
   1.380 -		} else if (strcmp(cmd, "unload") == 0) {
   1.381 +		}
   1.382 +		else if (strcmp(cmd, "unload") == 0)
   1.383 +		{
   1.384  			if (argcount < 4)
   1.385  			{
   1.386  				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
   1.387 @@ -1141,3 +1255,29 @@
   1.388  
   1.389  	return false;
   1.390  }
   1.391 +
   1.392 +IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
   1.393 +											const char *filepath,
   1.394 +											const char *filename,
   1.395 +											char *error,
   1.396 +											size_t maxlength)
   1.397 +{
   1.398 +	IExtension *pAlready;
   1.399 +	if ((pAlready=FindExtensionByFile(filename)) != NULL)
   1.400 +	{
   1.401 +		return pAlready;
   1.402 +	}
   1.403 +
   1.404 +	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
   1.405 +
   1.406 +	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   1.407 +	{
   1.408 +		pExt->Unload();
   1.409 +		delete pExt;
   1.410 +		return NULL;
   1.411 +	}
   1.412 +
   1.413 +	m_Libs.push_back(pExt);
   1.414 +
   1.415 +	return pExt;
   1.416 +}
     2.1 --- a/core/systems/ExtensionSys.h	Sun Oct 28 05:51:10 2007 +0000
     2.2 +++ b/core/systems/ExtensionSys.h	Sun Oct 28 08:02:24 2007 +0000
     2.3 @@ -59,13 +59,11 @@
     2.4  {
     2.5  	friend class CExtensionManager;
     2.6  public:
     2.7 -	CExtension(const char *filename, char *error, size_t maxlen);
     2.8 -	~CExtension();
     2.9 +	virtual ~CExtension();
    2.10  public: //IExtension
    2.11  	IExtensionInterface *GetAPI();
    2.12  	const char *GetFilename();
    2.13  	IdentityToken_t *GetIdentity();
    2.14 -	bool IsLoaded();
    2.15  	ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface);
    2.16  	bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface);
    2.17  	void FreeDependencyIterator(ITERATOR *iter);
    2.18 @@ -79,14 +77,20 @@
    2.19  	void RemovePlugin(IPlugin *pPlugin);
    2.20  	void MarkAllLoaded();
    2.21  	void AddLibrary(const char *library);
    2.22 -private:
    2.23 -	bool Load(char *error, size_t maxlength);
    2.24 -private:
    2.25 +public:
    2.26 +	virtual bool Load(char *error, size_t maxlength);
    2.27 +	virtual bool IsLoaded() =0;
    2.28 +	virtual void Unload() =0;
    2.29 +protected:
    2.30 +	void Initialize(const char *filename, const char *path);
    2.31 +	bool PerformAPICheck(char *error, size_t maxlength);
    2.32 +	void CreateIdentity();
    2.33 +	void DestroyIdentity();
    2.34 +protected:
    2.35  	IdentityToken_t *m_pIdentToken;
    2.36  	IExtensionInterface *m_pAPI;
    2.37  	String m_File;
    2.38  	String m_Path;
    2.39 -	ILibrary *m_pLib;
    2.40  	String m_Error;
    2.41  	List<IfaceInfo> m_Deps;			/** Dependencies */
    2.42  	List<IfaceInfo> m_ChildDeps;	/** Children who might depend on us */
    2.43 @@ -95,9 +99,33 @@
    2.44  	List<const sp_nativeinfo_t *> m_Natives;
    2.45  	List<WeakNative> m_WeakNatives;
    2.46  	List<String> m_Libraries;
    2.47 +	unsigned int unload_code;
    2.48 +	bool m_bFullyLoaded;
    2.49 +};
    2.50 +
    2.51 +class CLocalExtension : public CExtension
    2.52 +{
    2.53 +public:
    2.54 +	CLocalExtension(const char *filename);
    2.55 +public:
    2.56 +	bool Load(char *error, size_t maxlength);
    2.57 +	bool IsLoaded();
    2.58 +	void Unload();
    2.59 +	bool IsExternal();
    2.60 +private:
    2.61  	PluginId m_PlId;
    2.62 -	unsigned int unload_code;
    2.63 -	bool m_FullyLoaded;
    2.64 +	ILibrary *m_pLib;
    2.65 +};
    2.66 +
    2.67 +class CRemoteExtension : public CExtension
    2.68 +{
    2.69 +public:
    2.70 +	CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path);
    2.71 +public:
    2.72 +	bool Load(char *error, size_t maxlength);
    2.73 +	bool IsLoaded();
    2.74 +	void Unload();
    2.75 +	bool IsExternal();
    2.76  };
    2.77  
    2.78  class CExtensionManager : 
    2.79 @@ -114,12 +142,16 @@
    2.80  	void OnSourceModShutdown();
    2.81  public: //IExtensionManager
    2.82  	IExtension *LoadExtension(const char *path, 
    2.83 -		ExtensionLifetime lifetime, 
    2.84  		char *error,
    2.85  		size_t maxlength);
    2.86  	bool UnloadExtension(IExtension *pExt);
    2.87  	IExtension *FindExtensionByFile(const char *file);
    2.88  	IExtension *FindExtensionByName(const char *ext);
    2.89 +	IExtension *LoadExternal(IExtensionInterface *pInterface,
    2.90 +		const char *filepath,
    2.91 +		const char *filename,
    2.92 +		char *error,
    2.93 +		size_t maxlength);
    2.94  public: //IPluginsListener
    2.95  	void OnPluginDestroyed(IPlugin *plugin);
    2.96  public: //IRootConsoleCommand
     3.1 --- a/public/IExtensionSys.h	Sun Oct 28 05:51:10 2007 +0000
     3.2 +++ b/public/IExtensionSys.h	Sun Oct 28 08:02:24 2007 +0000
     3.3 @@ -65,7 +65,9 @@
     3.4  
     3.5  		/**
     3.6  		 * @brief Returns the filename of the extension, relative to the
     3.7 -		 * extension folder.
     3.8 +		 * extension folder.   If the extension is an "external" extension, 
     3.9 +		 * the file path is specified by the extension itself, and may be 
    3.10 +		 * arbitrary (not real).
    3.11  		 *
    3.12  		 * @return			A string containing the extension file name.
    3.13  		 */
    3.14 @@ -112,6 +114,15 @@
    3.15  		 * @return				True if extension is okay, false if not okay.
    3.16  		 */
    3.17  		virtual bool IsRunning(char *error, size_t maxlength) =0;
    3.18 +
    3.19 +		/**
    3.20 +		 * @brief Returns whether the extension is local (from the extensions 
    3.21 +		 * folder), or is from an external source (such as Metamod:Source).
    3.22 +		 *
    3.23 +		 * @return				True if from an external source, 
    3.24 +		 *						false if local to SourceMod.
    3.25 +		 */
    3.26 +		virtual bool IsExternal() =0;
    3.27  	};
    3.28  
    3.29  	/**
    3.30 @@ -142,10 +153,10 @@
    3.31  		 * @return			True if load should continue, false otherwise.
    3.32  		 */
    3.33  		virtual bool OnExtensionLoad(IExtension *me,
    3.34 -								  IShareSys *sys, 
    3.35 -								  char *error, 
    3.36 -								  size_t maxlength, 
    3.37 -								  bool late) =0;
    3.38 +			IShareSys *sys, 
    3.39 +			char *error, 
    3.40 +			size_t maxlength, 
    3.41 +			bool late) =0;
    3.42  
    3.43  		/**
    3.44  		 * @brief Called when the extension is about to be unloaded.
    3.45 @@ -166,8 +177,10 @@
    3.46  		virtual void OnExtensionPauseChange(bool pause) =0;
    3.47  
    3.48  		/**
    3.49 -		 * @brief Asks the extension whether it's safe to remove an external interface it's using.
    3.50 -		 * If it's not safe, return false, and the extension will be unloaded afterwards.
    3.51 +		 * @brief Asks the extension whether it's safe to remove an external 
    3.52 +		 * interface it's using.  If it's not safe, return false, and the 
    3.53 +		 * extension will be unloaded afterwards.
    3.54 +		 *
    3.55  		 * NOTE: It is important to also hook NotifyInterfaceDrop() in order to clean up resources.
    3.56  		 *
    3.57  		 * @param pInterface		Pointer to interface being dropped.
    3.58 @@ -199,27 +212,84 @@
    3.59  			return true;
    3.60  		}
    3.61  	public:
    3.62 +		/**
    3.63 +		 * @brief For extensions loaded through SourceMod, this should return true 
    3.64 +		 * if the extension needs to attach to Metamod:Source.  If the extension 
    3.65 +		 * is loaded through Metamod:Source, and uses SourceMod optionally, it must 
    3.66 +		 * return false.
    3.67 +		 *
    3.68 +		 * @return					True if Metamod:Source is needed.
    3.69 +		 */
    3.70  		virtual bool IsMetamodExtension() =0;
    3.71 +
    3.72 +		/**
    3.73 +		 * @brief Must return a string containing the extension's short name.
    3.74 +		 *
    3.75 +		 * @return					String containing extension name.
    3.76 +		 */
    3.77  		virtual const char *GetExtensionName() =0;
    3.78 +
    3.79 +		/**
    3.80 +		 * @brief Must return a string containing the extension's URL.
    3.81 +		 *
    3.82 +		 * @return					String containing extension URL.
    3.83 +		 */
    3.84  		virtual const char *GetExtensionURL() =0;
    3.85 +
    3.86 +		/**
    3.87 +		 * @brief Must return a string containing a short identifier tag.
    3.88 +		 *
    3.89 +		 * @return					String containing extension tag.
    3.90 +		 */
    3.91  		virtual const char *GetExtensionTag() =0;
    3.92 +
    3.93 +		/**
    3.94 +		 * @brief Must return a string containing a short author identifier.
    3.95 +		 *
    3.96 +		 * @return					String containing extension author.
    3.97 +		 */
    3.98  		virtual const char *GetExtensionAuthor() =0;
    3.99 +
   3.100 +		/**
   3.101 +		 * @brief Must return a string containing version information.
   3.102 +		 *
   3.103 +		 * Any version string format can be used, however, SourceMod 
   3.104 +		 * makes a special guarantee version numbers in the form of 
   3.105 +		 * A.B.C.D will always be fully displayed, where:
   3.106 +		 * 
   3.107 +		 * A is a major version number of at most one digit.
   3.108 +		 * B is a minor version number of at most two digits.
   3.109 +		 * C is a minor version number of at most two digits.
   3.110 +		 * D is a build number of at most 5 digits.
   3.111 +		 *
   3.112 +		 * Thus, thirteen characters of display is guaranteed.
   3.113 +		 *
   3.114 +		 * @return					String containing extension version.
   3.115 +		 */
   3.116  		virtual const char *GetExtensionVerString() =0;
   3.117 +
   3.118 +		/**
   3.119 +		 * @brief Must return a string containing description text.
   3.120 +		 *
   3.121 +		 * The description text may be longer than the other identifiers, 
   3.122 +		 * as it is only displayed when viewing one extension at a time.
   3.123 +		 * However, it should not have newlines, or any other characters 
   3.124 +		 * which would otherwise disrupt the display pattern.
   3.125 +		 * 
   3.126 +		 * @return					String containing extension description.
   3.127 +		 */
   3.128  		virtual const char *GetExtensionDescription() =0;
   3.129 +
   3.130 +		/**
   3.131 +		 * @brief Must return a string containing the compilation date.
   3.132 +		 *
   3.133 +		 * @return					String containing the compilation date.
   3.134 +		 */
   3.135  		virtual const char *GetExtensionDateString() =0;
   3.136  	};
   3.137  
   3.138  	#define SMINTERFACE_EXTENSIONMANAGER_NAME			"IExtensionManager"
   3.139 -	#define SMINTERFACE_EXTENSIONMANAGER_VERSION		1
   3.140 -
   3.141 -	/**
   3.142 -	 * @brief Not currently used.
   3.143 -	 */
   3.144 -	enum ExtensionLifetime
   3.145 -	{
   3.146 -		ExtLifetime_Forever,			//Extension will never be unloaded automatically
   3.147 -		ExtLifetime_Map,				//Extension will be unloaded at the end of the map
   3.148 -	};
   3.149 +	#define SMINTERFACE_EXTENSIONMANAGER_VERSION		2
   3.150  
   3.151  	/**
   3.152  	 * @brief Manages the loading/unloading of extensions.
   3.153 @@ -235,26 +305,66 @@
   3.154  		{
   3.155  			return SMINTERFACE_EXTENSIONMANAGER_VERSION;
   3.156  		}
   3.157 +		virtual bool IsVersionCompatible(unsigned int version)
   3.158 +		{
   3.159 +			if (version < 2)
   3.160 +			{
   3.161 +				return false;
   3.162 +			}
   3.163 +			return SMInterface::IsVersionCompatible(version);
   3.164 +		}
   3.165  	public:
   3.166  		/**
   3.167  		 * @brief Loads a extension into the extension system.
   3.168  		 *
   3.169 -		 * @param path		Path to extension file, relative to the extensions folder.
   3.170 -		 * @param lifetime	Lifetime of the extension (currently ignored).
   3.171 -		 * @param error		Error buffer.
   3.172 -		 * @param maxlength	Maximum error buffer length.
   3.173 -		 * @return			New IExtension on success, NULL on failure.
   3.174 +		 * @param path			Path to extension file, relative to the 
   3.175 +		 *						extensions folder.
   3.176 +		 * @param lifetime		Lifetime of the extension (currently ignored).
   3.177 +		 * @param error			Error buffer.
   3.178 +		 * @param maxlength		Maximum error buffer length.
   3.179 +		 * @return				New IExtension on success, NULL on failure.
   3.180 +		 *						If	NULL is returned, the error buffer will be 
   3.181 +		 *						filled with a null-terminated string.
   3.182  		 */
   3.183  		virtual IExtension *LoadExtension(const char *path, 
   3.184 -									ExtensionLifetime lifetime, 
   3.185 -									char *error,
   3.186 -									size_t maxlength) =0;
   3.187 +			char *error,
   3.188 +			size_t maxlength) =0;
   3.189  
   3.190  		/**
   3.191 -		 * @brief Attempts to unload a module.
   3.192 +		 * @brief Loads an extension into the extension system, directly, 
   3.193 +		 * as an external extension.  
   3.194 +		 *
   3.195 +		 * The extension receives all normal callbacks.  However, it is 
   3.196 +		 * never opened via LoadLibrary/dlopen or closed via FreeLibrary
   3.197 +		 * or dlclose.
   3.198  		 *
   3.199 -		 * @param pExt		IExtension pointer.
   3.200 -		 * @return			True if successful, false otherwise.
   3.201 +		 * @param pInterface	Pointer to an IExtensionInterface instance.
   3.202 +		 * @param filepath		Absolute path to the extension's file.
   3.203 +		 * @param filename		Name to use to uniquely identify the extension. 
   3.204 +		 *						The name should be generic, without any 
   3.205 +		 *						platform-specific suffices.  For example, 
   3.206 +		 *						sdktools.ext instead of sdktools.ext.so.
   3.207 +		 *						This filename is used to detect if the 
   3.208 +		 *						extension is already loaded, and to verify 
   3.209 +		 *						plugins that require the same extension.
   3.210 +		 * @param error			Buffer to store error message.
   3.211 +		 * @param maxlength		Maximum size of the error buffer.
   3.212 +		 * @return				IExtension pointer on success, NULL on failure.
   3.213 +		 *						If NULL is returned, the error buffer will be 
   3.214 +		 *						filled with a null-terminated string.
   3.215 +		 */
   3.216 +		virtual IExtension *LoadExternal(IExtensionInterface *pInterface,
   3.217 +			const char *filepath,
   3.218 +			const char *filename,
   3.219 +			char *error,
   3.220 +			size_t maxlength) =0;
   3.221 +
   3.222 +		/**
   3.223 +		 * @brief Attempts to unload an extension.  External extensions must 
   3.224 +		 * call this before unloading.
   3.225 +		 *
   3.226 +		 * @param pExt			IExtension pointer.
   3.227 +		 * @return				True if successful, false otherwise.
   3.228  		 */
   3.229  		virtual bool UnloadExtension(IExtension *pExt) =0;
   3.230  	};