core/systems/PluginSys.cpp
author Borja Ferrer <faluco@alliedmods.net>
Sun Feb 04 22:41:44 2007 +0000 (2007-02-04)
changeset 430 ed37e9767ed4
parent 426 d2b0862c974e
child 450 4af70fceaa7f
permissions -rw-r--r--
Optimized forwards, now paused functions are stored in a temp list so we dont have to check if they're runnable on each function execution.
     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 		g_Logger.LogError("Could not obtain plugin time stamp, error: %d", errno);
   482 		return 0;
   483 	} else {
   484 		return s.st_mtime;
   485 	}
   486 }
   487 
   488 time_t CPlugin::GetTimeStamp() const
   489 {
   490 	return m_LastAccess;
   491 }
   492 
   493 void CPlugin::SetTimeStamp(time_t t)
   494 {
   495 	m_LastAccess = t;
   496 }
   497 
   498 void CPlugin::AddLangFile(unsigned int index)
   499 {
   500 	m_PhraseFiles.push_back(index);
   501 }
   502 
   503 size_t CPlugin::GetLangFileCount() const
   504 {
   505 	return m_PhraseFiles.size();
   506 }
   507 
   508 unsigned int CPlugin::GetFileByIndex(unsigned int index) const
   509 {
   510 	return m_PhraseFiles.at(index);
   511 }
   512 
   513 /*******************
   514  * PLUGIN ITERATOR *
   515  *******************/
   516 
   517 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
   518 {
   519 	mylist = _mylist;
   520 	Reset();
   521 }
   522 
   523 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   524 {
   525 	return (*current);
   526 }
   527 
   528 bool CPluginManager::CPluginIterator::MorePlugins()
   529 {
   530 	return (current != mylist->end());
   531 }
   532 
   533 void CPluginManager::CPluginIterator::NextPlugin()
   534 {
   535 	current++;
   536 }
   537 
   538 void CPluginManager::CPluginIterator::Release()
   539 {
   540 	g_PluginSys.ReleaseIterator(this);
   541 }
   542 
   543 CPluginManager::CPluginIterator::~CPluginIterator()
   544 {
   545 }
   546 
   547 void CPluginManager::CPluginIterator::Reset()
   548 {
   549 	current = mylist->begin();
   550 }
   551 
   552 /******************
   553  * PLUGIN MANAGER *
   554  ******************/
   555 
   556 CPluginManager::CPluginManager()
   557 {
   558 	m_LoadLookup = sm_trie_create();
   559 	m_AllPluginsLoaded = false;
   560 	m_MyIdent = NULL;
   561 }
   562 
   563 CPluginManager::~CPluginManager()
   564 {
   565 	/* :NOTICE: 
   566 	 * Ignore the fact that there might be plugins in the cache.
   567 	 * This usually means that Core is not being unloaded properly, and everything
   568 	 * will crash anyway.  YAY
   569 	 */
   570 	sm_trie_destroy(m_LoadLookup);
   571 }
   572 
   573 
   574 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
   575 {
   576 	/* First read in the database of plugin settings */
   577 	SMCParseError err;
   578 	unsigned int line, col;
   579 	m_AllPluginsLoaded = false;
   580 	if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
   581 	{
   582 		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
   583 		const char *err_msg = g_TextParser.GetSMCErrorString(err);
   584 		if (err_msg)
   585 		{
   586 			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
   587 		}
   588 	}
   589 
   590 	LoadPluginsFromDir(basedir, NULL);
   591 }
   592 
   593 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
   594 {
   595 	char base_path[PLATFORM_MAX_PATH+1];
   596 
   597 	/* Form the current path to start reading from */
   598 	if (localpath == NULL)
   599 	{
   600 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
   601 	} else {
   602 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
   603 	}
   604 
   605 	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
   606 
   607 	if (!dir)
   608 	{
   609 		char error[256];
   610 		g_LibSys.GetPlatformError(error, sizeof(error));
   611 		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
   612 		g_Logger.LogError("[SM] Platform returned error: %s", error);
   613 		return;
   614 	}
   615 
   616 	while (dir->MoreFiles())
   617 	{
   618 		if (dir->IsEntryDirectory() 
   619 			&& (strcmp(dir->GetEntryName(), ".") != 0)
   620 			&& (strcmp(dir->GetEntryName(), "..") != 0)
   621 			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
   622 			&& (strcmp(dir->GetEntryName(), "optional") != 0))
   623 		{
   624 			char new_local[PLATFORM_MAX_PATH+1];
   625 			if (localpath == NULL)
   626 			{
   627 				/* If no path yet, don't add a former slash */
   628 				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
   629 			} else {
   630 				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
   631 			}
   632 			LoadPluginsFromDir(basedir, new_local);
   633 		} else if (dir->IsEntryFile()) {
   634 			const char *name = dir->GetEntryName();
   635 			size_t len = strlen(name);
   636 			if (len >= 4
   637 				&& strcmp(&name[len-4], ".smx") == 0)
   638 			{
   639 				/* If the filename matches, load the plugin */
   640 				char plugin[PLATFORM_MAX_PATH+1];
   641 				if (localpath == NULL)
   642 				{
   643 					snprintf(plugin, sizeof(plugin), "%s", name);
   644 				} else {
   645 					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
   646 				}
   647 				LoadAutoPlugin(plugin);
   648 			}
   649 		}
   650 		dir->NextEntry();
   651 	}
   652 	g_LibSys.CloseDirectory(dir);
   653 }
   654 
   655 //well i have discovered that gabe newell is very fat, so i wrote this comment now
   656 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
   657 {
   658 	/**
   659 	 * Does this plugin already exist?
   660 	 */
   661 	CPlugin *pPlugin;
   662 	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
   663 	{
   664 		/* Check to see if we should try reloading it */
   665 		if (pPlugin->GetStatus() == Plugin_BadLoad
   666 			|| pPlugin->GetStatus() == Plugin_Error
   667 			|| pPlugin->GetStatus() == Plugin_Failed)
   668 		{
   669 			UnloadPlugin(pPlugin);
   670 		} else {
   671 			if (_plugin)
   672 			{
   673 				*_plugin = pPlugin;
   674 			}
   675 			return LoadRes_AlreadyLoaded;
   676 		}
   677 	}
   678 
   679 	pPlugin = CPlugin::CreatePlugin(path, error, err_max);
   680 
   681 	assert(pPlugin != NULL);
   682 
   683 	pPlugin->m_type = PluginType_MapUpdated;
   684 
   685 	ICompilation *co = NULL;
   686 
   687 	if (pPlugin->m_status == Plugin_Uncompiled)
   688 	{
   689 		co = pPlugin->StartMyCompile(g_pVM);
   690 	}
   691 
   692 	PluginSettings *pset;
   693 	unsigned int setcount = m_PluginInfo.GetSettingsNum();
   694 	for (unsigned int i=0; i<setcount; i++)
   695 	{
   696 		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
   697 		{
   698 			continue;
   699 		}
   700 		pPlugin->m_type = pset->type_val;
   701 		if (co)
   702 		{
   703 			for (unsigned int j=0; j<pset->opts_num; j++)
   704 			{
   705 				const char *key, *val;
   706 				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
   707 				if (!key || !val)
   708 				{
   709 					continue;
   710 				}
   711 				if (!g_pVM->SetCompilationOption(co, key, val))
   712 				{
   713 					if (error)
   714 					{
   715 						snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
   716 					}
   717 					pPlugin->CancelMyCompile();
   718 					co = NULL;
   719 					break;
   720 				}
   721 			}
   722 		}
   723 	}
   724 
   725 	/* Do the actual compiling */
   726 	if (co)
   727 	{
   728 		pPlugin->FinishMyCompile(NULL, 0);
   729 		co = NULL;
   730 	}
   731 
   732 	/* Get the status */
   733 	if (pPlugin->GetStatus() == Plugin_Created)
   734 	{
   735 		AddCoreNativesToPlugin(pPlugin);
   736 		pPlugin->InitIdentity();
   737 		if (pPlugin->Call_AskPluginLoad(error, err_max))
   738 		{
   739 			/* Autoload any modules */
   740 			LoadOrRequireExtensions(pPlugin, 1, error, err_max);
   741 		}
   742 	}
   743 
   744 	/* Save the time stamp */
   745 	time_t t = pPlugin->GetFileTimeStamp();
   746 	pPlugin->SetTimeStamp(t);
   747 
   748 	if (_plugin)
   749 	{
   750 		*_plugin = pPlugin;
   751 	}
   752 
   753 	return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
   754 }
   755 
   756 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max, bool *wasloaded)
   757 {
   758 	CPlugin *pl;
   759 	LoadRes res;
   760 
   761 	*wasloaded = false;
   762 	if ((res=_LoadPlugin(&pl, path, debug, type, error, err_max)) == LoadRes_Failure)
   763 	{
   764 		delete pl;
   765 		return NULL;
   766 	}
   767 
   768 	if (res == LoadRes_AlreadyLoaded)
   769 	{
   770 		*wasloaded = true;
   771 		return pl;
   772 	}
   773 
   774 	AddPlugin(pl);
   775 
   776 	/* Run second pass if we need to */
   777 	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
   778 	{
   779 		if (!RunSecondPass(pl, error, err_max))
   780 		{
   781 			UnloadPlugin(pl);
   782 			return NULL;
   783 		}
   784 	}
   785 
   786 	return pl;
   787 }
   788 
   789 void CPluginManager::LoadAutoPlugin(const char *plugin)
   790 {
   791 	CPlugin *pl;
   792 	LoadRes res;
   793 	char error[255] = "Unknown error";
   794 
   795 	if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
   796 	{
   797 		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
   798 		pl->SetErrorState(Plugin_Failed, "%s", error);
   799 	}
   800 
   801 	if (res == LoadRes_Successful)
   802 	{
   803 		AddPlugin(pl);
   804 	}
   805 }
   806 
   807 void CPluginManager::AddPlugin(CPlugin *pPlugin)
   808 {
   809 	List<IPluginsListener *>::iterator iter;
   810 	IPluginsListener *pListener;
   811 
   812 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   813 	{
   814 		pListener = (*iter);
   815 		pListener->OnPluginCreated(pPlugin);
   816 	}
   817 
   818 	m_plugins.push_back(pPlugin);
   819 	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
   820 }
   821 
   822 void CPluginManager::LoadAll_SecondPass()
   823 {
   824 	List<CPlugin *>::iterator iter;
   825 	CPlugin *pPlugin;
   826 
   827 	char error[256];
   828 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
   829 	{
   830 		pPlugin = (*iter);
   831 		if (pPlugin->GetStatus() == Plugin_Loaded)
   832 		{
   833 			error[0] = '\0';
   834 			if (!RunSecondPass(pPlugin, error, sizeof(error)))
   835 			{
   836 				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
   837 				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
   838 			}
   839 		}
   840 	}
   841 
   842 	m_AllPluginsLoaded = true;
   843 }
   844 
   845 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
   846 {
   847 	/* Find any extensions this plugin needs */
   848 	struct _ext
   849 	{
   850 		cell_t name;
   851 		cell_t file;
   852 		cell_t autoload;
   853 		cell_t required;
   854 	} *ext;
   855 
   856 	IPluginContext *pBase = pPlugin->GetBaseContext();
   857 	uint32_t num = pBase->GetPubVarsNum();
   858 	sp_pubvar_t *pubvar;
   859 	IExtension *pExt;
   860 	char path[PLATFORM_MAX_PATH+1];
   861 	char *file, *name;
   862 	for (uint32_t i=0; i<num; i++)
   863 	{
   864 		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
   865 		{
   866 			continue;
   867 		}
   868 		if (strncmp(pubvar->name, "__ext_", 6) == 0)
   869 		{
   870 			ext = (_ext *)pubvar->offs;
   871 			if (!ext->required && !ext->autoload)
   872 			{
   873 				continue;
   874 			}
   875 			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
   876 			{
   877 				continue;
   878 			}
   879 			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
   880 			{
   881 				continue;
   882 			}
   883 			if (pass == 1)
   884 			{
   885 				/* Attempt to auto-load if necessary */
   886 				if (ext->autoload)
   887 				{
   888 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   889 					g_Extensions.LoadAutoExtension(path);
   890 				}
   891 			} else if (pass == 2) {
   892 				/* Is this required? */
   893 				if (ext->required)
   894 				{
   895 					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   896 					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
   897 					{
   898 						pExt = g_Extensions.FindExtensionByName(name);
   899 					}
   900 					/* :TODO: should we bind to unloaded extensions?
   901 					 * Currently the extension manager will ignore this.
   902 					 */
   903 					if (!pExt || !pExt->IsRunning(NULL, 0))
   904 					{
   905 						if (error)
   906 						{
   907 							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
   908 						}
   909 						return false;
   910 					} else {
   911 						g_Extensions.BindChildPlugin(pExt, pPlugin);
   912 					}
   913 				}
   914 			}
   915 		}
   916 	}
   917 
   918 	return true;
   919 }
   920 
   921 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
   922 {
   923 	/* Second pass for extension requirements */
   924 	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
   925 	{
   926 		return false;
   927 	}
   928 
   929 	/* Bind all extra natives */
   930 	g_Extensions.BindAllNativesToPlugin(pPlugin);
   931 
   932 	/* Find any unbound natives
   933 	 * Right now, these are not allowed
   934 	 */
   935 	IPluginContext *pContext = pPlugin->GetBaseContext();
   936 	uint32_t num = pContext->GetNativesNum();
   937 	sp_native_t *native;
   938 	for (unsigned int i=0; i<num; i++)
   939 	{
   940 		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
   941 		{
   942 			break;
   943 		}
   944 		if (native->status == SP_NATIVE_UNBOUND)
   945 		{
   946 			if (error)
   947 			{
   948 				snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
   949 			}
   950 			return false;
   951 		}
   952 	}
   953 
   954 	/* Finish by telling all listeners */
   955 	List<IPluginsListener *>::iterator iter;
   956 	IPluginsListener *pListener;
   957 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   958 	{
   959 		pListener = (*iter);
   960 		pListener->OnPluginLoaded(pPlugin);
   961 	}
   962 	
   963 	/* Tell this plugin to finish initializing itself */
   964 	pPlugin->Call_OnPluginStart();
   965 
   966 	return true;
   967 }
   968 
   969 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
   970 {
   971 	List<sp_nativeinfo_t *>::iterator iter;
   972 
   973 	for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
   974 	{
   975 		sp_nativeinfo_t *natives = (*iter);
   976 		IPluginContext *ctx = pPlugin->GetBaseContext();
   977 		unsigned int i=0;
   978 		/* Attempt to bind every native! */
   979 		while (natives[i].func != NULL)
   980 		{
   981 			ctx->BindNative(&natives[i++]);
   982 		}
   983 	}
   984 }
   985 
   986 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
   987 {
   988 	CPlugin *pPlugin = (CPlugin *)plugin;
   989 
   990 	/* This prevents removal during insertion or anything else weird */
   991 	if (m_plugins.find(pPlugin) == m_plugins.end())
   992 	{
   993 		return false;
   994 	}
   995 
   996 	List<IPluginsListener *>::iterator iter;
   997 	IPluginsListener *pListener;
   998 
   999 	if (pPlugin->GetStatus() <= Plugin_Error)
  1000 	{
  1001 		/* Notify listeners of unloading */
  1002 		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1003 		{
  1004 			pListener = (*iter);
  1005 			pListener->OnPluginUnloaded(pPlugin);
  1006 		}
  1007 		/* Notify plugin */
  1008 		pPlugin->Call_OnPluginEnd();
  1009 	}
  1010 
  1011 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1012 	{
  1013 		/* Notify listeners of destruction */
  1014 		pListener = (*iter);
  1015 		pListener->OnPluginDestroyed(pPlugin);
  1016 	}
  1017 
  1018 	/* Remove us from the lookup table and linked list */
  1019 	m_plugins.remove(pPlugin);
  1020 	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
  1021 	
  1022 	/* Tell the plugin to delete itself */
  1023 	delete pPlugin;
  1024 
  1025 	return true;
  1026 }
  1027 
  1028 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
  1029 {
  1030 	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
  1031 	return pl;
  1032 }
  1033 
  1034 unsigned int CPluginManager::GetPluginCount()
  1035 {
  1036 	return m_plugins.size();
  1037 }
  1038 
  1039 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
  1040 {
  1041 	m_listeners.push_back(listener);
  1042 }
  1043 
  1044 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
  1045 {
  1046 	m_listeners.remove(listener);
  1047 }
  1048 
  1049 IPluginIterator *CPluginManager::GetPluginIterator()
  1050 {
  1051 	if (m_iters.empty())
  1052 	{
  1053 		return new CPluginIterator(&m_plugins);
  1054 	} else {
  1055 		CPluginIterator *iter = m_iters.front();
  1056 		m_iters.pop();
  1057 		iter->Reset();
  1058 		return iter;
  1059 	}
  1060 }
  1061 
  1062 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
  1063 {
  1064 	m_iters.push(iter);
  1065 }
  1066 
  1067 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
  1068 {
  1069 	/* As an optimization, we do not call strlen, but compute the length in the first pass */
  1070 	size_t alias_len = 0;
  1071 	size_t local_len = 0;
  1072 
  1073 	const char *ptr = alias;
  1074 	unsigned int alias_explicit_paths = 0;
  1075 	unsigned int alias_path_end = 0;
  1076 	while (*ptr != '\0')
  1077 	{
  1078 		if (*ptr == '\\' || *ptr == '/')
  1079 		{
  1080 			alias_explicit_paths++;
  1081 			alias_path_end = alias_len;
  1082 		}
  1083 		alias_len++;
  1084 		ptr++;
  1085 	}
  1086 
  1087 	if (alias_explicit_paths && alias_path_end == alias_len - 1)
  1088 	{
  1089 		/* Trailing slash is totally invalid here */
  1090 		return false;
  1091 	}
  1092 
  1093 	ptr = localpath;
  1094 	unsigned int local_explicit_paths = 0;
  1095 	unsigned int local_path_end = 0;
  1096 	while (*ptr != '\0')
  1097 	{
  1098 		if (*ptr == '\\' || *ptr == '/')
  1099 		{
  1100 			local_explicit_paths++;
  1101 			local_path_end = local_len;
  1102 		}
  1103 		local_len++;
  1104 		ptr++;
  1105 	}
  1106 
  1107 	/* If the alias has more explicit paths than the real path,
  1108 	 * no match will be possible.
  1109 	 */
  1110 	if (alias_explicit_paths > local_explicit_paths)
  1111 	{
  1112 		return false;
  1113 	}
  1114 
  1115 	if (alias_explicit_paths)
  1116 	{
  1117 		/* We need to find if the paths match now.  For example, these should all match:
  1118 		 * csdm     csdm
  1119 		 * csdm     optional/csdm
  1120 		 * csdm/ban optional/crab/csdm/ban
  1121 		*/
  1122 		const char *aliasptr = alias;
  1123 		const char *localptr = localpath;
  1124 		bool match = true;
  1125 		do
  1126 		{
  1127 			if (*aliasptr != *localptr)
  1128 			{
  1129 				/* We have to knock one path off */
  1130 				local_explicit_paths--;
  1131 				if (alias_explicit_paths > local_explicit_paths)
  1132 				{
  1133 					/* Skip out if we're gonna have an impossible match */
  1134 					return false;
  1135 				}
  1136 				/* Eat up localptr tokens until we get a result */
  1137 				while (((localptr - localpath) < (int)local_path_end)
  1138 					&& *localptr != '/'
  1139 					&& *localptr != '\\')
  1140 				{
  1141 					localptr++;
  1142 				}
  1143 				/* Check if we hit the end of our searchable area.
  1144 				 * This probably isn't possible because of the path 
  1145 				 * count check, but it's a good idea anyway.
  1146 				 */
  1147 				if ((localptr - localpath) >= (int)local_path_end)
  1148 				{
  1149 					return false;
  1150 				} else {
  1151 					/* Consume the slash token */
  1152 					localptr++;
  1153 				}
  1154 				/* Reset the alias pointer so we can continue consuming */
  1155 				aliasptr = alias;
  1156 				match = false;
  1157 				continue;
  1158 			}
  1159 			/* Note:
  1160 			 * This is safe because if localptr terminates early, aliasptr will too
  1161 			 */
  1162 			do
  1163 			{
  1164 				/* We should never reach the end of the string because of this check. */
  1165 				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
  1166 				bool localend = (localptr - localpath) > (int)local_path_end;
  1167 				if (aliasend || localend)
  1168 				{
  1169 					if (aliasend && localend)
  1170 					{
  1171 						/* we matched, and we can break out now */
  1172 						match = true;
  1173 						break;
  1174 					}
  1175 					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
  1176 					match = false;
  1177 					break;
  1178 				}
  1179 
  1180 				/* If we got here, it's safe to compare the next two tokens */
  1181 				if (*localptr != *aliasptr)
  1182 				{
  1183 					match = false;
  1184 					break;
  1185 				}
  1186 				localptr++;
  1187 				aliasptr++;
  1188 			} while (true);
  1189 		} while (!match);
  1190 	}
  1191 
  1192 	/* If we got here, it's time to compare filenames */
  1193 	const char *aliasptr = alias;
  1194 	const char *localptr = localpath;
  1195 
  1196 	if (alias_explicit_paths)
  1197 	{
  1198 		aliasptr = &alias[alias_path_end + 1];
  1199 	}
  1200 
  1201 	if (local_explicit_paths)
  1202 	{
  1203 		localptr = &localpath[local_path_end + 1];
  1204 	}
  1205 
  1206 	while (true)
  1207 	{
  1208 		if (*aliasptr == '*')
  1209 		{
  1210 			/* First, see if this is the last character */
  1211 			if ((unsigned)(aliasptr - alias) == alias_len - 1)
  1212 			{
  1213 				/* If so, there's no need to match anything else */
  1214 				return true;
  1215 			}
  1216 			/* Otherwise, we need to search for an appropriate matching sequence in local.
  1217 			 * Note that we only need to search up to the next asterisk.
  1218 			 */
  1219 			aliasptr++;
  1220 			bool match = true;
  1221 			const char *local_orig = localptr;
  1222 			do
  1223 			{
  1224 				match = true;
  1225 				while (*aliasptr != '\0' && *aliasptr != '*')
  1226 				{
  1227 					/* Since aliasptr is never '\0', localptr hitting the end will fail */
  1228 					if (*aliasptr != *localptr)
  1229 					{
  1230 						match = false;
  1231 						break;
  1232 					}
  1233 					aliasptr++;
  1234 					localptr++;
  1235 				}
  1236 				if (!match)
  1237 				{
  1238 					/* If we didn't get a match, we need to advance the search stream.
  1239 					 * This will let us skip tokens while still searching for another match.
  1240 					 */
  1241 					localptr = ++local_orig;
  1242 					/* Make sure we don't go out of bounds */
  1243 					if (*localptr == '\0')
  1244 					{
  1245 						break;
  1246 					}
  1247 				}
  1248 			} while (!match);
  1249 
  1250 			if (!match)
  1251 			{
  1252 				return false;
  1253 			} else {
  1254 				/* If we got a match, move on to the next token */
  1255 				continue;
  1256 			}
  1257 		} else if (*aliasptr == '\0') {
  1258 			if (*localptr == '\0'
  1259 				||
  1260 				strcmp(localptr, ".smx") == 0)
  1261 			{
  1262 				return true;
  1263 			} else {
  1264 				return false;
  1265 			}
  1266 		} else if (*aliasptr != *localptr) {
  1267 			return false;
  1268 		}
  1269 		aliasptr++;
  1270 		localptr++;
  1271 	}
  1272 
  1273 	return true;
  1274 }
  1275 
  1276 bool CPluginManager::IsLateLoadTime() const
  1277 {
  1278 	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
  1279 }
  1280 
  1281 void CPluginManager::OnSourceModAllInitialized()
  1282 {
  1283 	m_MyIdent = g_ShareSys.CreateCoreIdentity();
  1284 
  1285 	HandleAccess sec;
  1286 	g_HandleSys.InitAccessDefaults(NULL, &sec);
  1287 
  1288 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
  1289 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
  1290 
  1291 	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
  1292 	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
  1293 
  1294 	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
  1295 
  1296 	g_ShareSys.AddInterface(NULL, this);
  1297 }
  1298 
  1299 void CPluginManager::OnSourceModShutdown()
  1300 {
  1301 	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
  1302 
  1303 	List<CPlugin *>::iterator iter;
  1304 	while ( (iter = m_plugins.begin()) != m_plugins.end() )
  1305 	{
  1306 		UnloadPlugin((*iter));
  1307 	}
  1308 
  1309 	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
  1310 	g_ShareSys.DestroyIdentType(g_PluginIdent);
  1311 	g_ShareSys.DestroyIdentity(m_MyIdent);
  1312 }
  1313 
  1314 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
  1315 {
  1316 	/* We don't care about the internal object, actually */
  1317 }
  1318 
  1319 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
  1320 {
  1321 	m_natives.push_back(natives);
  1322 }
  1323 
  1324 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
  1325 {
  1326 	IPlugin *pPlugin;
  1327 	HandleError _err;
  1328 
  1329 	HandleSecurity sec;
  1330 
  1331 	sec.pOwner = NULL;
  1332 	sec.pIdentity = m_MyIdent;
  1333 
  1334 	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
  1335 	{
  1336 		pPlugin = NULL;
  1337 	}
  1338 
  1339 	if (err)
  1340 	{
  1341 		*err = _err;
  1342 	}
  1343 
  1344 	return pPlugin;
  1345 }
  1346 
  1347 CPlugin *CPluginManager::GetPluginByOrder(int num)
  1348 {
  1349 	if (num < 1 || num > (int)GetPluginCount())
  1350 	{
  1351 		return NULL;
  1352 	}
  1353 
  1354 	CPlugin *pl;
  1355 	int id = 1;
  1356 
  1357 	IPluginIterator *iter = GetPluginIterator();
  1358 	for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
  1359 
  1360 	pl = (CPlugin *)(iter->GetPlugin());
  1361 	iter->Release();
  1362 
  1363 	return pl;
  1364 }
  1365 
  1366 const char *CPluginManager::GetStatusText(PluginStatus st)
  1367 {
  1368 	switch (st)
  1369 	{
  1370 	case Plugin_Running:
  1371 		return "Running";
  1372 	case Plugin_Paused:
  1373 		return "Paused";
  1374 	case Plugin_Error:
  1375 		return "Error";
  1376 	case Plugin_Uncompiled:
  1377 		return "Uncompiled";
  1378 	case Plugin_BadLoad:
  1379 		return "Bad Load";
  1380 	case Plugin_Failed:
  1381 		return "Failed";
  1382 	default:
  1383 		assert(false);
  1384 		return "-";
  1385 	}
  1386 }
  1387 
  1388 #define IS_STR_FILLED(var) (var[0] != '\0')
  1389 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
  1390 {
  1391 	if (argcount >= 3)
  1392 	{
  1393 		const char *cmd = g_RootMenu.GetArgument(2);
  1394 		if (strcmp(cmd, "list") == 0)
  1395 		{
  1396 			char buffer[256];
  1397 			unsigned int id = 1;
  1398 			int plnum = GetPluginCount();
  1399 
  1400 			if (!plnum)
  1401 			{
  1402 				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
  1403 				return;
  1404 			} else {
  1405 				g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
  1406 			}
  1407 
  1408 			IPluginIterator *iter = GetPluginIterator();
  1409 			for (; iter->MorePlugins(); iter->NextPlugin(), id++)
  1410 			{
  1411 				IPlugin *pl = iter->GetPlugin();
  1412 				assert(pl->GetStatus() != Plugin_Created);
  1413 				int len = 0;
  1414 				const sm_plugininfo_t *info = pl->GetPublicInfo();
  1415 				if (pl->GetStatus() != Plugin_Running)
  1416 				{
  1417 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
  1418 				} else {
  1419 					len += UTIL_Format(buffer, sizeof(buffer), "  %02d", id);
  1420 				}
  1421 				len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1422 				if (IS_STR_FILLED(info->version))
  1423 				{
  1424 					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
  1425 				}
  1426 				if (IS_STR_FILLED(info->author))
  1427 				{
  1428 					UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
  1429 				}
  1430 				g_RootMenu.ConsolePrint("%s", buffer);
  1431 			}
  1432 
  1433 			iter->Release();
  1434 			return;
  1435 		} else if (strcmp(cmd, "load") == 0) {
  1436 			if (argcount < 4)
  1437 			{
  1438 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
  1439 				return;
  1440 			}
  1441 
  1442 			char error[128];
  1443 			bool wasloaded;
  1444 			const char *filename = g_RootMenu.GetArgument(3);
  1445 			IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
  1446 
  1447 			if (wasloaded)
  1448 			{
  1449 				g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", filename);
  1450 				return;
  1451 			}
  1452 
  1453 			if (pl)
  1454 			{
  1455 				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
  1456 			} else {
  1457 				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
  1458 			}
  1459 
  1460 			return;
  1461 		} else if (strcmp(cmd, "unload") == 0) {
  1462 			if (argcount < 4)
  1463 			{
  1464 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
  1465 				return;
  1466 			}
  1467 
  1468 			int id = 1;
  1469 			int num = atoi(g_RootMenu.GetArgument(3));
  1470 			if (num < 1 || num > (int)GetPluginCount())
  1471 			{
  1472 				g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
  1473 				return;
  1474 			}
  1475 
  1476 			IPluginIterator *iter = GetPluginIterator();
  1477 			for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
  1478 				;
  1479 			IPlugin *pl = iter->GetPlugin();
  1480 
  1481 			char name[PLATFORM_MAX_PATH+1];
  1482 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1483 			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1484 
  1485 			if (UnloadPlugin(pl))
  1486 			{
  1487 				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
  1488 			} else {
  1489 				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
  1490 			}
  1491 
  1492 			iter->Release();
  1493 			return;
  1494 		} else if (strcmp(cmd, "info") == 0) {
  1495 			if (argcount < 4)
  1496 			{
  1497 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
  1498 				return;
  1499 			}
  1500 
  1501 			int num = atoi(g_RootMenu.GetArgument(3));
  1502 			if (num < 1 || num > (int)GetPluginCount())
  1503 			{
  1504 				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
  1505 				return;
  1506 			}
  1507 
  1508 			CPlugin *pl = GetPluginByOrder(num);
  1509 			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1510 
  1511 			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
  1512 			if (pl->GetStatus() <= Plugin_Error)
  1513 			{
  1514 				if (IS_STR_FILLED(info->name))
  1515 				{
  1516 					g_RootMenu.ConsolePrint("  Title: %s", info->name);
  1517 				}
  1518 				if (IS_STR_FILLED(info->version))
  1519 				{
  1520 					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  1521 				}
  1522 				if (IS_STR_FILLED(info->url))
  1523 				{
  1524 					g_RootMenu.ConsolePrint("  URL: %s", info->url);
  1525 				}
  1526 				if (IS_STR_FILLED(info->author))
  1527 				{
  1528 					g_RootMenu.ConsolePrint("  Author: %s", info->author);
  1529 				}
  1530 				if (IS_STR_FILLED(info->version))
  1531 				{
  1532 					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  1533 				}
  1534 				if (IS_STR_FILLED(info->description))
  1535 				{
  1536 					g_RootMenu.ConsolePrint("  Description: %s", info->description);
  1537 				}	
  1538 				g_RootMenu.ConsolePrint("  Debugging: %s", pl->IsDebugging() ? "yes" : "no");
  1539 				g_RootMenu.ConsolePrint("  Paused: %s", pl->GetStatus() == Plugin_Running ? "no" : "yes");
  1540 			} else {
  1541 				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
  1542 				g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
  1543 										info->name ? info->name : "<none>",
  1544 										info->version ? info->version : "<none>");
  1545 				if (IS_STR_FILLED(info->url))
  1546 				{
  1547 					g_RootMenu.ConsolePrint("  File URL: %s", info->url);
  1548 				}
  1549 			}
  1550 
  1551 			return;
  1552 		} else if (strcmp(cmd, "debug") == 0) {
  1553 			if (argcount < 5)
  1554 			{
  1555 				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
  1556 				return;
  1557 			}
  1558 
  1559 			int num = atoi(g_RootMenu.GetArgument(3));
  1560 			if (num < 1 || num > (int)GetPluginCount())
  1561 			{
  1562 				g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
  1563 				return;
  1564 			}
  1565 
  1566 			int res;
  1567 			const char *mode = g_RootMenu.GetArgument(4);
  1568 			if ((res=strcmp("on", mode)) && strcmp("off", mode))
  1569 			{
  1570 				g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
  1571 				return;
  1572 			}
  1573 
  1574 			bool debug;
  1575 			if (!res)
  1576 			{
  1577 				debug = true;
  1578 			} else {
  1579 				debug = false;
  1580 			}
  1581 
  1582 			CPlugin *pl = GetPluginByOrder(num);
  1583 			if (debug && pl->IsDebugging())
  1584 			{
  1585 				g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
  1586 				return;
  1587 			} else if (!debug && !pl->IsDebugging()) {
  1588 				g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
  1589 				return;
  1590 			}
  1591 
  1592 			char error[256];
  1593 			if (pl->ToggleDebugMode(debug, error, sizeof(error)))
  1594 			{
  1595 				g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
  1596 				return;
  1597 			} else {
  1598 				g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
  1599 				g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
  1600 				return;
  1601 			}
  1602 		}
  1603 	}
  1604 
  1605 	/* Draw the main menu */
  1606 	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
  1607 	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
  1608 	g_RootMenu.DrawGenericOption("load", "Load a plugin");
  1609 	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
  1610 	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
  1611 	g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
  1612 }
  1613 
  1614 void CPluginManager::ReloadOrUnloadPlugins()
  1615 {
  1616 	List<CPlugin *>::iterator iter;
  1617 	List<CPlugin *> tmp_list = m_plugins;
  1618 	CPlugin *pl;
  1619 	time_t t;
  1620 
  1621 	for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
  1622 	{
  1623 		pl = (*iter);
  1624 		if (pl->GetType() ==  PluginType_MapOnly)
  1625 		{
  1626 			UnloadPlugin((IPlugin *)pl);
  1627 		} else if (pl->GetType() == PluginType_MapUpdated) {
  1628 			t=pl->GetFileTimeStamp();
  1629 			if (!t)
  1630 			{
  1631 				continue;
  1632 			}
  1633 			if (t > pl->GetTimeStamp())
  1634 			{
  1635 				pl->SetTimeStamp(t);
  1636 				UnloadPlugin((IPlugin *)pl);
  1637 			}
  1638 		}
  1639 	}
  1640 }
  1641 
  1642 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
  1643 {
  1644 	List<IPluginsListener *>::iterator iter;
  1645 	IPluginsListener *pListener;
  1646 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1647 	{
  1648 		pListener = (*iter);
  1649 		pListener->OnPluginPauseChange(pl, paused);
  1650 	}	
  1651 }