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