core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Wed Feb 07 19:34:58 2007 +0000 (2007-02-07)
changeset 450 4af70fceaa7f
parent 430 ed37e9767ed4
child 457 9b8a85bf4175
permissions -rw-r--r--
fixed a bug where mapupdated plugins would not get unloaded on deletion
     1 /**
     2  * ===============================================================
     3  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4  * ===============================================================
     5  *
     6  * This file is not open source and may not be copied without explicit
     7  * written permission of AlliedModders LLC.  This file may not be redistributed 
     8  * in whole or significant part.
     9  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10  *
    11  * Version: $Id$
    12  */
    13 
    14 #include <stdio.h>
    15 #include "PluginSys.h"
    16 #include "ShareSys.h"
    17 #include "LibrarySys.h"
    18 #include "HandleSys.h"
    19 #include "sourcemm_api.h"
    20 #include "sourcemod.h"
    21 #include "CTextParsers.h"
    22 #include "CLogger.h"
    23 #include "ExtensionSys.h"
    24 #include "sm_srvcmds.h"
    25 #include "sm_stringutil.h"
    26 
    27 CPluginManager g_PluginSys;
    28 HandleType_t g_PluginType = 0;
    29 IdentityType_t g_PluginIdent = 0;
    30 
    31 CPlugin::CPlugin(const char *file)
    32 {
    33 	static int MySerial = 0;
    34 
    35 	m_type = PluginType_Private;
    36 	m_status = Plugin_Uncompiled;
    37 	m_serial = ++MySerial;
    38 	m_plugin = NULL;
    39 	m_errormsg[256] = '\0';
    40 	snprintf(m_filename, sizeof(m_filename), "%s", file);
    41 	m_handle = 0;
    42 	m_ident = NULL;
    43 }
    44 
    45 CPlugin::~CPlugin()
    46 {
    47 	if (m_handle)
    48 	{
    49 		HandleSecurity sec;
    50 		sec.pOwner = g_PluginSys.GetIdentity();
    51 		sec.pIdentity = sec.pOwner;
    52 
    53 		g_HandleSys.FreeHandle(m_handle, &sec);
    54 		g_ShareSys.DestroyIdentity(m_ident);
    55 	}
    56 
    57 	if (m_ctx.base)
    58 	{
    59 		delete m_ctx.base;
    60 		m_ctx.base = NULL;
    61 	}
    62 	if (m_ctx.ctx)
    63 	{
    64 		m_ctx.vm->FreeContext(m_ctx.ctx);
    65 		m_ctx.ctx = NULL;
    66 	}
    67 	if (m_ctx.co)
    68 	{
    69 		m_ctx.vm->AbortCompilation(m_ctx.co);
    70 		m_ctx.co = NULL;
    71 	}
    72 
    73 	if (m_plugin)
    74 	{
    75 		g_pSourcePawn->FreeFromMemory(m_plugin);
    76 		m_plugin = NULL;
    77 	}
    78 }
    79 
    80 void CPlugin::InitIdentity()
    81 {
    82 	if (!m_handle)
    83 	{
    84 		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
    85 		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
    86 		m_ctx.base->SetIdentity(m_ident);
    87 	}
    88 }
    89 
    90 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
    91 {
    92 	char fullpath[PLATFORM_MAX_PATH+1];
    93 	g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
    94 	FILE *fp = fopen(fullpath, "rb");
    95 
    96 	CPlugin *pPlugin = new CPlugin(file);
    97 
    98 	if (!fp)
    99 	{
   100 		if (error)
   101 		{
   102 			snprintf(error, maxlength, "Unable to open file");
   103 		}
   104 		pPlugin->m_status = Plugin_BadLoad;
   105 		return pPlugin;
   106 	}
   107 
   108 	int err;
   109 	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
   110 	if (pl == NULL)
   111 	{
   112 		fclose(fp);
   113 		if (error)
   114 		{
   115 			snprintf(error, maxlength, "Error %d while parsing plugin", err);
   116 		}
   117 		pPlugin->m_status = Plugin_BadLoad;
   118 		return pPlugin;
   119 	}
   120 
   121 	fclose(fp);
   122 
   123 	pPlugin->m_plugin = pl;
   124 
   125 	return pPlugin;
   126 }
   127 
   128 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
   129 {
   130 	if (!m_plugin)
   131 	{
   132 		return NULL;
   133 	}
   134 
   135 	/* :NOTICE: We will eventually need to change these natives
   136 	 * for swapping in new contexts
   137 	 */
   138 	if (m_ctx.co || m_ctx.ctx)
   139 	{
   140 		return NULL;
   141 	}
   142 
   143 	m_status = Plugin_Uncompiled;
   144 
   145 	m_ctx.vm = vm;
   146 	m_ctx.co = vm->StartCompilation(m_plugin);
   147 
   148 	return m_ctx.co;
   149 }
   150 
   151 void CPlugin::CancelMyCompile()
   152 {
   153 	if (!m_ctx.co)
   154 	{
   155 		return;
   156 	}
   157 
   158 	m_ctx.vm->AbortCompilation(m_ctx.co);
   159 	m_ctx.co = NULL;
   160 	m_ctx.vm = NULL;
   161 }
   162 
   163 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
   164 {
   165 	if (!m_ctx.co)
   166 	{
   167 		return false;
   168 	}
   169 
   170 	int err;
   171 	m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
   172 	if (!m_ctx.ctx)
   173 	{
   174 		memset(&m_ctx, 0, sizeof(m_ctx));
   175 		if (error)
   176 		{
   177 			snprintf(error, maxlength, "Failed to compile (error %d)", err);
   178 		}
   179 		return false;
   180 	}
   181 
   182 	m_ctx.base = new BaseContext(m_ctx.ctx);
   183 	m_ctx.base->SetRunnable(false);
   184 	m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
   185 
   186 	m_status = Plugin_Created;
   187 	m_ctx.co = NULL;
   188 
   189 	UpdateInfo();
   190 
   191 	return true;
   192 }
   193 
   194 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
   195 {
   196 	m_status = status;
   197 
   198 	va_list ap;
   199 	va_start(ap, error_fmt);
   200 	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
   201 	va_end(ap);
   202 
   203 	if (m_ctx.base)
   204 	{
   205 		m_ctx.base->SetRunnable(false);
   206 	}
   207 }
   208 
   209 void CPlugin::UpdateInfo()
   210 {
   211 	/* Now grab the info */
   212 	uint32_t idx;
   213 	IPluginContext *base = GetBaseContext();
   214 	int err = base->FindPubvarByName("myinfo", &idx);
   215 
   216 	memset(&m_info, 0, sizeof(m_info));
   217 
   218 	if (err == SP_ERROR_NONE)
   219 	{
   220 		struct sm_plugininfo_c_t
   221 		{
   222 			cell_t name;
   223 			cell_t description;
   224 			cell_t author;
   225 			cell_t version;
   226 			cell_t url;
   227 		};
   228 		sm_plugininfo_c_t *cinfo;
   229 		cell_t local_addr;
   230 
   231 		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
   232 		base->LocalToString(cinfo->name, (char **)&m_info.name);
   233 		base->LocalToString(cinfo->description, (char **)&m_info.description);
   234 		base->LocalToString(cinfo->author, (char **)&m_info.author);
   235 		base->LocalToString(cinfo->url, (char **)&m_info.url);
   236 		base->LocalToString(cinfo->version, (char **)&m_info.version);
   237 	}
   238 
   239 	m_info.author = m_info.author ? m_info.author : "";
   240 	m_info.description = m_info.description ? m_info.description : "";
   241 	m_info.name = m_info.name ? m_info.name : "";
   242 	m_info.url = m_info.url ? m_info.url : "";
   243 	m_info.version = m_info.version ? m_info.version : "";
   244 }
   245 
   246 void CPlugin::Call_OnPluginStart()
   247 {
   248 	if (m_status != Plugin_Loaded)
   249 	{
   250 		return;
   251 	}
   252 
   253 	m_status = Plugin_Running;
   254 
   255 	cell_t result;
   256 	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
   257 	if (!pFunction)
   258 	{
   259 		return;
   260 	}
   261 
   262 	pFunction->PushCell(m_handle);
   263 	pFunction->Execute(&result);
   264 }
   265 
   266 void CPlugin::Call_OnPluginEnd()
   267 {
   268 	if (m_status < Plugin_Paused)
   269 	{
   270 		return;
   271 	}
   272 
   273 	cell_t result;
   274 	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
   275 	if (!pFunction)
   276 	{
   277 		return;
   278 	}
   279 
   280 	pFunction->Execute(&result);
   281 }
   282 
   283 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
   284 {
   285 	if (m_status != Plugin_Created)
   286 	{
   287 		return false;
   288 	}
   289 
   290 	m_status = Plugin_Loaded;
   291 	m_ctx.base->SetRunnable(true);
   292 
   293 	int err;
   294 	cell_t result;
   295 	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
   296 
   297 	if (!pFunction)
   298 	{
   299 		return true;
   300 	}
   301 
   302 	pFunction->PushCell(m_handle);
   303 	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
   304 	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
   305 	pFunction->PushCell(maxlength);
   306 	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   307 	{
   308 		return false;
   309 	}
   310 
   311 	if (!result || m_status != Plugin_Loaded)
   312 	{
   313 		return false;
   314 	}
   315 
   316 	return true;
   317 }
   318 
   319 const sp_plugin_t *CPlugin::GetPluginStructure() const
   320 {
   321 	return m_plugin;
   322 }
   323 
   324 IPluginContext *CPlugin::GetBaseContext() const
   325 {
   326 	return m_ctx.base;
   327 }
   328 
   329 sp_context_t *CPlugin::GetContext() const
   330 {
   331 	return m_ctx.ctx;
   332 }
   333 
   334 const char *CPlugin::GetFilename() const
   335 {
   336 	return m_filename;
   337 }
   338 
   339 PluginType CPlugin::GetType() const
   340 {
   341 	return m_type;
   342 }
   343 
   344 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
   345 {
   346 	return &m_info;
   347 }
   348 
   349 unsigned int CPlugin::GetSerial() const
   350 {
   351 	return m_serial;
   352 }
   353 
   354 PluginStatus CPlugin::GetStatus() const
   355 {
   356 	return m_status;
   357 }
   358 
   359 bool CPlugin::IsDebugging() const
   360 {
   361 	if (!m_ctx.ctx)
   362 	{
   363 		return false;
   364 	}
   365 
   366 	return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   367 }
   368 
   369 bool CPlugin::SetPauseState(bool paused)
   370 {
   371 	if (paused && GetStatus() != Plugin_Paused)
   372 	{
   373 		return false;
   374 	} else if (!paused && GetStatus() != Plugin_Running) {
   375 		return false;
   376 	}
   377 
   378 	m_status = (paused) ? Plugin_Paused : Plugin_Running;
   379 
   380 	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
   381 	if (pFunction)
   382 	{
   383 		cell_t result;
   384 		pFunction->PushCell(paused ? 1 : 0);
   385 		pFunction->Execute(&result);
   386 	}
   387 
   388 	g_PluginSys._SetPauseState(this, paused);
   389 
   390 	return true;
   391 }
   392 
   393 IdentityToken_t *CPlugin::GetIdentity() const
   394 {
   395 	return m_ident;
   396 }
   397 
   398 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
   399 {
   400 	int err;
   401 
   402 	if (!IsRunnable())
   403 	{
   404 		if (error)
   405 		{
   406 			snprintf(error, maxlength, "Plugin is not runnable.");
   407 		}
   408 		return false;
   409 	}
   410 
   411 	if (debug && IsDebugging())
   412 	{
   413 		if (error)
   414 		{
   415 			snprintf(error, maxlength, "Plugin is already in debug mode.");
   416 		}
   417 		return false;
   418 	} else if (!debug && !IsDebugging()) {
   419 		if (error)
   420 		{
   421 			snprintf(error, maxlength, "Plugins is already in production mode.");
   422 		}
   423 		return false;
   424 	}
   425 
   426 	ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
   427 	if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
   428 	{
   429 		if (error)
   430 		{
   431 			snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
   432 		}
   433 		return false;
   434 	}
   435 
   436 	sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
   437 
   438 	if (new_ctx)
   439 	{
   440 		memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
   441 		new_ctx->hp = m_ctx.ctx->hp;
   442 		new_ctx->sp = m_ctx.ctx->sp;
   443 		new_ctx->frm = m_ctx.ctx->frm;
   444 		new_ctx->dbreak = m_ctx.ctx->dbreak;
   445 		new_ctx->context = m_ctx.ctx->context;
   446 		memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
   447 
   448 		g_pVM->FreeContext(m_ctx.ctx);
   449 		m_ctx.ctx = new_ctx;
   450 		m_ctx.base->SetContext(new_ctx);
   451 
   452 		UpdateInfo();
   453 	} else {
   454 		if (error)
   455 		{
   456 			snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
   457 		}
   458 		return false;
   459 	}
   460 
   461 	return true;
   462 }
   463 
   464 bool CPlugin::IsRunnable() const
   465 {
   466 	return (m_status <= Plugin_Paused) ? true : false;
   467 }
   468 
   469 time_t CPlugin::GetFileTimeStamp()
   470 {
   471 	char path[PLATFORM_MAX_PATH+1];
   472 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
   473 #ifdef PLATFORM_WINDOWS
   474 	struct _stat s;
   475 	if (_stat(path, &s) != 0)
   476 #elif defined PLATFORM_POSIX
   477 	struct stat s;
   478 	if (stat(path, &s) != 0)
   479 #endif
   480 	{
   481 		return 0;
   482 	} else {
   483 		return s.st_mtime;
   484 	}
   485 }
   486 
   487 time_t CPlugin::GetTimeStamp() const
   488 {
   489 	return m_LastAccess;
   490 }
   491 
   492 void CPlugin::SetTimeStamp(time_t t)
   493 {
   494 	m_LastAccess = t;
   495 }
   496 
   497 void CPlugin::AddLangFile(unsigned int index)
   498 {
   499 	m_PhraseFiles.push_back(index);
   500 }
   501 
   502 size_t CPlugin::GetLangFileCount() const
   503 {
   504 	return m_PhraseFiles.size();
   505 }
   506 
   507 unsigned int CPlugin::GetFileByIndex(unsigned int index) const
   508 {
   509 	return m_PhraseFiles.at(index);
   510 }
   511 
   512 /*******************
   513  * PLUGIN ITERATOR *
   514  *******************/
   515 
   516 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
   517 {
   518 	mylist = _mylist;
   519 	Reset();
   520 }
   521 
   522 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   523 {
   524 	return (*current);
   525 }
   526 
   527 bool CPluginManager::CPluginIterator::MorePlugins()
   528 {
   529 	return (current != mylist->end());
   530 }
   531 
   532 void CPluginManager::CPluginIterator::NextPlugin()
   533 {
   534 	current++;
   535 }
   536 
   537 void CPluginManager::CPluginIterator::Release()
   538 {
   539 	g_PluginSys.ReleaseIterator(this);
   540 }
   541 
   542 CPluginManager::CPluginIterator::~CPluginIterator()
   543 {
   544 }
   545 
   546 void CPluginManager::CPluginIterator::Reset()
   547 {
   548 	current = mylist->begin();
   549 }
   550 
   551 /******************
   552  * PLUGIN MANAGER *
   553  ******************/
   554 
   555 CPluginManager::CPluginManager()
   556 {
   557 	m_LoadLookup = sm_trie_create();
   558 	m_AllPluginsLoaded = false;
   559 	m_MyIdent = NULL;
   560 }
   561 
   562 CPluginManager::~CPluginManager()
   563 {
   564 	/* :NOTICE: 
   565 	 * Ignore the fact that there might be plugins in the cache.
   566 	 * This usually means that Core is not being unloaded properly, and everything
   567 	 * will crash anyway.  YAY
   568 	 */
   569 	sm_trie_destroy(m_LoadLookup);
   570 }
   571 
   572 
   573 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
   574 {
   575 	/* First read in the database of plugin settings */
   576 	SMCParseError err;
   577 	unsigned int line, col;
   578 	m_AllPluginsLoaded = false;
   579 	if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
   580 	{
   581 		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
   582 		const char *err_msg = g_TextParser.GetSMCErrorString(err);
   583 		if (err_msg)
   584 		{
   585 			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
   586 		}
   587 	}
   588 
   589 	LoadPluginsFromDir(basedir, NULL);
   590 }
   591 
   592 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
   593 {
   594 	char base_path[PLATFORM_MAX_PATH+1];
   595 
   596 	/* Form the current path to start reading from */
   597 	if (localpath == NULL)
   598 	{
   599 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
   600 	} else {
   601 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
   602 	}
   603 
   604 	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
   605 
   606 	if (!dir)
   607 	{
   608 		char error[256];
   609 		g_LibSys.GetPlatformError(error, sizeof(error));
   610 		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
   611 		g_Logger.LogError("[SM] Platform returned error: %s", error);
   612 		return;
   613 	}
   614 
   615 	while (dir->MoreFiles())
   616 	{
   617 		if (dir->IsEntryDirectory() 
   618 			&& (strcmp(dir->GetEntryName(), ".") != 0)
   619 			&& (strcmp(dir->GetEntryName(), "..") != 0)
   620 			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
   621 			&& (strcmp(dir->GetEntryName(), "optional") != 0))
   622 		{
   623 			char new_local[PLATFORM_MAX_PATH+1];
   624 			if (localpath == NULL)
   625 			{
   626 				/* If no path yet, don't add a former slash */
   627 				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
   628 			} else {
   629 				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
   630 			}
   631 			LoadPluginsFromDir(basedir, new_local);
   632 		} else if (dir->IsEntryFile()) {
   633 			const char *name = dir->GetEntryName();
   634 			size_t len = strlen(name);
   635 			if (len >= 4
   636 				&& strcmp(&name[len-4], ".smx") == 0)
   637 			{
   638 				/* If the filename matches, load the plugin */
   639 				char plugin[PLATFORM_MAX_PATH+1];
   640 				if (localpath == NULL)
   641 				{
   642 					snprintf(plugin, sizeof(plugin), "%s", name);
   643 				} else {
   644 					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
   645 				}
   646 				LoadAutoPlugin(plugin);
   647 			}
   648 		}
   649 		dir->NextEntry();
   650 	}
   651 	g_LibSys.CloseDirectory(dir);
   652 }
   653 
   654 //well i have discovered that gabe newell is very fat, so i wrote this comment now
   655 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
   656 {
   657 	/**
   658 	 * Does this plugin already exist?
   659 	 */
   660 	CPlugin *pPlugin;
   661 	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
   662 	{
   663 		/* Check to see if we should try reloading it */
   664 		if (pPlugin->GetStatus() == Plugin_BadLoad
   665 			|| pPlugin->GetStatus() == Plugin_Error
   666 			|| pPlugin->GetStatus() == Plugin_Failed)
   667 		{
   668 			UnloadPlugin(pPlugin);
   669 		} else {
   670 			if (_plugin)
   671 			{
   672 				*_plugin = pPlugin;
   673 			}
   674 			return LoadRes_AlreadyLoaded;
   675 		}
   676 	}
   677 
   678 	pPlugin = CPlugin::CreatePlugin(path, error, err_max);
   679 
   680 	assert(pPlugin != NULL);
   681 
   682 	pPlugin->m_type = PluginType_MapUpdated;
   683 
   684 	ICompilation *co = NULL;
   685 
   686 	if (pPlugin->m_status == Plugin_Uncompiled)
   687 	{
   688 		co = pPlugin->StartMyCompile(g_pVM);
   689 	}
   690 
   691 	PluginSettings *pset;
   692 	unsigned int setcount = m_PluginInfo.GetSettingsNum();
   693 	for (unsigned int i=0; i<setcount; i++)
   694 	{
   695 		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
   696 		{
   697 			continue;
   698 		}
   699 		pPlugin->m_type = pset->type_val;
   700 		if (co)
   701 		{
   702 			for (unsigned int j=0; j<pset->opts_num; j++)
   703 			{
   704 				const char *key, *val;
   705 				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
   706 				if (!key || !val)
   707 				{
   708 					continue;
   709 				}
   710 				if (!g_pVM->SetCompilationOption(co, key, val))
   711 				{
   712 					if (error)
   713 					{
   714 						snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
   715 					}
   716 					pPlugin->CancelMyCompile();
   717 					co = NULL;
   718 					break;
   719 				}
   720 			}
   721 		}
   722 	}
   723 
   724 	/* Do the actual compiling */
   725 	if (co)
   726 	{
   727 		pPlugin->FinishMyCompile(NULL, 0);
   728 		co = NULL;
   729 	}
   730 
   731 	/* Get the status */
   732 	if (pPlugin->GetStatus() == Plugin_Created)
   733 	{
   734 		AddCoreNativesToPlugin(pPlugin);
   735 		pPlugin->InitIdentity();
   736 		if (pPlugin->Call_AskPluginLoad(error, err_max))
   737 		{
   738 			/* Autoload any modules */
   739 			LoadOrRequireExtensions(pPlugin, 1, error, err_max);
   740 		}
   741 	}
   742 
   743 	/* Save the time stamp */
   744 	time_t t = pPlugin->GetFileTimeStamp();
   745 	pPlugin->SetTimeStamp(t);
   746 
   747 	if (_plugin)
   748 	{
   749 		*_plugin = pPlugin;
   750 	}
   751 
   752 	return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
   753 }
   754 
   755 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max, bool *wasloaded)
   756 {
   757 	CPlugin *pl;
   758 	LoadRes res;
   759 
   760 	*wasloaded = false;
   761 	if ((res=_LoadPlugin(&pl, path, debug, type, error, err_max)) == LoadRes_Failure)
   762 	{
   763 		delete pl;
   764 		return NULL;
   765 	}
   766 
   767 	if (res == LoadRes_AlreadyLoaded)
   768 	{
   769 		*wasloaded = true;
   770 		return pl;
   771 	}
   772 
   773 	AddPlugin(pl);
   774 
   775 	/* Run second pass if we need to */
   776 	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
   777 	{
   778 		if (!RunSecondPass(pl, error, err_max))
   779 		{
   780 			UnloadPlugin(pl);
   781 			return NULL;
   782 		}
   783 	}
   784 
   785 	return pl;
   786 }
   787 
   788 void CPluginManager::LoadAutoPlugin(const char *plugin)
   789 {
   790 	CPlugin *pl;
   791 	LoadRes res;
   792 	char error[255] = "Unknown error";
   793 
   794 	if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
   795 	{
   796 		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
   797 		pl->SetErrorState(Plugin_Failed, "%s", error);
   798 	}
   799 
   800 	if (res == LoadRes_Successful)
   801 	{
   802 		AddPlugin(pl);
   803 	}
   804 }
   805 
   806 void CPluginManager::AddPlugin(CPlugin *pPlugin)
   807 {
   808 	List<IPluginsListener *>::iterator iter;
   809 	IPluginsListener *pListener;
   810 
   811 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   812 	{
   813 		pListener = (*iter);
   814 		pListener->OnPluginCreated(pPlugin);
   815 	}
   816 
   817 	m_plugins.push_back(pPlugin);
   818 	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
   819 }
   820 
   821 void CPluginManager::LoadAll_SecondPass()
   822 {
   823 	List<CPlugin *>::iterator iter;
   824 	CPlugin *pPlugin;
   825 
   826 	char error[256];
   827 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
   828 	{
   829 		pPlugin = (*iter);
   830 		if (pPlugin->GetStatus() == Plugin_Loaded)
   831 		{
   832 			error[0] = '\0';
   833 			if (!RunSecondPass(pPlugin, error, sizeof(error)))
   834 			{
   835 				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
   836 				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
   837 			}
   838 		}
   839 	}
   840 
   841 	m_AllPluginsLoaded = true;
   842 }
   843 
   844 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
   845 {
   846 	/* Find any extensions this plugin needs */
   847 	struct _ext
   848 	{
   849 		cell_t name;
   850 		cell_t file;
   851 		cell_t autoload;
   852 		cell_t required;
   853 	} *ext;
   854 
   855 	IPluginContext *pBase = pPlugin->GetBaseContext();
   856 	uint32_t num = pBase->GetPubVarsNum();
   857 	sp_pubvar_t *pubvar;
   858 	IExtension *pExt;
   859 	char path[PLATFORM_MAX_PATH+1];
   860 	char *file, *name;
   861 	for (uint32_t i=0; i<num; i++)
   862 	{
   863 		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
   864 		{
   865 			continue;
   866 		}
   867 		if (strncmp(pubvar->name, "__ext_", 6) == 0)
   868 		{
   869 			ext = (_ext *)pubvar->offs;
   870 			if (!ext->required && !ext->autoload)
   871 			{
   872 				continue;
   873 			}
   874 			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
   875 			{
   876 				continue;
   877 			}
   878 			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
   879 			{
   880 				continue;
   881 			}
   882 			if (pass == 1)
   883 			{
   884 				/* Attempt to auto-load if necessary */
   885 				if (ext->autoload)
   886 				{
   887 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   888 					g_Extensions.LoadAutoExtension(path);
   889 				}
   890 			} else if (pass == 2) {
   891 				/* Is this required? */
   892 				if (ext->required)
   893 				{
   894 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   895 					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
   896 					{
   897 						pExt = g_Extensions.FindExtensionByName(name);
   898 					}
   899 					/* :TODO: should we bind to unloaded extensions?
   900 					 * Currently the extension manager will ignore this.
   901 					 */
   902 					if (!pExt || !pExt->IsRunning(NULL, 0))
   903 					{
   904 						if (error)
   905 						{
   906 							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
   907 						}
   908 						return false;
   909 					} else {
   910 						g_Extensions.BindChildPlugin(pExt, pPlugin);
   911 					}
   912 				}
   913 			}
   914 		}
   915 	}
   916 
   917 	return true;
   918 }
   919 
   920 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
   921 {
   922 	/* Second pass for extension requirements */
   923 	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
   924 	{
   925 		return false;
   926 	}
   927 
   928 	/* Bind all extra natives */
   929 	g_Extensions.BindAllNativesToPlugin(pPlugin);
   930 
   931 	/* Find any unbound natives
   932 	 * Right now, these are not allowed
   933 	 */
   934 	IPluginContext *pContext = pPlugin->GetBaseContext();
   935 	uint32_t num = pContext->GetNativesNum();
   936 	sp_native_t *native;
   937 	for (unsigned int i=0; i<num; i++)
   938 	{
   939 		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
   940 		{
   941 			break;
   942 		}
   943 		if (native->status == SP_NATIVE_UNBOUND)
   944 		{
   945 			if (error)
   946 			{
   947 				snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
   948 			}
   949 			return false;
   950 		}
   951 	}
   952 
   953 	/* Finish by telling all listeners */
   954 	List<IPluginsListener *>::iterator iter;
   955 	IPluginsListener *pListener;
   956 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   957 	{
   958 		pListener = (*iter);
   959 		pListener->OnPluginLoaded(pPlugin);
   960 	}
   961 	
   962 	/* Tell this plugin to finish initializing itself */
   963 	pPlugin->Call_OnPluginStart();
   964 
   965 	return true;
   966 }
   967 
   968 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
   969 {
   970 	List<sp_nativeinfo_t *>::iterator iter;
   971 
   972 	for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
   973 	{
   974 		sp_nativeinfo_t *natives = (*iter);
   975 		IPluginContext *ctx = pPlugin->GetBaseContext();
   976 		unsigned int i=0;
   977 		/* Attempt to bind every native! */
   978 		while (natives[i].func != NULL)
   979 		{
   980 			ctx->BindNative(&natives[i++]);
   981 		}
   982 	}
   983 }
   984 
   985 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
   986 {
   987 	CPlugin *pPlugin = (CPlugin *)plugin;
   988 
   989 	/* This prevents removal during insertion or anything else weird */
   990 	if (m_plugins.find(pPlugin) == m_plugins.end())
   991 	{
   992 		return false;
   993 	}
   994 
   995 	List<IPluginsListener *>::iterator iter;
   996 	IPluginsListener *pListener;
   997 
   998 	if (pPlugin->GetStatus() <= Plugin_Error)
   999 	{
  1000 		/* Notify listeners of unloading */
  1001 		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1002 		{
  1003 			pListener = (*iter);
  1004 			pListener->OnPluginUnloaded(pPlugin);
  1005 		}
  1006 		/* Notify plugin */
  1007 		pPlugin->Call_OnPluginEnd();
  1008 	}
  1009 
  1010 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1011 	{
  1012 		/* Notify listeners of destruction */
  1013 		pListener = (*iter);
  1014 		pListener->OnPluginDestroyed(pPlugin);
  1015 	}
  1016 
  1017 	/* Remove us from the lookup table and linked list */
  1018 	m_plugins.remove(pPlugin);
  1019 	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
  1020 	
  1021 	/* Tell the plugin to delete itself */
  1022 	delete pPlugin;
  1023 
  1024 	return true;
  1025 }
  1026 
  1027 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
  1028 {
  1029 	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
  1030 	return pl;
  1031 }
  1032 
  1033 unsigned int CPluginManager::GetPluginCount()
  1034 {
  1035 	return m_plugins.size();
  1036 }
  1037 
  1038 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
  1039 {
  1040 	m_listeners.push_back(listener);
  1041 }
  1042 
  1043 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
  1044 {
  1045 	m_listeners.remove(listener);
  1046 }
  1047 
  1048 IPluginIterator *CPluginManager::GetPluginIterator()
  1049 {
  1050 	if (m_iters.empty())
  1051 	{
  1052 		return new CPluginIterator(&m_plugins);
  1053 	} else {
  1054 		CPluginIterator *iter = m_iters.front();
  1055 		m_iters.pop();
  1056 		iter->Reset();
  1057 		return iter;
  1058 	}
  1059 }
  1060 
  1061 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
  1062 {
  1063 	m_iters.push(iter);
  1064 }
  1065 
  1066 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
  1067 {
  1068 	/* As an optimization, we do not call strlen, but compute the length in the first pass */
  1069 	size_t alias_len = 0;
  1070 	size_t local_len = 0;
  1071 
  1072 	const char *ptr = alias;
  1073 	unsigned int alias_explicit_paths = 0;
  1074 	unsigned int alias_path_end = 0;
  1075 	while (*ptr != '\0')
  1076 	{
  1077 		if (*ptr == '\\' || *ptr == '/')
  1078 		{
  1079 			alias_explicit_paths++;
  1080 			alias_path_end = alias_len;
  1081 		}
  1082 		alias_len++;
  1083 		ptr++;
  1084 	}
  1085 
  1086 	if (alias_explicit_paths && alias_path_end == alias_len - 1)
  1087 	{
  1088 		/* Trailing slash is totally invalid here */
  1089 		return false;
  1090 	}
  1091 
  1092 	ptr = localpath;
  1093 	unsigned int local_explicit_paths = 0;
  1094 	unsigned int local_path_end = 0;
  1095 	while (*ptr != '\0')
  1096 	{
  1097 		if (*ptr == '\\' || *ptr == '/')
  1098 		{
  1099 			local_explicit_paths++;
  1100 			local_path_end = local_len;
  1101 		}
  1102 		local_len++;
  1103 		ptr++;
  1104 	}
  1105 
  1106 	/* If the alias has more explicit paths than the real path,
  1107 	 * no match will be possible.
  1108 	 */
  1109 	if (alias_explicit_paths > local_explicit_paths)
  1110 	{
  1111 		return false;
  1112 	}
  1113 
  1114 	if (alias_explicit_paths)
  1115 	{
  1116 		/* We need to find if the paths match now.  For example, these should all match:
  1117 		 * csdm     csdm
  1118 		 * csdm     optional/csdm
  1119 		 * csdm/ban optional/crab/csdm/ban
  1120 		*/
  1121 		const char *aliasptr = alias;
  1122 		const char *localptr = localpath;
  1123 		bool match = true;
  1124 		do
  1125 		{
  1126 			if (*aliasptr != *localptr)
  1127 			{
  1128 				/* We have to knock one path off */
  1129 				local_explicit_paths--;
  1130 				if (alias_explicit_paths > local_explicit_paths)
  1131 				{
  1132 					/* Skip out if we're gonna have an impossible match */
  1133 					return false;
  1134 				}
  1135 				/* Eat up localptr tokens until we get a result */
  1136 				while (((localptr - localpath) < (int)local_path_end)
  1137 					&& *localptr != '/'
  1138 					&& *localptr != '\\')
  1139 				{
  1140 					localptr++;
  1141 				}
  1142 				/* Check if we hit the end of our searchable area.
  1143 				 * This probably isn't possible because of the path 
  1144 				 * count check, but it's a good idea anyway.
  1145 				 */
  1146 				if ((localptr - localpath) >= (int)local_path_end)
  1147 				{
  1148 					return false;
  1149 				} else {
  1150 					/* Consume the slash token */
  1151 					localptr++;
  1152 				}
  1153 				/* Reset the alias pointer so we can continue consuming */
  1154 				aliasptr = alias;
  1155 				match = false;
  1156 				continue;
  1157 			}
  1158 			/* Note:
  1159 			 * This is safe because if localptr terminates early, aliasptr will too
  1160 			 */
  1161 			do
  1162 			{
  1163 				/* We should never reach the end of the string because of this check. */
  1164 				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
  1165 				bool localend = (localptr - localpath) > (int)local_path_end;
  1166 				if (aliasend || localend)
  1167 				{
  1168 					if (aliasend && localend)
  1169 					{
  1170 						/* we matched, and we can break out now */
  1171 						match = true;
  1172 						break;
  1173 					}
  1174 					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
  1175 					match = false;
  1176 					break;
  1177 				}
  1178 
  1179 				/* If we got here, it's safe to compare the next two tokens */
  1180 				if (*localptr != *aliasptr)
  1181 				{
  1182 					match = false;
  1183 					break;
  1184 				}
  1185 				localptr++;
  1186 				aliasptr++;
  1187 			} while (true);
  1188 		} while (!match);
  1189 	}
  1190 
  1191 	/* If we got here, it's time to compare filenames */
  1192 	const char *aliasptr = alias;
  1193 	const char *localptr = localpath;
  1194 
  1195 	if (alias_explicit_paths)
  1196 	{
  1197 		aliasptr = &alias[alias_path_end + 1];
  1198 	}
  1199 
  1200 	if (local_explicit_paths)
  1201 	{
  1202 		localptr = &localpath[local_path_end + 1];
  1203 	}
  1204 
  1205 	while (true)
  1206 	{
  1207 		if (*aliasptr == '*')
  1208 		{
  1209 			/* First, see if this is the last character */
  1210 			if ((unsigned)(aliasptr - alias) == alias_len - 1)
  1211 			{
  1212 				/* If so, there's no need to match anything else */
  1213 				return true;
  1214 			}
  1215 			/* Otherwise, we need to search for an appropriate matching sequence in local.
  1216 			 * Note that we only need to search up to the next asterisk.
  1217 			 */
  1218 			aliasptr++;
  1219 			bool match = true;
  1220 			const char *local_orig = localptr;
  1221 			do
  1222 			{
  1223 				match = true;
  1224 				while (*aliasptr != '\0' && *aliasptr != '*')
  1225 				{
  1226 					/* Since aliasptr is never '\0', localptr hitting the end will fail */
  1227 					if (*aliasptr != *localptr)
  1228 					{
  1229 						match = false;
  1230 						break;
  1231 					}
  1232 					aliasptr++;
  1233 					localptr++;
  1234 				}
  1235 				if (!match)
  1236 				{
  1237 					/* If we didn't get a match, we need to advance the search stream.
  1238 					 * This will let us skip tokens while still searching for another match.
  1239 					 */
  1240 					localptr = ++local_orig;
  1241 					/* Make sure we don't go out of bounds */
  1242 					if (*localptr == '\0')
  1243 					{
  1244 						break;
  1245 					}
  1246 				}
  1247 			} while (!match);
  1248 
  1249 			if (!match)
  1250 			{
  1251 				return false;
  1252 			} else {
  1253 				/* If we got a match, move on to the next token */
  1254 				continue;
  1255 			}
  1256 		} else if (*aliasptr == '\0') {
  1257 			if (*localptr == '\0'
  1258 				||
  1259 				strcmp(localptr, ".smx") == 0)
  1260 			{
  1261 				return true;
  1262 			} else {
  1263 				return false;
  1264 			}
  1265 		} else if (*aliasptr != *localptr) {
  1266 			return false;
  1267 		}
  1268 		aliasptr++;
  1269 		localptr++;
  1270 	}
  1271 
  1272 	return true;
  1273 }
  1274 
  1275 bool CPluginManager::IsLateLoadTime() const
  1276 {
  1277 	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
  1278 }
  1279 
  1280 void CPluginManager::OnSourceModAllInitialized()
  1281 {
  1282 	m_MyIdent = g_ShareSys.CreateCoreIdentity();
  1283 
  1284 	HandleAccess sec;
  1285 	g_HandleSys.InitAccessDefaults(NULL, &sec);
  1286 
  1287 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
  1288 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
  1289 
  1290 	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
  1291 	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
  1292 
  1293 	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
  1294 
  1295 	g_ShareSys.AddInterface(NULL, this);
  1296 }
  1297 
  1298 void CPluginManager::OnSourceModShutdown()
  1299 {
  1300 	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
  1301 
  1302 	List<CPlugin *>::iterator iter;
  1303 	while ( (iter = m_plugins.begin()) != m_plugins.end() )
  1304 	{
  1305 		UnloadPlugin((*iter));
  1306 	}
  1307 
  1308 	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
  1309 	g_ShareSys.DestroyIdentType(g_PluginIdent);
  1310 	g_ShareSys.DestroyIdentity(m_MyIdent);
  1311 }
  1312 
  1313 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
  1314 {
  1315 	/* We don't care about the internal object, actually */
  1316 }
  1317 
  1318 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
  1319 {
  1320 	m_natives.push_back(natives);
  1321 }
  1322 
  1323 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
  1324 {
  1325 	IPlugin *pPlugin;
  1326 	HandleError _err;
  1327 
  1328 	HandleSecurity sec;
  1329 
  1330 	sec.pOwner = NULL;
  1331 	sec.pIdentity = m_MyIdent;
  1332 
  1333 	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
  1334 	{
  1335 		pPlugin = NULL;
  1336 	}
  1337 
  1338 	if (err)
  1339 	{
  1340 		*err = _err;
  1341 	}
  1342 
  1343 	return pPlugin;
  1344 }
  1345 
  1346 CPlugin *CPluginManager::GetPluginByOrder(int num)
  1347 {
  1348 	if (num < 1 || num > (int)GetPluginCount())
  1349 	{
  1350 		return NULL;
  1351 	}
  1352 
  1353 	CPlugin *pl;
  1354 	int id = 1;
  1355 
  1356 	IPluginIterator *iter = GetPluginIterator();
  1357 	for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
  1358 
  1359 	pl = (CPlugin *)(iter->GetPlugin());
  1360 	iter->Release();
  1361 
  1362 	return pl;
  1363 }
  1364 
  1365 const char *CPluginManager::GetStatusText(PluginStatus st)
  1366 {
  1367 	switch (st)
  1368 	{
  1369 	case Plugin_Running:
  1370 		return "Running";
  1371 	case Plugin_Paused:
  1372 		return "Paused";
  1373 	case Plugin_Error:
  1374 		return "Error";
  1375 	case Plugin_Uncompiled:
  1376 		return "Uncompiled";
  1377 	case Plugin_BadLoad:
  1378 		return "Bad Load";
  1379 	case Plugin_Failed:
  1380 		return "Failed";
  1381 	default:
  1382 		assert(false);
  1383 		return "-";
  1384 	}
  1385 }
  1386 
  1387 #define IS_STR_FILLED(var) (var[0] != '\0')
  1388 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
  1389 {
  1390 	if (argcount >= 3)
  1391 	{
  1392 		const char *cmd = g_RootMenu.GetArgument(2);
  1393 		if (strcmp(cmd, "list") == 0)
  1394 		{
  1395 			char buffer[256];
  1396 			unsigned int id = 1;
  1397 			int plnum = GetPluginCount();
  1398 
  1399 			if (!plnum)
  1400 			{
  1401 				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
  1402 				return;
  1403 			} else {
  1404 				g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
  1405 			}
  1406 
  1407 			IPluginIterator *iter = GetPluginIterator();
  1408 			for (; iter->MorePlugins(); iter->NextPlugin(), id++)
  1409 			{
  1410 				IPlugin *pl = iter->GetPlugin();
  1411 				assert(pl->GetStatus() != Plugin_Created);
  1412 				int len = 0;
  1413 				const sm_plugininfo_t *info = pl->GetPublicInfo();
  1414 				if (pl->GetStatus() != Plugin_Running)
  1415 				{
  1416 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
  1417 				} else {
  1418 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d", id);
  1419 				}
  1420 				len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1421 				if (IS_STR_FILLED(info->version))
  1422 				{
  1423 					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
  1424 				}
  1425 				if (IS_STR_FILLED(info->author))
  1426 				{
  1427 					UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
  1428 				}
  1429 				g_RootMenu.ConsolePrint("%s", buffer);
  1430 			}
  1431 
  1432 			iter->Release();
  1433 			return;
  1434 		} else if (strcmp(cmd, "load") == 0) {
  1435 			if (argcount < 4)
  1436 			{
  1437 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
  1438 				return;
  1439 			}
  1440 
  1441 			char error[128];
  1442 			bool wasloaded;
  1443 			const char *filename = g_RootMenu.GetArgument(3);
  1444 			IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
  1445 
  1446 			if (wasloaded)
  1447 			{
  1448 				g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", filename);
  1449 				return;
  1450 			}
  1451 
  1452 			if (pl)
  1453 			{
  1454 				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
  1455 			} else {
  1456 				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
  1457 			}
  1458 
  1459 			return;
  1460 		} else if (strcmp(cmd, "unload") == 0) {
  1461 			if (argcount < 4)
  1462 			{
  1463 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
  1464 				return;
  1465 			}
  1466 
  1467 			int id = 1;
  1468 			int num = atoi(g_RootMenu.GetArgument(3));
  1469 			if (num < 1 || num > (int)GetPluginCount())
  1470 			{
  1471 				g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
  1472 				return;
  1473 			}
  1474 
  1475 			IPluginIterator *iter = GetPluginIterator();
  1476 			for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
  1477 				;
  1478 			IPlugin *pl = iter->GetPlugin();
  1479 
  1480 			char name[PLATFORM_MAX_PATH+1];
  1481 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1482 			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1483 
  1484 			if (UnloadPlugin(pl))
  1485 			{
  1486 				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
  1487 			} else {
  1488 				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
  1489 			}
  1490 
  1491 			iter->Release();
  1492 			return;
  1493 		} else if (strcmp(cmd, "info") == 0) {
  1494 			if (argcount < 4)
  1495 			{
  1496 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
  1497 				return;
  1498 			}
  1499 
  1500 			int num = atoi(g_RootMenu.GetArgument(3));
  1501 			if (num < 1 || num > (int)GetPluginCount())
  1502 			{
  1503 				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
  1504 				return;
  1505 			}
  1506 
  1507 			CPlugin *pl = GetPluginByOrder(num);
  1508 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1509 
  1510 			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
  1511 			if (pl->GetStatus() <= Plugin_Error)
  1512 			{
  1513 				if (IS_STR_FILLED(info->name))
  1514 				{
  1515 					g_RootMenu.ConsolePrint("  Title: %s", info->name);
  1516 				}
  1517 				if (IS_STR_FILLED(info->version))
  1518 				{
  1519 					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  1520 				}
  1521 				if (IS_STR_FILLED(info->url))
  1522 				{
  1523 					g_RootMenu.ConsolePrint("  URL: %s", info->url);
  1524 				}
  1525 				if (IS_STR_FILLED(info->author))
  1526 				{
  1527 					g_RootMenu.ConsolePrint("  Author: %s", info->author);
  1528 				}
  1529 				if (IS_STR_FILLED(info->version))
  1530 				{
  1531 					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  1532 				}
  1533 				if (IS_STR_FILLED(info->description))
  1534 				{
  1535 					g_RootMenu.ConsolePrint("  Description: %s", info->description);
  1536 				}	
  1537 				g_RootMenu.ConsolePrint("  Debugging: %s", pl->IsDebugging() ? "yes" : "no");
  1538 				g_RootMenu.ConsolePrint("  Paused: %s", pl->GetStatus() == Plugin_Running ? "no" : "yes");
  1539 			} else {
  1540 				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
  1541 				g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
  1542 										info->name ? info->name : "<none>",
  1543 										info->version ? info->version : "<none>");
  1544 				if (IS_STR_FILLED(info->url))
  1545 				{
  1546 					g_RootMenu.ConsolePrint("  File URL: %s", info->url);
  1547 				}
  1548 			}
  1549 
  1550 			return;
  1551 		} else if (strcmp(cmd, "debug") == 0) {
  1552 			if (argcount < 5)
  1553 			{
  1554 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
  1555 				return;
  1556 			}
  1557 
  1558 			int num = atoi(g_RootMenu.GetArgument(3));
  1559 			if (num < 1 || num > (int)GetPluginCount())
  1560 			{
  1561 				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
  1562 				return;
  1563 			}
  1564 
  1565 			int res;
  1566 			const char *mode = g_RootMenu.GetArgument(4);
  1567 			if ((res=strcmp("on", mode)) && strcmp("off", mode))
  1568 			{
  1569 				g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
  1570 				return;
  1571 			}
  1572 
  1573 			bool debug;
  1574 			if (!res)
  1575 			{
  1576 				debug = true;
  1577 			} else {
  1578 				debug = false;
  1579 			}
  1580 
  1581 			CPlugin *pl = GetPluginByOrder(num);
  1582 			if (debug && pl->IsDebugging())
  1583 			{
  1584 				g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
  1585 				return;
  1586 			} else if (!debug && !pl->IsDebugging()) {
  1587 				g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
  1588 				return;
  1589 			}
  1590 
  1591 			char error[256];
  1592 			if (pl->ToggleDebugMode(debug, error, sizeof(error)))
  1593 			{
  1594 				g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
  1595 				return;
  1596 			} else {
  1597 				g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
  1598 				g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
  1599 				return;
  1600 			}
  1601 		}
  1602 	}
  1603 
  1604 	/* Draw the main menu */
  1605 	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
  1606 	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
  1607 	g_RootMenu.DrawGenericOption("load", "Load a plugin");
  1608 	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
  1609 	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
  1610 	g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
  1611 }
  1612 
  1613 void CPluginManager::ReloadOrUnloadPlugins()
  1614 {
  1615 	List<CPlugin *>::iterator iter;
  1616 	List<CPlugin *> tmp_list = m_plugins;
  1617 	CPlugin *pl;
  1618 	time_t t;
  1619 
  1620 	for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
  1621 	{
  1622 		pl = (*iter);
  1623 		if (pl->GetType() ==  PluginType_MapOnly)
  1624 		{
  1625 			UnloadPlugin((IPlugin *)pl);
  1626 		} else if (pl->GetType() == PluginType_MapUpdated) {
  1627 			t = pl->GetFileTimeStamp();
  1628 			if (!t || t > pl->GetTimeStamp())
  1629 			{
  1630 				pl->SetTimeStamp(t);
  1631 				UnloadPlugin((IPlugin *)pl);
  1632 			}
  1633 		}
  1634 	}
  1635 }
  1636 
  1637 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
  1638 {
  1639 	List<IPluginsListener *>::iterator iter;
  1640 	IPluginsListener *pListener;
  1641 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1642 	{
  1643 		pListener = (*iter);
  1644 		pListener->OnPluginPauseChange(pl, paused);
  1645 	}	
  1646 }