core/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Sun Nov 16 16:06:18 2008 -0800 (2008-11-16)
changeset 2411 8dfaa3d730b5
parent 2289 core/systems/ExtensionSys.cpp@44045ead61cd
child 2413 f7fee39a0e56
permissions -rw-r--r--
Removed pointless "systems" folder from core.
     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(const 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::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface)
   637 {
   638 	CExtension *pExt = (CExtension *)ext;
   639 	CExtension *pOwner = GetExtensionFromIdent(other);
   640 
   641 	IfaceInfo info;
   642 
   643 	info.iface = (SMInterface *)iface;
   644 	info.owner = pOwner;
   645 
   646 	pExt->AddDependency(&info);
   647 	pOwner->AddChildDependent(pExt, (SMInterface *)iface);
   648 }
   649 
   650 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   651 {
   652 	CExtension *pExt = (CExtension *)pOwner;
   653 
   654 	pExt->AddInterface(pInterface);
   655 }
   656 
   657 void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin )
   658 {
   659 	CExtension *pExt = (CExtension *)pParent;
   660 
   661 	pExt->AddPlugin(pPlugin);
   662 }
   663 
   664 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   665 {
   666 	List<CExtension *>::iterator iter;
   667 
   668 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   669 	{
   670 		(*iter)->DropRefsTo((CPlugin *)plugin);
   671 	}
   672 }
   673 
   674 CExtension *CExtensionManager::FindByOrder(unsigned int num)
   675 {
   676 	if (num < 1 || num > m_Libs.size())
   677 	{
   678 		return NULL;
   679 	}
   680 
   681 	List<CExtension *>::iterator iter = m_Libs.begin();
   682 
   683 	while (iter != m_Libs.end())
   684 	{
   685 		if (--num == 0)
   686 		{
   687 			return (*iter);
   688 		}
   689 		iter++;
   690 	}
   691 
   692 	return NULL;
   693 }
   694 
   695 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   696 {
   697 	if (!_pExt)
   698 	{
   699 		return false;
   700 	}
   701 
   702 	CExtension *pExt = (CExtension *)_pExt;
   703 
   704 	if (m_Libs.find(pExt) == m_Libs.end())
   705 	{
   706 		return false;
   707 	}
   708 
   709 	/* First remove us from internal lists */
   710 	g_ShareSys.RemoveInterfaces(_pExt);
   711 	m_Libs.remove(pExt);
   712 
   713 	List<CExtension *> UnloadQueue;
   714 
   715 	/* Handle dependencies */
   716 	if (pExt->IsLoaded())
   717 	{
   718 		/* Unload any dependent plugins */
   719 		List<CPlugin *>::iterator p_iter = pExt->m_Dependents.begin();
   720 		while (p_iter != pExt->m_Dependents.end())
   721 		{
   722 			/* We have to manually unlink ourselves here, since we're no longer being managed */
   723 			g_PluginSys.UnloadPlugin((*p_iter));
   724 			p_iter = pExt->m_Dependents.erase(p_iter);
   725 		}
   726 
   727 		List<String>::iterator s_iter;
   728 		for (s_iter = pExt->m_Libraries.begin();
   729 			 s_iter != pExt->m_Libraries.end();
   730 			 s_iter++)
   731 		{
   732 			g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
   733 		}
   734 
   735 		/* Notify and/or unload all dependencies */
   736 		List<CExtension *>::iterator c_iter;
   737 		CExtension *pDep;
   738 		IExtensionInterface *pAPI;
   739 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   740 		{
   741 			pDep = (*c_iter);
   742 			if ((pAPI=pDep->GetAPI()) == NULL)
   743 			{
   744 				continue;
   745 			}
   746 			if (pDep == pExt)
   747 			{
   748 				continue;
   749 			}
   750 			/* Now, get its dependency list */
   751 			bool dropped = false;
   752 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   753 			while (i_iter != pDep->m_Deps.end())
   754 			{
   755 				if ((*i_iter).owner == _pExt)
   756 				{
   757 					if (!pAPI->QueryInterfaceDrop((*i_iter).iface))
   758 					{
   759 						if (!dropped)
   760 						{
   761 							dropped = true;
   762 							UnloadQueue.push_back(pDep);
   763 						}
   764 					}
   765 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   766 					i_iter = pDep->m_Deps.erase(i_iter);
   767 				}
   768 				else
   769 				{
   770 					i_iter++;
   771 				}
   772 			}
   773 			/* Flush out any back references to this plugin */
   774 			i_iter = pDep->m_ChildDeps.begin();
   775 			while (i_iter != pDep->m_ChildDeps.end())
   776 			{
   777 				if ((*i_iter).owner == pExt)
   778 				{
   779 					i_iter = pDep->m_ChildDeps.erase(i_iter);
   780 				}
   781 				else
   782 				{
   783 					i_iter++;
   784 				}
   785 			}
   786 		}
   787 
   788 		/* Unbind our natives from Core */
   789 		pExt->DropEverything();
   790 	}
   791 
   792 	IdentityToken_t *pIdentity;
   793 	if ((pIdentity = pExt->GetIdentity()) != NULL)
   794 	{
   795 		SMGlobalClass *glob = SMGlobalClass::head;
   796 		while (glob)
   797 		{
   798 			glob->OnSourceModIdentityDropped(pIdentity);
   799 			glob = glob->m_pGlobalClassNext;
   800 		}
   801 	}
   802 
   803 	/* Tell it to unload */
   804 	if (pExt->IsLoaded())
   805 	{
   806 		IExtensionInterface *pAPI = pExt->GetAPI();
   807 		pAPI->OnExtensionUnload();
   808 	}
   809 
   810 	pExt->Unload();
   811 	delete pExt;
   812 
   813 	List<CExtension *>::iterator iter;
   814 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   815 	{
   816 		/* NOTE: This is safe because the unload function backs out of anything not present */
   817 		UnloadExtension((*iter));
   818 	}
   819 
   820 	return true;
   821 }
   822 
   823 void CExtensionManager::MarkAllLoaded()
   824 {
   825 	List<CExtension *>::iterator iter;
   826 	CExtension *pExt;
   827 
   828 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   829 	{
   830 		pExt = (*iter);
   831 		if (!pExt->IsLoaded())
   832 		{
   833 			continue;
   834 		}
   835 		if (pExt->m_bFullyLoaded)
   836 		{
   837 			continue;
   838 		}
   839 		pExt->MarkAllLoaded();
   840 	}
   841 }
   842 
   843 void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
   844 {
   845 	/* This function doesn't really need to do anything now.  We make sure the 
   846 	 * other extension is loaded, but handling of dependencies is really done 
   847 	 * by the interface fetcher.
   848 	 */
   849 	if (required || autoload)
   850 	{
   851 		LoadAutoExtension(file);
   852 	}
   853 }
   854 
   855 void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   856 {
   857 	int argcount = command.ArgC();
   858 	if (argcount >= 3)
   859 	{
   860 		const char *cmd = command.Arg(2);
   861 		if (strcmp(cmd, "list") == 0)
   862 		{
   863 			List<CExtension *>::iterator iter;
   864 			CExtension *pExt;
   865 			unsigned int num = 1;
   866 			switch (m_Libs.size())
   867 			{
   868 			case 1:
   869 				{
   870 					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
   871 					break;
   872 				}
   873 			case 0:
   874 				{
   875 					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   876 					break;
   877 				}
   878 			default:
   879 				{
   880 					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
   881 					break;
   882 				}
   883 			}
   884 			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
   885 			{
   886 				pExt = (*iter);
   887 				if (pExt->IsLoaded())
   888 				{
   889 					char error[255];
   890 					if (!pExt->IsRunning(error, sizeof(error)))
   891 					{
   892 						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
   893 					}
   894 					else
   895 					{
   896 						IExtensionInterface *pAPI = pExt->GetAPI();
   897 						const char *name = pAPI->GetExtensionName();
   898 						const char *version = pAPI->GetExtensionVerString();
   899 						const char *descr = pAPI->GetExtensionDescription();
   900 						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
   901 					}
   902 				} else {
   903 					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
   904 				}
   905 			}
   906 			return;
   907 		}
   908 		else if (strcmp(cmd, "info") == 0)
   909 		{
   910 			if (argcount < 4)
   911 			{
   912 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   913 				return;
   914 			}
   915 
   916 			const char *sId = command.Arg(3);
   917 			unsigned int id = atoi(sId);
   918 			if (id <= 0)
   919 			{
   920 				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
   921 				return;
   922 			}
   923 
   924 			if (m_Libs.size() == 0)
   925 			{
   926 				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   927 				return;
   928 			}
   929 
   930 			if (id > m_Libs.size())
   931 			{
   932 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   933 				return;
   934 			}
   935 
   936 			List<CExtension *>::iterator iter = m_Libs.begin();
   937 			CExtension *pExt = NULL;
   938 			while (iter != m_Libs.end())
   939 			{
   940 				if (--id == 0)
   941 				{
   942 					pExt = (*iter);
   943 					break;
   944 				}
   945 				iter++;
   946 			}
   947 			/* This should never happen */
   948 			if (!pExt)
   949 			{
   950 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
   951 				return;
   952 			}
   953 
   954 			if (!pExt->IsLoaded())
   955 			{
   956 				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   957 				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
   958 			}
   959 			else
   960 			{
   961 				char error[255];
   962 				if (!pExt->IsRunning(error, sizeof(error)))
   963 				{
   964 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   965 					g_RootMenu.ConsolePrint(" Loaded: Yes");
   966 					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
   967 				}
   968 				else
   969 				{
   970 					IExtensionInterface *pAPI = pExt->GetAPI();
   971 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
   972 					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
   973 					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
   974 					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
   975 					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
   976 
   977 					if (pExt->IsExternal())
   978 					{
   979 						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
   980 					}
   981 					else if (pAPI->IsMetamodExtension())
   982 					{
   983 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
   984 					}
   985 					else
   986 					{
   987 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
   988 					}
   989 				}
   990 			}
   991 			return;
   992 		}
   993 		else if (strcmp(cmd, "unload") == 0)
   994 		{
   995 			if (argcount < 4)
   996 			{
   997 				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
   998 				return;
   999 			}
  1000 
  1001 			const char *arg = command.Arg(3);
  1002 			unsigned int num = atoi(arg);
  1003 			CExtension *pExt = FindByOrder(num);
  1004 
  1005 			if (!pExt)
  1006 			{
  1007 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
  1008 				return;
  1009 			}
  1010 
  1011 			if (argcount > 4 && pExt->unload_code)
  1012 			{
  1013 				const char *unload = command.Arg(4);
  1014 				if (pExt->unload_code == (unsigned)atoi(unload))
  1015 				{
  1016 					char filename[PLATFORM_MAX_PATH];
  1017 					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1018 					UnloadExtension(pExt);
  1019 					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1020 				}
  1021 				else
  1022 				{
  1023 					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
  1024 				}
  1025 				return;
  1026 			}
  1027 
  1028 			if (!pExt->IsLoaded() 
  1029 				|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
  1030 			{
  1031 				char filename[PLATFORM_MAX_PATH];
  1032 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1033 				UnloadExtension(pExt);
  1034 				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1035 				return;
  1036 			}
  1037 			else
  1038 			{
  1039 				List<CPlugin *> plugins;
  1040 				if (pExt->m_ChildDeps.size())
  1041 				{
  1042 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
  1043 					List<CExtension *>::iterator iter;
  1044 					CExtension *pOther;
  1045 					/* Get list of all extensions */
  1046 					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1047 					{
  1048 						List<IfaceInfo>::iterator i_iter;
  1049 						pOther = (*iter);
  1050 						if (!pOther->IsLoaded() || pOther == pExt)
  1051 						{
  1052 							continue;
  1053 						}
  1054 						/* Get their dependencies */
  1055 						for (i_iter=pOther->m_Deps.begin();
  1056 							 i_iter!=pOther->m_Deps.end();
  1057 							 i_iter++)
  1058 						{
  1059 							/* Is this dependency to us? */
  1060 							if ((*i_iter).owner != pExt)
  1061 							{
  1062 								continue;
  1063 							}
  1064 							/* Will our dependent care? */
  1065 							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
  1066 							{
  1067 								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
  1068 								/* Add to plugin unload list */
  1069 								List<CPlugin *>::iterator p_iter;
  1070 								for (p_iter=pOther->m_Dependents.begin();
  1071 									 p_iter!=pOther->m_Dependents.end();
  1072 									 p_iter++)
  1073 								{
  1074 									if (plugins.find((*p_iter)) == plugins.end())
  1075 									{
  1076 										plugins.push_back((*p_iter));
  1077 									}
  1078 								}
  1079 							}
  1080 						}
  1081 					}
  1082 				}
  1083 				if (pExt->m_Dependents.size())
  1084 				{
  1085 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
  1086 					List<CPlugin *>::iterator iter;
  1087 					CPlugin *pPlugin;
  1088 					for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++)
  1089 					{
  1090 						pPlugin = (*iter);
  1091 						if (plugins.find(pPlugin) == plugins.end())
  1092 						{
  1093 							plugins.push_back(pPlugin);
  1094 						}
  1095 					}
  1096 					for (iter = plugins.begin(); iter != plugins.end(); iter++)
  1097 					{
  1098 						pPlugin = (*iter);
  1099 						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
  1100 					}
  1101 				}
  1102 				srand(static_cast<int>(time(NULL)));
  1103 				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
  1104 				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
  1105 				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
  1106 
  1107 				return;
  1108 			}
  1109 		}
  1110 	}
  1111 
  1112 	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
  1113 	g_RootMenu.DrawGenericOption("info", "Extra extension information");
  1114 	g_RootMenu.DrawGenericOption("list", "List extensions");
  1115 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
  1116 }
  1117 
  1118 CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
  1119 {
  1120 	if (ptr->type == g_ExtType)
  1121 	{
  1122 		return (CExtension *)(ptr->ptr);
  1123 	}
  1124 
  1125 	return NULL;
  1126 }
  1127 
  1128 CExtensionManager::CExtensionManager()
  1129 {
  1130 }
  1131 
  1132 CExtensionManager::~CExtensionManager()
  1133 {
  1134 }
  1135 
  1136 void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
  1137 {
  1138 	CExtension *pExt = (CExtension *)pSource;
  1139 	pExt->AddLibrary(library);
  1140 	g_PluginSys.OnLibraryAction(library, false, false);
  1141 }
  1142 
  1143 bool CExtensionManager::LibraryExists(const char *library)
  1144 {
  1145 	CExtension *pExt;
  1146 
  1147 	for (List<CExtension *>::iterator iter = m_Libs.begin();
  1148 		 iter != m_Libs.end();
  1149 		 iter++)
  1150 	{
  1151 		pExt = (*iter);
  1152 		for (List<String>::iterator s_iter = pExt->m_Libraries.begin();
  1153 			 s_iter != pExt->m_Libraries.end();
  1154 			 s_iter++)
  1155 		{
  1156 			if ((*s_iter).compare(library) == 0)
  1157 			{
  1158 				return true;
  1159 			}
  1160 		}
  1161 	}
  1162 
  1163 	return false;
  1164 }
  1165 
  1166 IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
  1167 											const char *filepath,
  1168 											const char *filename,
  1169 											char *error,
  1170 											size_t maxlength)
  1171 {
  1172 	IExtension *pAlready;
  1173 	if ((pAlready=FindExtensionByFile(filename)) != NULL)
  1174 	{
  1175 		return pAlready;
  1176 	}
  1177 
  1178 	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
  1179 
  1180 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
  1181 	{
  1182 		pExt->Unload();
  1183 		delete pExt;
  1184 		return NULL;
  1185 	}
  1186 
  1187 	m_Libs.push_back(pExt);
  1188 
  1189 	return pExt;
  1190 }
  1191 
  1192 void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
  1193 {
  1194 	IExtensionInterface *pAPI;
  1195 	List<CExtension *>::iterator iter;
  1196 
  1197 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1198 	{
  1199 		if ((pAPI = (*iter)->GetAPI()) == NULL)
  1200 		{
  1201 			continue;
  1202 		}
  1203 		if (pAPI->GetExtensionVersion() > 3)
  1204 		{
  1205 			pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax);
  1206 		}
  1207 	}
  1208 }