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