core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
parent 2219 5ddd93671335
child 2289 44045ead61cd
permissions -rw-r--r--
Landed sourcepawn-1.2. The big changes:

1) JIT compilation/optimization now occurs per-function, and only when functions are first used. We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT). This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive. If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.

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