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