core/systems/ExtensionSys.cpp
author Borja Ferrer <faluco@alliedmods.net>
Sun Jul 15 01:04:34 2007 +0000 (2007-07-15)
changeset 1112 152ceef17866
parent 1089 275107f9ca95
child 1121 92222625d14c
permissions -rw-r--r--
added amb292, optional natives
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <stdlib.h>
    16 #include "ExtensionSys.h"
    17 #include "LibrarySys.h"
    18 #include "ShareSys.h"
    19 #include "Logger.h"
    20 #include "sourcemm_api.h"
    21 #include "sm_srvcmds.h"
    22 #include "sm_stringutil.h"
    23 
    24 CExtensionManager g_Extensions;
    25 IdentityType_t g_ExtType;
    26 
    27 CExtension::CExtension(const char *filename, char *error, size_t maxlength)
    28 {
    29 	m_File.assign(filename);
    30 	m_pAPI = NULL;
    31 	m_pIdentToken = NULL;
    32 	m_PlId = 0;
    33 	unload_code = 0;
    34 	m_FullyLoaded = false;
    35 
    36 	char path[PLATFORM_MAX_PATH];
    37 	g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
    38 	m_Path.assign(path);
    39 
    40 	m_pLib = NULL;
    41 }
    42 
    43 bool CExtension::Load(char *error, size_t maxlength)
    44 {
    45 	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
    46 
    47 	if (m_pLib == NULL)
    48 	{
    49 		return false;
    50 	}
    51 
    52 	typedef IExtensionInterface *(*GETAPI)();
    53 	GETAPI pfnGetAPI = NULL;
    54 
    55 	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
    56 	{
    57 		m_pLib->CloseLibrary();
    58 		m_pLib = NULL;
    59 		snprintf(error, maxlength, "Unable to find extension entry point");
    60 		return false;
    61 	}
    62 
    63 	m_pAPI = pfnGetAPI();
    64 	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
    65 	{
    66 		m_pLib->CloseLibrary();
    67 		m_pLib = NULL;
    68 		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
    69 		return false;
    70 	}
    71 
    72 	if (m_pAPI->IsMetamodExtension())
    73 	{
    74 		bool already;
    75 		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
    76 	}
    77 
    78 	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
    79 
    80 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
    81 	{
    82 		if (m_pAPI->IsMetamodExtension())
    83 		{
    84 			if (m_PlId)
    85 			{
    86 				char dummy[255];
    87 				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
    88 				m_PlId = 0;
    89 			}
    90 		}
    91 		m_pAPI = NULL;
    92 		m_pLib->CloseLibrary();
    93 		m_pLib = NULL;
    94 		g_ShareSys.DestroyIdentity(m_pIdentToken);
    95 		m_pIdentToken = NULL;
    96 		return false;
    97 	} else {
    98 		/* Check if we're past load time */
    99 		if (!g_SourceMod.IsMapLoading())
   100 		{
   101 			m_pAPI->OnExtensionsAllLoaded();
   102 		}
   103 	}
   104 
   105 	return true;
   106 }
   107 
   108 CExtension::~CExtension()
   109 {
   110 	if (m_pAPI)
   111 	{
   112 		m_pAPI->OnExtensionUnload();
   113 		if (m_PlId)
   114 		{
   115 			g_pMMPlugins->Unload(m_PlId, true, NULL, 0);
   116 		}
   117 	}
   118 
   119 	if (m_pIdentToken)
   120 	{
   121 		g_ShareSys.DestroyIdentity(m_pIdentToken);
   122 	}
   123 
   124 	if (m_pLib)
   125 	{
   126 		m_pLib->CloseLibrary();
   127 	}
   128 
   129 	List<WeakNative *>::iterator iter;
   130 	for (iter=m_WeakNatives.begin(); iter!=m_WeakNatives.end(); iter++)
   131 	{
   132 		delete (*iter);
   133 	}
   134 	m_WeakNatives.clear();
   135 }
   136 
   137 void CExtension::MarkAllLoaded()
   138 {
   139 	assert(m_FullyLoaded == false);
   140 	if (!m_FullyLoaded)
   141 	{
   142 		m_FullyLoaded = true;
   143 		m_pAPI->OnExtensionsAllLoaded();
   144 	}
   145 }
   146 
   147 void CExtension::AddPlugin(IPlugin *pPlugin)
   148 {
   149 	/* Unfortunately we have to do this :( */
   150 	if (m_Plugins.find(pPlugin) != m_Plugins.end())
   151 	{
   152 		m_Plugins.push_back(pPlugin);
   153 	}
   154 }
   155 
   156 void CExtension::RemovePlugin(IPlugin *pPlugin)
   157 {
   158 	m_Plugins.remove(pPlugin);
   159 }
   160 
   161 void CExtension::SetError(const char *error)
   162 {
   163 	m_Error.assign(error);
   164 }
   165 
   166 IExtensionInterface *CExtension::GetAPI()
   167 {
   168 	return m_pAPI;
   169 }
   170 
   171 const char *CExtension::GetFilename()
   172 {
   173 	return m_File.c_str();
   174 }
   175 
   176 IdentityToken_t *CExtension::GetIdentity()
   177 {
   178 	return m_pIdentToken;
   179 }
   180 
   181 bool CExtension::IsLoaded()
   182 {
   183 	return (m_pLib != NULL);
   184 }
   185 
   186 void CExtension::AddDependency(IfaceInfo *pInfo)
   187 {
   188 	m_Deps.push_back(*pInfo);
   189 }
   190 
   191 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
   192 {
   193 	List<IfaceInfo>::iterator iter = m_Deps.begin();
   194 
   195 	if (iter == m_Deps.end())
   196 	{
   197 		return NULL;
   198 	}
   199 
   200 	if (pOwner)
   201 	{
   202 		*pOwner = (*iter).owner;
   203 	}
   204 	if (pInterface)
   205 	{
   206 		*pInterface = (*iter).iface;
   207 	}
   208 
   209 	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
   210 
   211 	return (ITERATOR *)pIter;
   212 }
   213 
   214 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
   215 {
   216 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   217 	List<IfaceInfo>::iterator _iter;
   218 
   219 	if (_iter == m_Deps.end())
   220 	{
   221 		return false;
   222 	}
   223 
   224 	_iter++;
   225 
   226 	if (pOwner)
   227 	{
   228 		*pOwner = (*_iter).owner;
   229 	}
   230 	if (pInterface)
   231 	{
   232 		*pInterface = (*_iter).iface;
   233 	}
   234 
   235 	*pIter = _iter;
   236 
   237 	if (_iter == m_Deps.end())
   238 	{
   239 		return false;
   240 	}
   241 
   242 	return true;
   243 }
   244 
   245 void CExtension::FreeDependencyIterator(ITERATOR *iter)
   246 {
   247 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   248 
   249 	delete pIter;
   250 }
   251 
   252 void CExtension::AddInterface(SMInterface *pInterface)
   253 {
   254 	m_Interfaces.push_back(pInterface);
   255 }
   256 
   257 bool CExtension::IsRunning(char *error, size_t maxlength)
   258 {
   259 	if (!IsLoaded())
   260 	{
   261 		if (error)
   262 		{
   263 			snprintf(error, maxlength, "%s", m_Error.c_str());
   264 		}
   265 		return false;
   266 	}
   267 
   268 	return m_pAPI->QueryRunning(error, maxlength);
   269 }
   270 
   271 /*********************
   272  * EXTENSION MANAGER *
   273  *********************/
   274 
   275 void CExtensionManager::OnSourceModAllInitialized()
   276 {
   277 	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
   278 	g_PluginSys.AddPluginsListener(this);
   279 	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
   280 	g_ShareSys.AddInterface(NULL, this);
   281 }
   282 
   283 void CExtensionManager::OnSourceModShutdown()
   284 {
   285 	g_RootMenu.RemoveRootConsoleCommand("exts", this);
   286 	g_ShareSys.DestroyIdentType(g_ExtType);
   287 }
   288 
   289 void CExtensionManager::Shutdown()
   290 {
   291 	List<CExtension *>::iterator iter;
   292 
   293 	while ((iter = m_Libs.begin()) != m_Libs.end())
   294 	{
   295 		UnloadExtension((*iter));
   296 	}
   297 }
   298 
   299 void CExtensionManager::TryAutoload()
   300 {
   301 	char path[PLATFORM_MAX_PATH];
   302 
   303 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
   304 
   305 	IDirectory *pDir = g_LibSys.OpenDirectory(path);
   306 	if (!pDir)
   307 	{
   308 		return;
   309 	}
   310 
   311 	const char *lfile;
   312 	size_t len;
   313 	while (pDir->MoreFiles())
   314 	{
   315 		if (pDir->IsEntryDirectory())
   316 		{
   317 			pDir->NextEntry();
   318 			continue;
   319 		}
   320 
   321 		lfile = pDir->GetEntryName();
   322 		len = strlen(lfile);
   323 		if (len <= 9) /* size of ".autoload" */
   324 		{
   325 			pDir->NextEntry();
   326 			continue;
   327 		}
   328 
   329 		if (strcmp(&lfile[len - 9], ".autoload") != 0)
   330 		{
   331 			pDir->NextEntry();
   332 			continue;
   333 		}
   334 
   335 		char file[PLATFORM_MAX_PATH];
   336 		len = UTIL_Format(file, sizeof(file), "%s", lfile);
   337 		strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT);
   338 
   339 		LoadAutoExtension(file);
   340 		
   341 		pDir->NextEntry();
   342 	}
   343 }
   344 
   345 IExtension *CExtensionManager::LoadAutoExtension(const char *path)
   346 {
   347 	if (!strstr(path, "." PLATFORM_LIB_EXT))
   348 	{
   349 		char newpath[PLATFORM_MAX_PATH];
   350 		snprintf(newpath, PLATFORM_MAX_PATH, "%s.%s", path, PLATFORM_LIB_EXT);
   351 		return LoadAutoExtension(newpath);
   352 	}
   353 
   354 	IExtension *pAlready;
   355 	if ((pAlready=FindExtensionByFile(path)) != NULL)
   356 	{
   357 		return pAlready;
   358 	}
   359 
   360 	char error[256];
   361 	CExtension *p = new CExtension(path, error, sizeof(error));
   362 
   363 	/* We put us in the list beforehand so extensions that check for each other
   364 	 * won't recursively load each other.
   365 	 */
   366 	m_Libs.push_back(p);
   367 
   368 	if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
   369 	{
   370 		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
   371 		p->SetError(error);
   372 	}
   373 
   374 	return p;
   375 }
   376 
   377 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
   378 {
   379 	List<CExtension *>::iterator iter;
   380 	CExtension *pExt;
   381 
   382 	if (!strstr(file, "." PLATFORM_LIB_EXT))
   383 	{
   384 		char path[PLATFORM_MAX_PATH];
   385 		snprintf(path, PLATFORM_MAX_PATH, "%s.%s", file, PLATFORM_LIB_EXT);
   386 		return FindExtensionByFile(path);
   387 	}
   388 
   389 	/* Make sure the file direction is right */
   390 	char path[PLATFORM_MAX_PATH];
   391 	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   392 
   393 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   394 	{
   395 		pExt = (*iter);
   396 		if (strcmp(pExt->GetFilename(), path) == 0)
   397 		{
   398 			return pExt;
   399 		}
   400 	}
   401 
   402 	return NULL;
   403 }
   404 
   405 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
   406 {
   407 	List<CExtension *>::iterator iter;
   408 	CExtension *pExt;
   409 	IExtensionInterface *pAPI;
   410 	const char *name;
   411 
   412 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   413 	{
   414 		pExt = (*iter);
   415 		if (!pExt->IsLoaded())
   416 		{
   417 			continue;
   418 		}
   419 		if ((pAPI = pExt->GetAPI()) == NULL)
   420 		{
   421 			continue;
   422 		}
   423 		name = pAPI->GetExtensionName();
   424 		if (!name)
   425 		{
   426 			continue;
   427 		}
   428 		if (strcmp(name, ext) == 0)
   429 		{
   430 			return pExt;
   431 		}
   432 	}
   433 
   434 	return NULL;
   435 }
   436 
   437 IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t maxlength)
   438 {
   439 	IExtension *pAlready;
   440 	if ((pAlready=FindExtensionByFile(file)) != NULL)
   441 	{
   442 		return pAlready;
   443 	}
   444 
   445 	CExtension *pExt = new CExtension(file, error, maxlength);
   446 
   447 	/* :NOTE: lifetime is currently ignored */
   448 
   449 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   450 	{
   451 		delete pExt;
   452 		return NULL;
   453 	}
   454 
   455 	/* :TODO: do QueryRunning check if the map is loaded */
   456 
   457 	m_Libs.push_back(pExt);
   458 
   459 	return pExt;
   460 }
   461 
   462 void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo)
   463 {
   464 	CExtension *pExt = (CExtension *)pOwner;
   465 
   466 	pExt->AddDependency(pInfo);
   467 }
   468 
   469 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   470 {
   471 	CExtension *pExt = (CExtension *)pOwner;
   472 
   473 	pExt->AddInterface(pInterface);
   474 }
   475 
   476 void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
   477 {
   478 	CExtension *pExt = (CExtension *)pParent;
   479 
   480 	pExt->AddPlugin(pPlugin);
   481 }
   482 
   483 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   484 {
   485 	List<CExtension *>::iterator iter;
   486 
   487 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   488 	{
   489 		(*iter)->RemovePlugin(plugin);
   490 	}
   491 }
   492 
   493 CExtension *CExtensionManager::FindByOrder(unsigned int num)
   494 {
   495 	if (num < 1 || num > m_Libs.size())
   496 	{
   497 		return NULL;
   498 	}
   499 
   500 	List<CExtension *>::iterator iter = m_Libs.begin();
   501 
   502 	while (iter != m_Libs.end())
   503 	{
   504 		if (--num == 0)
   505 		{
   506 			return (*iter);
   507 		}
   508 		iter++;
   509 	}
   510 
   511 	return NULL;
   512 }
   513 
   514 void CExtensionManager::BindAllNativesToPlugin(IPlugin *pPlugin)
   515 {
   516 	List<CExtension *>::iterator iter;
   517 	CExtension *pExt;
   518 	IPluginContext *pContext = pPlugin->GetBaseContext();
   519 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   520 	{
   521 		pExt = (*iter);
   522 		if (!pExt->IsLoaded())
   523 		{
   524 			continue;
   525 		}
   526 		if (!pExt->m_Natives.size())
   527 		{
   528 			continue;
   529 		}
   530 		bool set = false;
   531 		List<const sp_nativeinfo_t *>::iterator n_iter;
   532 		const sp_nativeinfo_t *natives;
   533 		for (n_iter = pExt->m_Natives.begin();
   534 			 n_iter != pExt->m_Natives.end();
   535 			 n_iter++)
   536 		{
   537 			natives = (*n_iter);
   538 			uint32_t idx;
   539 			unsigned int i=0;
   540 			while (natives[i].func != NULL && natives[i].name != NULL)
   541 			{
   542 				if (pContext->BindNative(&natives[i]) == SP_ERROR_NONE)
   543 				{
   544 					pContext->FindNativeByName(natives[i].name, &idx);
   545 					if (!(pContext->GetContext()->natives[idx].flags & SP_NTVFLAG_OPTIONAL))
   546 					{
   547 						set = true;
   548 					} else {
   549 						WeakNative *wkn = new WeakNative((CPlugin *)pPlugin, idx);
   550 						pExt->m_WeakNatives.push_back(wkn);
   551 					}
   552 				}
   553 				i++;
   554 			}
   555 		}
   556 		if (set && (pExt->m_Plugins.find(pPlugin) == pExt->m_Plugins.end()))
   557 		{
   558 			/* For now, mark this plugin as a requirement.  Later we'll introduce native filtering. */
   559 			pExt->m_Plugins.push_back(pPlugin);
   560 		}
   561 	}
   562 }
   563 
   564 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   565 {
   566 	if (!_pExt)
   567 	{
   568 		return false;
   569 	}
   570 
   571 	CExtension *pExt = (CExtension *)_pExt;
   572 
   573 	if (m_Libs.find(pExt) == m_Libs.end())
   574 	{
   575 		return false;
   576 	}
   577 
   578 	/* First remove us from internal lists */
   579 	g_ShareSys.RemoveInterfaces(_pExt);
   580 	m_Libs.remove(pExt);
   581 
   582 	List<CExtension *> UnloadQueue;
   583 
   584 	/* Handle dependencies */
   585 	if (pExt->IsLoaded())
   586 	{
   587 		/* Unload any dependent plugins */
   588 		List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
   589 		while (p_iter != pExt->m_Plugins.end())
   590 		{
   591 			/* We have to manually unlink ourselves here, since we're no longer being managed */
   592 			g_PluginSys.UnloadPlugin((*p_iter));
   593 			p_iter = pExt->m_Plugins.erase(p_iter);
   594 		}
   595 
   596 		/* Unbound weak natives */
   597 		WeakNative *wkn;
   598 		List<WeakNative *>::iterator wkn_iter;
   599 		for (wkn_iter=pExt->m_WeakNatives.begin(); wkn_iter!=pExt->m_WeakNatives.end(); wkn_iter++)
   600 		{
   601 			wkn = (*wkn_iter);
   602 			sp_context_t *ctx = wkn->pl->GetContext();
   603 			ctx->natives[wkn->idx].status = SP_NATIVE_UNBOUND;
   604 		}
   605 
   606 		/* Notify and/or unload all dependencies */
   607 		List<CExtension *>::iterator c_iter;
   608 		CExtension *pDep;
   609 		IExtensionInterface *pAPI;
   610 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   611 		{
   612 			pDep = (*c_iter);
   613 			if ((pAPI=pDep->GetAPI()) == NULL)
   614 			{
   615 				continue;
   616 			}
   617 			if (pDep == pExt)
   618 			{
   619 				continue;
   620 			}
   621 			/* Now, get its dependency list */
   622 			bool dropped = false;
   623 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   624 			while (i_iter != pDep->m_Deps.end())
   625 			{
   626 				if ((*i_iter).owner == _pExt)
   627 				{
   628 					if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface))
   629 					{
   630 						dropped = true;
   631 					}
   632 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   633 					i_iter = pDep->m_Deps.erase(i_iter);
   634 				} else {
   635 					i_iter++;
   636 				}
   637 			}
   638 		}
   639 	}
   640 
   641 	delete pExt;
   642 
   643 	List<CExtension *>::iterator iter;
   644 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   645 	{
   646 		/* NOTE: This is safe because the unload function backs out of anything not present */
   647 		UnloadExtension((*iter));
   648 	}
   649 
   650 	return true;
   651 }
   652 
   653 void CExtensionManager::AddNatives(IExtension *pOwner, const sp_nativeinfo_t *natives)
   654 {
   655 	CExtension *pExt = (CExtension *)pOwner;
   656 
   657 	pExt->m_Natives.push_back(natives);
   658 }
   659 
   660 void CExtensionManager::MarkAllLoaded()
   661 {
   662 	List<CExtension *>::iterator iter;
   663 	CExtension *pExt;
   664 
   665 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   666 	{
   667 		pExt = (*iter);
   668 		if (!pExt->IsLoaded())
   669 		{
   670 			continue;
   671 		}
   672 		if (pExt->m_FullyLoaded)
   673 		{
   674 			continue;
   675 		}
   676 		pExt->MarkAllLoaded();
   677 	}
   678 }
   679 
   680 void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
   681 {
   682 	/* This function doesn't really need to do anything now.  We make sure the 
   683 	 * other extension is loaded, but handling of dependencies is really done 
   684 	 * by the interface fetcher.
   685 	 */
   686 	if (required || autoload)
   687 	{
   688 		LoadAutoExtension(file);
   689 	}
   690 }
   691 
   692 void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argcount)
   693 {
   694 	if (argcount >= 3)
   695 	{
   696 		const char *cmd = g_RootMenu.GetArgument(2);
   697 		if (strcmp(cmd, "list") == 0)
   698 		{
   699 			List<CExtension *>::iterator iter;
   700 			CExtension *pExt;
   701 			unsigned int num = 1;
   702 			switch (m_Libs.size())
   703 			{
   704 			case 1:
   705 				{
   706 					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
   707 					break;
   708 				}
   709 			case 0:
   710 				{
   711 					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   712 					break;
   713 				}
   714 			default:
   715 				{
   716 					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
   717 					break;
   718 				}
   719 			}
   720 			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
   721 			{
   722 				pExt = (*iter);
   723 				if (pExt->IsLoaded())
   724 				{
   725 					char error[255];
   726 					if (!pExt->IsRunning(error, sizeof(error)))
   727 					{
   728 						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
   729 					} else {
   730 						IExtensionInterface *pAPI = pExt->GetAPI();
   731 						const char *name = pAPI->GetExtensionName();
   732 						const char *version = pAPI->GetExtensionVerString();
   733 						const char *descr = pAPI->GetExtensionDescription();
   734 						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
   735 					}
   736 				} else {
   737 					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
   738 				}
   739 			}
   740 			return;
   741 		} else if (strcmp(cmd, "info") == 0) {
   742 			if (argcount < 4)
   743 			{
   744 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   745 				return;
   746 			}
   747 
   748 			const char *sId = g_RootMenu.GetArgument(3);
   749 			unsigned int id = atoi(sId);
   750 			if (id <= 0)
   751 			{
   752 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   753 				return;
   754 			}
   755 
   756 			if (m_Libs.size() == 0)
   757 			{
   758 				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   759 				return;
   760 			}
   761 
   762 			if (id > m_Libs.size())
   763 			{
   764 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   765 				return;
   766 			}
   767 
   768 			List<CExtension *>::iterator iter = m_Libs.begin();
   769 			CExtension *pExt = NULL;
   770 			while (iter != m_Libs.end())
   771 			{
   772 				if (--id == 0)
   773 				{
   774 					pExt = (*iter);
   775 					break;
   776 				}
   777 				iter++;
   778 			}
   779 			/* This should never happen */
   780 			if (!pExt)
   781 			{
   782 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   783 				return;
   784 			}
   785 
   786 			if (!pExt->IsLoaded())
   787 			{
   788 				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   789 				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
   790 			} else {
   791 				char error[255];
   792 				if (!pExt->IsRunning(error, sizeof(error)))
   793 				{
   794 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   795 					g_RootMenu.ConsolePrint(" Loaded: Yes");
   796 					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
   797 				} else {
   798 					IExtensionInterface *pAPI = pExt->GetAPI();
   799 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   800 					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
   801 					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
   802 					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
   803 					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
   804 					g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no");
   805 				}
   806 			}
   807 			return;
   808 		} else if (strcmp(cmd, "unload") == 0) {
   809 			if (argcount < 4)
   810 			{
   811 				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
   812 				return;
   813 			}
   814 
   815 			const char *arg = g_RootMenu.GetArgument(3);
   816 			unsigned int num = atoi(arg);
   817 			CExtension *pExt = FindByOrder(num);
   818 
   819 			if (!pExt)
   820 			{
   821 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
   822 				return;
   823 			}
   824 
   825 			if (argcount > 4 && pExt->unload_code)
   826 			{
   827 				const char *unload = g_RootMenu.GetArgument(4);
   828 				if (pExt->unload_code == (unsigned)atoi(unload))
   829 				{
   830 					char filename[PLATFORM_MAX_PATH];
   831 					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
   832 					UnloadExtension(pExt);
   833 					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
   834 				} else {
   835 					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
   836 				}
   837 				return;
   838 			}
   839 
   840 			if (!pExt->IsLoaded() 
   841 				|| (!pExt->m_Deps.size() && !pExt->m_Plugins.size()))
   842 			{
   843 				char filename[PLATFORM_MAX_PATH];
   844 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
   845 				UnloadExtension(pExt);
   846 				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
   847 				return;
   848 			} else {
   849 				List<IPlugin *> plugins;
   850 				if (pExt->m_Deps.size())
   851 				{
   852 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
   853 					List<CExtension *>::iterator iter;
   854 					CExtension *pOther;
   855 					/* Get list of all extensions */
   856 					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   857 					{
   858 						List<IfaceInfo>::iterator i_iter;
   859 						pOther = (*iter);
   860 						if (!pOther->IsLoaded() || pOther == pExt)
   861 						{
   862 							continue;
   863 						}
   864 						/* Get their dependencies */
   865 						for (i_iter=pOther->m_Deps.begin();
   866 							 i_iter!=pOther->m_Deps.end();
   867 							 i_iter++)
   868 						{
   869 							/* Is this dependency to us? */
   870 							if ((*i_iter).owner != pExt)
   871 							{
   872 								continue;
   873 							}
   874 							/* Will our dependent care? */
   875 							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
   876 							{
   877 								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
   878 								/* Add to plugin unload list */
   879 								List<IPlugin *>::iterator p_iter;
   880 								for (p_iter=pOther->m_Plugins.begin();
   881 									 p_iter!=pOther->m_Plugins.end();
   882 									 p_iter++)
   883 								{
   884 									if (plugins.find((*p_iter)) == plugins.end())
   885 									{
   886 										plugins.push_back((*p_iter));
   887 									}
   888 								}
   889 							}
   890 						}
   891 					}
   892 				}
   893 				if (pExt->m_Plugins.size())
   894 				{
   895 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
   896 					List<IPlugin *>::iterator iter;
   897 					IPlugin *pPlugin;
   898 					for (iter = pExt->m_Plugins.begin(); iter != pExt->m_Plugins.end(); iter++)
   899 					{
   900 						pPlugin = (*iter);
   901 						if (plugins.find(pPlugin) == plugins.end())
   902 						{
   903 							plugins.push_back(pPlugin);
   904 						}
   905 					}
   906 					for (iter = plugins.begin(); iter != plugins.end(); iter++)
   907 					{
   908 						pPlugin = (*iter);
   909 						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
   910 					}
   911 				}
   912 				srand(static_cast<int>(time(NULL)));
   913 				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
   914 				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
   915 				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
   916 
   917 				return;
   918 			}
   919 		}
   920 	}
   921 
   922 	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
   923 	g_RootMenu.DrawGenericOption("info", "Extra extension information");
   924 	g_RootMenu.DrawGenericOption("list", "List extensions");
   925 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
   926 }
   927 
   928 CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
   929 {
   930 	if (ptr->type == g_ExtType)
   931 	{
   932 		return (CExtension *)(ptr->ptr);
   933 	}
   934 
   935 	return NULL;
   936 }