core/systems/PluginSys.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1252 00865e7118b6
child 1348 ad1199b211ac
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

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