core/PluginSys.cpp
author Fyren <fyrenmoo@gmail.com>
Wed Mar 18 19:20:40 2009 -0700 (2009-03-18)
changeset 2675 1502dc625325
parent 2636 dfa3c6dd31a5
child 2726 c96f3fab8211
permissions -rw-r--r--
Add AskPluginLoad2() to allow silent failure, deprecate AskPluginLoad() (bug 3716, r=dvander)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdio.h>
    33 #include "PluginSys.h"
    34 #include "ShareSys.h"
    35 #include "LibrarySys.h"
    36 #include "HandleSys.h"
    37 #include "ForwardSys.h"
    38 #include "sourcemm_api.h"
    39 #include "sourcemod.h"
    40 #include "TextParsers.h"
    41 #include "Logger.h"
    42 #include "ExtensionSys.h"
    43 #include "sm_srvcmds.h"
    44 #include "sm_stringutil.h"
    45 #include "ConCmdManager.h"
    46 #include "PlayerManager.h"
    47 #include "CoreConfig.h"
    48 #include "Translator.h"
    49 
    50 CPluginManager g_PluginSys;
    51 HandleType_t g_PluginType = 0;
    52 IdentityType_t g_PluginIdent = 0;
    53 
    54 CPlugin::CPlugin(const char *file)
    55 {
    56 	static int MySerial = 0;
    57 
    58 	m_type = PluginType_Private;
    59 	m_status = Plugin_Uncompiled;
    60 	m_bSilentlyFailed = false;
    61 	m_serial = ++MySerial;
    62 	m_pRuntime = NULL;
    63 	m_errormsg[256] = '\0';
    64 	snprintf(m_filename, sizeof(m_filename), "%s", file);
    65 	m_handle = 0;
    66 	m_ident = NULL;
    67 	m_pProps = sm_trie_create();
    68 	m_FakeNativesMissing = false;
    69 	m_LibraryMissing = false;
    70 	m_bGotAllLoaded = false;
    71 	m_pPhrases = g_Translator.CreatePhraseCollection();
    72 	m_MaxClientsVar = NULL;
    73 }
    74 
    75 CPlugin::~CPlugin()
    76 {
    77 	if (m_handle)
    78 	{
    79 		HandleSecurity sec;
    80 		sec.pOwner = g_PluginSys.GetIdentity();
    81 		sec.pIdentity = sec.pOwner;
    82 
    83 		g_HandleSys.FreeHandle(m_handle, &sec);
    84 		g_ShareSys.DestroyIdentity(m_ident);
    85 	}
    86 
    87 	if (m_pRuntime != NULL)
    88 	{
    89 		delete m_pRuntime;
    90 		m_pRuntime = NULL;
    91 	}
    92 
    93 	if (m_pProps)
    94 	{
    95 		sm_trie_destroy(m_pProps);
    96 	}
    97 	for (size_t i=0; i<m_configs.size(); i++)
    98 	{
    99 		delete m_configs[i];
   100 	}
   101 	m_configs.clear();
   102 	if (m_pPhrases != NULL)
   103 	{
   104 		m_pPhrases->Destroy();
   105 		m_pPhrases = NULL;
   106 	}
   107 }
   108 
   109 void CPlugin::InitIdentity()
   110 {
   111 	if (!m_handle)
   112 	{
   113 		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
   114 		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
   115 		m_pRuntime->GetDefaultContext()->SetKey(1, m_ident);
   116 		m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this);
   117 	}
   118 }
   119 
   120 unsigned int CPlugin::CalcMemUsage()
   121 {
   122 	unsigned int base_size = 
   123 		sizeof(CPlugin) 
   124 		+ sizeof(IdentityToken_t)
   125 		+ (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
   126 		+ sm_trie_mem_usage(m_pProps);
   127 
   128 	for (unsigned int i = 0; i < m_configs.size(); i++)
   129 	{
   130 		base_size += m_configs[i]->autocfg.size();
   131 		base_size += m_configs[i]->folder.size();
   132 	}
   133 
   134 	for (List<String>::iterator i = m_Libraries.begin();
   135 		 i != m_Libraries.end();
   136 		 i++)
   137 	{
   138 		base_size += (*i).size();
   139 	}
   140 
   141 	for (List<String>::iterator i = m_RequiredLibs.begin();
   142 		 i != m_RequiredLibs.end();
   143 		 i++)
   144 	{
   145 		base_size += (*i).size();
   146 	}
   147 
   148 	return base_size;
   149 }
   150 
   151 Handle_t CPlugin::GetMyHandle()
   152 {
   153 	return m_handle;
   154 }
   155 
   156 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
   157 {
   158 	char fullpath[PLATFORM_MAX_PATH];
   159 	g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
   160 	FILE *fp = fopen(fullpath, "rb");
   161 
   162 	CPlugin *pPlugin = new CPlugin(file);
   163 
   164 	if (!fp)
   165 	{
   166 		if (error)
   167 		{
   168 			snprintf(error, maxlength, "Unable to open file");
   169 		}
   170 		pPlugin->m_status = Plugin_BadLoad;
   171 		return pPlugin;
   172 	}
   173 
   174 	fclose(fp);
   175 
   176 	return pPlugin;
   177 }
   178 
   179 bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
   180 {
   181 	bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
   182 
   183 	if (exists && remove)
   184 	{
   185 		sm_trie_delete(m_pProps, prop);
   186 	}
   187 
   188 	return exists;
   189 }
   190 
   191 bool CPlugin::SetProperty(const char *prop, void *ptr)
   192 {
   193 	return sm_trie_insert(m_pProps, prop, ptr);
   194 }
   195 
   196 IPluginRuntime *CPlugin::GetRuntime()
   197 {
   198 	return m_pRuntime;
   199 }
   200 
   201 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
   202 {
   203 	PluginStatus old_status = m_status;
   204 	m_status = status;
   205 
   206 	if (old_status == Plugin_Running)
   207 	{
   208 		/* Tell everyone we're now paused */
   209 		g_PluginSys._SetPauseState(this, true);
   210 	}
   211 
   212 	va_list ap;
   213 	va_start(ap, error_fmt);
   214 	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
   215 	va_end(ap);
   216 
   217 	if (m_pRuntime != NULL)
   218 	{
   219 		m_pRuntime->SetPauseState(true);
   220 	}
   221 }
   222 
   223 bool CPlugin::UpdateInfo()
   224 {
   225 	/* Now grab the info */
   226 	uint32_t idx;
   227 	IPluginContext *base = GetBaseContext();
   228 	int err = base->FindPubvarByName("myinfo", &idx);
   229 
   230 	memset(&m_info, 0, sizeof(m_info));
   231 
   232 	if (err == SP_ERROR_NONE)
   233 	{
   234 		struct sm_plugininfo_c_t
   235 		{
   236 			cell_t name;
   237 			cell_t description;
   238 			cell_t author;
   239 			cell_t version;
   240 			cell_t url;
   241 		};
   242 		sm_plugininfo_c_t *cinfo;
   243 		cell_t local_addr;
   244 
   245 		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
   246 		base->LocalToString(cinfo->name, (char **)&m_info.name);
   247 		base->LocalToString(cinfo->description, (char **)&m_info.description);
   248 		base->LocalToString(cinfo->author, (char **)&m_info.author);
   249 		base->LocalToString(cinfo->url, (char **)&m_info.url);
   250 		base->LocalToString(cinfo->version, (char **)&m_info.version);
   251 	}
   252 
   253 	m_info.author = m_info.author ? m_info.author : "";
   254 	m_info.description = m_info.description ? m_info.description : "";
   255 	m_info.name = m_info.name ? m_info.name : "";
   256 	m_info.url = m_info.url ? m_info.url : "";
   257 	m_info.version = m_info.version ? m_info.version : "";
   258 
   259 	if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE)
   260 	{
   261 		struct __version_info
   262 		{
   263 			cell_t version;
   264 			cell_t filevers;
   265 			cell_t date;
   266 			cell_t time;
   267 		};
   268 		__version_info *info;
   269 		cell_t local_addr;
   270 		const char *pDate, *pTime, *pFileVers;
   271 
   272 		pDate = "";
   273 		pTime = "";
   274 
   275 		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
   276 		m_FileVersion = info->version;
   277 		if (m_FileVersion >= 4)
   278 		{
   279 			base->LocalToString(info->date, (char **)&pDate);
   280 			base->LocalToString(info->time, (char **)&pTime);
   281 			UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
   282 		}
   283 		if (m_FileVersion > 5)
   284 		{
   285 			base->LocalToString(info->filevers, (char **)&pFileVers);
   286 			SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers);
   287 			return false;
   288 		}
   289 	}
   290 	else
   291 	{
   292 		m_FileVersion = 0;
   293 	}
   294 
   295 	if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE)
   296 	{
   297 		base->GetPubvarByIndex(idx, &m_MaxClientsVar);
   298 	}
   299 
   300 	return true;
   301 }
   302 
   303 void CPlugin::SyncMaxClients(int max_clients)
   304 {
   305 	if (m_MaxClientsVar == NULL)
   306 	{
   307 		return;
   308 	}
   309 
   310 	*m_MaxClientsVar->offs = max_clients;
   311 }
   312 
   313 void CPlugin::Call_OnPluginStart()
   314 {
   315 	if (m_status != Plugin_Loaded)
   316 	{
   317 		return;
   318 	}
   319 
   320 	m_status = Plugin_Running;
   321 
   322 	SyncMaxClients(g_Players.MaxClients());
   323 
   324 	cell_t result;
   325 	IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart");
   326 	if (!pFunction)
   327 	{
   328 		return;
   329 	}
   330 
   331 	int err;
   332 	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   333 	{
   334 		SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
   335 	}
   336 }
   337 
   338 void CPlugin::Call_OnPluginEnd()
   339 {
   340 	if (m_status > Plugin_Paused)
   341 	{
   342 		return;
   343 	}
   344 
   345 	cell_t result;
   346 	IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd");
   347 	if (!pFunction)
   348 	{
   349 		return;
   350 	}
   351 
   352 	pFunction->Execute(&result);
   353 }
   354 
   355 void CPlugin::Call_OnAllPluginsLoaded()
   356 {
   357 	if (m_status > Plugin_Paused)
   358 	{
   359 		return;
   360 	}
   361 
   362 	if (m_bGotAllLoaded)
   363 	{
   364 		return;
   365 	}
   366 
   367 	m_bGotAllLoaded = true;
   368 
   369 	cell_t result;
   370 	IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded");
   371 	if (pFunction != NULL)
   372 	{
   373 		pFunction->Execute(&result);
   374 	}
   375 
   376 	if (g_OnMapStarted)
   377 	{
   378 		if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL)
   379 		{
   380 			pFunction->Execute(NULL);
   381 		}
   382 	}
   383 
   384 	if (SM_AreConfigsExecuted())
   385 	{
   386 		SM_ExecuteForPlugin(GetBaseContext());
   387 	}
   388 }
   389 
   390 APLRes CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
   391 {
   392 	if (m_status != Plugin_Created)
   393 	{
   394 		return APLRes_Failure;
   395 	}
   396 
   397 	m_status = Plugin_Loaded;
   398 
   399 	int err;
   400 	cell_t result;
   401 	bool haveNewAPL = false;
   402 	IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad2");
   403 
   404 	if (pFunction) 
   405 	{
   406 		haveNewAPL = true;
   407 	}
   408 	else if (!(pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad")))
   409 	{
   410 		return APLRes_Success;
   411 	}
   412 
   413 	pFunction->PushCell(m_handle);
   414 	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
   415 	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
   416 	pFunction->PushCell(maxlength);
   417 	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   418 	{
   419 		return APLRes_Failure;
   420 	}
   421 
   422 	if (haveNewAPL) 
   423 	{
   424 		return (APLRes)result;
   425 	}
   426 	else if (result)
   427 	{
   428 		return APLRes_Success;
   429 	}
   430 	else
   431 	{
   432 		return APLRes_Failure;
   433 	}
   434 }
   435 
   436 void *CPlugin::GetPluginStructure()
   437 {
   438 	return NULL;
   439 }
   440 
   441 IPluginContext *CPlugin::GetBaseContext()
   442 {
   443 	if (m_pRuntime == NULL)
   444 	{
   445 		return NULL;
   446 	}
   447 
   448 	return m_pRuntime->GetDefaultContext();
   449 }
   450 
   451 sp_context_t *CPlugin::GetContext()
   452 {
   453 	return NULL;
   454 }
   455 
   456 const char *CPlugin::GetFilename()
   457 {
   458 	return m_filename;
   459 }
   460 
   461 PluginType CPlugin::GetType()
   462 {
   463 	return m_type;
   464 }
   465 
   466 const sm_plugininfo_t *CPlugin::GetPublicInfo()
   467 {
   468 	if (GetStatus() >= Plugin_Created)
   469 	{
   470 		return NULL;
   471 	}
   472 	return &m_info;
   473 }
   474 
   475 unsigned int CPlugin::GetSerial()
   476 {
   477 	return m_serial;
   478 }
   479 
   480 PluginStatus CPlugin::GetStatus()
   481 {
   482 	return m_status;
   483 }
   484 
   485 bool CPlugin::IsSilentlyFailed()
   486 {
   487 	return m_bSilentlyFailed;
   488 }
   489 
   490 bool CPlugin::IsDebugging()
   491 {
   492 	if (m_pRuntime == NULL)
   493 	{
   494 		return false;
   495 	}
   496 
   497 	return true;
   498 }
   499 
   500 void CPlugin::SetSilentlyFailed(bool sf)
   501 {
   502 	m_bSilentlyFailed = sf;
   503 }
   504 
   505 void CPlugin::LibraryActions(bool dropping)
   506 {
   507 	List<String>::iterator iter;
   508 	for (iter = m_Libraries.begin();
   509 		iter != m_Libraries.end();
   510 		iter++)
   511 	{
   512 		g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping);
   513 	}
   514 }
   515 
   516 bool CPlugin::SetPauseState(bool paused)
   517 {
   518 	if (paused && GetStatus() != Plugin_Running)
   519 	{
   520 		return false;
   521 	} else if (!paused && GetStatus() != Plugin_Paused) {
   522 		return false;
   523 	}
   524 
   525 	if (paused)
   526 	{
   527 		LibraryActions(true);
   528 	}
   529 
   530 	IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange");
   531 	if (pFunction)
   532 	{
   533 		cell_t result;
   534 		pFunction->PushCell(paused ? 1 : 0);
   535 		pFunction->Execute(&result);
   536 	}
   537 
   538 	if (paused)
   539 	{
   540 		m_status = Plugin_Paused;
   541 		m_pRuntime->SetPauseState(true);
   542 	} else {
   543 		m_status = Plugin_Running;
   544 		m_pRuntime->SetPauseState(false);
   545 	}
   546 
   547 	g_PluginSys._SetPauseState(this, paused);
   548 
   549 	if (!paused)
   550 	{
   551 		LibraryActions(false);
   552 	}
   553 
   554 	return true;
   555 }
   556 
   557 IdentityToken_t *CPlugin::GetIdentity()
   558 {
   559 	return m_ident;
   560 }
   561 
   562 bool CPlugin::IsRunnable()
   563 {
   564 	return (m_status <= Plugin_Paused) ? true : false;
   565 }
   566 
   567 time_t CPlugin::GetFileTimeStamp()
   568 {
   569 	char path[PLATFORM_MAX_PATH];
   570 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
   571 #ifdef PLATFORM_WINDOWS
   572 	struct _stat s;
   573 	if (_stat(path, &s) != 0)
   574 #elif defined PLATFORM_POSIX
   575 	struct stat s;
   576 	if (stat(path, &s) != 0)
   577 #endif
   578 	{
   579 		return 0;
   580 	}
   581 	else
   582 	{
   583 		return s.st_mtime;
   584 	}
   585 }
   586 
   587 time_t CPlugin::GetTimeStamp()
   588 {
   589 	return m_LastAccess;
   590 }
   591 
   592 void CPlugin::SetTimeStamp(time_t t)
   593 {
   594 	m_LastAccess = t;
   595 }
   596 
   597 IPhraseCollection *CPlugin::GetPhrases()
   598 {
   599 	return m_pPhrases;
   600 }
   601 
   602 void CPlugin::DependencyDropped(CPlugin *pOwner)
   603 {
   604 	if (!m_pRuntime)
   605 	{
   606 		return;
   607 	}
   608 
   609 	List<String>::iterator reqlib_iter;
   610 	List<String>::iterator lib_iter;
   611 	for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
   612 	{
   613 		for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
   614 		{
   615 			if ((*reqlib_iter) == (*lib_iter))
   616 			{
   617 				m_LibraryMissing = true;
   618 			}
   619 		}	
   620 	}
   621 
   622 	List<NativeEntry *>::iterator iter;
   623 	NativeEntry *pNative;
   624 	sp_native_t *native;
   625 	uint32_t idx;
   626 	unsigned int unbound = 0;
   627 
   628 	for (iter = pOwner->m_Natives.begin();
   629 		 iter != pOwner->m_Natives.end();
   630 		 iter++)
   631 	{
   632 		pNative = (*iter);
   633 		/* Find this native! */
   634 		if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
   635 		{
   636 			continue;
   637 		}
   638 		/* Unbind it */
   639 		m_pRuntime->GetNativeByIndex(idx, &native);
   640 		native->pfn = NULL;
   641 		native->status = SP_NATIVE_UNBOUND;
   642 		unbound++;
   643 	}
   644 
   645 	if (unbound)
   646 	{
   647 		m_FakeNativesMissing = true;
   648 	}
   649 
   650 	/* :IDEA: in the future, add native trapping? */
   651 	if (m_FakeNativesMissing || m_LibraryMissing)
   652 	{
   653 		SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
   654 	}
   655 }
   656 
   657 unsigned int CPlugin::GetConfigCount()
   658 {
   659 	return (unsigned int)m_configs.size();
   660 }
   661 
   662 AutoConfig *CPlugin::GetConfig(unsigned int i)
   663 {
   664 	if (i >= GetConfigCount())
   665 	{
   666 		return NULL;
   667 	}
   668 
   669 	return m_configs[i];
   670 }
   671 
   672 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
   673 {
   674 	/* Do a check for duplicates to prevent double-execution */
   675 	for (size_t i = 0; i < m_configs.size(); i++)
   676 	{
   677 		if (m_configs[i]->autocfg.compare(cfg) == 0
   678 			&& m_configs[i]->folder.compare(folder) == 0
   679 			&& m_configs[i]->create == autoCreate)
   680 		{
   681 			return;
   682 		}
   683 	}
   684 
   685 	AutoConfig *c = new AutoConfig;
   686 
   687 	c->autocfg = cfg;
   688 	c->folder = folder;
   689 	c->create = autoCreate;
   690 
   691 	m_configs.push_back(c);
   692 }
   693 
   694 void CPlugin::DropEverything()
   695 {
   696 	CPlugin *pOther;
   697 	List<CPlugin *>::iterator iter;
   698 	List<WeakNative>::iterator wk_iter;
   699 
   700 	/* Tell everyone that depends on us that we're about to drop */
   701 	for (iter = m_Dependents.begin();
   702 		 iter != m_Dependents.end(); 
   703 		 iter++)
   704 	{
   705 		pOther = (*iter);
   706 		pOther->DependencyDropped(this);
   707 	}
   708 
   709 	/* Note: we don't care about things we depend on. 
   710 	 * The reason is that extensions have their own cleanup 
   711 	 * code for plugins.  Although the "right" design would be 
   712 	 * to centralize that here, i'm omitting it for now.  Thus, 
   713 	 * the code below to walk the plugins list will suffice.
   714 	 */
   715 	
   716 	/* Other plugins could be holding weak references that were 
   717 	 * added by us.  We need to clean all of those up now.
   718 	 */
   719 	for (iter = g_PluginSys.m_plugins.begin();
   720 		 iter != g_PluginSys.m_plugins.end();
   721 		 iter++)
   722 	{
   723 		(*iter)->DropRefsTo(this);
   724 	}
   725 
   726 	/* Proceed with the rest of the necessities. */
   727 	CNativeOwner::DropEverything();
   728 }
   729 
   730 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
   731 {
   732 	NativeEntry *pEntry;
   733 
   734 	if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
   735 	{
   736 		return false;
   737 	}
   738 
   739 	m_Natives.push_back(pEntry);
   740 
   741 	return true;
   742 }
   743 
   744 /*******************
   745  * PLUGIN ITERATOR *
   746  *******************/
   747 
   748 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
   749 {
   750 	mylist = _mylist;
   751 	Reset();
   752 }
   753 
   754 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   755 {
   756 	return (*current);
   757 }
   758 
   759 bool CPluginManager::CPluginIterator::MorePlugins()
   760 {
   761 	return (current != mylist->end());
   762 }
   763 
   764 void CPluginManager::CPluginIterator::NextPlugin()
   765 {
   766 	current++;
   767 }
   768 
   769 void CPluginManager::CPluginIterator::Release()
   770 {
   771 	g_PluginSys.ReleaseIterator(this);
   772 }
   773 
   774 CPluginManager::CPluginIterator::~CPluginIterator()
   775 {
   776 }
   777 
   778 void CPluginManager::CPluginIterator::Reset()
   779 {
   780 	current = mylist->begin();
   781 }
   782 
   783 /******************
   784  * PLUGIN MANAGER *
   785  ******************/
   786 
   787 CPluginManager::CPluginManager()
   788 {
   789 	m_LoadLookup = sm_trie_create();
   790 	m_AllPluginsLoaded = false;
   791 	m_MyIdent = NULL;
   792 	m_LoadingLocked = false;
   793 }
   794 
   795 CPluginManager::~CPluginManager()
   796 {
   797 	/* :NOTICE: 
   798 	 * Ignore the fact that there might be plugins in the cache.
   799 	 * This usually means that Core is not being unloaded properly, and everything
   800 	 * will crash anyway.  YAY
   801 	 */
   802 	sm_trie_destroy(m_LoadLookup);
   803 
   804 	CStack<CPluginManager::CPluginIterator *>::iterator iter;
   805 	for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
   806 	{
   807 		delete (*iter);
   808 	}
   809 	m_iters.popall();
   810 }
   811 
   812 void CPluginManager::Shutdown()
   813 {
   814 	List<CPlugin *>::iterator iter;
   815 
   816 	while ((iter = m_plugins.begin()) != m_plugins.end())
   817 	{
   818 		UnloadPlugin(*iter);
   819 	}
   820 }
   821 
   822 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
   823 {
   824 	/* First read in the database of plugin settings */
   825 	SMCError err;
   826 	SMCStates states;
   827 	m_AllPluginsLoaded = false;
   828 	if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
   829 	{
   830 		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
   831 		const char *err_msg = textparsers->GetSMCErrorString(err);
   832 		if (err_msg)
   833 		{
   834 			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
   835 		}
   836 	}
   837 
   838 	LoadPluginsFromDir(basedir, NULL);
   839 }
   840 
   841 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
   842 {
   843 	char base_path[PLATFORM_MAX_PATH];
   844 
   845 	/* Form the current path to start reading from */
   846 	if (localpath == NULL)
   847 	{
   848 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
   849 	} else {
   850 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
   851 	}
   852 
   853 	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
   854 
   855 	if (!dir)
   856 	{
   857 		char error[256];
   858 		g_LibSys.GetPlatformError(error, sizeof(error));
   859 		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
   860 		g_Logger.LogError("[SM] Platform returned error: %s", error);
   861 		return;
   862 	}
   863 
   864 	while (dir->MoreFiles())
   865 	{
   866 		if (dir->IsEntryDirectory() 
   867 			&& (strcmp(dir->GetEntryName(), ".") != 0)
   868 			&& (strcmp(dir->GetEntryName(), "..") != 0)
   869 			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
   870 			&& (strcmp(dir->GetEntryName(), "optional") != 0))
   871 		{
   872 			char new_local[PLATFORM_MAX_PATH];
   873 			if (localpath == NULL)
   874 			{
   875 				/* If no path yet, don't add a former slash */
   876 				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
   877 			} else {
   878 				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
   879 			}
   880 			LoadPluginsFromDir(basedir, new_local);
   881 		} else if (dir->IsEntryFile()) {
   882 			const char *name = dir->GetEntryName();
   883 			size_t len = strlen(name);
   884 			if (len >= 4
   885 				&& strcmp(&name[len-4], ".smx") == 0)
   886 			{
   887 				/* If the filename matches, load the plugin */
   888 				char plugin[PLATFORM_MAX_PATH];
   889 				if (localpath == NULL)
   890 				{
   891 					snprintf(plugin, sizeof(plugin), "%s", name);
   892 				} else {
   893 					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
   894 				}
   895 				LoadAutoPlugin(plugin);
   896 			}
   897 		}
   898 		dir->NextEntry();
   899 	}
   900 	g_LibSys.CloseDirectory(dir);
   901 }
   902 
   903 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
   904 {
   905 	if (m_LoadingLocked)
   906 	{
   907 		return LoadRes_NeverLoad;
   908 	}
   909 
   910 	int err;
   911 	bool no_load = false;
   912 	PluginSettings *pset;
   913 	unsigned int setcount = m_PluginInfo.GetSettingsNum();
   914 	for (unsigned int i=0; i<setcount; i++)
   915 	{
   916 		if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
   917 		{
   918 			continue;
   919 		}
   920 		if (pset->blockload_val)
   921 		{
   922 			no_load = true;
   923 			break;
   924 		}
   925 	}
   926 
   927 	/**
   928 	 * Does this plugin already exist?
   929 	 */
   930 	CPlugin *pPlugin;
   931 	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
   932 	{
   933 		/* Check to see if we should try reloading it */
   934 		if (pPlugin->GetStatus() == Plugin_BadLoad
   935 			|| pPlugin->GetStatus() == Plugin_Error
   936 			|| pPlugin->GetStatus() == Plugin_Failed
   937 			|| no_load)
   938 		{
   939 			UnloadPlugin(pPlugin);
   940 		}
   941 		else
   942 		{
   943 			if (_plugin)
   944 			{
   945 				*_plugin = pPlugin;
   946 			}
   947 			return LoadRes_AlreadyLoaded;
   948 		}
   949 	}
   950 
   951 	if (no_load)
   952 	{
   953 		return LoadRes_NeverLoad;
   954 	}
   955 
   956 	pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
   957 
   958 	assert(pPlugin != NULL);
   959 
   960 	pPlugin->m_type = PluginType_MapUpdated;
   961 
   962 	ICompilation *co = NULL;
   963 
   964 	if (pPlugin->m_status == Plugin_Uncompiled)
   965 	{
   966 		co = g_pSourcePawn2->StartCompilation();
   967 	}
   968 
   969 	for (unsigned int i=0; i<setcount; i++)
   970 	{
   971 		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
   972 		{
   973 			continue;
   974 		}
   975 		pPlugin->m_type = pset->type_val;
   976 		if (co)
   977 		{
   978 			for (unsigned int j=0; j<pset->opts_num; j++)
   979 			{
   980 				const char *key, *val;
   981 				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
   982 				if (!key || !val)
   983 				{
   984 					continue;
   985 				}
   986 				if ((err = co->SetOption(key, val)) == SP_ERROR_NONE)
   987 				{
   988 					if (error)
   989 					{
   990 						snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
   991 					}
   992 					co->Abort();
   993 					co = NULL;
   994 					break;
   995 				}
   996 			}
   997 		}
   998 	}
   999 
  1000 	/* Do the actual compiling */
  1001 	if (co != NULL)
  1002 	{
  1003 		char fullpath[PLATFORM_MAX_PATH];
  1004 		g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
  1005 
  1006 		pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
  1007 		if (pPlugin->m_pRuntime == NULL)
  1008 		{
  1009 			snprintf(error, 
  1010 				maxlength, 
  1011 				"Unable to load plugin (error %d: %s)", 
  1012 				err, 
  1013 				g_pSourcePawn2->GetErrorString(err));
  1014 			pPlugin->m_status = Plugin_BadLoad;
  1015 		}
  1016 		else
  1017 		{
  1018 			if (pPlugin->UpdateInfo())
  1019 			{
  1020 				pPlugin->m_status = Plugin_Created;
  1021 			}
  1022 			else
  1023 			{
  1024 				UTIL_Format(error, maxlength, "%s", pPlugin->m_errormsg);
  1025 			}
  1026 		}
  1027 	}
  1028 
  1029 	LoadRes loadFailure = LoadRes_Failure;
  1030 	/* Get the status */
  1031 	if (pPlugin->GetStatus() == Plugin_Created)
  1032 	{
  1033 		/* First native pass - add anything from Core */
  1034 		g_ShareSys.BindNativesToPlugin(pPlugin, true);
  1035 		pPlugin->InitIdentity();
  1036 		APLRes result = pPlugin->Call_AskPluginLoad(error, maxlength);  
  1037 		switch (result)
  1038 		{
  1039 		case APLRes_Success:
  1040 			/* Autoload any modules */
  1041 			LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
  1042 			break;
  1043 		case APLRes_Failure:
  1044 			pPlugin->SetErrorState(Plugin_Failed, "%s", error);
  1045 			loadFailure = LoadRes_Failure;
  1046 			break;
  1047 		case APLRes_SilentFailure:
  1048 			pPlugin->SetErrorState(Plugin_Failed, "%s", error);
  1049 			loadFailure = LoadRes_SilentFailure;
  1050 			pPlugin->SetSilentlyFailed(true);
  1051 			break;
  1052 		default:
  1053 			assert(false);
  1054 		}
  1055 	}
  1056 
  1057 	/* Save the time stamp */
  1058 	time_t t = pPlugin->GetFileTimeStamp();
  1059 	pPlugin->SetTimeStamp(t);
  1060 
  1061 	if (_plugin)
  1062 	{
  1063 		*_plugin = pPlugin;
  1064 	}
  1065 
  1066 	return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : loadFailure;
  1067 }
  1068 
  1069 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
  1070 {
  1071 	CPlugin *pl;
  1072 	LoadRes res;
  1073 
  1074 	*wasloaded = false;
  1075 	if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure)
  1076 	{
  1077 		delete pl;
  1078 		return NULL;
  1079 	}
  1080 
  1081 	if (res == LoadRes_AlreadyLoaded)
  1082 	{
  1083 		*wasloaded = true;
  1084 		return pl;
  1085 	}
  1086 
  1087 	if (res == LoadRes_NeverLoad)
  1088 	{
  1089 		if (m_LoadingLocked)
  1090 		{
  1091 			UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
  1092 		}
  1093 		else
  1094 		{
  1095 			UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
  1096 		}
  1097 		return NULL;
  1098 	}
  1099 
  1100 	AddPlugin(pl);
  1101 
  1102 	/* Run second pass if we need to */
  1103 	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
  1104 	{
  1105 		if (!RunSecondPass(pl, error, maxlength))
  1106 		{
  1107 			UnloadPlugin(pl);
  1108 			return NULL;
  1109 		}
  1110 		pl->Call_OnAllPluginsLoaded();
  1111 	}
  1112 
  1113 	return pl;
  1114 }
  1115 
  1116 void CPluginManager::LoadAutoPlugin(const char *plugin)
  1117 {
  1118 	CPlugin *pl = NULL;
  1119 	LoadRes res;
  1120 	char error[255] = "Unknown error";
  1121 
  1122 	if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
  1123 	{
  1124 		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
  1125 		pl->SetErrorState(
  1126 			pl->GetStatus() == Plugin_BadLoad ? Plugin_BadLoad : Plugin_Failed, 
  1127 			"%s",
  1128 			error);
  1129 	}
  1130 
  1131 	if (res == LoadRes_Successful || res == LoadRes_Failure || res == LoadRes_SilentFailure)
  1132 	{
  1133 		AddPlugin(pl);
  1134 	}
  1135 }
  1136 
  1137 void CPluginManager::AddPlugin(CPlugin *pPlugin)
  1138 {
  1139 	List<IPluginsListener *>::iterator iter;
  1140 	IPluginsListener *pListener;
  1141 
  1142 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1143 	{
  1144 		pListener = (*iter);
  1145 		pListener->OnPluginCreated(pPlugin);
  1146 	}
  1147 
  1148 	m_plugins.push_back(pPlugin);
  1149 	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
  1150 }
  1151 
  1152 void CPluginManager::LoadAll_SecondPass()
  1153 {
  1154 	List<CPlugin *>::iterator iter;
  1155 	CPlugin *pPlugin;
  1156 
  1157 	char error[256];
  1158 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1159 	{
  1160 		pPlugin = (*iter);
  1161 		if (pPlugin->GetStatus() == Plugin_Loaded)
  1162 		{
  1163 			error[0] = '\0';
  1164 			if (!RunSecondPass(pPlugin, error, sizeof(error)))
  1165 			{
  1166 				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
  1167 				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
  1168 			}
  1169 		}
  1170 	}
  1171 
  1172 	m_AllPluginsLoaded = true;
  1173 }
  1174 
  1175 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
  1176 {
  1177 	struct _pl
  1178 	{
  1179 		cell_t name;
  1180 		cell_t file;
  1181 		cell_t required;
  1182 	} *pl;
  1183 
  1184 	IPluginContext *pBase = pPlugin->GetBaseContext();
  1185 	uint32_t num = pBase->GetPubVarsNum();
  1186 	sp_pubvar_t *pubvar;
  1187 	char *name, *file;
  1188 	char pathfile[PLATFORM_MAX_PATH];
  1189 
  1190 	for (uint32_t i=0; i<num; i++)
  1191 	{
  1192 		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  1193 		{
  1194 			continue;
  1195 		}
  1196 		if (strncmp(pubvar->name, "__pl_", 5) == 0)
  1197 		{
  1198 			pl = (_pl *)pubvar->offs;
  1199 			if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
  1200 			{
  1201 				continue;
  1202 			}
  1203 			if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
  1204 			{
  1205 				continue;
  1206 			}
  1207 			g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
  1208 			if (strcmp(pathfile, file) == 0)
  1209 			{
  1210 				continue;
  1211 			}
  1212 			if (pl->required == false)
  1213 			{
  1214 				IPluginFunction *pFunc;
  1215 				char buffer[64];
  1216 				UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
  1217 				if ((pFunc=pBase->GetFunctionByName(buffer)))
  1218 				{
  1219 					cell_t res;
  1220 					pFunc->Execute(&res);
  1221 					if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
  1222 					{
  1223 						if (error)
  1224 						{
  1225 							snprintf(error, maxlength, "Fatal error during initializing plugin load");
  1226 						}
  1227 						return false;
  1228 					}
  1229 				}
  1230 			}
  1231 			else
  1232 			{
  1233 				/* Check that we aren't registering the same library twice */
  1234 				if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
  1235 				{
  1236 					pPlugin->m_RequiredLibs.push_back(name);
  1237 				}
  1238 				else
  1239 				{
  1240 					continue;
  1241 				}
  1242 				List<CPlugin *>::iterator iter;
  1243 				CPlugin *pl;
  1244 				bool found = false;
  1245 				for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1246 				{
  1247 					pl = (*iter);
  1248 					if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
  1249 					{
  1250 						found = true;
  1251 						break;
  1252 					}
  1253 				}
  1254 				if (!found)
  1255 				{
  1256 					if (error)
  1257 					{
  1258 						snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
  1259 					}
  1260 					return false;
  1261 				}
  1262 			}
  1263 		}
  1264 	}
  1265 
  1266 	return true;
  1267 }
  1268 
  1269 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
  1270 {
  1271 	/* Find any extensions this plugin needs */
  1272 	struct _ext
  1273 	{
  1274 		cell_t name;
  1275 		cell_t file;
  1276 		cell_t autoload;
  1277 		cell_t required;
  1278 	} *ext;
  1279 
  1280 	IPluginContext *pBase = pPlugin->GetBaseContext();
  1281 	uint32_t num = pBase->GetPubVarsNum();
  1282 	sp_pubvar_t *pubvar;
  1283 	IExtension *pExt;
  1284 	char path[PLATFORM_MAX_PATH];
  1285 	char *file, *name;
  1286 	for (uint32_t i=0; i<num; i++)
  1287 	{
  1288 		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  1289 		{
  1290 			continue;
  1291 		}
  1292 		if (strncmp(pubvar->name, "__ext_", 6) == 0)
  1293 		{
  1294 			ext = (_ext *)pubvar->offs;
  1295 			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
  1296 			{
  1297 				continue;
  1298 			}
  1299 			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
  1300 			{
  1301 				continue;
  1302 			}
  1303 			if (pass == 1)
  1304 			{
  1305 				/* Attempt to auto-load if necessary */
  1306 				if (ext->autoload)
  1307 				{
  1308 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
  1309 					g_Extensions.LoadAutoExtension(path);
  1310 				}
  1311 			}
  1312 			else if (pass == 2)
  1313 			{
  1314 				/* Is this required? */
  1315 				if (ext->required)
  1316 				{
  1317 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
  1318 					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
  1319 					{
  1320 						pExt = g_Extensions.FindExtensionByName(name);
  1321 					}
  1322 					/* :TODO: should we bind to unloaded extensions?
  1323 					 * Currently the extension manager will ignore this.
  1324 					 */
  1325 					if (!pExt || !pExt->IsRunning(NULL, 0))
  1326 					{
  1327 						if (error)
  1328 						{
  1329 							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
  1330 						}
  1331 						return false;
  1332 					}
  1333 					else
  1334 					{
  1335 						g_Extensions.BindChildPlugin(pExt, pPlugin);
  1336 					}
  1337 				}
  1338 				else
  1339 				{
  1340 					IPluginFunction *pFunc;
  1341 					char buffer[64];
  1342 					UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
  1343 
  1344 					if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
  1345 					{
  1346 						cell_t res;
  1347 						pFunc->Execute(&res);
  1348 						if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
  1349 						{
  1350 							if (error)
  1351 							{
  1352 								UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
  1353 							}
  1354 							return false;
  1355 						}
  1356 					}
  1357 				}
  1358 			}
  1359 		}
  1360 	}
  1361 
  1362 	return true;
  1363 }
  1364 
  1365 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
  1366 {
  1367 	/* Second pass for extension requirements */
  1368 	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
  1369 	{
  1370 		return false;
  1371 	}
  1372 
  1373 	if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
  1374 	{
  1375 		return false;
  1376 	}
  1377 
  1378 	/* Run another binding pass */
  1379 	g_ShareSys.BindNativesToPlugin(pPlugin, false);
  1380 
  1381 	/* Find any unbound natives. Right now, these are not allowed. */
  1382 	IPluginContext *pContext = pPlugin->GetBaseContext();
  1383 	uint32_t num = pContext->GetNativesNum();
  1384 	sp_native_t *native;
  1385 	for (unsigned int i=0; i<num; i++)
  1386 	{
  1387 		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
  1388 		{
  1389 			break;
  1390 		}
  1391 		if (native->status == SP_NATIVE_UNBOUND
  1392 			&& native->name[0] != '@'
  1393 			&& !(native->flags & SP_NTVFLAG_OPTIONAL))
  1394 		{
  1395 			if (error)
  1396 			{
  1397 				snprintf(error, maxlength, "Native \"%s\" was not found", native->name);
  1398 			}
  1399 			return false;
  1400 		}
  1401 	}
  1402 
  1403 	/* Finish by telling all listeners */
  1404 	List<IPluginsListener *>::iterator iter;
  1405 	IPluginsListener *pListener;
  1406 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1407 	{
  1408 		pListener = (*iter);
  1409 		pListener->OnPluginLoaded(pPlugin);
  1410 	}
  1411 	
  1412 	/* Tell this plugin to finish initializing itself */
  1413 	pPlugin->Call_OnPluginStart();
  1414 
  1415 	/* Now, if we have fake natives, go through all plugins that might need rebinding */
  1416 	if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
  1417 	{
  1418 		List<CPlugin *>::iterator pl_iter;
  1419 		CPlugin *pOther;
  1420 		for (pl_iter = m_plugins.begin();
  1421 			 pl_iter != m_plugins.end();
  1422 			 pl_iter++)
  1423 		{
  1424 			pOther = (*pl_iter);
  1425 			if ((pOther->GetStatus() == Plugin_Error
  1426 				&& (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
  1427 				|| pOther->m_FakeNativesMissing)
  1428 			{
  1429 				TryRefreshDependencies(pOther);
  1430 			}
  1431 			else if ((pOther->GetStatus() == Plugin_Running
  1432 					  || pOther->GetStatus() == Plugin_Paused)
  1433 					 && pOther != pPlugin)
  1434 			{
  1435 				List<NativeEntry *>::iterator nv_iter;
  1436 				for (nv_iter = pPlugin->m_Natives.begin();
  1437 					 nv_iter != pPlugin->m_Natives.end();
  1438 					 nv_iter++)
  1439 				{
  1440 					g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
  1441 				}
  1442 			}
  1443 		}
  1444 	}
  1445 
  1446 	/* Go through our libraries and tell other plugins they're added */
  1447 	List<String>::iterator s_iter;
  1448 	for (s_iter = pPlugin->m_Libraries.begin();
  1449 		s_iter != pPlugin->m_Libraries.end();
  1450 		s_iter++)
  1451 	{
  1452 		OnLibraryAction((*s_iter).c_str(), true, false);
  1453 	}
  1454 
  1455 	/* :TODO: optimize? does this even matter? */
  1456 	pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
  1457 
  1458 	return true;
  1459 }
  1460 
  1461 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
  1462 {
  1463 	assert(pPlugin->GetBaseContext() != NULL);
  1464 
  1465 	g_ShareSys.BindNativesToPlugin(pPlugin, false);
  1466 
  1467 	List<String>::iterator lib_iter;
  1468 	List<String>::iterator req_iter;
  1469 	List<CPlugin *>::iterator pl_iter;
  1470 	CPlugin *pl;
  1471 	for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
  1472 	{
  1473 		bool found = false;
  1474 		for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
  1475 		{
  1476 			pl = (*pl_iter);
  1477 			for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
  1478 			{
  1479 				if ((*req_iter) == (*lib_iter))
  1480 				{
  1481 					found = true;
  1482 				}
  1483 			}
  1484 		}
  1485 		if (!found)
  1486 		{
  1487 			pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
  1488 			return;
  1489 		}
  1490 	}
  1491 
  1492 	/* Find any unbound natives
  1493 	 * Right now, these are not allowed
  1494 	 */
  1495 	IPluginContext *pContext = pPlugin->GetBaseContext();
  1496 	uint32_t num = pContext->GetNativesNum();
  1497 	sp_native_t *native;
  1498 	for (unsigned int i=0; i<num; i++)
  1499 	{
  1500 		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
  1501 		{
  1502 			break;
  1503 		}
  1504 		if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
  1505 		{
  1506 			pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
  1507 			return;
  1508 		}
  1509 	}
  1510 
  1511 	if (pPlugin->GetStatus() == Plugin_Error)
  1512 	{
  1513 		/* If we got here, all natives are okay again! */
  1514 		pPlugin->m_status = Plugin_Running;
  1515 		if (pPlugin->m_pRuntime->IsPaused())
  1516 		{
  1517 			pPlugin->m_pRuntime->SetPauseState(false);
  1518 			_SetPauseState(pPlugin, false);
  1519 		}
  1520 	}
  1521 }
  1522 
  1523 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
  1524 {
  1525 	CPlugin *pPlugin = (CPlugin *)plugin;
  1526 
  1527 	/* This prevents removal during insertion or anything else weird */
  1528 	if (m_plugins.find(pPlugin) == m_plugins.end())
  1529 	{
  1530 		return false;
  1531 	}
  1532 
  1533 	IPluginContext *pContext = plugin->GetBaseContext();
  1534 	if (pContext != NULL && pContext->IsInExec())
  1535 	{
  1536 		char buffer[255];
  1537 		UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
  1538 		engine->ServerCommand(buffer);
  1539 		return false;
  1540 	}
  1541 
  1542 	/* Remove us from the lookup table and linked list */
  1543 	m_plugins.remove(pPlugin);
  1544 	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
  1545 
  1546 	/* Go through our libraries and tell other plugins they're gone */
  1547 	List<String>::iterator s_iter;
  1548 	for (s_iter = pPlugin->m_Libraries.begin();
  1549 		 s_iter != pPlugin->m_Libraries.end();
  1550 		 s_iter++)
  1551 	{
  1552 		OnLibraryAction((*s_iter).c_str(), true, true);
  1553 	}
  1554 
  1555 	List<IPluginsListener *>::iterator iter;
  1556 	IPluginsListener *pListener;
  1557 
  1558 	if (pPlugin->GetStatus() <= Plugin_Error)
  1559 	{
  1560 		/* Notify listeners of unloading */
  1561 		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1562 		{
  1563 			pListener = (*iter);
  1564 			pListener->OnPluginUnloaded(pPlugin);
  1565 		}
  1566 		/* Notify plugin */
  1567 		pPlugin->Call_OnPluginEnd();
  1568 	}
  1569 
  1570 	pPlugin->DropEverything();
  1571 
  1572 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1573 	{
  1574 		/* Notify listeners of destruction */
  1575 		pListener = (*iter);
  1576 		pListener->OnPluginDestroyed(pPlugin);
  1577 	}
  1578 	
  1579 	/* Tell the plugin to delete itself */
  1580 	delete pPlugin;
  1581 
  1582 	return true;
  1583 }
  1584 
  1585 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
  1586 {
  1587 	IPlugin *pPlugin;
  1588 	IPluginContext *pContext;
  1589 
  1590 	pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
  1591 
  1592 	if (pContext->GetKey(2, (void **)&pPlugin))
  1593 	{
  1594 		return pPlugin;
  1595 	}
  1596 
  1597 	return NULL;
  1598 }
  1599 
  1600 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
  1601 {
  1602 	return (CPlugin *)FindPluginByContext(ctx);
  1603 }
  1604 
  1605 unsigned int CPluginManager::GetPluginCount()
  1606 {
  1607 	return m_plugins.size();
  1608 }
  1609 
  1610 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
  1611 {
  1612 	m_listeners.push_back(listener);
  1613 }
  1614 
  1615 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
  1616 {
  1617 	m_listeners.remove(listener);
  1618 }
  1619 
  1620 IPluginIterator *CPluginManager::GetPluginIterator()
  1621 {
  1622 	if (m_iters.empty())
  1623 	{
  1624 		return new CPluginIterator(&m_plugins);
  1625 	} else {
  1626 		CPluginIterator *iter = m_iters.front();
  1627 		m_iters.pop();
  1628 		iter->Reset();
  1629 		return iter;
  1630 	}
  1631 }
  1632 
  1633 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
  1634 {
  1635 	m_iters.push(iter);
  1636 }
  1637 
  1638 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
  1639 {
  1640 	/* As an optimization, we do not call strlen, but compute the length in the first pass */
  1641 	size_t alias_len = 0;
  1642 	size_t local_len = 0;
  1643 
  1644 	const char *ptr = alias;
  1645 	unsigned int alias_explicit_paths = 0;
  1646 	unsigned int alias_path_end = 0;
  1647 	while (*ptr != '\0')
  1648 	{
  1649 		if (*ptr == '\\' || *ptr == '/')
  1650 		{
  1651 			alias_explicit_paths++;
  1652 			alias_path_end = alias_len;
  1653 		}
  1654 		alias_len++;
  1655 		ptr++;
  1656 	}
  1657 
  1658 	if (alias_explicit_paths && alias_path_end == alias_len - 1)
  1659 	{
  1660 		/* Trailing slash is totally invalid here */
  1661 		return false;
  1662 	}
  1663 
  1664 	ptr = localpath;
  1665 	unsigned int local_explicit_paths = 0;
  1666 	unsigned int local_path_end = 0;
  1667 	while (*ptr != '\0')
  1668 	{
  1669 		if (*ptr == '\\' || *ptr == '/')
  1670 		{
  1671 			local_explicit_paths++;
  1672 			local_path_end = local_len;
  1673 		}
  1674 		local_len++;
  1675 		ptr++;
  1676 	}
  1677 
  1678 	/* If the alias has more explicit paths than the real path,
  1679 	 * no match will be possible.
  1680 	 */
  1681 	if (alias_explicit_paths > local_explicit_paths)
  1682 	{
  1683 		return false;
  1684 	}
  1685 
  1686 	if (alias_explicit_paths)
  1687 	{
  1688 		/* We need to find if the paths match now.  For example, these should all match:
  1689 		 * csdm     csdm
  1690 		 * csdm     optional/csdm
  1691 		 * csdm/ban optional/crab/csdm/ban
  1692 		*/
  1693 		const char *aliasptr = alias;
  1694 		const char *localptr = localpath;
  1695 		bool match = true;
  1696 		do
  1697 		{
  1698 			if (*aliasptr != *localptr)
  1699 			{
  1700 				/* We have to knock one path off */
  1701 				local_explicit_paths--;
  1702 				if (alias_explicit_paths > local_explicit_paths)
  1703 				{
  1704 					/* Skip out if we're gonna have an impossible match */
  1705 					return false;
  1706 				}
  1707 				/* Eat up localptr tokens until we get a result */
  1708 				while (((localptr - localpath) < (int)local_path_end)
  1709 					&& *localptr != '/'
  1710 					&& *localptr != '\\')
  1711 				{
  1712 					localptr++;
  1713 				}
  1714 				/* Check if we hit the end of our searchable area.
  1715 				 * This probably isn't possible because of the path 
  1716 				 * count check, but it's a good idea anyway.
  1717 				 */
  1718 				if ((localptr - localpath) >= (int)local_path_end)
  1719 				{
  1720 					return false;
  1721 				} else {
  1722 					/* Consume the slash token */
  1723 					localptr++;
  1724 				}
  1725 				/* Reset the alias pointer so we can continue consuming */
  1726 				aliasptr = alias;
  1727 				match = false;
  1728 				continue;
  1729 			}
  1730 			/* Note:
  1731 			 * This is safe because if localptr terminates early, aliasptr will too
  1732 			 */
  1733 			do
  1734 			{
  1735 				/* We should never reach the end of the string because of this check. */
  1736 				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
  1737 				bool localend = (localptr - localpath) > (int)local_path_end;
  1738 				if (aliasend || localend)
  1739 				{
  1740 					if (aliasend && localend)
  1741 					{
  1742 						/* we matched, and we can break out now */
  1743 						match = true;
  1744 						break;
  1745 					}
  1746 					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
  1747 					match = false;
  1748 					break;
  1749 				}
  1750 
  1751 				/* If we got here, it's safe to compare the next two tokens */
  1752 				if (*localptr != *aliasptr)
  1753 				{
  1754 					match = false;
  1755 					break;
  1756 				}
  1757 				localptr++;
  1758 				aliasptr++;
  1759 			} while (true);
  1760 		} while (!match);
  1761 	}
  1762 
  1763 	/* If we got here, it's time to compare filenames */
  1764 	const char *aliasptr = alias;
  1765 	const char *localptr = localpath;
  1766 
  1767 	if (alias_explicit_paths)
  1768 	{
  1769 		aliasptr = &alias[alias_path_end + 1];
  1770 	}
  1771 
  1772 	if (local_explicit_paths)
  1773 	{
  1774 		localptr = &localpath[local_path_end + 1];
  1775 	}
  1776 
  1777 	while (true)
  1778 	{
  1779 		if (*aliasptr == '*')
  1780 		{
  1781 			/* First, see if this is the last character */
  1782 			if ((unsigned)(aliasptr - alias) == alias_len - 1)
  1783 			{
  1784 				/* If so, there's no need to match anything else */
  1785 				return true;
  1786 			}
  1787 			/* Otherwise, we need to search for an appropriate matching sequence in local.
  1788 			 * Note that we only need to search up to the next asterisk.
  1789 			 */
  1790 			aliasptr++;
  1791 			bool match = true;
  1792 			const char *local_orig = localptr;
  1793 			do
  1794 			{
  1795 				match = true;
  1796 				while (*aliasptr != '\0' && *aliasptr != '*')
  1797 				{
  1798 					/* Since aliasptr is never '\0', localptr hitting the end will fail */
  1799 					if (*aliasptr != *localptr)
  1800 					{
  1801 						match = false;
  1802 						break;
  1803 					}
  1804 					aliasptr++;
  1805 					localptr++;
  1806 				}
  1807 				if (!match)
  1808 				{
  1809 					/* If we didn't get a match, we need to advance the search stream.
  1810 					 * This will let us skip tokens while still searching for another match.
  1811 					 */
  1812 					localptr = ++local_orig;
  1813 					/* Make sure we don't go out of bounds */
  1814 					if (*localptr == '\0')
  1815 					{
  1816 						break;
  1817 					}
  1818 				}
  1819 			} while (!match);
  1820 
  1821 			if (!match)
  1822 			{
  1823 				return false;
  1824 			} else {
  1825 				/* If we got a match, move on to the next token */
  1826 				continue;
  1827 			}
  1828 		} else if (*aliasptr == '\0') {
  1829 			if (*localptr == '\0'
  1830 				||
  1831 				strcmp(localptr, ".smx") == 0)
  1832 			{
  1833 				return true;
  1834 			} else {
  1835 				return false;
  1836 			}
  1837 		} else if (*aliasptr != *localptr) {
  1838 			return false;
  1839 		}
  1840 		aliasptr++;
  1841 		localptr++;
  1842 	}
  1843 
  1844 	return true;
  1845 }
  1846 
  1847 bool CPluginManager::IsLateLoadTime() const
  1848 {
  1849 	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
  1850 }
  1851 
  1852 void CPluginManager::OnSourceModAllInitialized()
  1853 {
  1854 	m_MyIdent = g_ShareSys.CreateCoreIdentity();
  1855 
  1856 	HandleAccess sec;
  1857 	g_HandleSys.InitAccessDefaults(NULL, &sec);
  1858 
  1859 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
  1860 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
  1861 
  1862 	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
  1863 	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
  1864 
  1865 	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
  1866 
  1867 	g_ShareSys.AddInterface(NULL, this);
  1868 }
  1869 
  1870 void CPluginManager::OnSourceModShutdown()
  1871 {
  1872 	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
  1873 
  1874 	UnloadAll();
  1875 
  1876 	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
  1877 	g_ShareSys.DestroyIdentType(g_PluginIdent);
  1878 	g_ShareSys.DestroyIdentity(m_MyIdent);
  1879 }
  1880 
  1881 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
  1882 {
  1883 	/* We don't care about the internal object, actually */
  1884 }
  1885 
  1886 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
  1887 {
  1888 	*pSize = ((CPlugin *)object)->CalcMemUsage();
  1889 	return true;
  1890 }
  1891 
  1892 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
  1893 {
  1894 	IPlugin *pPlugin;
  1895 	HandleError _err;
  1896 
  1897 	HandleSecurity sec;
  1898 
  1899 	sec.pOwner = NULL;
  1900 	sec.pIdentity = m_MyIdent;
  1901 
  1902 	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
  1903 	{
  1904 		pPlugin = NULL;
  1905 	}
  1906 
  1907 	if (err)
  1908 	{
  1909 		*err = _err;
  1910 	}
  1911 
  1912 	return pPlugin;
  1913 }
  1914 
  1915 CPlugin *CPluginManager::GetPluginByOrder(int num)
  1916 {
  1917 	if (num < 1 || num > (int)GetPluginCount())
  1918 	{
  1919 		return NULL;
  1920 	}
  1921 
  1922 	CPlugin *pl;
  1923 	int id = 1;
  1924 
  1925 	SourceHook::List<CPlugin *>::iterator iter;
  1926 	for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
  1927 
  1928 	pl = *iter;
  1929 
  1930 	return pl;
  1931 }
  1932 
  1933 const char *CPluginManager::GetStatusText(PluginStatus st)
  1934 {
  1935 	switch (st)
  1936 	{
  1937 	case Plugin_Running:
  1938 		return "Running";
  1939 	case Plugin_Paused:
  1940 		return "Paused";
  1941 	case Plugin_Error:
  1942 		return "Error";
  1943 	case Plugin_Uncompiled:
  1944 		return "Uncompiled";
  1945 	case Plugin_BadLoad:
  1946 		return "Bad Load";
  1947 	case Plugin_Failed:
  1948 		return "Failed";
  1949 	default:
  1950 		assert(false);
  1951 		return "-";
  1952 	}
  1953 }
  1954 
  1955 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
  1956 {
  1957 	int argcount = command.ArgC();
  1958 	if (argcount >= 3)
  1959 	{
  1960 		const char *cmd = command.Arg(2);
  1961 		if (strcmp(cmd, "list") == 0)
  1962 		{
  1963 			char buffer[256];
  1964 			unsigned int id = 1;
  1965 			int plnum = GetPluginCount();
  1966 
  1967 			if (!plnum)
  1968 			{
  1969 				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
  1970 				return;
  1971 			}
  1972 			else
  1973 			{
  1974 				g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
  1975 			}
  1976 
  1977 			CPlugin *pl;
  1978 			SourceHook::List<CPlugin *>::iterator iter;
  1979 			SourceHook::List<CPlugin *> m_FailList;
  1980 
  1981 			for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
  1982 			{
  1983 				pl = (*iter);
  1984 				assert(pl->GetStatus() != Plugin_Created);
  1985 				int len = 0;
  1986 				const sm_plugininfo_t *info = pl->GetPublicInfo();
  1987 				if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
  1988 				{
  1989 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
  1990 
  1991 					if (pl->GetStatus() <= Plugin_Error)
  1992 					{
  1993 						/* Plugin has failed to load. */
  1994 						m_FailList.push_back(pl);
  1995 					}
  1996 				}
  1997 				else
  1998 				{
  1999 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d", id);
  2000 				}
  2001 				if (pl->GetStatus() < Plugin_Created)
  2002 				{
  2003 					if (pl->IsSilentlyFailed())
  2004 						len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " Disabled:");
  2005 					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  2006 					if (IS_STR_FILLED(info->version))
  2007 					{
  2008 						len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
  2009 					}
  2010 					if (IS_STR_FILLED(info->author))
  2011 					{
  2012 						UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
  2013 					}
  2014 				}
  2015 				else
  2016 				{
  2017 					UTIL_Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
  2018 				}
  2019 				g_RootMenu.ConsolePrint("%s", buffer);
  2020 			}
  2021 
  2022 			if (!m_FailList.empty())
  2023 			{
  2024 				g_RootMenu.ConsolePrint("Load Errors:");
  2025 
  2026 				SourceHook::List<CPlugin *>::iterator _iter;
  2027 
  2028 				CPlugin *pl;
  2029 
  2030 				for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
  2031 				{
  2032 					pl = (CPlugin *)*_iter;
  2033 					g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
  2034 				}
  2035 			}
  2036 
  2037 			return;
  2038 		}
  2039 		else if (strcmp(cmd, "load") == 0)
  2040 		{
  2041 			if (argcount < 4)
  2042 			{
  2043 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
  2044 				return;
  2045 			}
  2046 
  2047 			char error[128];
  2048 			bool wasloaded;
  2049 			const char *filename = command.Arg(3);
  2050 
  2051 			char pluginfile[256];
  2052 			const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
  2053 			UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
  2054 
  2055 			IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
  2056 
  2057 			if (wasloaded)
  2058 			{
  2059 				g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
  2060 				return;
  2061 			}
  2062 
  2063 			if (pl)
  2064 			{
  2065 				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
  2066 			}
  2067 			else
  2068 			{
  2069 				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
  2070 			}
  2071 
  2072 			return;
  2073 		}
  2074 		else if (strcmp(cmd, "unload") == 0)
  2075 		{
  2076 			if (argcount < 4)
  2077 			{
  2078 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
  2079 				return;
  2080 			}
  2081 
  2082 			CPlugin *pl;
  2083 			char *end;
  2084 			const char *arg = command.Arg(3);
  2085 			int id = strtol(arg, &end, 10);
  2086 
  2087 			if (*end == '\0')
  2088 			{
  2089 				pl = GetPluginByOrder(id);
  2090 				if (!pl)
  2091 				{
  2092 					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  2093 					return;
  2094 				}
  2095 			}
  2096 			else
  2097 			{
  2098 				char pluginfile[256];
  2099 				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  2100 				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  2101 
  2102 				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  2103 				{
  2104 					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  2105 					return;
  2106 				}
  2107 			}
  2108 
  2109 			char name[PLATFORM_MAX_PATH];
  2110 
  2111 			if (pl->GetStatus() < Plugin_Created)
  2112 			{
  2113 				const sm_plugininfo_t *info = pl->GetPublicInfo();
  2114 				UTIL_Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  2115 			}
  2116 			else
  2117 			{
  2118 				UTIL_Format(name, sizeof(name), "%s", pl->GetFilename());
  2119 			}
  2120 
  2121 			if (UnloadPlugin(pl))
  2122 			{
  2123 				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
  2124 			}
  2125 			else
  2126 			{
  2127 				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
  2128 			}
  2129 
  2130 			return;
  2131 		}
  2132 		else if (strcmp(cmd, "unload_all") == 0)
  2133 		{
  2134 			g_PluginSys.UnloadAll();
  2135 			g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
  2136 			return;
  2137 		}
  2138 		else if (strcmp(cmd, "load_lock") == 0)
  2139 		{
  2140 			if (m_LoadingLocked)
  2141 			{
  2142 				g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
  2143 			}
  2144 			else
  2145 			{
  2146 				m_LoadingLocked = true;
  2147 				g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
  2148 			}
  2149 			return;
  2150 		}
  2151 		else if (strcmp(cmd, "load_unlock") == 0)
  2152 		{
  2153 			if (m_LoadingLocked)
  2154 			{
  2155 				m_LoadingLocked = false;
  2156 				g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
  2157 			}
  2158 			else
  2159 			{
  2160 				g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
  2161 			}
  2162 			return;
  2163 		}
  2164 		else if (strcmp(cmd, "info") == 0)
  2165 		{
  2166 			if (argcount < 4)
  2167 			{
  2168 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
  2169 				return;
  2170 			}
  2171 
  2172 			CPlugin *pl;
  2173 			char *end;
  2174 			const char *arg = command.Arg(3);
  2175 			int id = strtol(arg, &end, 10);
  2176 
  2177 			if (*end == '\0')
  2178 			{
  2179 				pl = GetPluginByOrder(id);
  2180 				if (!pl)
  2181 				{
  2182 					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  2183 					return;
  2184 				}
  2185 			}
  2186 			else
  2187 			{
  2188 				char pluginfile[256];
  2189 				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  2190 				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  2191 
  2192 				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  2193 				{
  2194 					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  2195 					return;
  2196 				}
  2197 			}
  2198 
  2199 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  2200 
  2201 			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
  2202 			if (pl->GetStatus() <= Plugin_Error)
  2203 			{
  2204 				if (IS_STR_FILLED(info->name))
  2205 				{
  2206 					if (IS_STR_FILLED(info->description))
  2207 					{
  2208 						g_RootMenu.ConsolePrint("  Title: %s (%s)", info->name, info->description);
  2209 					} else {
  2210 						g_RootMenu.ConsolePrint("  Title: %s", info->name);
  2211 					}
  2212 				}
  2213 				if (IS_STR_FILLED(info->author))
  2214 				{
  2215 					g_RootMenu.ConsolePrint("  Author: %s", info->author);
  2216 				}
  2217 				if (IS_STR_FILLED(info->version))
  2218 				{
  2219 					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  2220 				}
  2221 				if (IS_STR_FILLED(info->url))
  2222 				{
  2223 					g_RootMenu.ConsolePrint("  URL: %s", info->url);
  2224 				}
  2225 				if (pl->GetStatus() == Plugin_Error)
  2226 				{
  2227 					g_RootMenu.ConsolePrint("  Error: %s", pl->m_errormsg);
  2228 				}
  2229 				else
  2230 				{
  2231 					if (pl->GetStatus() == Plugin_Running)
  2232 					{
  2233 						g_RootMenu.ConsolePrint("  Status: running");
  2234 					}
  2235 					else
  2236 					{
  2237 						g_RootMenu.ConsolePrint("  Status: not running");
  2238 					}
  2239 
  2240 					const char *typestr = "";
  2241 					switch (pl->GetType())
  2242 					{
  2243 					case PluginType_MapUpdated:
  2244 						typestr = "Map Change if Updated";
  2245 						break;
  2246 					case PluginType_MapOnly:
  2247 						typestr = "Map Change";
  2248 						break;
  2249 					case PluginType_Private:
  2250 					case PluginType_Global:
  2251 						typestr = "Never";
  2252 						break;
  2253 					}
  2254 
  2255 					g_RootMenu.ConsolePrint("  Reloads: %s", typestr);
  2256 				}
  2257 				if (pl->m_FileVersion >= 3)
  2258 				{
  2259 					g_RootMenu.ConsolePrint("  Timestamp: %s", pl->m_DateTime);
  2260 				}
  2261 			}
  2262 			else
  2263 			{
  2264 				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
  2265 				if (pl->GetStatus() < Plugin_Created)
  2266 				{
  2267 					g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
  2268 											info->name ? info->name : "<none>",
  2269 											info->version ? info->version : "<none>");
  2270 					if (IS_STR_FILLED(info->url))
  2271 					{
  2272 						g_RootMenu.ConsolePrint("  File URL: %s", info->url);
  2273 					}
  2274 				}
  2275 			}
  2276 
  2277 			return;
  2278 		}
  2279 		else if (strcmp(cmd, "refresh") == 0)
  2280 		{
  2281 			g_SourceMod.DoGlobalPluginLoads();
  2282 			g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
  2283 			return;
  2284 		}
  2285 		else if (strcmp(cmd, "reload") == 0)
  2286 		{
  2287 			if (argcount < 4)
  2288 			{
  2289 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
  2290 				return;
  2291 			}
  2292 
  2293 			CPlugin *pl;
  2294 			char *end;
  2295 			const char *arg = command.Arg(3);
  2296 			int id = strtol(arg, &end, 10);
  2297 
  2298 			if (*end == '\0')
  2299 			{
  2300 				pl = GetPluginByOrder(id);
  2301 				if (!pl)
  2302 				{
  2303 					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  2304 					return;
  2305 				}
  2306 			}
  2307 			else
  2308 			{
  2309 				char pluginfile[256];
  2310 				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  2311 				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  2312 
  2313 				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  2314 				{
  2315 					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  2316 					return;
  2317 				}
  2318 			}
  2319 
  2320 			char name[PLATFORM_MAX_PATH];
  2321 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  2322 			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  2323 
  2324 			if (ReloadPlugin(pl))
  2325 			{
  2326 				g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
  2327 			}
  2328 			else
  2329 			{
  2330 				g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
  2331 			}
  2332 
  2333 			return;
  2334 		}
  2335 	}
  2336 
  2337 	/* Draw the main menu */
  2338 	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
  2339 	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
  2340 	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
  2341 	g_RootMenu.DrawGenericOption("load", "Load a plugin");
  2342 	g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
  2343 	g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
  2344 	g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
  2345 	g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
  2346 	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
  2347 	g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
  2348 }
  2349 
  2350 bool CPluginManager::ReloadPlugin(CPlugin *pl)
  2351 {
  2352 	List<CPlugin *>::iterator iter;
  2353 	char filename[PLATFORM_MAX_PATH];
  2354 	bool wasloaded;
  2355 	PluginType ptype;
  2356 	IPlugin *newpl;
  2357 	int id = 1;
  2358 
  2359 	strcpy(filename, pl->m_filename);
  2360 	ptype = pl->GetType();
  2361 
  2362 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
  2363 	{
  2364 		if ((*iter) == pl)
  2365 		{
  2366 			break;
  2367 		}
  2368 	}
  2369 
  2370 	if (!UnloadPlugin(pl))
  2371 	{
  2372 		return false;
  2373 	}
  2374 	if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
  2375 	{
  2376 		return false;
  2377 	}
  2378 
  2379 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  2380 	{
  2381 		if ((*iter) == (CPlugin *)newpl)
  2382 		{
  2383 			m_plugins.erase(iter);
  2384 			break;
  2385 		}
  2386 	}
  2387 
  2388 	int i;
  2389 	for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
  2390 	{
  2391 	}
  2392 	m_plugins.insert(iter, (CPlugin *)newpl);
  2393 
  2394 	return true;
  2395 }
  2396 
  2397 void CPluginManager::ReloadOrUnloadPlugins()
  2398 {
  2399 	/* If we're in a load lock, just skip this whole bit. */
  2400 	if (m_LoadingLocked)
  2401 	{
  2402 		return;
  2403 	}
  2404 
  2405 	List<CPlugin *>::iterator iter;
  2406 	List<CPlugin *> tmp_list = m_plugins;
  2407 	CPlugin *pl;
  2408 	time_t t;
  2409 
  2410 	for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
  2411 	{
  2412 		pl = (*iter);
  2413 		if (pl->GetType() ==  PluginType_MapOnly)
  2414 		{
  2415 			UnloadPlugin((IPlugin *)pl);
  2416 		}
  2417 		else if (pl->GetType() == PluginType_MapUpdated)
  2418 		{
  2419 			t = pl->GetFileTimeStamp();
  2420 			if (!t || t > pl->GetTimeStamp())
  2421 			{
  2422 				pl->SetTimeStamp(t);
  2423 				UnloadPlugin((IPlugin *)pl);
  2424 			}
  2425 		}
  2426 	}
  2427 }
  2428 
  2429 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
  2430 {
  2431 	List<IPluginsListener *>::iterator iter;
  2432 	IPluginsListener *pListener;
  2433 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  2434 	{
  2435 		pListener = (*iter);
  2436 		pListener->OnPluginPauseChange(pl, paused);
  2437 	}	
  2438 }
  2439 
  2440 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
  2441 {
  2442 	List<CPlugin *>::iterator iter;
  2443 	CPlugin *pPlugin;
  2444 	IPluginFunction *pFunction;
  2445 
  2446 	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
  2447 	{
  2448 		pPlugin = (*iter);
  2449 
  2450 		if (pPlugin->GetStatus() <= Plugin_Paused)
  2451 		{
  2452 			pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
  2453 
  2454 			if (pFunction)
  2455 			{
  2456 				pForward->AddFunction(pFunction);
  2457 			}
  2458 		}
  2459 	}
  2460 }
  2461 
  2462 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
  2463 {
  2464 	if (pToken->type != g_PluginIdent)
  2465 	{
  2466 		return NULL;
  2467 	}
  2468 
  2469 	return (CPlugin *)(pToken->ptr);
  2470 }
  2471 
  2472 void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
  2473 {
  2474 	List<CPlugin *>::iterator iter;
  2475 
  2476 	struct _pl
  2477 	{
  2478 		cell_t name;
  2479 		cell_t file;
  2480 		cell_t required;
  2481 	} *plc;
  2482 
  2483 	struct _ext
  2484 	{
  2485 		cell_t name;
  2486 		cell_t file;
  2487 		cell_t autoload;
  2488 		cell_t required;
  2489 	} *ext;
  2490 
  2491 	const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
  2492 
  2493 	for (iter=m_plugins.begin();
  2494 		 iter!=m_plugins.end();
  2495 		 iter++)
  2496 	{
  2497 		CPlugin *pl = (*iter);
  2498 		if (pl->GetStatus() != Plugin_Running)
  2499 		{
  2500 			continue;
  2501 		}
  2502 		IPluginContext *pContext = pl->GetBaseContext();
  2503 		IPluginFunction *pf = pContext->GetFunctionByName(name);
  2504 		if (!pf)
  2505 		{
  2506 			continue;
  2507 		}
  2508 		uint32_t num_vars = pContext->GetPubVarsNum();
  2509 		for (uint32_t i=0; i<num_vars; i++)
  2510 		{
  2511 			sp_pubvar_t *pubvar;
  2512 			if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  2513 			{
  2514 				continue;
  2515 			}
  2516 			if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
  2517 			{
  2518 				plc = (_pl *)pubvar->offs;
  2519 				if (plc->required)
  2520 				{
  2521 					continue;
  2522 				}
  2523 				char *str;
  2524 				pContext->LocalToString(plc->name, &str);
  2525 				if (strcmp(str, lib) != 0)
  2526 				{
  2527 					continue;
  2528 				}
  2529 				pf->PushString(lib);
  2530 				pf->Execute(NULL);
  2531 			}
  2532 			else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
  2533 			{
  2534 				ext = (_ext *)pubvar->offs;
  2535 				if (ext->required)
  2536 				{
  2537 					continue;
  2538 				}
  2539 				char *str;
  2540 				pContext->LocalToString(ext->name, &str);
  2541 				if (strcmp(str, lib) != 0)
  2542 				{
  2543 					continue;
  2544 				}
  2545 				pf->PushString(lib);
  2546 				pf->Execute(NULL);
  2547 			}
  2548 		}
  2549 	}
  2550 }
  2551 
  2552 bool CPluginManager::LibraryExists(const char *lib)
  2553 {
  2554 	List<CPlugin *>::iterator iter;
  2555 
  2556 	for (iter=m_plugins.begin();
  2557 		 iter!=m_plugins.end();
  2558 		 iter++)
  2559 	{
  2560 		CPlugin *pl = (*iter);
  2561 		if (pl->GetStatus() != Plugin_Running)
  2562 		{
  2563 			continue;
  2564 		}
  2565 		List<String>::iterator s_iter;
  2566 		for (s_iter = pl->m_Libraries.begin();
  2567 			 s_iter != pl->m_Libraries.end();
  2568 			 s_iter++)
  2569 		{
  2570 			if ((*s_iter).compare(lib) == 0)
  2571 			{
  2572 				return true;
  2573 			}
  2574 		}
  2575 	}
  2576 
  2577 	return false;
  2578 }
  2579 
  2580 void CPluginManager::AllPluginsLoaded()
  2581 {
  2582 	List<CPlugin *>::iterator iter;
  2583 	CPlugin *pl;
  2584 
  2585 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  2586 	{
  2587 		pl = (*iter);
  2588 		pl->Call_OnAllPluginsLoaded();
  2589 	}
  2590 }
  2591 
  2592 void CPluginManager::UnloadAll()
  2593 {
  2594 	List<CPlugin *>::iterator iter;
  2595 	while ( (iter = m_plugins.begin()) != m_plugins.end() )
  2596 	{
  2597 		UnloadPlugin((*iter));
  2598 	}
  2599 }
  2600 
  2601 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
  2602 {
  2603 	int id = 1;
  2604 	List<CPlugin *>::iterator iter;
  2605 
  2606 	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
  2607 	{
  2608 		if ((*iter) == pl)
  2609 		{
  2610 			return id;
  2611 		}
  2612 	}
  2613 
  2614 	return -1;
  2615 }
  2616 
  2617 CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
  2618 {
  2619 	int id;
  2620 	char *end;
  2621 	CPlugin *pl;
  2622 	
  2623 	id = strtol(arg, &end, 10);
  2624 
  2625 	if (*end == '\0')
  2626 	{
  2627 		pl = GetPluginByOrder(id);
  2628 		if (!pl)
  2629 		{
  2630 			return NULL;
  2631 		}
  2632 	}
  2633 	else
  2634 	{
  2635 		char pluginfile[256];
  2636 		const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  2637 		UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  2638 
  2639 		if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  2640 		{
  2641 			return NULL;
  2642 		}
  2643 	}
  2644 
  2645 	return pl;
  2646 }
  2647 
  2648 void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue)
  2649 {
  2650 	SyncMaxClients(newvalue);
  2651 }
  2652 
  2653 void CPluginManager::SyncMaxClients(int max_clients)
  2654 {
  2655 	List<CPlugin *>::iterator iter;
  2656 
  2657 	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
  2658 	{
  2659 		(*iter)->SyncMaxClients(max_clients);
  2660 	}
  2661 }
  2662 
  2663 void CPluginManager::ListPluginsToClient(CPlayer *player, const CCommand &args)
  2664 {
  2665 	char buffer[256];
  2666 	unsigned int id = 0;
  2667 	int plnum = GetPluginCount();
  2668 	edict_t *e = player->GetEdict();
  2669 	unsigned int start = 0;
  2670 
  2671 	if (!plnum)
  2672 	{
  2673 		ClientConsolePrint(e, "[SM] No plugins found.");
  2674 		return;
  2675 	}
  2676 
  2677 	if (args.ArgC() > 2)
  2678 	{
  2679 		start = atoi(args.Arg(2));
  2680 	}
  2681 
  2682 	CPlugin *pl;
  2683 	SourceHook::List<CPlugin *>::iterator iter;
  2684 	SourceHook::List<CPlugin *> m_FailList;
  2685 
  2686 	for (iter = m_plugins.begin();
  2687 		 iter != m_plugins.end();
  2688 		 iter++)
  2689 	{
  2690 		pl = (*iter);
  2691 
  2692 		if (pl->GetStatus() != Plugin_Running)
  2693 		{
  2694 			continue;
  2695 		}
  2696 
  2697 		/* Count valid plugins */
  2698 		id++;
  2699 		if (id < start)
  2700 		{
  2701 			continue;
  2702 		}
  2703 
  2704 		if (id - start > 10)
  2705 		{
  2706 			break;
  2707 		}
  2708 
  2709 		size_t len;
  2710 		const sm_plugininfo_t *info = pl->GetPublicInfo();
  2711 		len = UTIL_Format(buffer, sizeof(buffer), " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  2712 		if (IS_STR_FILLED(info->version))
  2713 		{
  2714 			len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
  2715 		}
  2716 		if (IS_STR_FILLED(info->author))
  2717 		{
  2718 			UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
  2719 		}
  2720 		else
  2721 		{
  2722 			UTIL_Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
  2723 		}
  2724 		ClientConsolePrint(e, "%s", buffer);
  2725 	}
  2726 
  2727 	/* See if we can get more plugins */
  2728 	while (iter != m_plugins.end())
  2729 	{
  2730 		if ((*iter)->GetStatus() == Plugin_Running)
  2731 		{
  2732 			break;
  2733 		}
  2734 	}
  2735 
  2736 	/* Do we actually have more plugins? */
  2737 	if (iter != m_plugins.end())
  2738 	{
  2739 		ClientConsolePrint(e, "To see more, type \"sm plugins %d\"", id + 1);
  2740 	}
  2741 }