core/systems/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Mon May 26 07:51:11 2008 +0000 (2008-05-26)
changeset 2086 debb2ab2c782
parent 1998 cf2fdf09195a
child 2134 f1d8a7cd3c94
permissions -rw-r--r--
rewrite of the native and dependency binding/interaction code. this will ease the transition for native overrides, and greatly simplifies most of the logic. all native binding code now takes place almost entirely in ShareSys, and PluginSys supplements this logic where appropriate. extensionsys has been cleaned up
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdlib.h>
    33 #include "ExtensionSys.h"
    34 #include "LibrarySys.h"
    35 #include "ShareSys.h"
    36 #include "Logger.h"
    37 #include "sourcemm_api.h"
    38 #include "sm_srvcmds.h"
    39 #include "sm_stringutil.h"
    40 
    41 CExtensionManager g_Extensions;
    42 IdentityType_t g_ExtType;
    43 
    44 void CExtension::Initialize(const char *filename, const char *path)
    45 {
    46 	m_pAPI = NULL;
    47 	m_pIdentToken = NULL;
    48 	unload_code = 0;
    49 	m_bFullyLoaded = false;
    50 	m_File.assign(filename);
    51 	m_Path.assign(path);
    52 }
    53 
    54 CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path)
    55 {
    56 	Initialize(filename, path);
    57 	m_pAPI = pAPI;
    58 }
    59 
    60 CLocalExtension::CLocalExtension(const char *filename)
    61 {
    62 	m_PlId = 0;
    63 	m_pLib = NULL;
    64 
    65 	char path[PLATFORM_MAX_PATH];
    66 
    67 	/* First see if there is an engine specific build! */
    68 	g_SourceMod.BuildPath(Path_SM, 
    69 		path, 
    70 		PLATFORM_MAX_PATH, 
    71 #if defined METAMOD_PLAPI_VERSION
    72 #if defined ORANGEBOX_BUILD
    73 		"extensions/auto.2.ep2/%s",
    74 #else
    75 		"extensions/auto.2.ep1/%s",
    76 #endif //ORANGEBOX_BUILD
    77 #else  //METAMOD_PLAPI_VERSION
    78 		"extensions/auto.1.ep1/%s",
    79 #endif //METAMOD_PLAPI_VERSION
    80 		filename);
    81 
    82 	/* Try the "normal" version */
    83 	if (!g_LibSys.IsPathFile(path))
    84 	{
    85 		g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
    86 	}
    87 
    88 	Initialize(filename, path);
    89 }
    90 
    91 bool CRemoteExtension::Load(char *error, size_t maxlength)
    92 {
    93 	if (!PerformAPICheck(error, maxlength))
    94 	{
    95 		m_pAPI = NULL;
    96 		return false;
    97 	}
    98 
    99 	if (!CExtension::Load(error, maxlength))
   100 	{
   101 		m_pAPI = NULL;
   102 		return false;
   103 	}
   104 
   105 	return true;
   106 }
   107 
   108 bool CLocalExtension::Load(char *error, size_t maxlength)
   109 {
   110 	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
   111 
   112 	if (m_pLib == NULL)
   113 	{
   114 		return false;
   115 	}
   116 
   117 	typedef IExtensionInterface *(*GETAPI)();
   118 	GETAPI pfnGetAPI = NULL;
   119 
   120 	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
   121 	{
   122 		m_pLib->CloseLibrary();
   123 		m_pLib = NULL;
   124 		snprintf(error, maxlength, "Unable to find extension entry point");
   125 		return false;
   126 	}
   127 
   128 	m_pAPI = pfnGetAPI();
   129 
   130 	/* Check pointer and version */
   131 	if (!PerformAPICheck(error, maxlength))
   132 	{
   133 		m_pLib->CloseLibrary();
   134 		m_pLib = NULL;
   135 		m_pAPI = NULL;
   136 		return false;
   137 	}
   138 
   139 	/* Load as MM:S */
   140 	if (m_pAPI->IsMetamodExtension())
   141 	{
   142 		bool already;
   143 		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
   144 	}
   145 
   146 	if (!CExtension::Load(error, maxlength))
   147 	{
   148 		if (m_pAPI->IsMetamodExtension())
   149 		{
   150 			if (m_PlId)
   151 			{
   152 				char dummy[255];
   153 				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
   154 				m_PlId = 0;
   155 			}
   156 		}
   157 		m_pLib->CloseLibrary();
   158 		m_pLib = NULL;
   159 		m_pAPI = NULL;
   160 		return false;
   161 	}
   162 
   163 	return true;
   164 }
   165 
   166 void CRemoteExtension::Unload()
   167 {
   168 }
   169 
   170 void CLocalExtension::Unload()
   171 {
   172 	if (m_pAPI != NULL && m_PlId)
   173 	{
   174 		char temp_buffer[255];
   175 		g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
   176 		m_PlId = 0;
   177 	}
   178 
   179 	if (m_pLib != NULL)
   180 	{
   181 		m_pLib->CloseLibrary();
   182 		m_pLib = NULL;
   183 	}
   184 }
   185 
   186 bool CRemoteExtension::IsExternal()
   187 {
   188 	return true;
   189 }
   190 
   191 bool CLocalExtension::IsExternal()
   192 {
   193 	return false;
   194 }
   195 
   196 CExtension::~CExtension()
   197 {
   198 	DestroyIdentity();
   199 }
   200 
   201 bool CExtension::PerformAPICheck(char *error, size_t maxlength)
   202 {
   203 	if (!m_pAPI)
   204 	{
   205 		snprintf(error, maxlength, "No IExtensionInterface instance provided");
   206 		return false;
   207 	}
   208 	
   209 	if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
   210 	{
   211 		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
   212 		return false;
   213 	}
   214 
   215 	return true;
   216 }
   217 
   218 bool CExtension::Load(char *error, size_t maxlength)
   219 {
   220 	CreateIdentity();
   221 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
   222 	{
   223 		DestroyIdentity();
   224 		return false;
   225 	}
   226 	else
   227 	{
   228 		/* Check if we're past load time */
   229 		if (!g_SourceMod.IsMapLoading())
   230 		{
   231 			m_pAPI->OnExtensionsAllLoaded();
   232 		}
   233 	}
   234 
   235 	return true;
   236 }
   237 
   238 void CExtension::CreateIdentity()
   239 {
   240 	if (m_pIdentToken != NULL)
   241 	{
   242 		return;
   243 	}
   244 
   245 	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
   246 }
   247 
   248 void CExtension::DestroyIdentity()
   249 {
   250 	if (m_pIdentToken == NULL)
   251 	{
   252 		return;
   253 	}
   254 
   255 	g_ShareSys.DestroyIdentity(m_pIdentToken);
   256 	m_pIdentToken = NULL;
   257 }
   258 
   259 void CExtension::MarkAllLoaded()
   260 {
   261 	assert(m_bFullyLoaded == false);
   262 	if (!m_bFullyLoaded)
   263 	{
   264 		m_bFullyLoaded = true;
   265 		m_pAPI->OnExtensionsAllLoaded();
   266 	}
   267 }
   268 
   269 void CExtension::AddPlugin(CPlugin *pPlugin)
   270 {
   271 	/* Unfortunately we have to do this :( */
   272 	if (m_Dependents.find(pPlugin) != m_Dependents.end())
   273 	{
   274 		m_Dependents.push_back(pPlugin);
   275 	}
   276 }
   277 
   278 void CExtension::SetError(const char *error)
   279 {
   280 	m_Error.assign(error);
   281 }
   282 
   283 IExtensionInterface *CExtension::GetAPI()
   284 {
   285 	return m_pAPI;
   286 }
   287 
   288 const char *CExtension::GetFilename()
   289 {
   290 	return m_File.c_str();
   291 }
   292 
   293 IdentityToken_t *CExtension::GetIdentity()
   294 {
   295 	return m_pIdentToken;
   296 }
   297 
   298 bool CRemoteExtension::IsLoaded()
   299 {
   300 	return (m_pAPI != NULL);
   301 }
   302 
   303 bool CLocalExtension::IsLoaded()
   304 {
   305 	return (m_pLib != NULL);
   306 }
   307 
   308 void CExtension::AddDependency(IfaceInfo *pInfo)
   309 {
   310 	if (m_Deps.find(*pInfo) == m_Deps.end())
   311 	{
   312 		m_Deps.push_back(*pInfo);
   313 	}
   314 }
   315 
   316 bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2)
   317 {
   318 	return (i1.iface == i2.iface) && (i1.owner == i2.owner);
   319 }
   320 
   321 void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
   322 {
   323 	IfaceInfo info;
   324 	info.iface = iface;
   325 	info.owner = pOther;
   326 
   327 	List<IfaceInfo>::iterator iter;
   328 	for (iter = m_ChildDeps.begin();
   329 		 iter != m_ChildDeps.end();
   330 		 iter++)
   331 	{
   332 		IfaceInfo &other = (*iter);
   333 		if (other == info)
   334 		{
   335 			return;
   336 		}
   337 	}
   338 
   339 	m_ChildDeps.push_back(info);
   340 }
   341 
   342 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
   343 {
   344 	List<IfaceInfo>::iterator iter = m_Deps.begin();
   345 
   346 	if (iter == m_Deps.end())
   347 	{
   348 		return NULL;
   349 	}
   350 
   351 	if (pOwner)
   352 	{
   353 		*pOwner = (*iter).owner;
   354 	}
   355 	if (pInterface)
   356 	{
   357 		*pInterface = (*iter).iface;
   358 	}
   359 
   360 	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
   361 
   362 	return (ITERATOR *)pIter;
   363 }
   364 
   365 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
   366 {
   367 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   368 	List<IfaceInfo>::iterator _iter;
   369 
   370 	if (_iter == m_Deps.end())
   371 	{
   372 		return false;
   373 	}
   374 
   375 	_iter++;
   376 
   377 	if (pOwner)
   378 	{
   379 		*pOwner = (*_iter).owner;
   380 	}
   381 	if (pInterface)
   382 	{
   383 		*pInterface = (*_iter).iface;
   384 	}
   385 
   386 	*pIter = _iter;
   387 
   388 	if (_iter == m_Deps.end())
   389 	{
   390 		return false;
   391 	}
   392 
   393 	return true;
   394 }
   395 
   396 void CExtension::FreeDependencyIterator(ITERATOR *iter)
   397 {
   398 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   399 
   400 	delete pIter;
   401 }
   402 
   403 void CExtension::AddInterface(SMInterface *pInterface)
   404 {
   405 	m_Interfaces.push_back(pInterface);
   406 }
   407 
   408 bool CExtension::IsRunning(char *error, size_t maxlength)
   409 {
   410 	if (!IsLoaded())
   411 	{
   412 		if (error)
   413 		{
   414 			snprintf(error, maxlength, "%s", m_Error.c_str());
   415 		}
   416 		return false;
   417 	}
   418 
   419 	return m_pAPI->QueryRunning(error, maxlength);
   420 }
   421 
   422 void CExtension::AddLibrary(const char *library)
   423 {
   424 	m_Libraries.push_back(library);
   425 }
   426 
   427 /*********************
   428  * EXTENSION MANAGER *
   429  *********************/
   430 
   431 void CExtensionManager::OnSourceModAllInitialized()
   432 {
   433 	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
   434 	g_PluginSys.AddPluginsListener(this);
   435 	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
   436 	g_ShareSys.AddInterface(NULL, this);
   437 }
   438 
   439 void CExtensionManager::OnSourceModShutdown()
   440 {
   441 	g_RootMenu.RemoveRootConsoleCommand("exts", this);
   442 	g_ShareSys.DestroyIdentType(g_ExtType);
   443 }
   444 
   445 void CExtensionManager::Shutdown()
   446 {
   447 	List<CExtension *>::iterator iter;
   448 
   449 	while ((iter = m_Libs.begin()) != m_Libs.end())
   450 	{
   451 		UnloadExtension((*iter));
   452 	}
   453 }
   454 
   455 void CExtensionManager::TryAutoload()
   456 {
   457 	char path[PLATFORM_MAX_PATH];
   458 
   459 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
   460 
   461 	IDirectory *pDir = g_LibSys.OpenDirectory(path);
   462 	if (!pDir)
   463 	{
   464 		return;
   465 	}
   466 
   467 	const char *lfile;
   468 	size_t len;
   469 	while (pDir->MoreFiles())
   470 	{
   471 		if (pDir->IsEntryDirectory())
   472 		{
   473 			pDir->NextEntry();
   474 			continue;
   475 		}
   476 
   477 		lfile = pDir->GetEntryName();
   478 		len = strlen(lfile);
   479 		if (len <= 9) /* size of ".autoload" */
   480 		{
   481 			pDir->NextEntry();
   482 			continue;
   483 		}
   484 
   485 		if (strcmp(&lfile[len - 9], ".autoload") != 0)
   486 		{
   487 			pDir->NextEntry();
   488 			continue;
   489 		}
   490 
   491 		char file[PLATFORM_MAX_PATH];
   492 		len = UTIL_Format(file, sizeof(file), "%s", lfile);
   493 		strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT);
   494 
   495 		LoadAutoExtension(file);
   496 		
   497 		pDir->NextEntry();
   498 	}
   499 }
   500 
   501 IExtension *CExtensionManager::LoadAutoExtension(const char *path)
   502 {
   503 	if (!strstr(path, "." PLATFORM_LIB_EXT))
   504 	{
   505 		char newpath[PLATFORM_MAX_PATH];
   506 		g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT);
   507 		return LoadAutoExtension(newpath);
   508 	}
   509 
   510 	IExtension *pAlready;
   511 	if ((pAlready=FindExtensionByFile(path)) != NULL)
   512 	{
   513 		return pAlready;
   514 	}
   515 
   516 	char error[256];
   517 	CExtension *p = new CLocalExtension(path);
   518 
   519 	/* We put us in the list beforehand so extensions that check for each other
   520 	 * won't recursively load each other.
   521 	 */
   522 	m_Libs.push_back(p);
   523 
   524 	if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
   525 	{
   526 		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
   527 		p->SetError(error);
   528 	}
   529 
   530 	return p;
   531 }
   532 
   533 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
   534 {
   535 	List<CExtension *>::iterator iter;
   536 	CExtension *pExt;
   537 
   538 	/* Chomp off the path */
   539 	char lookup[PLATFORM_MAX_PATH];
   540 	g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
   541 
   542 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   543 	{
   544 		pExt = (*iter);
   545 		char short_file[PLATFORM_MAX_PATH];
   546 		g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename());
   547 		if (strcmp(lookup, short_file) == 0)
   548 		{
   549 			return pExt;
   550 		}
   551 	}
   552 
   553 	/* If we got no results, test if there was a platform extension.
   554 	 * If not, add one.
   555 	 */
   556 	if (!strstr(file, "." PLATFORM_LIB_EXT))
   557 	{
   558 		char path[PLATFORM_MAX_PATH];
   559 		UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT);
   560 		return FindExtensionByFile(path);
   561 	}
   562 
   563 	return NULL;
   564 }
   565 
   566 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
   567 {
   568 	List<CExtension *>::iterator iter;
   569 	CExtension *pExt;
   570 	IExtensionInterface *pAPI;
   571 	const char *name;
   572 
   573 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   574 	{
   575 		pExt = (*iter);
   576 		if (!pExt->IsLoaded())
   577 		{
   578 			continue;
   579 		}
   580 		if ((pAPI = pExt->GetAPI()) == NULL)
   581 		{
   582 			continue;
   583 		}
   584 		name = pAPI->GetExtensionName();
   585 		if (!name)
   586 		{
   587 			continue;
   588 		}
   589 		if (strcmp(name, ext) == 0)
   590 		{
   591 			return pExt;
   592 		}
   593 	}
   594 
   595 	return NULL;
   596 }
   597 
   598 IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
   599 {
   600 	IExtension *pAlready;
   601 	if ((pAlready=FindExtensionByFile(file)) != NULL)
   602 	{
   603 		return pAlready;
   604 	}
   605 
   606 	CExtension *pExt = new CLocalExtension(file);
   607 
   608 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   609 	{
   610 		pExt->Unload();
   611 		delete pExt;
   612 		return NULL;
   613 	}
   614 
   615 	/* :TODO: do QueryRunning check if the map is loaded */
   616 
   617 	m_Libs.push_back(pExt);
   618 
   619 	return pExt;
   620 }
   621 
   622 void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo)
   623 {
   624 	CExtension *pExt = (CExtension *)pRequester;
   625 	CExtension *pOwner = (CExtension *)pInfo->owner;
   626 
   627 	pExt->AddDependency(pInfo);
   628 
   629 	IExtensionInterface *pAPI = pExt->GetAPI();
   630 	if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface))
   631 	{
   632 		pOwner->AddChildDependent(pExt, pInfo->iface);
   633 	}
   634 }
   635 
   636 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   637 {
   638 	CExtension *pExt = (CExtension *)pOwner;
   639 
   640 	pExt->AddInterface(pInterface);
   641 }
   642 
   643 void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin )
   644 {
   645 	CExtension *pExt = (CExtension *)pParent;
   646 
   647 	pExt->AddPlugin(pPlugin);
   648 }
   649 
   650 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   651 {
   652 	List<CExtension *>::iterator iter;
   653 
   654 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   655 	{
   656 		(*iter)->DropRefsTo((CPlugin *)plugin);
   657 	}
   658 }
   659 
   660 CExtension *CExtensionManager::FindByOrder(unsigned int num)
   661 {
   662 	if (num < 1 || num > m_Libs.size())
   663 	{
   664 		return NULL;
   665 	}
   666 
   667 	List<CExtension *>::iterator iter = m_Libs.begin();
   668 
   669 	while (iter != m_Libs.end())
   670 	{
   671 		if (--num == 0)
   672 		{
   673 			return (*iter);
   674 		}
   675 		iter++;
   676 	}
   677 
   678 	return NULL;
   679 }
   680 
   681 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   682 {
   683 	if (!_pExt)
   684 	{
   685 		return false;
   686 	}
   687 
   688 	CExtension *pExt = (CExtension *)_pExt;
   689 
   690 	if (m_Libs.find(pExt) == m_Libs.end())
   691 	{
   692 		return false;
   693 	}
   694 
   695 	/* First remove us from internal lists */
   696 	g_ShareSys.RemoveInterfaces(_pExt);
   697 	m_Libs.remove(pExt);
   698 
   699 	List<CExtension *> UnloadQueue;
   700 
   701 	/* Handle dependencies */
   702 	if (pExt->IsLoaded())
   703 	{
   704 		/* Unload any dependent plugins */
   705 		List<CPlugin *>::iterator p_iter = pExt->m_Dependents.begin();
   706 		while (p_iter != pExt->m_Dependents.end())
   707 		{
   708 			/* We have to manually unlink ourselves here, since we're no longer being managed */
   709 			g_PluginSys.UnloadPlugin((*p_iter));
   710 			p_iter = pExt->m_Dependents.erase(p_iter);
   711 		}
   712 
   713 		List<String>::iterator s_iter;
   714 		for (s_iter = pExt->m_Libraries.begin();
   715 			 s_iter != pExt->m_Libraries.end();
   716 			 s_iter++)
   717 		{
   718 			g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
   719 		}
   720 
   721 		/* Notify and/or unload all dependencies */
   722 		List<CExtension *>::iterator c_iter;
   723 		CExtension *pDep;
   724 		IExtensionInterface *pAPI;
   725 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   726 		{
   727 			pDep = (*c_iter);
   728 			if ((pAPI=pDep->GetAPI()) == NULL)
   729 			{
   730 				continue;
   731 			}
   732 			if (pDep == pExt)
   733 			{
   734 				continue;
   735 			}
   736 			/* Now, get its dependency list */
   737 			bool dropped = false;
   738 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   739 			while (i_iter != pDep->m_Deps.end())
   740 			{
   741 				if ((*i_iter).owner == _pExt)
   742 				{
   743 					if (!pAPI->QueryInterfaceDrop((*i_iter).iface))
   744 					{
   745 						if (!dropped)
   746 						{
   747 							dropped = true;
   748 							UnloadQueue.push_back(pDep);
   749 						}
   750 					}
   751 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   752 					i_iter = pDep->m_Deps.erase(i_iter);
   753 				}
   754 				else
   755 				{
   756 					i_iter++;
   757 				}
   758 			}
   759 			/* Flush out any back references to this plugin */
   760 			i_iter = pDep->m_ChildDeps.begin();
   761 			while (i_iter != pDep->m_ChildDeps.end())
   762 			{
   763 				if ((*i_iter).owner == pExt)
   764 				{
   765 					i_iter = pDep->m_ChildDeps.erase(i_iter);
   766 				}
   767 				else
   768 				{
   769 					i_iter++;
   770 				}
   771 			}
   772 		}
   773 
   774 		/* Unbind our natives from Core */
   775 		pExt->DropEverything();
   776 
   777 		/* Tell it to unload */
   778 		pAPI = pExt->GetAPI();
   779 		pAPI->OnExtensionUnload();
   780 	}
   781 
   782 	IdentityToken_t *pIdentity;
   783 	if ((pIdentity = pExt->GetIdentity()) != NULL)
   784 	{
   785 		SMGlobalClass *glob = SMGlobalClass::head;
   786 		while (glob)
   787 		{
   788 			glob->OnSourceModIdentityDropped(pIdentity);
   789 			glob = glob->m_pGlobalClassNext;
   790 		}
   791 	}
   792 
   793 	pExt->Unload();
   794 	delete pExt;
   795 
   796 	List<CExtension *>::iterator iter;
   797 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   798 	{
   799 		/* NOTE: This is safe because the unload function backs out of anything not present */
   800 		UnloadExtension((*iter));
   801 	}
   802 
   803 	return true;
   804 }
   805 
   806 void CExtensionManager::MarkAllLoaded()
   807 {
   808 	List<CExtension *>::iterator iter;
   809 	CExtension *pExt;
   810 
   811 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   812 	{
   813 		pExt = (*iter);
   814 		if (!pExt->IsLoaded())
   815 		{
   816 			continue;
   817 		}
   818 		if (pExt->m_bFullyLoaded)
   819 		{
   820 			continue;
   821 		}
   822 		pExt->MarkAllLoaded();
   823 	}
   824 }
   825 
   826 void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
   827 {
   828 	/* This function doesn't really need to do anything now.  We make sure the 
   829 	 * other extension is loaded, but handling of dependencies is really done 
   830 	 * by the interface fetcher.
   831 	 */
   832 	if (required || autoload)
   833 	{
   834 		LoadAutoExtension(file);
   835 	}
   836 }
   837 
   838 void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   839 {
   840 	int argcount = command.ArgC();
   841 	if (argcount >= 3)
   842 	{
   843 		const char *cmd = command.Arg(2);
   844 		if (strcmp(cmd, "list") == 0)
   845 		{
   846 			List<CExtension *>::iterator iter;
   847 			CExtension *pExt;
   848 			unsigned int num = 1;
   849 			switch (m_Libs.size())
   850 			{
   851 			case 1:
   852 				{
   853 					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
   854 					break;
   855 				}
   856 			case 0:
   857 				{
   858 					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   859 					break;
   860 				}
   861 			default:
   862 				{
   863 					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
   864 					break;
   865 				}
   866 			}
   867 			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
   868 			{
   869 				pExt = (*iter);
   870 				if (pExt->IsLoaded())
   871 				{
   872 					char error[255];
   873 					if (!pExt->IsRunning(error, sizeof(error)))
   874 					{
   875 						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
   876 					}
   877 					else
   878 					{
   879 						IExtensionInterface *pAPI = pExt->GetAPI();
   880 						const char *name = pAPI->GetExtensionName();
   881 						const char *version = pAPI->GetExtensionVerString();
   882 						const char *descr = pAPI->GetExtensionDescription();
   883 						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
   884 					}
   885 				} else {
   886 					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
   887 				}
   888 			}
   889 			return;
   890 		}
   891 		else if (strcmp(cmd, "info") == 0)
   892 		{
   893 			if (argcount < 4)
   894 			{
   895 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   896 				return;
   897 			}
   898 
   899 			const char *sId = command.Arg(3);
   900 			unsigned int id = atoi(sId);
   901 			if (id <= 0)
   902 			{
   903 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   904 				return;
   905 			}
   906 
   907 			if (m_Libs.size() == 0)
   908 			{
   909 				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   910 				return;
   911 			}
   912 
   913 			if (id > m_Libs.size())
   914 			{
   915 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   916 				return;
   917 			}
   918 
   919 			List<CExtension *>::iterator iter = m_Libs.begin();
   920 			CExtension *pExt = NULL;
   921 			while (iter != m_Libs.end())
   922 			{
   923 				if (--id == 0)
   924 				{
   925 					pExt = (*iter);
   926 					break;
   927 				}
   928 				iter++;
   929 			}
   930 			/* This should never happen */
   931 			if (!pExt)
   932 			{
   933 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   934 				return;
   935 			}
   936 
   937 			if (!pExt->IsLoaded())
   938 			{
   939 				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   940 				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
   941 			}
   942 			else
   943 			{
   944 				char error[255];
   945 				if (!pExt->IsRunning(error, sizeof(error)))
   946 				{
   947 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   948 					g_RootMenu.ConsolePrint(" Loaded: Yes");
   949 					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
   950 				}
   951 				else
   952 				{
   953 					IExtensionInterface *pAPI = pExt->GetAPI();
   954 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   955 					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
   956 					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
   957 					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
   958 					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
   959 
   960 					if (pExt->IsExternal())
   961 					{
   962 						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
   963 					}
   964 					else if (pAPI->IsMetamodExtension())
   965 					{
   966 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
   967 					}
   968 					else
   969 					{
   970 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
   971 					}
   972 				}
   973 			}
   974 			return;
   975 		}
   976 		else if (strcmp(cmd, "unload") == 0)
   977 		{
   978 			if (argcount < 4)
   979 			{
   980 				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
   981 				return;
   982 			}
   983 
   984 			const char *arg = command.Arg(3);
   985 			unsigned int num = atoi(arg);
   986 			CExtension *pExt = FindByOrder(num);
   987 
   988 			if (!pExt)
   989 			{
   990 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
   991 				return;
   992 			}
   993 
   994 			if (argcount > 4 && pExt->unload_code)
   995 			{
   996 				const char *unload = command.Arg(4);
   997 				if (pExt->unload_code == (unsigned)atoi(unload))
   998 				{
   999 					char filename[PLATFORM_MAX_PATH];
  1000 					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1001 					UnloadExtension(pExt);
  1002 					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1003 				}
  1004 				else
  1005 				{
  1006 					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
  1007 				}
  1008 				return;
  1009 			}
  1010 
  1011 			if (!pExt->IsLoaded() 
  1012 				|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
  1013 			{
  1014 				char filename[PLATFORM_MAX_PATH];
  1015 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1016 				UnloadExtension(pExt);
  1017 				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1018 				return;
  1019 			}
  1020 			else
  1021 			{
  1022 				List<CPlugin *> plugins;
  1023 				if (pExt->m_ChildDeps.size())
  1024 				{
  1025 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
  1026 					List<CExtension *>::iterator iter;
  1027 					CExtension *pOther;
  1028 					/* Get list of all extensions */
  1029 					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1030 					{
  1031 						List<IfaceInfo>::iterator i_iter;
  1032 						pOther = (*iter);
  1033 						if (!pOther->IsLoaded() || pOther == pExt)
  1034 						{
  1035 							continue;
  1036 						}
  1037 						/* Get their dependencies */
  1038 						for (i_iter=pOther->m_Deps.begin();
  1039 							 i_iter!=pOther->m_Deps.end();
  1040 							 i_iter++)
  1041 						{
  1042 							/* Is this dependency to us? */
  1043 							if ((*i_iter).owner != pExt)
  1044 							{
  1045 								continue;
  1046 							}
  1047 							/* Will our dependent care? */
  1048 							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
  1049 							{
  1050 								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
  1051 								/* Add to plugin unload list */
  1052 								List<CPlugin *>::iterator p_iter;
  1053 								for (p_iter=pOther->m_Dependents.begin();
  1054 									 p_iter!=pOther->m_Dependents.end();
  1055 									 p_iter++)
  1056 								{
  1057 									if (plugins.find((*p_iter)) == plugins.end())
  1058 									{
  1059 										plugins.push_back((*p_iter));
  1060 									}
  1061 								}
  1062 							}
  1063 						}
  1064 					}
  1065 				}
  1066 				if (pExt->m_Dependents.size())
  1067 				{
  1068 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
  1069 					List<CPlugin *>::iterator iter;
  1070 					CPlugin *pPlugin;
  1071 					for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++)
  1072 					{
  1073 						pPlugin = (*iter);
  1074 						if (plugins.find(pPlugin) == plugins.end())
  1075 						{
  1076 							plugins.push_back(pPlugin);
  1077 						}
  1078 					}
  1079 					for (iter = plugins.begin(); iter != plugins.end(); iter++)
  1080 					{
  1081 						pPlugin = (*iter);
  1082 						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
  1083 					}
  1084 				}
  1085 				srand(static_cast<int>(time(NULL)));
  1086 				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
  1087 				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
  1088 				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
  1089 
  1090 				return;
  1091 			}
  1092 		}
  1093 	}
  1094 
  1095 	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
  1096 	g_RootMenu.DrawGenericOption("info", "Extra extension information");
  1097 	g_RootMenu.DrawGenericOption("list", "List extensions");
  1098 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
  1099 }
  1100 
  1101 CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
  1102 {
  1103 	if (ptr->type == g_ExtType)
  1104 	{
  1105 		return (CExtension *)(ptr->ptr);
  1106 	}
  1107 
  1108 	return NULL;
  1109 }
  1110 
  1111 CExtensionManager::CExtensionManager()
  1112 {
  1113 }
  1114 
  1115 CExtensionManager::~CExtensionManager()
  1116 {
  1117 }
  1118 
  1119 void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
  1120 {
  1121 	CExtension *pExt = (CExtension *)pSource;
  1122 	pExt->AddLibrary(library);
  1123 	g_PluginSys.OnLibraryAction(library, false, false);
  1124 }
  1125 
  1126 bool CExtensionManager::LibraryExists(const char *library)
  1127 {
  1128 	CExtension *pExt;
  1129 
  1130 	for (List<CExtension *>::iterator iter = m_Libs.begin();
  1131 		 iter != m_Libs.end();
  1132 		 iter++)
  1133 	{
  1134 		pExt = (*iter);
  1135 		for (List<String>::iterator s_iter = pExt->m_Libraries.begin();
  1136 			 s_iter != pExt->m_Libraries.end();
  1137 			 s_iter++)
  1138 		{
  1139 			if ((*s_iter).compare(library) == 0)
  1140 			{
  1141 				return true;
  1142 			}
  1143 		}
  1144 	}
  1145 
  1146 	return false;
  1147 }
  1148 
  1149 IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
  1150 											const char *filepath,
  1151 											const char *filename,
  1152 											char *error,
  1153 											size_t maxlength)
  1154 {
  1155 	IExtension *pAlready;
  1156 	if ((pAlready=FindExtensionByFile(filename)) != NULL)
  1157 	{
  1158 		return pAlready;
  1159 	}
  1160 
  1161 	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
  1162 
  1163 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
  1164 	{
  1165 		pExt->Unload();
  1166 		delete pExt;
  1167 		return NULL;
  1168 	}
  1169 
  1170 	m_Libs.push_back(pExt);
  1171 
  1172 	return pExt;
  1173 }
  1174 
  1175 void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
  1176 {
  1177 	IExtensionInterface *pAPI;
  1178 	List<CExtension *>::iterator iter;
  1179 
  1180 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1181 	{
  1182 		if ((pAPI = (*iter)->GetAPI()) == NULL)
  1183 		{
  1184 			continue;
  1185 		}
  1186 		if (pAPI->GetExtensionVersion() > 3)
  1187 		{
  1188 			pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax);
  1189 		}
  1190 	}
  1191 }