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