core/ExtensionSys.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sat Jun 02 10:07:25 2012 -0400 (2012-06-02)
changeset 3549 657ea0e26e7a
parent 3515 c14c14e06cc1
child 3613 b0740093756d
permissions -rw-r--r--
Added missing CS:GO GameRulesProxy gamedata.
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2010 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 #include "PlayerManager.h"
    41 
    42 CExtensionManager g_Extensions;
    43 IdentityType_t g_ExtType;
    44 
    45 void CExtension::Initialize(const char *filename, const char *path)
    46 {
    47 	m_pAPI = NULL;
    48 	m_pIdentToken = NULL;
    49 	unload_code = 0;
    50 	m_bFullyLoaded = false;
    51 	m_File.assign(filename);
    52 	m_Path.assign(path);
    53 	char real_name[PLATFORM_MAX_PATH];
    54 	g_LibSys.GetFileFromPath(real_name, sizeof(real_name), m_Path.c_str());
    55 	m_RealFile.assign(real_name);
    56 }
    57 
    58 CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path)
    59 {
    60 	Initialize(filename, path);
    61 	m_pAPI = pAPI;
    62 }
    63 
    64 #if defined METAMOD_PLAPI_VERSION
    65 #if SOURCE_ENGINE == SE_LEFT4DEAD
    66 #define GAMEFIX "2.l4d"
    67 #elif SOURCE_ENGINE == SE_LEFT4DEAD2
    68 #define GAMEFIX "2.l4d2"
    69 #elif SOURCE_ENGINE == SE_ALIENSWARM
    70 #define GAMEFIX "2.swarm"
    71 #elif SOURCE_ENGINE == SE_ORANGEBOX
    72 #define GAMEFIX "2.ep2"
    73 #elif SOURCE_ENGINE == SE_BLOODYGOODTIME
    74 #define GAMEFIX "2.bgt"
    75 #elif SOURCE_ENGINE == SE_EYE
    76 #define GAMEFIX "2.eye"
    77 #elif SOURCE_ENGINE == SE_ORANGEBOXVALVE
    78 #define GAMEFIX "2.ep2v"
    79 #elif SOURCE_ENGINE == SE_DARKMESSIAH
    80 #define GAMEFIX "2.darkm"
    81 #elif SOURCE_ENGINE == SE_PORTAL2
    82 #define GAMEFIX "2.portal2"
    83 #elif SOURCE_ENGINE == SE_CSGO
    84 #define GAMEFIX "2.csgo"
    85 #else
    86 #define GAMEFIX "2.ep1"
    87 #endif //(SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
    88 #else  //METAMOD_PLAPI_VERSION
    89 #define GAMEFIX "1.ep1"
    90 #endif //METAMOD_PLAPI_VERSION
    91 
    92 CLocalExtension::CLocalExtension(const char *filename)
    93 {
    94 	m_PlId = 0;
    95 	m_pLib = NULL;
    96 
    97 	char path[PLATFORM_MAX_PATH];
    98 
    99 	/* Special case for new bintools binary */
   100 	if (strcmp(filename, "bintools.ext") == 0)
   101 	{
   102 		goto normal;
   103 	}
   104 
   105 	/* Zeroth, see if there is an engine specific build in the new place. */
   106 	g_SourceMod.BuildPath(Path_SM,
   107 		path,
   108 		PLATFORM_MAX_PATH,
   109 		"extensions/%s." GAMEFIX "." PLATFORM_LIB_EXT,
   110 		filename);
   111 
   112 	if (g_LibSys.IsPathFile(path))
   113 	{
   114 		goto found;
   115 	}
   116 
   117 	/* First see if there is an engine specific build! */
   118 	g_SourceMod.BuildPath(Path_SM, 
   119 		path, 
   120 		PLATFORM_MAX_PATH,
   121 		"extensions/auto." GAMEFIX "/%s." PLATFORM_LIB_EXT,
   122 		filename);
   123 
   124 normal:
   125 	/* Try the "normal" version */
   126 	if (!g_LibSys.IsPathFile(path))
   127 	{
   128 		g_SourceMod.BuildPath(Path_SM,
   129 			path,
   130 			PLATFORM_MAX_PATH,
   131 			"extensions/%s." PLATFORM_LIB_EXT,
   132 			filename);
   133 	}
   134 
   135 found:
   136 	Initialize(filename, path);
   137 }
   138 
   139 bool CRemoteExtension::Load(char *error, size_t maxlength)
   140 {
   141 	if (!PerformAPICheck(error, maxlength))
   142 	{
   143 		m_pAPI = NULL;
   144 		return false;
   145 	}
   146 
   147 	if (!CExtension::Load(error, maxlength))
   148 	{
   149 		m_pAPI = NULL;
   150 		return false;
   151 	}
   152 
   153 	return true;
   154 }
   155 
   156 bool CLocalExtension::Load(char *error, size_t maxlength)
   157 {
   158 	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
   159 
   160 	if (m_pLib == NULL)
   161 	{
   162 		return false;
   163 	}
   164 
   165 	typedef IExtensionInterface *(*GETAPI)();
   166 	GETAPI pfnGetAPI = NULL;
   167 
   168 	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
   169 	{
   170 		m_pLib->CloseLibrary();
   171 		m_pLib = NULL;
   172 		snprintf(error, maxlength, "Unable to find extension entry point");
   173 		return false;
   174 	}
   175 
   176 	m_pAPI = pfnGetAPI();
   177 
   178 	/* Check pointer and version */
   179 	if (!PerformAPICheck(error, maxlength))
   180 	{
   181 		m_pLib->CloseLibrary();
   182 		m_pLib = NULL;
   183 		m_pAPI = NULL;
   184 		return false;
   185 	}
   186 
   187 	/* Load as MM:S */
   188 	if (m_pAPI->IsMetamodExtension())
   189 	{
   190 		bool already;
   191 		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
   192 
   193 		// Check the plugin didn't refuse load
   194 		Pl_Status status;
   195 		
   196 #ifndef METAMOD_PLAPI_VERSION
   197 		const char *file;
   198 		PluginId source;
   199 #endif
   200 		
   201 		if (!m_PlId || (
   202 #ifndef METAMOD_PLAPI_VERSION
   203 			g_pMMPlugins->Query(m_PlId, file, status, source)
   204 #else
   205 			g_pMMPlugins->Query(m_PlId, NULL, &status, NULL)
   206 #endif			
   207 			&& status < Pl_Paused))
   208 		{
   209 			m_pLib->CloseLibrary();
   210 			m_pLib = NULL;
   211 			m_pAPI = NULL;
   212 			return false;
   213 		}
   214 	}
   215 
   216 	if (!CExtension::Load(error, maxlength))
   217 	{
   218 		if (m_pAPI->IsMetamodExtension())
   219 		{
   220 			if (m_PlId)
   221 			{
   222 				char dummy[255];
   223 				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
   224 				m_PlId = 0;
   225 			}
   226 		}
   227 		m_pLib->CloseLibrary();
   228 		m_pLib = NULL;
   229 		m_pAPI = NULL;
   230 		return false;
   231 	}
   232 
   233 	return true;
   234 }
   235 
   236 void CRemoteExtension::Unload()
   237 {
   238 }
   239 
   240 void CLocalExtension::Unload()
   241 {
   242 	if (m_pAPI != NULL && m_PlId)
   243 	{
   244 		char temp_buffer[255];
   245 		g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
   246 		m_PlId = 0;
   247 	}
   248 
   249 	if (m_pLib != NULL)
   250 	{
   251 		m_pLib->CloseLibrary();
   252 		m_pLib = NULL;
   253 	}
   254 }
   255 
   256 bool CRemoteExtension::Reload(char *error, size_t maxlength)
   257 {
   258 	snprintf(error, maxlength, "Remote extensions do not support reloading");
   259 	return false;
   260 }
   261 
   262 bool CLocalExtension::Reload(char *error, size_t maxlength)
   263 {
   264 	if (m_pLib == NULL) // FIXME: just load it instead?
   265 		return false;
   266 	
   267 	m_pAPI->OnExtensionUnload();
   268 	Unload();
   269 	
   270 	return Load(error, maxlength);
   271 }
   272 
   273 bool CRemoteExtension::IsExternal()
   274 {
   275 	return true;
   276 }
   277 
   278 bool CLocalExtension::IsExternal()
   279 {
   280 	return false;
   281 }
   282 
   283 CExtension::~CExtension()
   284 {
   285 	DestroyIdentity();
   286 }
   287 
   288 bool CExtension::PerformAPICheck(char *error, size_t maxlength)
   289 {
   290 	if (!m_pAPI)
   291 	{
   292 		snprintf(error, maxlength, "No IExtensionInterface instance provided");
   293 		return false;
   294 	}
   295 	
   296 	if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
   297 	{
   298 		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
   299 		return false;
   300 	}
   301 
   302 	return true;
   303 }
   304 
   305 bool CExtension::Load(char *error, size_t maxlength)
   306 {
   307 	CreateIdentity();
   308 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
   309 	{
   310 		DestroyIdentity();
   311 		return false;
   312 	}
   313 	else
   314 	{
   315 		/* Check if we're past load time */
   316 		if (!g_SourceMod.IsMapLoading())
   317 		{
   318 			m_pAPI->OnExtensionsAllLoaded();
   319 		}
   320 	}
   321 
   322 	return true;
   323 }
   324 
   325 void CExtension::CreateIdentity()
   326 {
   327 	if (m_pIdentToken != NULL)
   328 	{
   329 		return;
   330 	}
   331 
   332 	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
   333 }
   334 
   335 void CExtension::DestroyIdentity()
   336 {
   337 	if (m_pIdentToken == NULL)
   338 	{
   339 		return;
   340 	}
   341 
   342 	g_ShareSys.DestroyIdentity(m_pIdentToken);
   343 	m_pIdentToken = NULL;
   344 }
   345 
   346 void CExtension::MarkAllLoaded()
   347 {
   348 	assert(m_bFullyLoaded == false);
   349 	if (!m_bFullyLoaded)
   350 	{
   351 		m_bFullyLoaded = true;
   352 		m_pAPI->OnExtensionsAllLoaded();
   353 	}
   354 }
   355 
   356 void CExtension::AddPlugin(CPlugin *pPlugin)
   357 {
   358 	/* Unfortunately we have to do this :( */
   359 	if (m_Dependents.find(pPlugin) != m_Dependents.end())
   360 	{
   361 		m_Dependents.push_back(pPlugin);
   362 	}
   363 }
   364 
   365 void CExtension::SetError(const char *error)
   366 {
   367 	m_Error.assign(error);
   368 }
   369 
   370 IExtensionInterface *CExtension::GetAPI()
   371 {
   372 	return m_pAPI;
   373 }
   374 
   375 const char *CExtension::GetFilename()
   376 {
   377 	return m_RealFile.c_str();
   378 }
   379 
   380 const char *CExtension::GetPath() const
   381 {
   382 	return m_Path.c_str();
   383 }
   384 
   385 IdentityToken_t *CExtension::GetIdentity()
   386 {
   387 	return m_pIdentToken;
   388 }
   389 
   390 bool CRemoteExtension::IsLoaded()
   391 {
   392 	return (m_pAPI != NULL);
   393 }
   394 
   395 bool CLocalExtension::IsLoaded()
   396 {
   397 	return (m_pLib != NULL);
   398 }
   399 
   400 void CExtension::AddDependency(const IfaceInfo *pInfo)
   401 {
   402 	if (m_Deps.find(*pInfo) == m_Deps.end())
   403 	{
   404 		m_Deps.push_back(*pInfo);
   405 	}
   406 }
   407 
   408 bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2)
   409 {
   410 	return (i1.iface == i2.iface) && (i1.owner == i2.owner);
   411 }
   412 
   413 void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
   414 {
   415 	IfaceInfo info;
   416 	info.iface = iface;
   417 	info.owner = pOther;
   418 
   419 	List<IfaceInfo>::iterator iter;
   420 	for (iter = m_ChildDeps.begin();
   421 		 iter != m_ChildDeps.end();
   422 		 iter++)
   423 	{
   424 		IfaceInfo &other = (*iter);
   425 		if (other == info)
   426 		{
   427 			return;
   428 		}
   429 	}
   430 
   431 	m_ChildDeps.push_back(info);
   432 }
   433 
   434 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
   435 {
   436 	List<IfaceInfo>::iterator iter = m_Deps.begin();
   437 
   438 	if (iter == m_Deps.end())
   439 	{
   440 		return NULL;
   441 	}
   442 
   443 	if (pOwner)
   444 	{
   445 		*pOwner = (*iter).owner;
   446 	}
   447 	if (pInterface)
   448 	{
   449 		*pInterface = (*iter).iface;
   450 	}
   451 
   452 	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
   453 
   454 	return (ITERATOR *)pIter;
   455 }
   456 
   457 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
   458 {
   459 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   460 	List<IfaceInfo>::iterator _iter;
   461 
   462 	if (_iter == m_Deps.end())
   463 	{
   464 		return false;
   465 	}
   466 
   467 	_iter++;
   468 
   469 	if (pOwner)
   470 	{
   471 		*pOwner = (*_iter).owner;
   472 	}
   473 	if (pInterface)
   474 	{
   475 		*pInterface = (*_iter).iface;
   476 	}
   477 
   478 	*pIter = _iter;
   479 
   480 	if (_iter == m_Deps.end())
   481 	{
   482 		return false;
   483 	}
   484 
   485 	return true;
   486 }
   487 
   488 void CExtension::FreeDependencyIterator(ITERATOR *iter)
   489 {
   490 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   491 
   492 	delete pIter;
   493 }
   494 
   495 void CExtension::AddInterface(SMInterface *pInterface)
   496 {
   497 	m_Interfaces.push_back(pInterface);
   498 }
   499 
   500 bool CExtension::IsRunning(char *error, size_t maxlength)
   501 {
   502 	if (!IsLoaded())
   503 	{
   504 		if (error)
   505 		{
   506 			snprintf(error, maxlength, "%s", m_Error.c_str());
   507 		}
   508 		return false;
   509 	}
   510 
   511 	return m_pAPI->QueryRunning(error, maxlength);
   512 }
   513 
   514 void CExtension::AddLibrary(const char *library)
   515 {
   516 	m_Libraries.push_back(library);
   517 }
   518 
   519 /*********************
   520  * EXTENSION MANAGER *
   521  *********************/
   522 
   523 void CExtensionManager::OnSourceModAllInitialized()
   524 {
   525 	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
   526 	g_PluginSys.AddPluginsListener(this);
   527 	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
   528 	g_ShareSys.AddInterface(NULL, this);
   529 }
   530 
   531 void CExtensionManager::OnSourceModShutdown()
   532 {
   533 	g_RootMenu.RemoveRootConsoleCommand("exts", this);
   534 	g_ShareSys.DestroyIdentType(g_ExtType);
   535 }
   536 
   537 void CExtensionManager::Shutdown()
   538 {
   539 	List<CExtension *>::iterator iter;
   540 
   541 	while ((iter = m_Libs.begin()) != m_Libs.end())
   542 	{
   543 		UnloadExtension((*iter));
   544 	}
   545 }
   546 
   547 void CExtensionManager::TryAutoload()
   548 {
   549 	char path[PLATFORM_MAX_PATH];
   550 
   551 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
   552 
   553 	IDirectory *pDir = g_LibSys.OpenDirectory(path);
   554 	if (!pDir)
   555 	{
   556 		return;
   557 	}
   558 
   559 	const char *lfile;
   560 	size_t len;
   561 	while (pDir->MoreFiles())
   562 	{
   563 		if (pDir->IsEntryDirectory())
   564 		{
   565 			pDir->NextEntry();
   566 			continue;
   567 		}
   568 
   569 		lfile = pDir->GetEntryName();
   570 		len = strlen(lfile);
   571 		if (len <= 9) /* size of ".autoload" */
   572 		{
   573 			pDir->NextEntry();
   574 			continue;
   575 		}
   576 
   577 		if (strcmp(&lfile[len - 9], ".autoload") != 0)
   578 		{
   579 			pDir->NextEntry();
   580 			continue;
   581 		}
   582 
   583 		char file[PLATFORM_MAX_PATH];
   584 		len = UTIL_Format(file, sizeof(file), "%s", lfile);
   585 		strcpy(&file[len - 9], ".ext");
   586 
   587 		LoadAutoExtension(file);
   588 		
   589 		pDir->NextEntry();
   590 	}
   591 }
   592 
   593 IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOnMissing)
   594 {
   595 	/* Remove platform extension if it's there. Compat hack. */
   596 	const char *ext = g_LibSys.GetFileExtension(path);
   597 	if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
   598 	{
   599 		char path2[PLATFORM_MAX_PATH];
   600 		UTIL_Format(path2, sizeof(path2), "%s", path);
   601 		path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
   602 		return LoadAutoExtension(path2, bErrorOnMissing);
   603 	}
   604 
   605 	IExtension *pAlready;
   606 	if ((pAlready=FindExtensionByFile(path)) != NULL)
   607 	{
   608 		return pAlready;
   609 	}
   610 
   611 	char error[256];
   612 	CExtension *p = new CLocalExtension(path);
   613 
   614 	/* We put us in the list beforehand so extensions that check for each other
   615 	 * won't recursively load each other.
   616 	 */
   617 	m_Libs.push_back(p);
   618 
   619 	if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
   620 	{
   621 		if (bErrorOnMissing || g_LibSys.IsPathFile(p->GetPath()))
   622 		{
   623 			g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
   624 		}
   625 
   626 		p->SetError(error);
   627 	}
   628 
   629 	return p;
   630 }
   631 
   632 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
   633 {
   634 	List<CExtension *>::iterator iter;
   635 	CExtension *pExt;
   636 
   637 	/* Chomp off the path */
   638 	char lookup[PLATFORM_MAX_PATH];
   639 	g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
   640 
   641 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   642 	{
   643 		pExt = (*iter);
   644 		if (pExt->IsSameFile(lookup))
   645 		{
   646 			return pExt;
   647 		}
   648 	}
   649 
   650 	return NULL;
   651 }
   652 
   653 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
   654 {
   655 	List<CExtension *>::iterator iter;
   656 	CExtension *pExt;
   657 	IExtensionInterface *pAPI;
   658 	const char *name;
   659 
   660 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   661 	{
   662 		pExt = (*iter);
   663 		if (!pExt->IsLoaded())
   664 		{
   665 			continue;
   666 		}
   667 		if ((pAPI = pExt->GetAPI()) == NULL)
   668 		{
   669 			continue;
   670 		}
   671 		name = pAPI->GetExtensionName();
   672 		if (!name)
   673 		{
   674 			continue;
   675 		}
   676 		if (strcmp(name, ext) == 0)
   677 		{
   678 			return pExt;
   679 		}
   680 	}
   681 
   682 	return NULL;
   683 }
   684 
   685 IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
   686 {
   687 	/* Remove platform extension if it's there. Compat hack. */
   688 	const char *ext = g_LibSys.GetFileExtension(file);
   689 	if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
   690 	{
   691 		char path2[PLATFORM_MAX_PATH];
   692 		UTIL_Format(path2, sizeof(path2), "%s", file);
   693 		path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
   694 		return LoadExtension(path2, error, maxlength);
   695 	}
   696 
   697 	IExtension *pAlready;
   698 	if ((pAlready=FindExtensionByFile(file)) != NULL)
   699 	{
   700 		return pAlready;
   701 	}
   702 
   703 	CExtension *pExt = new CLocalExtension(file);
   704 
   705 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
   706 	{
   707 		pExt->Unload();
   708 		delete pExt;
   709 		return NULL;
   710 	}
   711 
   712 	/* :TODO: do QueryRunning check if the map is loaded */
   713 
   714 	m_Libs.push_back(pExt);
   715 
   716 	return pExt;
   717 }
   718 
   719 void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo)
   720 {
   721 	CExtension *pExt = (CExtension *)pRequester;
   722 	CExtension *pOwner = (CExtension *)pInfo->owner;
   723 
   724 	pExt->AddDependency(pInfo);
   725 
   726 	IExtensionInterface *pAPI = pExt->GetAPI();
   727 	if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface))
   728 	{
   729 		pOwner->AddChildDependent(pExt, pInfo->iface);
   730 	}
   731 }
   732 
   733 void CExtensionManager::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface)
   734 {
   735 	CExtension *pExt = (CExtension *)ext;
   736 	CExtension *pOwner = GetExtensionFromIdent(other);
   737 
   738 	IfaceInfo info;
   739 
   740 	info.iface = (SMInterface *)iface;
   741 	info.owner = pOwner;
   742 
   743 	pExt->AddDependency(&info);
   744 	pOwner->AddChildDependent(pExt, (SMInterface *)iface);
   745 }
   746 
   747 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   748 {
   749 	CExtension *pExt = (CExtension *)pOwner;
   750 
   751 	pExt->AddInterface(pInterface);
   752 }
   753 
   754 void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin )
   755 {
   756 	CExtension *pExt = (CExtension *)pParent;
   757 
   758 	pExt->AddPlugin(pPlugin);
   759 }
   760 
   761 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   762 {
   763 	List<CExtension *>::iterator iter;
   764 
   765 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   766 	{
   767 		(*iter)->DropRefsTo((CPlugin *)plugin);
   768 	}
   769 }
   770 
   771 CExtension *CExtensionManager::FindByOrder(unsigned int num)
   772 {
   773 	if (num < 1 || num > m_Libs.size())
   774 	{
   775 		return NULL;
   776 	}
   777 
   778 	List<CExtension *>::iterator iter = m_Libs.begin();
   779 
   780 	while (iter != m_Libs.end())
   781 	{
   782 		if (--num == 0)
   783 		{
   784 			return (*iter);
   785 		}
   786 		iter++;
   787 	}
   788 
   789 	return NULL;
   790 }
   791 
   792 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   793 {
   794 	if (!_pExt)
   795 	{
   796 		return false;
   797 	}
   798 
   799 	CExtension *pExt = (CExtension *)_pExt;
   800 
   801 	if (m_Libs.find(pExt) == m_Libs.end())
   802 	{
   803 		return false;
   804 	}
   805 
   806 	/* First remove us from internal lists */
   807 	g_ShareSys.RemoveInterfaces(_pExt);
   808 	m_Libs.remove(pExt);
   809 
   810 	List<CExtension *> UnloadQueue;
   811 
   812 	/* Handle dependencies */
   813 	if (pExt->IsLoaded())
   814 	{
   815 		/* Unload any dependent plugins */
   816 		List<CPlugin *>::iterator p_iter = pExt->m_Dependents.begin();
   817 		while (p_iter != pExt->m_Dependents.end())
   818 		{
   819 			/* We have to manually unlink ourselves here, since we're no longer being managed */
   820 			g_PluginSys.UnloadPlugin((*p_iter));
   821 			p_iter = pExt->m_Dependents.erase(p_iter);
   822 		}
   823 
   824 		List<String>::iterator s_iter;
   825 		for (s_iter = pExt->m_Libraries.begin();
   826 			 s_iter != pExt->m_Libraries.end();
   827 			 s_iter++)
   828 		{
   829 			g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
   830 		}
   831 
   832 		/* Notify and/or unload all dependencies */
   833 		List<CExtension *>::iterator c_iter;
   834 		CExtension *pDep;
   835 		IExtensionInterface *pAPI;
   836 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   837 		{
   838 			pDep = (*c_iter);
   839 			if ((pAPI=pDep->GetAPI()) == NULL)
   840 			{
   841 				continue;
   842 			}
   843 			if (pDep == pExt)
   844 			{
   845 				continue;
   846 			}
   847 			/* Now, get its dependency list */
   848 			bool dropped = false;
   849 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   850 			while (i_iter != pDep->m_Deps.end())
   851 			{
   852 				if ((*i_iter).owner == _pExt)
   853 				{
   854 					if (!pAPI->QueryInterfaceDrop((*i_iter).iface))
   855 					{
   856 						if (!dropped)
   857 						{
   858 							dropped = true;
   859 							UnloadQueue.push_back(pDep);
   860 						}
   861 					}
   862 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   863 					i_iter = pDep->m_Deps.erase(i_iter);
   864 				}
   865 				else
   866 				{
   867 					i_iter++;
   868 				}
   869 			}
   870 			/* Flush out any back references to this plugin */
   871 			i_iter = pDep->m_ChildDeps.begin();
   872 			while (i_iter != pDep->m_ChildDeps.end())
   873 			{
   874 				if ((*i_iter).owner == pExt)
   875 				{
   876 					i_iter = pDep->m_ChildDeps.erase(i_iter);
   877 				}
   878 				else
   879 				{
   880 					i_iter++;
   881 				}
   882 			}
   883 		}
   884 
   885 		/* Unbind our natives from Core */
   886 		pExt->DropEverything();
   887 	}
   888 
   889 	IdentityToken_t *pIdentity;
   890 	if ((pIdentity = pExt->GetIdentity()) != NULL)
   891 	{
   892 		SMGlobalClass *glob = SMGlobalClass::head;
   893 		while (glob)
   894 		{
   895 			glob->OnSourceModIdentityDropped(pIdentity);
   896 			glob = glob->m_pGlobalClassNext;
   897 		}
   898 	}
   899 
   900 	/* Tell it to unload */
   901 	if (pExt->IsLoaded())
   902 	{
   903 		IExtensionInterface *pAPI = pExt->GetAPI();
   904 		pAPI->OnExtensionUnload();
   905 	}
   906 
   907 	pExt->Unload();
   908 	delete pExt;
   909 
   910 	List<CExtension *>::iterator iter;
   911 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   912 	{
   913 		/* NOTE: This is safe because the unload function backs out of anything not present */
   914 		UnloadExtension((*iter));
   915 	}
   916 
   917 	return true;
   918 }
   919 
   920 void CExtensionManager::MarkAllLoaded()
   921 {
   922 	List<CExtension *>::iterator iter;
   923 	CExtension *pExt;
   924 
   925 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   926 	{
   927 		pExt = (*iter);
   928 		if (!pExt->IsLoaded())
   929 		{
   930 			continue;
   931 		}
   932 		if (pExt->m_bFullyLoaded)
   933 		{
   934 			continue;
   935 		}
   936 		pExt->MarkAllLoaded();
   937 	}
   938 }
   939 
   940 void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
   941 {
   942 	/* This function doesn't really need to do anything now.  We make sure the 
   943 	 * other extension is loaded, but handling of dependencies is really done 
   944 	 * by the interface fetcher.
   945 	 */
   946 	if (required || autoload)
   947 	{
   948 		LoadAutoExtension(file);
   949 	}
   950 }
   951 
   952 void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
   953 {
   954 	int argcount = command.ArgC();
   955 	if (argcount >= 3)
   956 	{
   957 		const char *cmd = command.Arg(2);
   958 		if (strcmp(cmd, "list") == 0)
   959 		{
   960 			List<CExtension *>::iterator iter;
   961 			CExtension *pExt;
   962 			unsigned int num = 1;
   963 			switch (m_Libs.size())
   964 			{
   965 			case 1:
   966 				{
   967 					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
   968 					break;
   969 				}
   970 			case 0:
   971 				{
   972 					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
   973 					break;
   974 				}
   975 			default:
   976 				{
   977 					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
   978 					break;
   979 				}
   980 			}
   981 			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
   982 			{
   983 				pExt = (*iter);
   984 				if (pExt->IsLoaded())
   985 				{
   986 					char error[255];
   987 					if (!pExt->IsRunning(error, sizeof(error)))
   988 					{
   989 						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
   990 					}
   991 					else
   992 					{
   993 						IExtensionInterface *pAPI = pExt->GetAPI();
   994 						const char *name = pAPI->GetExtensionName();
   995 						const char *version = pAPI->GetExtensionVerString();
   996 						const char *descr = pAPI->GetExtensionDescription();
   997 						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
   998 					}
   999 				} else {
  1000 					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
  1001 				}
  1002 			}
  1003 			return;
  1004 		}
  1005 		else if (strcmp(cmd, "load") == 0)
  1006 		{
  1007 			if (argcount < 4)
  1008 			{
  1009 				g_RootMenu.ConsolePrint("[SM] Usage: sm exts load <file>");
  1010 				return;
  1011 			}
  1012 
  1013 			const char *filename = command.Arg(3);
  1014 			char path[PLATFORM_MAX_PATH];
  1015 			char error[256];
  1016 
  1017 			UTIL_Format(path, sizeof(path), "%s%s%s", filename, !strstr(filename, ".ext") ? ".ext" : "",
  1018 				!strstr(filename, "." PLATFORM_LIB_EXT) ? "." PLATFORM_LIB_EXT : "");
  1019 			
  1020 			if (FindExtensionByFile(path) != NULL)
  1021 			{
  1022 				g_RootMenu.ConsolePrint("[SM] Extension %s is already loaded.", path);
  1023 				return;
  1024 			}
  1025 			
  1026 			if (LoadExtension(path, error, sizeof(error)))
  1027 			{
  1028 				g_RootMenu.ConsolePrint("[SM] Loaded extension %s successfully.", path);
  1029 			} else
  1030 			{
  1031 				g_RootMenu.ConsolePrint("[SM] Extension %s failed to load: %s", path, error);
  1032 			}
  1033 			
  1034 			return;
  1035 		}
  1036 		else if (strcmp(cmd, "info") == 0)
  1037 		{
  1038 			if (argcount < 4)
  1039 			{
  1040 				g_RootMenu.ConsolePrint("[SM] Usage: sm exts info <#>");
  1041 				return;
  1042 			}
  1043 
  1044 			const char *sId = command.Arg(3);
  1045 			unsigned int id = atoi(sId);
  1046 			if (id <= 0)
  1047 			{
  1048 				g_RootMenu.ConsolePrint("[SM] Usage: sm exts info <#>");
  1049 				return;
  1050 			}
  1051 
  1052 			if (m_Libs.size() == 0)
  1053 			{
  1054 				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
  1055 				return;
  1056 			}
  1057 
  1058 			if (id > m_Libs.size())
  1059 			{
  1060 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
  1061 				return;
  1062 			}
  1063 
  1064 			List<CExtension *>::iterator iter = m_Libs.begin();
  1065 			CExtension *pExt = NULL;
  1066 			while (iter != m_Libs.end())
  1067 			{
  1068 				if (--id == 0)
  1069 				{
  1070 					pExt = (*iter);
  1071 					break;
  1072 				}
  1073 				iter++;
  1074 			}
  1075 			/* This should never happen */
  1076 			if (!pExt)
  1077 			{
  1078 				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
  1079 				return;
  1080 			}
  1081 
  1082 			if (!pExt->IsLoaded())
  1083 			{
  1084 				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1085 				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
  1086 			}
  1087 			else
  1088 			{
  1089 				char error[255];
  1090 				if (!pExt->IsRunning(error, sizeof(error)))
  1091 				{
  1092 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1093 					g_RootMenu.ConsolePrint(" Loaded: Yes");
  1094 					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
  1095 				}
  1096 				else
  1097 				{
  1098 					IExtensionInterface *pAPI = pExt->GetAPI();
  1099 					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
  1100 					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
  1101 					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
  1102 					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
  1103 					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
  1104 
  1105 					if (pExt->IsExternal())
  1106 					{
  1107 						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
  1108 					}
  1109 					else if (pAPI->IsMetamodExtension())
  1110 					{
  1111 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
  1112 					}
  1113 					else
  1114 					{
  1115 						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
  1116 					}
  1117 				}
  1118 			}
  1119 			return;
  1120 		}
  1121 		else if (strcmp(cmd, "unload") == 0)
  1122 		{
  1123 			if (argcount < 4)
  1124 			{
  1125 				g_RootMenu.ConsolePrint("[SM] Usage: sm exts unload <#> [code]");
  1126 				return;
  1127 			}
  1128 
  1129 			const char *arg = command.Arg(3);
  1130 			unsigned int num = atoi(arg);
  1131 			CExtension *pExt = FindByOrder(num);
  1132 
  1133 			if (!pExt)
  1134 			{
  1135 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
  1136 				return;
  1137 			}
  1138 
  1139 			if (argcount > 4 && pExt->unload_code)
  1140 			{
  1141 				const char *unload = command.Arg(4);
  1142 				if (pExt->unload_code == (unsigned)atoi(unload))
  1143 				{
  1144 					char filename[PLATFORM_MAX_PATH];
  1145 					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1146 					UnloadExtension(pExt);
  1147 					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1148 				}
  1149 				else
  1150 				{
  1151 					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
  1152 				}
  1153 				return;
  1154 			}
  1155 
  1156 			if (!pExt->IsLoaded() 
  1157 				|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
  1158 			{
  1159 				char filename[PLATFORM_MAX_PATH];
  1160 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1161 				UnloadExtension(pExt);
  1162 				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
  1163 				return;
  1164 			}
  1165 			else
  1166 			{
  1167 				List<CPlugin *> plugins;
  1168 				if (pExt->m_ChildDeps.size())
  1169 				{
  1170 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
  1171 					List<CExtension *>::iterator iter;
  1172 					CExtension *pOther;
  1173 					/* Get list of all extensions */
  1174 					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1175 					{
  1176 						List<IfaceInfo>::iterator i_iter;
  1177 						pOther = (*iter);
  1178 						if (!pOther->IsLoaded() || pOther == pExt)
  1179 						{
  1180 							continue;
  1181 						}
  1182 						/* Get their dependencies */
  1183 						for (i_iter=pOther->m_Deps.begin();
  1184 							 i_iter!=pOther->m_Deps.end();
  1185 							 i_iter++)
  1186 						{
  1187 							/* Is this dependency to us? */
  1188 							if ((*i_iter).owner != pExt)
  1189 							{
  1190 								continue;
  1191 							}
  1192 							/* Will our dependent care? */
  1193 							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
  1194 							{
  1195 								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
  1196 								/* Add to plugin unload list */
  1197 								List<CPlugin *>::iterator p_iter;
  1198 								for (p_iter=pOther->m_Dependents.begin();
  1199 									 p_iter!=pOther->m_Dependents.end();
  1200 									 p_iter++)
  1201 								{
  1202 									if (plugins.find((*p_iter)) == plugins.end())
  1203 									{
  1204 										plugins.push_back((*p_iter));
  1205 									}
  1206 								}
  1207 							}
  1208 						}
  1209 					}
  1210 				}
  1211 				if (pExt->m_Dependents.size())
  1212 				{
  1213 					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
  1214 					List<CPlugin *>::iterator iter;
  1215 					CPlugin *pPlugin;
  1216 					for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++)
  1217 					{
  1218 						pPlugin = (*iter);
  1219 						if (plugins.find(pPlugin) == plugins.end())
  1220 						{
  1221 							plugins.push_back(pPlugin);
  1222 						}
  1223 					}
  1224 					for (iter = plugins.begin(); iter != plugins.end(); iter++)
  1225 					{
  1226 						pPlugin = (*iter);
  1227 						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
  1228 					}
  1229 				}
  1230 				srand(static_cast<int>(time(NULL)));
  1231 				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
  1232 				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
  1233 				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
  1234 
  1235 				return;
  1236 			}
  1237 		}
  1238 		else if (strcmp(cmd, "reload") == 0)
  1239 		{
  1240 			if (argcount < 4)
  1241 			{
  1242 				g_RootMenu.ConsolePrint("[SM] Usage: sm exts reload <#>");
  1243 				return;
  1244 			}
  1245 			
  1246 			const char *arg = command.Arg(3);
  1247 			unsigned int num = atoi(arg);
  1248 			CExtension *pExt = FindByOrder(num);
  1249 
  1250 			if (!pExt)
  1251 			{
  1252 				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
  1253 				return;
  1254 			}
  1255 			
  1256 			if (pExt->IsLoaded())
  1257 			{
  1258 				char filename[PLATFORM_MAX_PATH];
  1259 				char error[255];
  1260 				
  1261 				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
  1262 				
  1263 				if (pExt->Reload(error, sizeof(error)))
  1264 				{
  1265 					g_RootMenu.ConsolePrint("[SM] Extension %s is now reloaded.", filename);
  1266 				}
  1267 				else
  1268 				{
  1269 					g_RootMenu.ConsolePrint("[SM] Extension %s failed to reload: %s", filename, error);
  1270 				}
  1271 					
  1272 				return;
  1273 			} 
  1274 			else
  1275 			{
  1276 				g_RootMenu.ConsolePrint("[SM] Extension %s is not loaded.", pExt->GetFilename());
  1277 				
  1278 				return;
  1279 			}
  1280 			
  1281 		}
  1282 	}
  1283 
  1284 	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
  1285 	g_RootMenu.DrawGenericOption("info", "Extra extension information");
  1286 	g_RootMenu.DrawGenericOption("list", "List extensions");
  1287 	g_RootMenu.DrawGenericOption("load", "Load an extension");
  1288 	g_RootMenu.DrawGenericOption("reload", "Reload an extension");
  1289 	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
  1290 }
  1291 
  1292 void CExtensionManager::ListExtensionsToClient(CPlayer *player, const CCommand &args)
  1293 {
  1294 	char buffer[256];
  1295 	int numExtensions = m_Libs.size();
  1296 	edict_t *edict = player->GetEdict();
  1297 	unsigned int id = 0;
  1298 	unsigned int start = 0;
  1299 
  1300 	if (!numExtensions)
  1301 	{
  1302 		ClientConsolePrint(edict, "[SM] No extensions found.");
  1303 		return;
  1304 	}
  1305 
  1306 	if (args.ArgC() > 2)
  1307 	{
  1308 		start = atoi(args.Arg(2));
  1309 	}
  1310 
  1311 	CExtension *ext;
  1312 	SourceHook::List<CExtension *>::iterator iter;
  1313 
  1314 	for (iter = m_Libs.begin();
  1315 		 iter != m_Libs.end();
  1316 		 iter++)
  1317 	{
  1318 		ext = (*iter);
  1319 
  1320 		char error[255];
  1321 		if (!ext->IsRunning(error, sizeof(error)))
  1322 		{
  1323 			continue;
  1324 		}
  1325 
  1326 		id++;
  1327 		if (id < start)
  1328 		{
  1329 			continue;
  1330 		}
  1331 
  1332 		if (id - start > 10)
  1333 		{
  1334 			break;
  1335 		}
  1336 
  1337 		IExtensionInterface *api = ext->GetAPI();
  1338 
  1339 		const char *name = api->GetExtensionName();
  1340 		const char *version = api->GetExtensionVerString();
  1341 		const char *author = api->GetExtensionAuthor();
  1342 		const char *description = api->GetExtensionDescription();
  1343 
  1344 		size_t len = UTIL_Format(buffer, sizeof(buffer), " \"%s\"", name);
  1345 
  1346 		if (version != NULL && IS_STR_FILLED(version))
  1347 		{
  1348 			len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", version);
  1349 		}
  1350 
  1351 		if (author != NULL && IS_STR_FILLED(author))
  1352 		{
  1353 			len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", author);
  1354 		}
  1355 
  1356 		if (description != NULL && IS_STR_FILLED(description))
  1357 		{
  1358 			len += UTIL_Format(&buffer[len], sizeof(buffer)-len, ": %s", description);
  1359 		}
  1360 
  1361 
  1362 		ClientConsolePrint(edict, "%s", buffer);
  1363 	}
  1364 
  1365 	while (iter != m_Libs.end())
  1366 	{
  1367 		char error[255];
  1368 		if ((*iter)->IsRunning(error, sizeof(error)))
  1369 		{
  1370 			break;
  1371 		}
  1372 	}
  1373 
  1374 	if (iter != m_Libs.end())
  1375 	{
  1376 		ClientConsolePrint(edict, "To see more, type \"sm exts %d\"", id);
  1377 	}
  1378 }
  1379 
  1380 CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
  1381 {
  1382 	if (ptr->type == g_ExtType)
  1383 	{
  1384 		return (CExtension *)(ptr->ptr);
  1385 	}
  1386 
  1387 	return NULL;
  1388 }
  1389 
  1390 CExtensionManager::CExtensionManager()
  1391 {
  1392 }
  1393 
  1394 CExtensionManager::~CExtensionManager()
  1395 {
  1396 }
  1397 
  1398 void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
  1399 {
  1400 	CExtension *pExt = (CExtension *)pSource;
  1401 	pExt->AddLibrary(library);
  1402 	g_PluginSys.OnLibraryAction(library, false, false);
  1403 }
  1404 
  1405 bool CExtensionManager::LibraryExists(const char *library)
  1406 {
  1407 	CExtension *pExt;
  1408 
  1409 	for (List<CExtension *>::iterator iter = m_Libs.begin();
  1410 		 iter != m_Libs.end();
  1411 		 iter++)
  1412 	{
  1413 		pExt = (*iter);
  1414 		for (List<String>::iterator s_iter = pExt->m_Libraries.begin();
  1415 			 s_iter != pExt->m_Libraries.end();
  1416 			 s_iter++)
  1417 		{
  1418 			if ((*s_iter).compare(library) == 0)
  1419 			{
  1420 				return true;
  1421 			}
  1422 		}
  1423 	}
  1424 
  1425 	return false;
  1426 }
  1427 
  1428 IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
  1429 											const char *filepath,
  1430 											const char *filename,
  1431 											char *error,
  1432 											size_t maxlength)
  1433 {
  1434 	IExtension *pAlready;
  1435 	if ((pAlready=FindExtensionByFile(filename)) != NULL)
  1436 	{
  1437 		return pAlready;
  1438 	}
  1439 
  1440 	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
  1441 
  1442 	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
  1443 	{
  1444 		pExt->Unload();
  1445 		delete pExt;
  1446 		return NULL;
  1447 	}
  1448 
  1449 	m_Libs.push_back(pExt);
  1450 
  1451 	return pExt;
  1452 }
  1453 
  1454 void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
  1455 {
  1456 	IExtensionInterface *pAPI;
  1457 	List<CExtension *>::iterator iter;
  1458 
  1459 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
  1460 	{
  1461 		if ((pAPI = (*iter)->GetAPI()) == NULL)
  1462 		{
  1463 			continue;
  1464 		}
  1465 		if (pAPI->GetExtensionVersion() > 3)
  1466 		{
  1467 			pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax);
  1468 		}
  1469 	}
  1470 }
  1471 
  1472 bool CLocalExtension::IsSameFile(const char *file)
  1473 {
  1474 	/* Only care about the shortened name. */
  1475 	return strcmp(file, m_File.c_str()) == 0;
  1476 }
  1477 
  1478 bool CRemoteExtension::IsSameFile(const char *file)
  1479 {
  1480 	/* :TODO: this could be better, but no one uses this API anyway. */
  1481 	return strcmp(file, m_Path.c_str()) == 0;
  1482 }
  1483