core/systems/PluginSys.cpp
changeset 1952 b7aa2b626807
child 2042 4a2faafca477
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/core/systems/PluginSys.cpp	Sun Mar 30 07:00:22 2008 +0000
     1.3 @@ -0,0 +1,2900 @@
     1.4 +/**
     1.5 + * vim: set ts=4 :
     1.6 + * =============================================================================
     1.7 + * SourceMod
     1.8 + * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     1.9 + * =============================================================================
    1.10 + *
    1.11 + * This program is free software; you can redistribute it and/or modify it under
    1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    1.13 + * Free Software Foundation.
    1.14 + * 
    1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    1.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    1.18 + * details.
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License along with
    1.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    1.22 + *
    1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    1.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    1.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    1.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    1.30 + * or <http://www.sourcemod.net/license.php>.
    1.31 + *
    1.32 + * Version: $Id$
    1.33 + */
    1.34 +
    1.35 +#include <stdio.h>
    1.36 +#include "PluginSys.h"
    1.37 +#include "ShareSys.h"
    1.38 +#include "LibrarySys.h"
    1.39 +#include "HandleSys.h"
    1.40 +#include "ForwardSys.h"
    1.41 +#include "sourcemm_api.h"
    1.42 +#include "sourcemod.h"
    1.43 +#include "TextParsers.h"
    1.44 +#include "Logger.h"
    1.45 +#include "ExtensionSys.h"
    1.46 +#include "sm_srvcmds.h"
    1.47 +#include "sm_stringutil.h"
    1.48 +#include "ConCmdManager.h"
    1.49 +#include "PlayerManager.h"
    1.50 +#include "CoreConfig.h"
    1.51 +#include "Translator.h"
    1.52 +
    1.53 +CPluginManager g_PluginSys;
    1.54 +HandleType_t g_PluginType = 0;
    1.55 +IdentityType_t g_PluginIdent = 0;
    1.56 +
    1.57 +CPlugin::CPlugin(const char *file)
    1.58 +{
    1.59 +	static int MySerial = 0;
    1.60 +
    1.61 +	m_type = PluginType_Private;
    1.62 +	m_status = Plugin_Uncompiled;
    1.63 +	m_serial = ++MySerial;
    1.64 +	m_plugin = NULL;
    1.65 +	m_errormsg[256] = '\0';
    1.66 +	snprintf(m_filename, sizeof(m_filename), "%s", file);
    1.67 +	m_handle = 0;
    1.68 +	m_ident = NULL;
    1.69 +	m_pProps = sm_trie_create();
    1.70 +	m_FakeNativesMissing = false;
    1.71 +	m_LibraryMissing = false;
    1.72 +	m_bGotAllLoaded = false;
    1.73 +}
    1.74 +
    1.75 +CPlugin::~CPlugin()
    1.76 +{
    1.77 +	if (m_handle)
    1.78 +	{
    1.79 +		HandleSecurity sec;
    1.80 +		sec.pOwner = g_PluginSys.GetIdentity();
    1.81 +		sec.pIdentity = sec.pOwner;
    1.82 +
    1.83 +		g_HandleSys.FreeHandle(m_handle, &sec);
    1.84 +		g_ShareSys.DestroyIdentity(m_ident);
    1.85 +	}
    1.86 +
    1.87 +	if (m_ctx.base)
    1.88 +	{
    1.89 +		delete m_ctx.base;
    1.90 +		m_ctx.base = NULL;
    1.91 +	}
    1.92 +	if (m_ctx.ctx)
    1.93 +	{
    1.94 +		m_ctx.vm->FreeContext(m_ctx.ctx);
    1.95 +		m_ctx.ctx = NULL;
    1.96 +	}
    1.97 +	if (m_ctx.co)
    1.98 +	{
    1.99 +		m_ctx.vm->AbortCompilation(m_ctx.co);
   1.100 +		m_ctx.co = NULL;
   1.101 +	}
   1.102 +
   1.103 +	if (m_plugin)
   1.104 +	{
   1.105 +		g_pSourcePawn->FreeFromMemory(m_plugin);
   1.106 +		m_plugin = NULL;
   1.107 +	}
   1.108 +	if (m_pProps)
   1.109 +	{
   1.110 +		sm_trie_destroy(m_pProps);
   1.111 +	}
   1.112 +	for (size_t i=0; i<m_configs.size(); i++)
   1.113 +	{
   1.114 +		delete m_configs[i];
   1.115 +	}
   1.116 +	m_configs.clear();
   1.117 +}
   1.118 +
   1.119 +void CPlugin::InitIdentity()
   1.120 +{
   1.121 +	if (!m_handle)
   1.122 +	{
   1.123 +		m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
   1.124 +		m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
   1.125 +		m_ctx.base->SetIdentity(m_ident);
   1.126 +	}
   1.127 +}
   1.128 +
   1.129 +unsigned int CPlugin::CalcMemUsage()
   1.130 +{
   1.131 +	unsigned int base_size = 
   1.132 +		sizeof(CPlugin) 
   1.133 +		+ sizeof(IdentityToken_t)
   1.134 +		+ (m_PhraseFiles.size() * sizeof(unsigned int))
   1.135 +		+ (m_dependents.size() * sizeof(CPlugin *))
   1.136 +		+ (m_dependsOn.size() * sizeof(CPlugin *))
   1.137 +		+ (m_fakeNatives.size() * (sizeof(FakeNative *) + sizeof(FakeNative)))
   1.138 +		+ (m_WeakNatives.size() * sizeof(WeakNative))
   1.139 +		+ (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
   1.140 +		+ sm_trie_mem_usage(m_pProps);
   1.141 +
   1.142 +	for (unsigned int i = 0; i < m_configs.size(); i++)
   1.143 +	{
   1.144 +		base_size += m_configs[i]->autocfg.size();
   1.145 +		base_size += m_configs[i]->folder.size();
   1.146 +	}
   1.147 +
   1.148 +	for (List<String>::iterator i = m_Libraries.begin();
   1.149 +		 i != m_Libraries.end();
   1.150 +		 i++)
   1.151 +	{
   1.152 +		base_size += (*i).size();
   1.153 +	}
   1.154 +
   1.155 +	for (List<String>::iterator i = m_RequiredLibs.begin();
   1.156 +		 i != m_RequiredLibs.end();
   1.157 +		 i++)
   1.158 +	{
   1.159 +		base_size += (*i).size();
   1.160 +	}
   1.161 +
   1.162 +	for (List<FakeNative *>::iterator i = m_fakeNatives.begin();
   1.163 +		 i != m_fakeNatives.end();
   1.164 +		 i++)
   1.165 +	{
   1.166 +		base_size += (*i)->name.size();
   1.167 +	}
   1.168 +
   1.169 +	if (m_plugin != NULL)
   1.170 +	{
   1.171 +		base_size += sizeof(sp_plugin_t);
   1.172 +		base_size += m_plugin->data_size;
   1.173 +		base_size += m_plugin->pcode_size;
   1.174 +		base_size += (m_plugin->info.natives_num * sizeof(sp_file_natives_t));
   1.175 +		base_size += (m_plugin->info.publics_num * sizeof(sp_file_publics_t));
   1.176 +		base_size += (m_plugin->info.pubvars_num * sizeof(sp_file_pubvars_t));
   1.177 +		base_size += (m_plugin->debug.files_num * sizeof(sp_fdbg_file_t));
   1.178 +		base_size += (m_plugin->debug.lines_num * sizeof(sp_fdbg_line_t));
   1.179 +		base_size += (m_plugin->debug.syms_num * sizeof(sp_fdbg_symbol_t));
   1.180 +		/* We can't get strtab size, oh well. */
   1.181 +	}
   1.182 +
   1.183 +	if (m_ctx.base != NULL)
   1.184 +	{
   1.185 +		base_size += sizeof(BaseContext);
   1.186 +		base_size += m_ctx.base->GetPublicsNum() * sizeof(CFunction);
   1.187 +	}
   1.188 +	if (m_ctx.ctx != NULL)
   1.189 +	{
   1.190 +		base_size += m_ctx.ctx->mem_size;
   1.191 +		base_size += (m_plugin->debug.files_num * sizeof(sp_debug_file_t));
   1.192 +		base_size += (m_plugin->debug.lines_num * sizeof(sp_debug_line_t));
   1.193 +		base_size += (m_plugin->debug.syms_num * sizeof(sp_debug_symbol_t));
   1.194 +		base_size += (m_plugin->info.pubvars_num * sizeof(sp_pubvar_t));
   1.195 +		base_size += (m_plugin->info.publics_num * sizeof(sp_public_t));
   1.196 +		base_size += (m_plugin->info.natives_num * sizeof(sp_native_t));
   1.197 +		/* We also don't know the JIT code size, oh well. */
   1.198 +	}
   1.199 +
   1.200 +	return base_size;
   1.201 +}
   1.202 +
   1.203 +Handle_t CPlugin::GetMyHandle()
   1.204 +{
   1.205 +	return m_handle;
   1.206 +}
   1.207 +
   1.208 +CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
   1.209 +{
   1.210 +	char fullpath[PLATFORM_MAX_PATH];
   1.211 +	g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
   1.212 +	FILE *fp = fopen(fullpath, "rb");
   1.213 +
   1.214 +	CPlugin *pPlugin = new CPlugin(file);
   1.215 +
   1.216 +	if (!fp)
   1.217 +	{
   1.218 +		if (error)
   1.219 +		{
   1.220 +			snprintf(error, maxlength, "Unable to open file");
   1.221 +		}
   1.222 +		pPlugin->m_status = Plugin_BadLoad;
   1.223 +		return pPlugin;
   1.224 +	}
   1.225 +
   1.226 +	int err;
   1.227 +	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
   1.228 +	if (pl == NULL)
   1.229 +	{
   1.230 +		fclose(fp);
   1.231 +		if (error)
   1.232 +		{
   1.233 +			snprintf(error, maxlength, "Error %d while parsing plugin", err);
   1.234 +		}
   1.235 +		pPlugin->m_status = Plugin_BadLoad;
   1.236 +		return pPlugin;
   1.237 +	}
   1.238 +
   1.239 +	fclose(fp);
   1.240 +
   1.241 +	pPlugin->m_plugin = pl;
   1.242 +
   1.243 +	return pPlugin;
   1.244 +}
   1.245 +
   1.246 +bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
   1.247 +{
   1.248 +	bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
   1.249 +
   1.250 +	if (exists && remove)
   1.251 +	{
   1.252 +		sm_trie_delete(m_pProps, prop);
   1.253 +	}
   1.254 +
   1.255 +	return exists;
   1.256 +}
   1.257 +
   1.258 +bool CPlugin::SetProperty(const char *prop, void *ptr)
   1.259 +{
   1.260 +	return sm_trie_insert(m_pProps, prop, ptr);
   1.261 +}
   1.262 +
   1.263 +ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
   1.264 +{
   1.265 +	if (!m_plugin)
   1.266 +	{
   1.267 +		return NULL;
   1.268 +	}
   1.269 +
   1.270 +	/* :NOTICE: We will eventually need to change these natives
   1.271 +	 * for swapping in new contexts
   1.272 +	 */
   1.273 +	if (m_ctx.co || m_ctx.ctx)
   1.274 +	{
   1.275 +		return NULL;
   1.276 +	}
   1.277 +
   1.278 +	m_status = Plugin_Uncompiled;
   1.279 +
   1.280 +	m_ctx.vm = vm;
   1.281 +	m_ctx.co = vm->StartCompilation(m_plugin);
   1.282 +
   1.283 +	return m_ctx.co;
   1.284 +}
   1.285 +
   1.286 +void CPlugin::CancelMyCompile()
   1.287 +{
   1.288 +	if (!m_ctx.co)
   1.289 +	{
   1.290 +		return;
   1.291 +	}
   1.292 +
   1.293 +	m_ctx.vm->AbortCompilation(m_ctx.co);
   1.294 +	m_ctx.co = NULL;
   1.295 +	m_ctx.vm = NULL;
   1.296 +}
   1.297 +
   1.298 +bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
   1.299 +{
   1.300 +	if (!m_ctx.co)
   1.301 +	{
   1.302 +		return false;
   1.303 +	}
   1.304 +
   1.305 +	int err;
   1.306 +	m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
   1.307 +	if (!m_ctx.ctx)
   1.308 +	{
   1.309 +		memset(&m_ctx, 0, sizeof(m_ctx));
   1.310 +		if (error)
   1.311 +		{
   1.312 +			snprintf(error, maxlength, "JIT failed to compile (error %d)", err);
   1.313 +		}
   1.314 +		return false;
   1.315 +	}
   1.316 +
   1.317 +	m_ctx.base = new BaseContext(m_ctx.ctx);
   1.318 +	m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
   1.319 +
   1.320 +	m_status = Plugin_Created;
   1.321 +	m_ctx.co = NULL;
   1.322 +
   1.323 +	UpdateInfo();
   1.324 +
   1.325 +	return true;
   1.326 +}
   1.327 +
   1.328 +void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
   1.329 +{
   1.330 +	PluginStatus old_status = m_status;
   1.331 +	m_status = status;
   1.332 +
   1.333 +	if (old_status == Plugin_Running)
   1.334 +	{
   1.335 +		/* Tell everyone we're now paused */
   1.336 +		g_PluginSys._SetPauseState(this, true);
   1.337 +	}
   1.338 +
   1.339 +	va_list ap;
   1.340 +	va_start(ap, error_fmt);
   1.341 +	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
   1.342 +	va_end(ap);
   1.343 +
   1.344 +	if (m_ctx.ctx)
   1.345 +	{
   1.346 +		m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
   1.347 +	}
   1.348 +}
   1.349 +
   1.350 +void CPlugin::UpdateInfo()
   1.351 +{
   1.352 +	/* Now grab the info */
   1.353 +	uint32_t idx;
   1.354 +	IPluginContext *base = GetBaseContext();
   1.355 +	int err = base->FindPubvarByName("myinfo", &idx);
   1.356 +
   1.357 +	memset(&m_info, 0, sizeof(m_info));
   1.358 +
   1.359 +	if (err == SP_ERROR_NONE)
   1.360 +	{
   1.361 +		struct sm_plugininfo_c_t
   1.362 +		{
   1.363 +			cell_t name;
   1.364 +			cell_t description;
   1.365 +			cell_t author;
   1.366 +			cell_t version;
   1.367 +			cell_t url;
   1.368 +		};
   1.369 +		sm_plugininfo_c_t *cinfo;
   1.370 +		cell_t local_addr;
   1.371 +
   1.372 +		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
   1.373 +		base->LocalToString(cinfo->name, (char **)&m_info.name);
   1.374 +		base->LocalToString(cinfo->description, (char **)&m_info.description);
   1.375 +		base->LocalToString(cinfo->author, (char **)&m_info.author);
   1.376 +		base->LocalToString(cinfo->url, (char **)&m_info.url);
   1.377 +		base->LocalToString(cinfo->version, (char **)&m_info.version);
   1.378 +	}
   1.379 +
   1.380 +	m_info.author = m_info.author ? m_info.author : "";
   1.381 +	m_info.description = m_info.description ? m_info.description : "";
   1.382 +	m_info.name = m_info.name ? m_info.name : "";
   1.383 +	m_info.url = m_info.url ? m_info.url : "";
   1.384 +	m_info.version = m_info.version ? m_info.version : "";
   1.385 +}
   1.386 +
   1.387 +void CPlugin::Call_OnPluginStart()
   1.388 +{
   1.389 +	if (m_status != Plugin_Loaded)
   1.390 +	{
   1.391 +		return;
   1.392 +	}
   1.393 +
   1.394 +	m_status = Plugin_Running;
   1.395 +
   1.396 +	cell_t result;
   1.397 +	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
   1.398 +	if (!pFunction)
   1.399 +	{
   1.400 +		return;
   1.401 +	}
   1.402 +
   1.403 +	int err;
   1.404 +	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   1.405 +	{
   1.406 +		SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
   1.407 +	}
   1.408 +}
   1.409 +
   1.410 +void CPlugin::Call_OnPluginEnd()
   1.411 +{
   1.412 +	if (m_status > Plugin_Paused)
   1.413 +	{
   1.414 +		return;
   1.415 +	}
   1.416 +
   1.417 +	cell_t result;
   1.418 +	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
   1.419 +	if (!pFunction)
   1.420 +	{
   1.421 +		return;
   1.422 +	}
   1.423 +
   1.424 +	pFunction->Execute(&result);
   1.425 +}
   1.426 +
   1.427 +void CPlugin::Call_OnAllPluginsLoaded()
   1.428 +{
   1.429 +	if (m_status > Plugin_Paused)
   1.430 +	{
   1.431 +		return;
   1.432 +	}
   1.433 +
   1.434 +	if (m_bGotAllLoaded)
   1.435 +	{
   1.436 +		return;
   1.437 +	}
   1.438 +
   1.439 +	m_bGotAllLoaded = true;
   1.440 +
   1.441 +	cell_t result;
   1.442 +	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnAllPluginsLoaded");
   1.443 +	if (pFunction != NULL)
   1.444 +	{
   1.445 +		pFunction->Execute(&result);
   1.446 +	}
   1.447 +
   1.448 +	if (g_OnMapStarted)
   1.449 +	{
   1.450 +		if ((pFunction = m_ctx.base->GetFunctionByName("OnMapStart")) != NULL)
   1.451 +		{
   1.452 +			pFunction->Execute(NULL);
   1.453 +		}
   1.454 +	}
   1.455 +
   1.456 +	if (SM_AreConfigsExecuted())
   1.457 +	{
   1.458 +		SM_ExecuteForPlugin(GetBaseContext());
   1.459 +	}
   1.460 +}
   1.461 +
   1.462 +bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
   1.463 +{
   1.464 +	if (m_status != Plugin_Created)
   1.465 +	{
   1.466 +		return false;
   1.467 +	}
   1.468 +
   1.469 +	m_status = Plugin_Loaded;
   1.470 +
   1.471 +	int err;
   1.472 +	cell_t result;
   1.473 +	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
   1.474 +
   1.475 +	if (!pFunction)
   1.476 +	{
   1.477 +		return true;
   1.478 +	}
   1.479 +
   1.480 +	pFunction->PushCell(m_handle);
   1.481 +	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
   1.482 +	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
   1.483 +	pFunction->PushCell(maxlength);
   1.484 +	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   1.485 +	{
   1.486 +		return false;
   1.487 +	}
   1.488 +
   1.489 +	if (!result || m_status != Plugin_Loaded)
   1.490 +	{
   1.491 +		return false;
   1.492 +	}
   1.493 +
   1.494 +	return true;
   1.495 +}
   1.496 +
   1.497 +const sp_plugin_t *CPlugin::GetPluginStructure()
   1.498 +{
   1.499 +	return m_plugin;
   1.500 +}
   1.501 +
   1.502 +IPluginContext *CPlugin::GetBaseContext()
   1.503 +{
   1.504 +	return m_ctx.base;
   1.505 +}
   1.506 +
   1.507 +sp_context_t *CPlugin::GetContext()
   1.508 +{
   1.509 +	return m_ctx.ctx;
   1.510 +}
   1.511 +
   1.512 +const char *CPlugin::GetFilename()
   1.513 +{
   1.514 +	return m_filename;
   1.515 +}
   1.516 +
   1.517 +PluginType CPlugin::GetType()
   1.518 +{
   1.519 +	return m_type;
   1.520 +}
   1.521 +
   1.522 +const sm_plugininfo_t *CPlugin::GetPublicInfo()
   1.523 +{
   1.524 +	return &m_info;
   1.525 +}
   1.526 +
   1.527 +unsigned int CPlugin::GetSerial()
   1.528 +{
   1.529 +	return m_serial;
   1.530 +}
   1.531 +
   1.532 +PluginStatus CPlugin::GetStatus()
   1.533 +{
   1.534 +	return m_status;
   1.535 +}
   1.536 +
   1.537 +bool CPlugin::IsDebugging()
   1.538 +{
   1.539 +	if (!m_ctx.ctx)
   1.540 +	{
   1.541 +		return false;
   1.542 +	}
   1.543 +
   1.544 +	return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   1.545 +}
   1.546 +
   1.547 +void CPlugin::LibraryActions(bool dropping)
   1.548 +{
   1.549 +	List<String>::iterator iter;
   1.550 +	for (iter = m_Libraries.begin();
   1.551 +		iter != m_Libraries.end();
   1.552 +		iter++)
   1.553 +	{
   1.554 +		g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping);
   1.555 +	}
   1.556 +}
   1.557 +
   1.558 +bool CPlugin::SetPauseState(bool paused)
   1.559 +{
   1.560 +	if (paused && GetStatus() != Plugin_Running)
   1.561 +	{
   1.562 +		return false;
   1.563 +	} else if (!paused && GetStatus() != Plugin_Paused) {
   1.564 +		return false;
   1.565 +	}
   1.566 +
   1.567 +	if (paused)
   1.568 +	{
   1.569 +		LibraryActions(true);
   1.570 +	}
   1.571 +
   1.572 +	IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
   1.573 +	if (pFunction)
   1.574 +	{
   1.575 +		cell_t result;
   1.576 +		pFunction->PushCell(paused ? 1 : 0);
   1.577 +		pFunction->Execute(&result);
   1.578 +	}
   1.579 +
   1.580 +	if (paused)
   1.581 +	{
   1.582 +		m_status = Plugin_Paused;
   1.583 +		m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
   1.584 +	} else {
   1.585 +		m_status = Plugin_Running;
   1.586 +		m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
   1.587 +	}
   1.588 +
   1.589 +	g_PluginSys._SetPauseState(this, paused);
   1.590 +
   1.591 +	if (!paused)
   1.592 +	{
   1.593 +		LibraryActions(false);
   1.594 +	}
   1.595 +
   1.596 +	return true;
   1.597 +}
   1.598 +
   1.599 +IdentityToken_t *CPlugin::GetIdentity()
   1.600 +{
   1.601 +	return m_ident;
   1.602 +}
   1.603 +
   1.604 +bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
   1.605 +{
   1.606 +	int err;
   1.607 +
   1.608 +	if (!IsRunnable())
   1.609 +	{
   1.610 +		if (error)
   1.611 +		{
   1.612 +			snprintf(error, maxlength, "Plugin is not runnable.");
   1.613 +		}
   1.614 +		return false;
   1.615 +	}
   1.616 +
   1.617 +	if (debug && IsDebugging())
   1.618 +	{
   1.619 +		if (error)
   1.620 +		{
   1.621 +			snprintf(error, maxlength, "Plugin is already in debug mode.");
   1.622 +		}
   1.623 +		return false;
   1.624 +	}
   1.625 +	else if (!debug && !IsDebugging())
   1.626 +	{
   1.627 +		if (error)
   1.628 +		{
   1.629 +			snprintf(error, maxlength, "Plugins is already in production mode.");
   1.630 +		}
   1.631 +		return false;
   1.632 +	}
   1.633 +
   1.634 +	ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
   1.635 +	if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
   1.636 +	{
   1.637 +		if (error)
   1.638 +		{
   1.639 +			snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
   1.640 +		}
   1.641 +		return false;
   1.642 +	}
   1.643 +
   1.644 +	sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
   1.645 +
   1.646 +	if (new_ctx)
   1.647 +	{
   1.648 +		memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
   1.649 +		new_ctx->hp = m_ctx.ctx->hp;
   1.650 +		new_ctx->sp = m_ctx.ctx->sp;
   1.651 +		new_ctx->frm = m_ctx.ctx->frm;
   1.652 +		new_ctx->dbreak = m_ctx.ctx->dbreak;
   1.653 +		new_ctx->context = m_ctx.ctx->context;
   1.654 +		memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
   1.655 +
   1.656 +		uint32_t nativeCount = m_plugin->info.natives_num;
   1.657 +		for (uint32_t i=0; i<nativeCount; i++)
   1.658 +		{
   1.659 +			new_ctx->natives[i].pfn = m_ctx.ctx->natives[i].pfn;
   1.660 +			new_ctx->natives[i].status = m_ctx.ctx->natives[i].status;
   1.661 +		}
   1.662 +
   1.663 +		g_pVM->FreeContext(m_ctx.ctx);
   1.664 +		m_ctx.ctx = new_ctx;
   1.665 +		m_ctx.base->SetContext(new_ctx);
   1.666 +
   1.667 +		UpdateInfo();
   1.668 +	}
   1.669 +	else
   1.670 +	{
   1.671 +		if (error)
   1.672 +		{
   1.673 +			snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
   1.674 +		}
   1.675 +		return false;
   1.676 +	}
   1.677 +
   1.678 +	return true;
   1.679 +}
   1.680 +
   1.681 +bool CPlugin::IsRunnable()
   1.682 +{
   1.683 +	return (m_status <= Plugin_Paused) ? true : false;
   1.684 +}
   1.685 +
   1.686 +time_t CPlugin::GetFileTimeStamp()
   1.687 +{
   1.688 +	char path[PLATFORM_MAX_PATH];
   1.689 +	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
   1.690 +#ifdef PLATFORM_WINDOWS
   1.691 +	struct _stat s;
   1.692 +	if (_stat(path, &s) != 0)
   1.693 +#elif defined PLATFORM_POSIX
   1.694 +	struct stat s;
   1.695 +	if (stat(path, &s) != 0)
   1.696 +#endif
   1.697 +	{
   1.698 +		return 0;
   1.699 +	}
   1.700 +	else
   1.701 +	{
   1.702 +		return s.st_mtime;
   1.703 +	}
   1.704 +}
   1.705 +
   1.706 +time_t CPlugin::GetTimeStamp()
   1.707 +{
   1.708 +	return m_LastAccess;
   1.709 +}
   1.710 +
   1.711 +void CPlugin::SetTimeStamp(time_t t)
   1.712 +{
   1.713 +	m_LastAccess = t;
   1.714 +}
   1.715 +
   1.716 +void CPlugin::AddLangFile(unsigned int index)
   1.717 +{
   1.718 +	m_PhraseFiles.push_back(index);
   1.719 +}
   1.720 +
   1.721 +size_t CPlugin::GetLangFileCount()
   1.722 +{
   1.723 +	return m_PhraseFiles.size();
   1.724 +}
   1.725 +
   1.726 +unsigned int CPlugin::GetLangFileByIndex(unsigned int index)
   1.727 +{
   1.728 +	return m_PhraseFiles.at(index);
   1.729 +}
   1.730 +
   1.731 +void CPlugin::DependencyDropped(CPlugin *pOwner)
   1.732 +{
   1.733 +	if (!m_ctx.ctx)
   1.734 +	{
   1.735 +		return;
   1.736 +	}
   1.737 +
   1.738 +	List<String>::iterator reqlib_iter;
   1.739 +	List<String>::iterator lib_iter;
   1.740 +	for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
   1.741 +	{
   1.742 +		for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
   1.743 +		{
   1.744 +			if ((*reqlib_iter) == (*lib_iter))
   1.745 +			{
   1.746 +				m_LibraryMissing = true;
   1.747 +			}
   1.748 +		}	
   1.749 +	}
   1.750 +
   1.751 +	List<FakeNative *>::iterator iter;
   1.752 +	FakeNative *pNative;
   1.753 +	sp_native_t *native;
   1.754 +	uint32_t idx;
   1.755 +	unsigned int unbound = 0;
   1.756 +
   1.757 +	for (iter = pOwner->m_fakeNatives.begin();
   1.758 +		 iter != pOwner->m_fakeNatives.end();
   1.759 +		 iter++)
   1.760 +	{
   1.761 +		pNative = (*iter);
   1.762 +		/* Find this native! */
   1.763 +		if (m_ctx.base->FindNativeByName(pNative->name.c_str(), &idx) != SP_ERROR_NONE)
   1.764 +		{
   1.765 +			continue;
   1.766 +		}
   1.767 +		/* Unbind it */
   1.768 +		m_ctx.base->GetNativeByIndex(idx, &native);
   1.769 +		native->pfn = NULL;
   1.770 +		native->status = SP_NATIVE_UNBOUND;
   1.771 +		unbound++;
   1.772 +	}
   1.773 +
   1.774 +	if (unbound)
   1.775 +	{
   1.776 +		m_FakeNativesMissing = true;
   1.777 +	}
   1.778 +
   1.779 +	/* :IDEA: in the future, add native trapping? */
   1.780 +	if (m_FakeNativesMissing || m_LibraryMissing)
   1.781 +	{
   1.782 +		SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
   1.783 +	}
   1.784 +
   1.785 +	m_dependsOn.remove(pOwner);
   1.786 +}
   1.787 +
   1.788 +unsigned int CPlugin::GetConfigCount()
   1.789 +{
   1.790 +	return (unsigned int)m_configs.size();
   1.791 +}
   1.792 +
   1.793 +AutoConfig *CPlugin::GetConfig(unsigned int i)
   1.794 +{
   1.795 +	if (i >= GetConfigCount())
   1.796 +	{
   1.797 +		return NULL;
   1.798 +	}
   1.799 +
   1.800 +	return m_configs[i];
   1.801 +}
   1.802 +
   1.803 +void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
   1.804 +{
   1.805 +	/* Do a check for duplicates to prevent double-execution */
   1.806 +	for (size_t i = 0; i < m_configs.size(); i++)
   1.807 +	{
   1.808 +		if (m_configs[i]->autocfg.compare(cfg) == 0
   1.809 +			&& m_configs[i]->folder.compare(folder) == 0
   1.810 +			&& m_configs[i]->create == autoCreate)
   1.811 +		{
   1.812 +			return;
   1.813 +		}
   1.814 +	}
   1.815 +
   1.816 +	AutoConfig *c = new AutoConfig;
   1.817 +
   1.818 +	c->autocfg = cfg;
   1.819 +	c->folder = folder;
   1.820 +	c->create = autoCreate;
   1.821 +
   1.822 +	m_configs.push_back(c);
   1.823 +}
   1.824 +
   1.825 +/*******************
   1.826 + * PLUGIN ITERATOR *
   1.827 + *******************/
   1.828 +
   1.829 +CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
   1.830 +{
   1.831 +	mylist = _mylist;
   1.832 +	Reset();
   1.833 +}
   1.834 +
   1.835 +IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   1.836 +{
   1.837 +	return (*current);
   1.838 +}
   1.839 +
   1.840 +bool CPluginManager::CPluginIterator::MorePlugins()
   1.841 +{
   1.842 +	return (current != mylist->end());
   1.843 +}
   1.844 +
   1.845 +void CPluginManager::CPluginIterator::NextPlugin()
   1.846 +{
   1.847 +	current++;
   1.848 +}
   1.849 +
   1.850 +void CPluginManager::CPluginIterator::Release()
   1.851 +{
   1.852 +	g_PluginSys.ReleaseIterator(this);
   1.853 +}
   1.854 +
   1.855 +CPluginManager::CPluginIterator::~CPluginIterator()
   1.856 +{
   1.857 +}
   1.858 +
   1.859 +void CPluginManager::CPluginIterator::Reset()
   1.860 +{
   1.861 +	current = mylist->begin();
   1.862 +}
   1.863 +
   1.864 +/******************
   1.865 + * PLUGIN MANAGER *
   1.866 + ******************/
   1.867 +
   1.868 +CPluginManager::CPluginManager()
   1.869 +{
   1.870 +	m_LoadLookup = sm_trie_create();
   1.871 +	m_AllPluginsLoaded = false;
   1.872 +	m_MyIdent = NULL;
   1.873 +	m_pNativeLookup = sm_trie_create();
   1.874 +	m_pCoreNatives = sm_trie_create();
   1.875 +	m_LoadingLocked = false;
   1.876 +}
   1.877 +
   1.878 +CPluginManager::~CPluginManager()
   1.879 +{
   1.880 +	/* :NOTICE: 
   1.881 +	 * Ignore the fact that there might be plugins in the cache.
   1.882 +	 * This usually means that Core is not being unloaded properly, and everything
   1.883 +	 * will crash anyway.  YAY
   1.884 +	 */
   1.885 +	sm_trie_destroy(m_LoadLookup);
   1.886 +	sm_trie_destroy(m_pNativeLookup);
   1.887 +	sm_trie_destroy(m_pCoreNatives);
   1.888 +
   1.889 +	CStack<CPluginManager::CPluginIterator *>::iterator iter;
   1.890 +	for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
   1.891 +	{
   1.892 +		delete (*iter);
   1.893 +	}
   1.894 +	m_iters.popall();
   1.895 +}
   1.896 +
   1.897 +void CPluginManager::Shutdown()
   1.898 +{
   1.899 +	List<CPlugin *>::iterator iter;
   1.900 +
   1.901 +	while ((iter = m_plugins.begin()) != m_plugins.end())
   1.902 +	{
   1.903 +		UnloadPlugin(*iter);
   1.904 +	}
   1.905 +}
   1.906 +
   1.907 +void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
   1.908 +{
   1.909 +	/* First read in the database of plugin settings */
   1.910 +	SMCError err;
   1.911 +	SMCStates states;
   1.912 +	m_AllPluginsLoaded = false;
   1.913 +	if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
   1.914 +	{
   1.915 +		g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
   1.916 +		const char *err_msg = textparsers->GetSMCErrorString(err);
   1.917 +		if (err_msg)
   1.918 +		{
   1.919 +			g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
   1.920 +		}
   1.921 +	}
   1.922 +
   1.923 +	LoadPluginsFromDir(basedir, NULL);
   1.924 +}
   1.925 +
   1.926 +void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
   1.927 +{
   1.928 +	char base_path[PLATFORM_MAX_PATH];
   1.929 +
   1.930 +	/* Form the current path to start reading from */
   1.931 +	if (localpath == NULL)
   1.932 +	{
   1.933 +		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
   1.934 +	} else {
   1.935 +		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
   1.936 +	}
   1.937 +
   1.938 +	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
   1.939 +
   1.940 +	if (!dir)
   1.941 +	{
   1.942 +		char error[256];
   1.943 +		g_LibSys.GetPlatformError(error, sizeof(error));
   1.944 +		g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
   1.945 +		g_Logger.LogError("[SM] Platform returned error: %s", error);
   1.946 +		return;
   1.947 +	}
   1.948 +
   1.949 +	while (dir->MoreFiles())
   1.950 +	{
   1.951 +		if (dir->IsEntryDirectory() 
   1.952 +			&& (strcmp(dir->GetEntryName(), ".") != 0)
   1.953 +			&& (strcmp(dir->GetEntryName(), "..") != 0)
   1.954 +			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
   1.955 +			&& (strcmp(dir->GetEntryName(), "optional") != 0))
   1.956 +		{
   1.957 +			char new_local[PLATFORM_MAX_PATH];
   1.958 +			if (localpath == NULL)
   1.959 +			{
   1.960 +				/* If no path yet, don't add a former slash */
   1.961 +				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
   1.962 +			} else {
   1.963 +				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
   1.964 +			}
   1.965 +			LoadPluginsFromDir(basedir, new_local);
   1.966 +		} else if (dir->IsEntryFile()) {
   1.967 +			const char *name = dir->GetEntryName();
   1.968 +			size_t len = strlen(name);
   1.969 +			if (len >= 4
   1.970 +				&& strcmp(&name[len-4], ".smx") == 0)
   1.971 +			{
   1.972 +				/* If the filename matches, load the plugin */
   1.973 +				char plugin[PLATFORM_MAX_PATH];
   1.974 +				if (localpath == NULL)
   1.975 +				{
   1.976 +					snprintf(plugin, sizeof(plugin), "%s", name);
   1.977 +				} else {
   1.978 +					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
   1.979 +				}
   1.980 +				LoadAutoPlugin(plugin);
   1.981 +			}
   1.982 +		}
   1.983 +		dir->NextEntry();
   1.984 +	}
   1.985 +	g_LibSys.CloseDirectory(dir);
   1.986 +}
   1.987 +
   1.988 +LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
   1.989 +{
   1.990 +	if (m_LoadingLocked)
   1.991 +	{
   1.992 +		return LoadRes_NeverLoad;
   1.993 +	}
   1.994 +
   1.995 +	bool no_load = false;
   1.996 +	PluginSettings *pset;
   1.997 +	unsigned int setcount = m_PluginInfo.GetSettingsNum();
   1.998 +	for (unsigned int i=0; i<setcount; i++)
   1.999 +	{
  1.1000 +		if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
  1.1001 +		{
  1.1002 +			continue;
  1.1003 +		}
  1.1004 +		if (pset->blockload_val)
  1.1005 +		{
  1.1006 +			no_load = true;
  1.1007 +			break;
  1.1008 +		}
  1.1009 +	}
  1.1010 +
  1.1011 +	/**
  1.1012 +	 * Does this plugin already exist?
  1.1013 +	 */
  1.1014 +	CPlugin *pPlugin;
  1.1015 +	if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
  1.1016 +	{
  1.1017 +		/* Check to see if we should try reloading it */
  1.1018 +		if (pPlugin->GetStatus() == Plugin_BadLoad
  1.1019 +			|| pPlugin->GetStatus() == Plugin_Error
  1.1020 +			|| pPlugin->GetStatus() == Plugin_Failed
  1.1021 +			|| no_load)
  1.1022 +		{
  1.1023 +			UnloadPlugin(pPlugin);
  1.1024 +		}
  1.1025 +		else
  1.1026 +		{
  1.1027 +			if (_plugin)
  1.1028 +			{
  1.1029 +				*_plugin = pPlugin;
  1.1030 +			}
  1.1031 +			return LoadRes_AlreadyLoaded;
  1.1032 +		}
  1.1033 +	}
  1.1034 +
  1.1035 +	if (no_load)
  1.1036 +	{
  1.1037 +		return LoadRes_NeverLoad;
  1.1038 +	}
  1.1039 +
  1.1040 +	pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
  1.1041 +
  1.1042 +	assert(pPlugin != NULL);
  1.1043 +
  1.1044 +	pPlugin->m_type = PluginType_MapUpdated;
  1.1045 +
  1.1046 +	ICompilation *co = NULL;
  1.1047 +
  1.1048 +	if (pPlugin->m_status == Plugin_Uncompiled)
  1.1049 +	{
  1.1050 +		co = pPlugin->StartMyCompile(g_pVM);
  1.1051 +	}
  1.1052 +
  1.1053 +	for (unsigned int i=0; i<setcount; i++)
  1.1054 +	{
  1.1055 +		if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
  1.1056 +		{
  1.1057 +			continue;
  1.1058 +		}
  1.1059 +		pPlugin->m_type = pset->type_val;
  1.1060 +		if (co)
  1.1061 +		{
  1.1062 +			for (unsigned int j=0; j<pset->opts_num; j++)
  1.1063 +			{
  1.1064 +				const char *key, *val;
  1.1065 +				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
  1.1066 +				if (!key || !val)
  1.1067 +				{
  1.1068 +					continue;
  1.1069 +				}
  1.1070 +				if (!g_pVM->SetCompilationOption(co, key, val))
  1.1071 +				{
  1.1072 +					if (error)
  1.1073 +					{
  1.1074 +						snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
  1.1075 +					}
  1.1076 +					pPlugin->CancelMyCompile();
  1.1077 +					co = NULL;
  1.1078 +					break;
  1.1079 +				}
  1.1080 +			}
  1.1081 +		}
  1.1082 +	}
  1.1083 +
  1.1084 +	/* Do the actual compiling */
  1.1085 +	if (co)
  1.1086 +	{
  1.1087 +		pPlugin->FinishMyCompile(error, maxlength);
  1.1088 +		co = NULL;
  1.1089 +	}
  1.1090 +
  1.1091 +	/* Get the status */
  1.1092 +	if (pPlugin->GetStatus() == Plugin_Created)
  1.1093 +	{
  1.1094 +		AddCoreNativesToPlugin(pPlugin);
  1.1095 +		pPlugin->InitIdentity();
  1.1096 +		if (pPlugin->Call_AskPluginLoad(error, maxlength))
  1.1097 +		{
  1.1098 +			/* Autoload any modules */
  1.1099 +			LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
  1.1100 +		}
  1.1101 +		else
  1.1102 +		{
  1.1103 +			pPlugin->SetErrorState(Plugin_Failed, "%s", error);
  1.1104 +		}
  1.1105 +	}
  1.1106 +
  1.1107 +	/* Save the time stamp */
  1.1108 +	time_t t = pPlugin->GetFileTimeStamp();
  1.1109 +	pPlugin->SetTimeStamp(t);
  1.1110 +
  1.1111 +	if (_plugin)
  1.1112 +	{
  1.1113 +		*_plugin = pPlugin;
  1.1114 +	}
  1.1115 +
  1.1116 +	return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
  1.1117 +}
  1.1118 +
  1.1119 +IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
  1.1120 +{
  1.1121 +	CPlugin *pl;
  1.1122 +	LoadRes res;
  1.1123 +
  1.1124 +	*wasloaded = false;
  1.1125 +	if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure)
  1.1126 +	{
  1.1127 +		delete pl;
  1.1128 +		return NULL;
  1.1129 +	}
  1.1130 +
  1.1131 +	if (res == LoadRes_AlreadyLoaded)
  1.1132 +	{
  1.1133 +		*wasloaded = true;
  1.1134 +		return pl;
  1.1135 +	}
  1.1136 +
  1.1137 +	if (res == LoadRes_NeverLoad)
  1.1138 +	{
  1.1139 +		if (m_LoadingLocked)
  1.1140 +		{
  1.1141 +			UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
  1.1142 +		}
  1.1143 +		else
  1.1144 +		{
  1.1145 +			UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
  1.1146 +		}
  1.1147 +		return NULL;
  1.1148 +	}
  1.1149 +
  1.1150 +	AddPlugin(pl);
  1.1151 +
  1.1152 +	/* Run second pass if we need to */
  1.1153 +	if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
  1.1154 +	{
  1.1155 +		if (!RunSecondPass(pl, error, maxlength))
  1.1156 +		{
  1.1157 +			UnloadPlugin(pl);
  1.1158 +			return NULL;
  1.1159 +		}
  1.1160 +		pl->Call_OnAllPluginsLoaded();
  1.1161 +	}
  1.1162 +
  1.1163 +	return pl;
  1.1164 +}
  1.1165 +
  1.1166 +void CPluginManager::LoadAutoPlugin(const char *plugin)
  1.1167 +{
  1.1168 +	CPlugin *pl = NULL;
  1.1169 +	LoadRes res;
  1.1170 +	char error[255] = "Unknown error";
  1.1171 +
  1.1172 +	if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
  1.1173 +	{
  1.1174 +		g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
  1.1175 +		pl->SetErrorState(Plugin_Failed, "%s", error);
  1.1176 +	}
  1.1177 +
  1.1178 +	if (res == LoadRes_Successful)
  1.1179 +	{
  1.1180 +		AddPlugin(pl);
  1.1181 +	}
  1.1182 +}
  1.1183 +
  1.1184 +void CPluginManager::AddPlugin(CPlugin *pPlugin)
  1.1185 +{
  1.1186 +	List<IPluginsListener *>::iterator iter;
  1.1187 +	IPluginsListener *pListener;
  1.1188 +
  1.1189 +	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1.1190 +	{
  1.1191 +		pListener = (*iter);
  1.1192 +		pListener->OnPluginCreated(pPlugin);
  1.1193 +	}
  1.1194 +
  1.1195 +	m_plugins.push_back(pPlugin);
  1.1196 +	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
  1.1197 +}
  1.1198 +
  1.1199 +void CPluginManager::LoadAll_SecondPass()
  1.1200 +{
  1.1201 +	List<CPlugin *>::iterator iter;
  1.1202 +	CPlugin *pPlugin;
  1.1203 +
  1.1204 +	char error[256];
  1.1205 +	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1.1206 +	{
  1.1207 +		pPlugin = (*iter);
  1.1208 +		if (pPlugin->GetStatus() == Plugin_Loaded)
  1.1209 +		{
  1.1210 +			error[0] = '\0';
  1.1211 +			if (!RunSecondPass(pPlugin, error, sizeof(error)))
  1.1212 +			{
  1.1213 +				g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
  1.1214 +				pPlugin->SetErrorState(Plugin_Failed, "%s", error);
  1.1215 +			}
  1.1216 +		}
  1.1217 +	}
  1.1218 +
  1.1219 +	m_AllPluginsLoaded = true;
  1.1220 +}
  1.1221 +
  1.1222 +bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
  1.1223 +{
  1.1224 +	struct _pl
  1.1225 +	{
  1.1226 +		cell_t name;
  1.1227 +		cell_t file;
  1.1228 +		cell_t required;
  1.1229 +	} *pl;
  1.1230 +
  1.1231 +	IPluginContext *pBase = pPlugin->GetBaseContext();
  1.1232 +	uint32_t num = pBase->GetPubVarsNum();
  1.1233 +	sp_pubvar_t *pubvar;
  1.1234 +	char *name, *file;
  1.1235 +	char pathfile[PLATFORM_MAX_PATH];
  1.1236 +
  1.1237 +	for (uint32_t i=0; i<num; i++)
  1.1238 +	{
  1.1239 +		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  1.1240 +		{
  1.1241 +			continue;
  1.1242 +		}
  1.1243 +		if (strncmp(pubvar->name, "__pl_", 5) == 0)
  1.1244 +		{
  1.1245 +			pl = (_pl *)pubvar->offs;
  1.1246 +			if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
  1.1247 +			{
  1.1248 +				continue;
  1.1249 +			}
  1.1250 +			if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
  1.1251 +			{
  1.1252 +				continue;
  1.1253 +			}
  1.1254 +			g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
  1.1255 +			if (strcmp(pathfile, file) == 0)
  1.1256 +			{
  1.1257 +				continue;
  1.1258 +			}
  1.1259 +			if (pl->required == false)
  1.1260 +			{
  1.1261 +				IPluginFunction *pFunc;
  1.1262 +				char buffer[64];
  1.1263 +				UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
  1.1264 +				if ((pFunc=pBase->GetFunctionByName(buffer)))
  1.1265 +				{
  1.1266 +					cell_t res;
  1.1267 +					pFunc->Execute(&res);
  1.1268 +					if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
  1.1269 +					{
  1.1270 +						if (error)
  1.1271 +						{
  1.1272 +							snprintf(error, maxlength, "Fatal error during initializing plugin load");
  1.1273 +						}
  1.1274 +						return false;
  1.1275 +					}
  1.1276 +				}
  1.1277 +			}
  1.1278 +			else
  1.1279 +			{
  1.1280 +				/* Check that we aren't registering the same library twice */
  1.1281 +				if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
  1.1282 +				{
  1.1283 +					pPlugin->m_RequiredLibs.push_back(name);
  1.1284 +				}
  1.1285 +				else
  1.1286 +				{
  1.1287 +					continue;
  1.1288 +				}
  1.1289 +				List<CPlugin *>::iterator iter;
  1.1290 +				CPlugin *pl;
  1.1291 +				bool found = false;
  1.1292 +				for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1.1293 +				{
  1.1294 +					pl = (*iter);
  1.1295 +					if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
  1.1296 +					{
  1.1297 +						found = true;
  1.1298 +						break;
  1.1299 +					}
  1.1300 +				}
  1.1301 +				if (!found)
  1.1302 +				{
  1.1303 +					if (error)
  1.1304 +					{
  1.1305 +						snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
  1.1306 +					}
  1.1307 +					return false;
  1.1308 +				}
  1.1309 +			}
  1.1310 +		}
  1.1311 +	}
  1.1312 +
  1.1313 +	return true;
  1.1314 +}
  1.1315 +
  1.1316 +bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
  1.1317 +{
  1.1318 +	/* Find any extensions this plugin needs */
  1.1319 +	struct _ext
  1.1320 +	{
  1.1321 +		cell_t name;
  1.1322 +		cell_t file;
  1.1323 +		cell_t autoload;
  1.1324 +		cell_t required;
  1.1325 +	} *ext;
  1.1326 +
  1.1327 +	IPluginContext *pBase = pPlugin->GetBaseContext();
  1.1328 +	uint32_t num = pBase->GetPubVarsNum();
  1.1329 +	sp_pubvar_t *pubvar;
  1.1330 +	IExtension *pExt;
  1.1331 +	char path[PLATFORM_MAX_PATH];
  1.1332 +	char *file, *name;
  1.1333 +	for (uint32_t i=0; i<num; i++)
  1.1334 +	{
  1.1335 +		if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  1.1336 +		{
  1.1337 +			continue;
  1.1338 +		}
  1.1339 +		if (strncmp(pubvar->name, "__ext_", 6) == 0)
  1.1340 +		{
  1.1341 +			ext = (_ext *)pubvar->offs;
  1.1342 +			if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
  1.1343 +			{
  1.1344 +				continue;
  1.1345 +			}
  1.1346 +			if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
  1.1347 +			{
  1.1348 +				continue;
  1.1349 +			}
  1.1350 +			if (pass == 1)
  1.1351 +			{
  1.1352 +				/* Attempt to auto-load if necessary */
  1.1353 +				if (ext->autoload)
  1.1354 +				{
  1.1355 +					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
  1.1356 +					g_Extensions.LoadAutoExtension(path);
  1.1357 +				}
  1.1358 +			}
  1.1359 +			else if (pass == 2)
  1.1360 +			{
  1.1361 +				/* Is this required? */
  1.1362 +				if (ext->required)
  1.1363 +				{
  1.1364 +					g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
  1.1365 +					if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
  1.1366 +					{
  1.1367 +						pExt = g_Extensions.FindExtensionByName(name);
  1.1368 +					}
  1.1369 +					/* :TODO: should we bind to unloaded extensions?
  1.1370 +					 * Currently the extension manager will ignore this.
  1.1371 +					 */
  1.1372 +					if (!pExt || !pExt->IsRunning(NULL, 0))
  1.1373 +					{
  1.1374 +						if (error)
  1.1375 +						{
  1.1376 +							snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
  1.1377 +						}
  1.1378 +						return false;
  1.1379 +					}
  1.1380 +					else
  1.1381 +					{
  1.1382 +						g_Extensions.BindChildPlugin(pExt, pPlugin);
  1.1383 +					}
  1.1384 +				}
  1.1385 +				else
  1.1386 +				{
  1.1387 +					IPluginFunction *pFunc;
  1.1388 +					char buffer[64];
  1.1389 +					UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
  1.1390 +
  1.1391 +					if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
  1.1392 +					{
  1.1393 +						cell_t res;
  1.1394 +						pFunc->Execute(&res);
  1.1395 +						if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
  1.1396 +						{
  1.1397 +							if (error)
  1.1398 +							{
  1.1399 +								UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
  1.1400 +							}
  1.1401 +							return false;
  1.1402 +						}
  1.1403 +					}
  1.1404 +				}
  1.1405 +			}
  1.1406 +		}
  1.1407 +	}
  1.1408 +
  1.1409 +	return true;
  1.1410 +}
  1.1411 +
  1.1412 +bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
  1.1413 +{
  1.1414 +	/* Second pass for extension requirements */
  1.1415 +	if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
  1.1416 +	{
  1.1417 +		return false;
  1.1418 +	}
  1.1419 +
  1.1420 +	/* Bind all extra natives */
  1.1421 +	g_Extensions.BindAllNativesToPlugin(pPlugin);
  1.1422 +
  1.1423 +	if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
  1.1424 +	{
  1.1425 +		return false;
  1.1426 +	}
  1.1427 +
  1.1428 +	AddFakeNativesToPlugin(pPlugin);
  1.1429 +
  1.1430 +	/* Find any unbound natives
  1.1431 +	 * Right now, these are not allowed
  1.1432 +	 */
  1.1433 +	IPluginContext *pContext = pPlugin->GetBaseContext();
  1.1434 +	uint32_t num = pContext->GetNativesNum();
  1.1435 +	sp_native_t *native;
  1.1436 +	for (unsigned int i=0; i<num; i++)
  1.1437 +	{
  1.1438 +		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
  1.1439 +		{
  1.1440 +			break;
  1.1441 +		}
  1.1442 +		if (native->status == SP_NATIVE_UNBOUND
  1.1443 +			&& native->name[0] != '@'
  1.1444 +			&& !(native->flags & SP_NTVFLAG_OPTIONAL))
  1.1445 +		{
  1.1446 +			if (error)
  1.1447 +			{
  1.1448 +				snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
  1.1449 +			}
  1.1450 +			return false;
  1.1451 +		}
  1.1452 +	}
  1.1453 +
  1.1454 +	/* Finish by telling all listeners */
  1.1455 +	List<IPluginsListener *>::iterator iter;
  1.1456 +	IPluginsListener *pListener;
  1.1457 +	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1.1458 +	{
  1.1459 +		pListener = (*iter);
  1.1460 +		pListener->OnPluginLoaded(pPlugin);
  1.1461 +	}
  1.1462 +	
  1.1463 +	/* Tell this plugin to finish initializing itself */
  1.1464 +	pPlugin->Call_OnPluginStart();
  1.1465 +
  1.1466 +	/* Now, if we have fake natives, go through all plugins that might need rebinding */
  1.1467 +	if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_fakeNatives.size())
  1.1468 +	{
  1.1469 +		List<CPlugin *>::iterator pl_iter;
  1.1470 +		CPlugin *pOther;
  1.1471 +		for (pl_iter = m_plugins.begin();
  1.1472 +			 pl_iter != m_plugins.end();
  1.1473 +			 pl_iter++)
  1.1474 +		{
  1.1475 +			pOther = (*pl_iter);
  1.1476 +			if ((pOther->GetStatus() == Plugin_Error
  1.1477 +				&& (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
  1.1478 +				|| pOther->m_FakeNativesMissing)
  1.1479 +			{
  1.1480 +				TryRefreshDependencies(pOther);
  1.1481 +			}
  1.1482 +		}
  1.1483 +	}
  1.1484 +
  1.1485 +	/* Go through our libraries and tell other plugins they're added */
  1.1486 +	List<String>::iterator s_iter;
  1.1487 +	for (s_iter = pPlugin->m_Libraries.begin();
  1.1488 +		s_iter != pPlugin->m_Libraries.end();
  1.1489 +		s_iter++)
  1.1490 +	{
  1.1491 +		OnLibraryAction((*s_iter).c_str(), true, false);
  1.1492 +	}
  1.1493 +
  1.1494 +	/* Finally, add the core language file */
  1.1495 +	pPlugin->AddLangFile(g_pCorePhraseID);
  1.1496 +
  1.1497 +	return true;
  1.1498 +}
  1.1499 +
  1.1500 +void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
  1.1501 +{
  1.1502 +	IPluginContext *pContext = pPlugin->GetBaseContext();
  1.1503 +	
  1.1504 +
  1.1505 +	uint32_t natives = pContext->GetNativesNum();
  1.1506 +	sp_native_t *native;
  1.1507 +	SPVM_NATIVE_FUNC pfn;
  1.1508 +	for (uint32_t i=0; i<natives; i++)
  1.1509 +	{
  1.1510 +		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
  1.1511 +		{
  1.1512 +			continue;
  1.1513 +		}
  1.1514 +		if (native->status == SP_NATIVE_BOUND)
  1.1515 +		{
  1.1516 +			continue;
  1.1517 +		}
  1.1518 +		if (!sm_trie_retrieve(m_pCoreNatives, native->name, (void **)&pfn))
  1.1519 +		{
  1.1520 +			continue;
  1.1521 +		}
  1.1522 +		pContext->BindNativeToIndex(i, pfn);
  1.1523 +	}
  1.1524 +}
  1.1525 +
  1.1526 +SPVM_NATIVE_FUNC CPluginManager::FindCoreNative(const char *name)
  1.1527 +{
  1.1528 +	SPVM_NATIVE_FUNC pfn;
  1.1529 +
  1.1530 +	if (!sm_trie_retrieve(m_pCoreNatives, name, (void **)&pfn))
  1.1531 +	{
  1.1532 +		return NULL;
  1.1533 +	}
  1.1534 +
  1.1535 +	return pfn;
  1.1536 +}
  1.1537 +
  1.1538 +void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
  1.1539 +{
  1.1540 +	assert(pPlugin->GetBaseContext() != NULL);
  1.1541 +
  1.1542 +	AddFakeNativesToPlugin(pPlugin);
  1.1543 +
  1.1544 +	List<String>::iterator lib_iter;
  1.1545 +	List<String>::iterator req_iter;
  1.1546 +	List<CPlugin *>::iterator pl_iter;
  1.1547 +	CPlugin *pl;
  1.1548 +	for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
  1.1549 +	{
  1.1550 +		bool found = false;
  1.1551 +		for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
  1.1552 +		{
  1.1553 +			pl = (*pl_iter);
  1.1554 +			for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
  1.1555 +			{
  1.1556 +				if ((*req_iter) == (*lib_iter))
  1.1557 +				{
  1.1558 +					found = true;
  1.1559 +				}
  1.1560 +			}
  1.1561 +		}
  1.1562 +		if (!found)
  1.1563 +		{
  1.1564 +			pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
  1.1565 +			return;
  1.1566 +		}
  1.1567 +	}
  1.1568 +
  1.1569 +	/* Find any unbound natives
  1.1570 +	 * Right now, these are not allowed
  1.1571 +	 */
  1.1572 +	IPluginContext *pContext = pPlugin->GetBaseContext();
  1.1573 +	uint32_t num = pContext->GetNativesNum();
  1.1574 +	sp_native_t *native;
  1.1575 +	for (unsigned int i=0; i<num; i++)
  1.1576 +	{
  1.1577 +		if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
  1.1578 +		{
  1.1579 +			break;
  1.1580 +		}
  1.1581 +		if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
  1.1582 +		{
  1.1583 +			pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
  1.1584 +			return;
  1.1585 +		}
  1.1586 +	}
  1.1587 +
  1.1588 +	if (pPlugin->GetStatus() == Plugin_Error)
  1.1589 +	{
  1.1590 +		/* If we got here, all natives are okay again! */
  1.1591 +		pPlugin->m_status = Plugin_Running;
  1.1592 +		if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
  1.1593 +		{
  1.1594 +			pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
  1.1595 +			_SetPauseState(pPlugin, false);
  1.1596 +		}
  1.1597 +	}
  1.1598 +}
  1.1599 +
  1.1600 +void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin)
  1.1601 +{
  1.1602 +	IPluginContext *pContext = pPlugin->GetBaseContext();
  1.1603 +	sp_nativeinfo_t native;
  1.1604 +
  1.1605 +	List<FakeNative *>::iterator iter;
  1.1606 +	FakeNative *pNative;
  1.1607 +	sp_context_t *ctx;
  1.1608 +	for (iter = m_Natives.begin(); iter != m_Natives.end(); iter++)
  1.1609 +	{
  1.1610 +		pNative = (*iter);
  1.1611 +		ctx = pNative->ctx->GetContext();
  1.1612 +		if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
  1.1613 +		{
  1.1614 +			/* Ignore natives in paused plugins */
  1.1615 +			continue;
  1.1616 +		}
  1.1617 +		native.name = pNative->name.c_str();
  1.1618 +		native.func = pNative->func;
  1.1619 +		if (pContext->BindNative(&native) == SP_ERROR_NONE)
  1.1620 +		{
  1.1621 +			uint32_t idx;
  1.1622 +			pContext->FindNativeByName(native.name, &idx);
  1.1623 +			if (pPlugin->GetContext()->natives[idx].flags & SP_NTVFLAG_OPTIONAL)
  1.1624 +			{
  1.1625 +				WeakNative wkn(pPlugin, idx);
  1.1626 +				GetPluginByCtx(ctx)->m_WeakNatives.push_back(wkn);
  1.1627 +				continue;
  1.1628 +			}
  1.1629 +			/* Add us as a dependency, but we're careful not to do this circularly! */
  1.1630 +			if (pNative->ctx != pContext)
  1.1631 +			{
  1.1632 +				CPlugin *pOther = GetPluginByCtx(ctx);
  1.1633 +				if (pOther->m_dependents.find(pPlugin) == pOther->m_dependents.end())
  1.1634 +				{
  1.1635 +					pOther->m_dependents.push_back(pPlugin);
  1.1636 +				}
  1.1637 +				if (pPlugin->m_dependsOn.find(pOther) == pPlugin->m_dependsOn.end())
  1.1638 +				{
  1.1639 +					pPlugin->m_dependsOn.push_back(pOther);
  1.1640 +				}
  1.1641 +			}
  1.1642 +		}
  1.1643 +	}
  1.1644 +}
  1.1645 +
  1.1646 +bool CPluginManager::UnloadPlugin(IPlugin *plugin)
  1.1647 +{
  1.1648 +	CPlugin *pPlugin = (CPlugin *)plugin;
  1.1649 +
  1.1650 +	/* This prevents removal during insertion or anything else weird */
  1.1651 +	if (m_plugins.find(pPlugin) == m_plugins.end())
  1.1652 +	{
  1.1653 +		return false;
  1.1654 +	}
  1.1655 +
  1.1656 +	IPluginContext *pContext = plugin->GetBaseContext();
  1.1657 +	if (pContext->IsInExec())
  1.1658 +	{
  1.1659 +		char buffer[255];
  1.1660 +		UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
  1.1661 +		engine->ServerCommand(buffer);
  1.1662 +		return false;
  1.1663 +	}
  1.1664 +
  1.1665 +	/* Remove us from the lookup table and linked list */
  1.1666 +	m_plugins.remove(pPlugin);
  1.1667 +	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
  1.1668 +
  1.1669 +	/* Go through our libraries and tell other plugins they're gone */
  1.1670 +	List<String>::iterator s_iter;
  1.1671 +	for (s_iter = pPlugin->m_Libraries.begin();
  1.1672 +		 s_iter != pPlugin->m_Libraries.end();
  1.1673 +		 s_iter++)
  1.1674 +	{
  1.1675 +		OnLibraryAction((*s_iter).c_str(), true, true);
  1.1676 +	}
  1.1677 +
  1.1678 +	/* Go through all dependent plugins and tell them this plugin is now gone */
  1.1679 +	List<CPlugin *>::iterator pl_iter;
  1.1680 +	CPlugin *pOther;
  1.1681 +	for (pl_iter = pPlugin->m_dependents.begin();
  1.1682 +		 pl_iter != pPlugin->m_dependents.end();
  1.1683 +		 pl_iter++)
  1.1684 +	{
  1.1685 +		pOther = (*pl_iter);
  1.1686 +		pOther->DependencyDropped(pPlugin);
  1.1687 +	}
  1.1688 +
  1.1689 +	/* Tell everyone we depend on that we no longer exist */
  1.1690 +	for (pl_iter = pPlugin->m_dependsOn.begin();
  1.1691 +		 pl_iter != pPlugin->m_dependsOn.end();
  1.1692 +		 pl_iter++)
  1.1693 +	{
  1.1694 +		pOther = (*pl_iter);
  1.1695 +		pOther->m_dependents.remove(pPlugin);
  1.1696 +	}
  1.1697 +
  1.1698 +	/* Remove weak references to us */
  1.1699 +	for (pl_iter = m_plugins.begin();
  1.1700 +		 pl_iter != m_plugins.end();
  1.1701 +		 pl_iter++)
  1.1702 +	{
  1.1703 +		pOther = (*pl_iter);
  1.1704 +		List<WeakNative>::iterator wk_iter = pOther->m_WeakNatives.begin();
  1.1705 +		while (wk_iter != pOther->m_WeakNatives.end())
  1.1706 +		{
  1.1707 +			if ((*wk_iter).pl == pPlugin)
  1.1708 +			{
  1.1709 +				wk_iter = pOther->m_WeakNatives.erase(wk_iter);
  1.1710 +			} else {
  1.1711 +				wk_iter++;
  1.1712 +			}
  1.1713 +		}
  1.1714 +	}
  1.1715 +
  1.1716 +	List<IPluginsListener *>::iterator iter;
  1.1717 +	IPluginsListener *pListener;
  1.1718 +
  1.1719 +	if (pPlugin->GetStatus() <= Plugin_Error)
  1.1720 +	{
  1.1721 +		/* Notify listeners of unloading */
  1.1722 +		for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1.1723 +		{
  1.1724 +			pListener = (*iter);
  1.1725 +			pListener->OnPluginUnloaded(pPlugin);
  1.1726 +		}
  1.1727 +		/* Notify plugin */
  1.1728 +		pPlugin->Call_OnPluginEnd();
  1.1729 +	}
  1.1730 +
  1.1731 +	/* Unbound weak natives */
  1.1732 +	List<WeakNative>::iterator wk_iter;
  1.1733 +	for (wk_iter=pPlugin->m_WeakNatives.begin(); wk_iter!=pPlugin->m_WeakNatives.end(); wk_iter++)
  1.1734 +	{
  1.1735 +		WeakNative & wkn = (*wk_iter);
  1.1736 +		sp_context_t *ctx = wkn.pl->GetContext();
  1.1737 +		ctx->natives[wkn.idx].status = SP_NATIVE_UNBOUND;
  1.1738 +		wkn.pl->m_FakeNativesMissing = true;
  1.1739 +	}
  1.1740 +
  1.1741 +	/* Remove all of our native functions */
  1.1742 +	List<FakeNative *>::iterator fn_iter;
  1.1743 +	FakeNative *pNative;
  1.1744 +	for (fn_iter = pPlugin->m_fakeNatives.begin();
  1.1745 +		fn_iter != pPlugin->m_fakeNatives.end();
  1.1746 +		fn_iter++)
  1.1747 +	{
  1.1748 +		pNative = (*fn_iter);
  1.1749 +		m_Natives.remove(pNative);
  1.1750 +		sm_trie_delete(m_pNativeLookup, pNative->name.c_str());
  1.1751 +		g_pVM->DestroyFakeNative(pNative->func);
  1.1752 +		delete pNative;
  1.1753 +	}
  1.1754 +
  1.1755 +	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1.1756 +	{
  1.1757 +		/* Notify listeners of destruction */
  1.1758 +		pListener = (*iter);
  1.1759 +		pListener->OnPluginDestroyed(pPlugin);
  1.1760 +	}
  1.1761 +	
  1.1762 +	/* Tell the plugin to delete itself */
  1.1763 +	delete pPlugin;
  1.1764 +
  1.1765 +	return true;
  1.1766 +}
  1.1767 +
  1.1768 +IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
  1.1769 +{
  1.1770 +	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
  1.1771 +	return pl;
  1.1772 +}
  1.1773 +
  1.1774 +unsigned int CPluginManager::GetPluginCount()
  1.1775 +{
  1.1776 +	return m_plugins.size();
  1.1777 +}
  1.1778 +
  1.1779 +void CPluginManager::AddPluginsListener(IPluginsListener *listener)
  1.1780 +{
  1.1781 +	m_listeners.push_back(listener);
  1.1782 +}
  1.1783 +
  1.1784 +void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
  1.1785 +{
  1.1786 +	m_listeners.remove(listener);
  1.1787 +}
  1.1788 +
  1.1789 +IPluginIterator *CPluginManager::GetPluginIterator()
  1.1790 +{
  1.1791 +	if (m_iters.empty())
  1.1792 +	{
  1.1793 +		return new CPluginIterator(&m_plugins);
  1.1794 +	} else {
  1.1795 +		CPluginIterator *iter = m_iters.front();
  1.1796 +		m_iters.pop();
  1.1797 +		iter->Reset();
  1.1798 +		return iter;
  1.1799 +	}
  1.1800 +}
  1.1801 +
  1.1802 +void CPluginManager::ReleaseIterator(CPluginIterator *iter)
  1.1803 +{
  1.1804 +	m_iters.push(iter);
  1.1805 +}
  1.1806 +
  1.1807 +bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
  1.1808 +{
  1.1809 +	/* As an optimization, we do not call strlen, but compute the length in the first pass */
  1.1810 +	size_t alias_len = 0;
  1.1811 +	size_t local_len = 0;
  1.1812 +
  1.1813 +	const char *ptr = alias;
  1.1814 +	unsigned int alias_explicit_paths = 0;
  1.1815 +	unsigned int alias_path_end = 0;
  1.1816 +	while (*ptr != '\0')
  1.1817 +	{
  1.1818 +		if (*ptr == '\\' || *ptr == '/')
  1.1819 +		{
  1.1820 +			alias_explicit_paths++;
  1.1821 +			alias_path_end = alias_len;
  1.1822 +		}
  1.1823 +		alias_len++;
  1.1824 +		ptr++;
  1.1825 +	}
  1.1826 +
  1.1827 +	if (alias_explicit_paths && alias_path_end == alias_len - 1)
  1.1828 +	{
  1.1829 +		/* Trailing slash is totally invalid here */
  1.1830 +		return false;
  1.1831 +	}
  1.1832 +
  1.1833 +	ptr = localpath;
  1.1834 +	unsigned int local_explicit_paths = 0;
  1.1835 +	unsigned int local_path_end = 0;
  1.1836 +	while (*ptr != '\0')
  1.1837 +	{
  1.1838 +		if (*ptr == '\\' || *ptr == '/')
  1.1839 +		{
  1.1840 +			local_explicit_paths++;
  1.1841 +			local_path_end = local_len;
  1.1842 +		}
  1.1843 +		local_len++;
  1.1844 +		ptr++;
  1.1845 +	}
  1.1846 +
  1.1847 +	/* If the alias has more explicit paths than the real path,
  1.1848 +	 * no match will be possible.
  1.1849 +	 */
  1.1850 +	if (alias_explicit_paths > local_explicit_paths)
  1.1851 +	{
  1.1852 +		return false;
  1.1853 +	}
  1.1854 +
  1.1855 +	if (alias_explicit_paths)
  1.1856 +	{
  1.1857 +		/* We need to find if the paths match now.  For example, these should all match:
  1.1858 +		 * csdm     csdm
  1.1859 +		 * csdm     optional/csdm
  1.1860 +		 * csdm/ban optional/crab/csdm/ban
  1.1861 +		*/
  1.1862 +		const char *aliasptr = alias;
  1.1863 +		const char *localptr = localpath;
  1.1864 +		bool match = true;
  1.1865 +		do
  1.1866 +		{
  1.1867 +			if (*aliasptr != *localptr)
  1.1868 +			{
  1.1869 +				/* We have to knock one path off */
  1.1870 +				local_explicit_paths--;
  1.1871 +				if (alias_explicit_paths > local_explicit_paths)
  1.1872 +				{
  1.1873 +					/* Skip out if we're gonna have an impossible match */
  1.1874 +					return false;
  1.1875 +				}
  1.1876 +				/* Eat up localptr tokens until we get a result */
  1.1877 +				while (((localptr - localpath) < (int)local_path_end)
  1.1878 +					&& *localptr != '/'
  1.1879 +					&& *localptr != '\\')
  1.1880 +				{
  1.1881 +					localptr++;
  1.1882 +				}
  1.1883 +				/* Check if we hit the end of our searchable area.
  1.1884 +				 * This probably isn't possible because of the path 
  1.1885 +				 * count check, but it's a good idea anyway.
  1.1886 +				 */
  1.1887 +				if ((localptr - localpath) >= (int)local_path_end)
  1.1888 +				{
  1.1889 +					return false;
  1.1890 +				} else {
  1.1891 +					/* Consume the slash token */
  1.1892 +					localptr++;
  1.1893 +				}
  1.1894 +				/* Reset the alias pointer so we can continue consuming */
  1.1895 +				aliasptr = alias;
  1.1896 +				match = false;
  1.1897 +				continue;
  1.1898 +			}
  1.1899 +			/* Note:
  1.1900 +			 * This is safe because if localptr terminates early, aliasptr will too
  1.1901 +			 */
  1.1902 +			do
  1.1903 +			{
  1.1904 +				/* We should never reach the end of the string because of this check. */
  1.1905 +				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
  1.1906 +				bool localend = (localptr - localpath) > (int)local_path_end;
  1.1907 +				if (aliasend || localend)
  1.1908 +				{
  1.1909 +					if (aliasend && localend)
  1.1910 +					{
  1.1911 +						/* we matched, and we can break out now */
  1.1912 +						match = true;
  1.1913 +						break;
  1.1914 +					}
  1.1915 +					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
  1.1916 +					match = false;
  1.1917 +					break;
  1.1918 +				}
  1.1919 +
  1.1920 +				/* If we got here, it's safe to compare the next two tokens */
  1.1921 +				if (*localptr != *aliasptr)
  1.1922 +				{
  1.1923 +					match = false;
  1.1924 +					break;
  1.1925 +				}
  1.1926 +				localptr++;
  1.1927 +				aliasptr++;
  1.1928 +			} while (true);
  1.1929 +		} while (!match);
  1.1930 +	}
  1.1931 +
  1.1932 +	/* If we got here, it's time to compare filenames */
  1.1933 +	const char *aliasptr = alias;
  1.1934 +	const char *localptr = localpath;
  1.1935 +
  1.1936 +	if (alias_explicit_paths)
  1.1937 +	{
  1.1938 +		aliasptr = &alias[alias_path_end + 1];
  1.1939 +	}
  1.1940 +
  1.1941 +	if (local_explicit_paths)
  1.1942 +	{
  1.1943 +		localptr = &localpath[local_path_end + 1];
  1.1944 +	}
  1.1945 +
  1.1946 +	while (true)
  1.1947 +	{
  1.1948 +		if (*aliasptr == '*')
  1.1949 +		{
  1.1950 +			/* First, see if this is the last character */
  1.1951 +			if ((unsigned)(aliasptr - alias) == alias_len - 1)
  1.1952 +			{
  1.1953 +				/* If so, there's no need to match anything else */
  1.1954 +				return true;
  1.1955 +			}
  1.1956 +			/* Otherwise, we need to search for an appropriate matching sequence in local.
  1.1957 +			 * Note that we only need to search up to the next asterisk.
  1.1958 +			 */
  1.1959 +			aliasptr++;
  1.1960 +			bool match = true;
  1.1961 +			const char *local_orig = localptr;
  1.1962 +			do
  1.1963 +			{
  1.1964 +				match = true;
  1.1965 +				while (*aliasptr != '\0' && *aliasptr != '*')
  1.1966 +				{
  1.1967 +					/* Since aliasptr is never '\0', localptr hitting the end will fail */
  1.1968 +					if (*aliasptr != *localptr)
  1.1969 +					{
  1.1970 +						match = false;
  1.1971 +						break;
  1.1972 +					}
  1.1973 +					aliasptr++;
  1.1974 +					localptr++;
  1.1975 +				}
  1.1976 +				if (!match)
  1.1977 +				{
  1.1978 +					/* If we didn't get a match, we need to advance the search stream.
  1.1979 +					 * This will let us skip tokens while still searching for another match.
  1.1980 +					 */
  1.1981 +					localptr = ++local_orig;
  1.1982 +					/* Make sure we don't go out of bounds */
  1.1983 +					if (*localptr == '\0')
  1.1984 +					{
  1.1985 +						break;
  1.1986 +					}
  1.1987 +				}
  1.1988 +			} while (!match);
  1.1989 +
  1.1990 +			if (!match)
  1.1991 +			{
  1.1992 +				return false;
  1.1993 +			} else {
  1.1994 +				/* If we got a match, move on to the next token */
  1.1995 +				continue;
  1.1996 +			}
  1.1997 +		} else if (*aliasptr == '\0') {
  1.1998 +			if (*localptr == '\0'
  1.1999 +				||
  1.2000 +				strcmp(localptr, ".smx") == 0)
  1.2001 +			{
  1.2002 +				return true;
  1.2003 +			} else {
  1.2004 +				return false;
  1.2005 +			}
  1.2006 +		} else if (*aliasptr != *localptr) {
  1.2007 +			return false;
  1.2008 +		}
  1.2009 +		aliasptr++;
  1.2010 +		localptr++;
  1.2011 +	}
  1.2012 +
  1.2013 +	return true;
  1.2014 +}
  1.2015 +
  1.2016 +bool CPluginManager::IsLateLoadTime() const
  1.2017 +{
  1.2018 +	return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
  1.2019 +}
  1.2020 +
  1.2021 +void CPluginManager::OnSourceModAllInitialized()
  1.2022 +{
  1.2023 +	m_MyIdent = g_ShareSys.CreateCoreIdentity();
  1.2024 +
  1.2025 +	HandleAccess sec;
  1.2026 +	g_HandleSys.InitAccessDefaults(NULL, &sec);
  1.2027 +
  1.2028 +	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
  1.2029 +	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
  1.2030 +
  1.2031 +	g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
  1.2032 +	g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
  1.2033 +
  1.2034 +	g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
  1.2035 +
  1.2036 +	g_ShareSys.AddInterface(NULL, this);
  1.2037 +}
  1.2038 +
  1.2039 +void CPluginManager::OnSourceModShutdown()
  1.2040 +{
  1.2041 +	g_RootMenu.RemoveRootConsoleCommand("plugins", this);
  1.2042 +
  1.2043 +	UnloadAll();
  1.2044 +
  1.2045 +	g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
  1.2046 +	g_ShareSys.DestroyIdentType(g_PluginIdent);
  1.2047 +	g_ShareSys.DestroyIdentity(m_MyIdent);
  1.2048 +}
  1.2049 +
  1.2050 +void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
  1.2051 +{
  1.2052 +	/* We don't care about the internal object, actually */
  1.2053 +}
  1.2054 +
  1.2055 +bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
  1.2056 +{
  1.2057 +	*pSize = ((CPlugin *)object)->CalcMemUsage();
  1.2058 +	return true;
  1.2059 +}
  1.2060 +
  1.2061 +void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
  1.2062 +{
  1.2063 +	for (unsigned int i = 0; natives[i].func != NULL; i++)
  1.2064 +	{
  1.2065 +		sm_trie_insert(m_pCoreNatives, natives[i].name, (void *)natives[i].func);
  1.2066 +	}
  1.2067 +}
  1.2068 +
  1.2069 +IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
  1.2070 +{
  1.2071 +	IPlugin *pPlugin;
  1.2072 +	HandleError _err;
  1.2073 +
  1.2074 +	HandleSecurity sec;
  1.2075 +
  1.2076 +	sec.pOwner = NULL;
  1.2077 +	sec.pIdentity = m_MyIdent;
  1.2078 +
  1.2079 +	if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
  1.2080 +	{
  1.2081 +		pPlugin = NULL;
  1.2082 +	}
  1.2083 +
  1.2084 +	if (err)
  1.2085 +	{
  1.2086 +		*err = _err;
  1.2087 +	}
  1.2088 +
  1.2089 +	return pPlugin;
  1.2090 +}
  1.2091 +
  1.2092 +CPlugin *CPluginManager::GetPluginByOrder(int num)
  1.2093 +{
  1.2094 +	if (num < 1 || num > (int)GetPluginCount())
  1.2095 +	{
  1.2096 +		return NULL;
  1.2097 +	}
  1.2098 +
  1.2099 +	CPlugin *pl;
  1.2100 +	int id = 1;
  1.2101 +
  1.2102 +	IPluginIterator *iter = GetPluginIterator();
  1.2103 +	for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
  1.2104 +
  1.2105 +	pl = (CPlugin *)(iter->GetPlugin());
  1.2106 +	iter->Release();
  1.2107 +
  1.2108 +	return pl;
  1.2109 +}
  1.2110 +
  1.2111 +const char *CPluginManager::GetStatusText(PluginStatus st)
  1.2112 +{
  1.2113 +	switch (st)
  1.2114 +	{
  1.2115 +	case Plugin_Running:
  1.2116 +		return "Running";
  1.2117 +	case Plugin_Paused:
  1.2118 +		return "Paused";
  1.2119 +	case Plugin_Error:
  1.2120 +		return "Error";
  1.2121 +	case Plugin_Uncompiled:
  1.2122 +		return "Uncompiled";
  1.2123 +	case Plugin_BadLoad:
  1.2124 +		return "Bad Load";
  1.2125 +	case Plugin_Failed:
  1.2126 +		return "Failed";
  1.2127 +	default:
  1.2128 +		assert(false);
  1.2129 +		return "-";
  1.2130 +	}
  1.2131 +}
  1.2132 +
  1.2133 +void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
  1.2134 +{
  1.2135 +	int argcount = command.ArgC();
  1.2136 +	if (argcount >= 3)
  1.2137 +	{
  1.2138 +		const char *cmd = command.Arg(2);
  1.2139 +		if (strcmp(cmd, "list") == 0)
  1.2140 +		{
  1.2141 +			char buffer[256];
  1.2142 +			unsigned int id = 1;
  1.2143 +			int plnum = GetPluginCount();
  1.2144 +
  1.2145 +			if (!plnum)
  1.2146 +			{
  1.2147 +				g_RootMenu.ConsolePrint("[SM] No plugins loaded");
  1.2148 +				return;
  1.2149 +			}
  1.2150 +			else
  1.2151 +			{
  1.2152 +				g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
  1.2153 +			}
  1.2154 +
  1.2155 +			SourceHook::List<IPlugin *> m_FailList;
  1.2156 +
  1.2157 +			IPluginIterator *iter = GetPluginIterator();
  1.2158 +			for (; iter->MorePlugins(); iter->NextPlugin(), id++)
  1.2159 +			{
  1.2160 +				IPlugin *pl = iter->GetPlugin();
  1.2161 +				assert(pl->GetStatus() != Plugin_Created);
  1.2162 +				int len = 0;
  1.2163 +				const sm_plugininfo_t *info = pl->GetPublicInfo();
  1.2164 +				if (pl->GetStatus() != Plugin_Running)
  1.2165 +				{
  1.2166 +					len += UTIL_Format(buffer, sizeof(buffer), "  %02d <%s>", id, GetStatusText(pl->GetStatus()));
  1.2167 +
  1.2168 +					if (pl->GetStatus() <= Plugin_Error)
  1.2169 +					{
  1.2170 +						/* Plugin has failed to load. */
  1.2171 +						m_FailList.push_back(pl);
  1.2172 +					}
  1.2173 +				}
  1.2174 +				else
  1.2175 +				{
  1.2176 +					len += UTIL_Format(buffer, sizeof(buffer), "  %02d", id);
  1.2177 +				}
  1.2178 +				len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1.2179 +				if (IS_STR_FILLED(info->version))
  1.2180 +				{
  1.2181 +					len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
  1.2182 +				}
  1.2183 +				if (IS_STR_FILLED(info->author))
  1.2184 +				{
  1.2185 +					UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
  1.2186 +				}
  1.2187 +				g_RootMenu.ConsolePrint("%s", buffer);
  1.2188 +			}
  1.2189 +
  1.2190 +			iter->Release();
  1.2191 +
  1.2192 +			if (!m_FailList.empty())
  1.2193 +			{
  1.2194 +				g_RootMenu.ConsolePrint("Load Errors:");
  1.2195 +
  1.2196 +				SourceHook::List<IPlugin *>::iterator _iter;
  1.2197 +
  1.2198 +				CPlugin *pl;
  1.2199 +
  1.2200 +				for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
  1.2201 +				{
  1.2202 +					pl = (CPlugin *)*_iter;
  1.2203 +					g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
  1.2204 +				}
  1.2205 +			}
  1.2206 +
  1.2207 +			return;
  1.2208 +		}
  1.2209 +		else if (strcmp(cmd, "load") == 0)
  1.2210 +		{
  1.2211 +			if (argcount < 4)
  1.2212 +			{
  1.2213 +				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
  1.2214 +				return;
  1.2215 +			}
  1.2216 +
  1.2217 +			char error[128];
  1.2218 +			bool wasloaded;
  1.2219 +			const char *filename = command.Arg(3);
  1.2220 +
  1.2221 +			char pluginfile[256];
  1.2222 +			const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
  1.2223 +			UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
  1.2224 +
  1.2225 +			IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
  1.2226 +
  1.2227 +			if (wasloaded)
  1.2228 +			{
  1.2229 +				g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
  1.2230 +				return;
  1.2231 +			}
  1.2232 +
  1.2233 +			if (pl)
  1.2234 +			{
  1.2235 +				g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
  1.2236 +			}
  1.2237 +			else
  1.2238 +			{
  1.2239 +				g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
  1.2240 +			}
  1.2241 +
  1.2242 +			return;
  1.2243 +		}
  1.2244 +		else if (strcmp(cmd, "unload") == 0)
  1.2245 +		{
  1.2246 +			if (argcount < 4)
  1.2247 +			{
  1.2248 +				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
  1.2249 +				return;
  1.2250 +			}
  1.2251 +
  1.2252 +			CPlugin *pl;
  1.2253 +			char *end;
  1.2254 +			const char *arg = command.Arg(3);
  1.2255 +			int id = strtol(arg, &end, 10);
  1.2256 +
  1.2257 +			if (*end == '\0')
  1.2258 +			{
  1.2259 +				pl = GetPluginByOrder(id);
  1.2260 +				if (!pl)
  1.2261 +				{
  1.2262 +					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  1.2263 +					return;
  1.2264 +				}
  1.2265 +			}
  1.2266 +			else
  1.2267 +			{
  1.2268 +				char pluginfile[256];
  1.2269 +				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  1.2270 +				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  1.2271 +
  1.2272 +				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  1.2273 +				{
  1.2274 +					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  1.2275 +					return;
  1.2276 +				}
  1.2277 +			}
  1.2278 +
  1.2279 +			char name[PLATFORM_MAX_PATH];
  1.2280 +			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1.2281 +			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1.2282 +
  1.2283 +			if (UnloadPlugin(pl))
  1.2284 +			{
  1.2285 +				g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
  1.2286 +			}
  1.2287 +			else
  1.2288 +			{
  1.2289 +				g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
  1.2290 +			}
  1.2291 +
  1.2292 +			return;
  1.2293 +		}
  1.2294 +		else if (strcmp(cmd, "unload_all") == 0)
  1.2295 +		{
  1.2296 +			g_PluginSys.UnloadAll();
  1.2297 +			g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
  1.2298 +			return;
  1.2299 +		}
  1.2300 +		else if (strcmp(cmd, "load_lock") == 0)
  1.2301 +		{
  1.2302 +			if (m_LoadingLocked)
  1.2303 +			{
  1.2304 +				g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
  1.2305 +			}
  1.2306 +			else
  1.2307 +			{
  1.2308 +				m_LoadingLocked = true;
  1.2309 +				g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
  1.2310 +			}
  1.2311 +			return;
  1.2312 +		}
  1.2313 +		else if (strcmp(cmd, "load_unlock") == 0)
  1.2314 +		{
  1.2315 +			if (m_LoadingLocked)
  1.2316 +			{
  1.2317 +				m_LoadingLocked = false;
  1.2318 +				g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
  1.2319 +			}
  1.2320 +			else
  1.2321 +			{
  1.2322 +				g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
  1.2323 +			}
  1.2324 +			return;
  1.2325 +		}
  1.2326 +		else if (strcmp(cmd, "info") == 0)
  1.2327 +		{
  1.2328 +			if (argcount < 4)
  1.2329 +			{
  1.2330 +				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
  1.2331 +				return;
  1.2332 +			}
  1.2333 +
  1.2334 +			CPlugin *pl;
  1.2335 +			char *end;
  1.2336 +			const char *arg = command.Arg(3);
  1.2337 +			int id = strtol(arg, &end, 10);
  1.2338 +
  1.2339 +			if (*end == '\0')
  1.2340 +			{
  1.2341 +				pl = GetPluginByOrder(id);
  1.2342 +				if (!pl)
  1.2343 +				{
  1.2344 +					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  1.2345 +					return;
  1.2346 +				}
  1.2347 +			}
  1.2348 +			else
  1.2349 +			{
  1.2350 +				char pluginfile[256];
  1.2351 +				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  1.2352 +				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  1.2353 +
  1.2354 +				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  1.2355 +				{
  1.2356 +					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  1.2357 +					return;
  1.2358 +				}
  1.2359 +			}
  1.2360 +
  1.2361 +			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1.2362 +
  1.2363 +			g_RootMenu.ConsolePrint("  Filename: %s", pl->GetFilename());
  1.2364 +			if (pl->GetStatus() <= Plugin_Error)
  1.2365 +			{
  1.2366 +				if (IS_STR_FILLED(info->name))
  1.2367 +				{
  1.2368 +					if (IS_STR_FILLED(info->description))
  1.2369 +					{
  1.2370 +						g_RootMenu.ConsolePrint("  Title: %s (%s)", info->name, info->description);
  1.2371 +					} else {
  1.2372 +						g_RootMenu.ConsolePrint("  Title: %s", info->name);
  1.2373 +					}
  1.2374 +				}
  1.2375 +				if (IS_STR_FILLED(info->author))
  1.2376 +				{
  1.2377 +					g_RootMenu.ConsolePrint("  Author: %s", info->author);
  1.2378 +				}
  1.2379 +				if (IS_STR_FILLED(info->version))
  1.2380 +				{
  1.2381 +					g_RootMenu.ConsolePrint("  Version: %s", info->version);
  1.2382 +				}
  1.2383 +				if (IS_STR_FILLED(info->url))
  1.2384 +				{
  1.2385 +					g_RootMenu.ConsolePrint("  URL: %s", info->url);
  1.2386 +				}
  1.2387 +				if (pl->GetStatus() == Plugin_Error)
  1.2388 +				{
  1.2389 +					g_RootMenu.ConsolePrint("  Error: %s", pl->m_errormsg);
  1.2390 +				}
  1.2391 +				else
  1.2392 +				{
  1.2393 +					g_RootMenu.ConsolePrint("  Debugging: %s", pl->IsDebugging() ? "Yes" : "No");
  1.2394 +					g_RootMenu.ConsolePrint("  Running: %s", pl->GetStatus() == Plugin_Running ? "Yes" : "No");
  1.2395 +
  1.2396 +					const char *typestr = "";
  1.2397 +					switch (pl->GetType())
  1.2398 +					{
  1.2399 +					case PluginType_MapUpdated:
  1.2400 +						typestr = "Map Change if Updated";
  1.2401 +						break;
  1.2402 +					case PluginType_MapOnly:
  1.2403 +						typestr = "Map Change";
  1.2404 +						break;
  1.2405 +					case PluginType_Private:
  1.2406 +					case PluginType_Global:
  1.2407 +						typestr = "Never";
  1.2408 +						break;
  1.2409 +					}
  1.2410 +
  1.2411 +					g_RootMenu.ConsolePrint("  Reloads: %s", typestr);
  1.2412 +				}
  1.2413 +			}
  1.2414 +			else
  1.2415 +			{
  1.2416 +				g_RootMenu.ConsolePrint("  Load error: %s", pl->m_errormsg);
  1.2417 +				g_RootMenu.ConsolePrint("  File info: (title \"%s\") (version \"%s\")", 
  1.2418 +										info->name ? info->name : "<none>",
  1.2419 +										info->version ? info->version : "<none>");
  1.2420 +				if (IS_STR_FILLED(info->url))
  1.2421 +				{
  1.2422 +					g_RootMenu.ConsolePrint("  File URL: %s", info->url);
  1.2423 +				}
  1.2424 +			}
  1.2425 +
  1.2426 +			return;
  1.2427 +		}
  1.2428 +		else if (strcmp(cmd, "debug") == 0)
  1.2429 +		{
  1.2430 +			if (argcount < 5)
  1.2431 +			{
  1.2432 +				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
  1.2433 +				return;
  1.2434 +			}
  1.2435 +
  1.2436 +			CPlugin *pl;
  1.2437 +			char *end;
  1.2438 +			const char *arg = command.Arg(3);
  1.2439 +			int id = strtol(arg, &end, 10);
  1.2440 +
  1.2441 +			if (*end == '\0')
  1.2442 +			{
  1.2443 +				pl = GetPluginByOrder(id);
  1.2444 +				if (!pl)
  1.2445 +				{
  1.2446 +					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  1.2447 +					return;
  1.2448 +				}
  1.2449 +			}
  1.2450 +			else
  1.2451 +			{
  1.2452 +				char pluginfile[256];
  1.2453 +				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  1.2454 +				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  1.2455 +
  1.2456 +				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  1.2457 +				{
  1.2458 +					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  1.2459 +					return;
  1.2460 +				}
  1.2461 +			}
  1.2462 +
  1.2463 +			int res;
  1.2464 +			const char *mode = command.Arg(4);
  1.2465 +			if ((res=strcmp("on", mode)) && strcmp("off", mode))
  1.2466 +			{
  1.2467 +				g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
  1.2468 +				return;
  1.2469 +			}
  1.2470 +
  1.2471 +			bool debug;
  1.2472 +			if (!res)
  1.2473 +			{
  1.2474 +				debug = true;
  1.2475 +			}
  1.2476 +			else
  1.2477 +			{
  1.2478 +				debug = false;
  1.2479 +			}
  1.2480 +
  1.2481 +			if (debug && pl->IsDebugging())
  1.2482 +			{
  1.2483 +				g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
  1.2484 +				return;
  1.2485 +			}
  1.2486 +			else if (!debug && !pl->IsDebugging())
  1.2487 +			{
  1.2488 +				g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
  1.2489 +				return;
  1.2490 +			}
  1.2491 +
  1.2492 +			char error[256];
  1.2493 +			if (pl->ToggleDebugMode(debug, error, sizeof(error)))
  1.2494 +			{
  1.2495 +				g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
  1.2496 +				return;
  1.2497 +			}
  1.2498 +			else
  1.2499 +			{
  1.2500 +				g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
  1.2501 +				g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
  1.2502 +				return;
  1.2503 +			}
  1.2504 +		}
  1.2505 +		else if (strcmp(cmd, "refresh") == 0)
  1.2506 +		{
  1.2507 +			g_SourceMod.DoGlobalPluginLoads();
  1.2508 +			g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
  1.2509 +			return;
  1.2510 +		}
  1.2511 +		else if (strcmp(cmd, "reload") == 0)
  1.2512 +		{
  1.2513 +			if (argcount < 4)
  1.2514 +			{
  1.2515 +				g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
  1.2516 +				return;
  1.2517 +			}
  1.2518 +
  1.2519 +			CPlugin *pl;
  1.2520 +			char *end;
  1.2521 +			const char *arg = command.Arg(3);
  1.2522 +			int id = strtol(arg, &end, 10);
  1.2523 +
  1.2524 +			if (*end == '\0')
  1.2525 +			{
  1.2526 +				pl = GetPluginByOrder(id);
  1.2527 +				if (!pl)
  1.2528 +				{
  1.2529 +					g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
  1.2530 +					return;
  1.2531 +				}
  1.2532 +			}
  1.2533 +			else
  1.2534 +			{
  1.2535 +				char pluginfile[256];
  1.2536 +				const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  1.2537 +				UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  1.2538 +
  1.2539 +				if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  1.2540 +				{
  1.2541 +					g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
  1.2542 +					return;
  1.2543 +				}
  1.2544 +			}
  1.2545 +
  1.2546 +			char name[PLATFORM_MAX_PATH];
  1.2547 +			const sm_plugininfo_t *info = pl->GetPublicInfo();
  1.2548 +			strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
  1.2549 +
  1.2550 +			if (ReloadPlugin(pl))
  1.2551 +			{
  1.2552 +				g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
  1.2553 +			}
  1.2554 +			else
  1.2555 +			{
  1.2556 +				g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
  1.2557 +			}
  1.2558 +
  1.2559 +			return;
  1.2560 +		}
  1.2561 +	}
  1.2562 +
  1.2563 +	/* Draw the main menu */
  1.2564 +	g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
  1.2565 +	g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
  1.2566 +	g_RootMenu.DrawGenericOption("info", "Information about a plugin");
  1.2567 +	g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
  1.2568 +	g_RootMenu.DrawGenericOption("load", "Load a plugin");
  1.2569 +	g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
  1.2570 +	g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
  1.2571 +	g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
  1.2572 +	g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
  1.2573 +	g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
  1.2574 +	g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
  1.2575 +}
  1.2576 +
  1.2577 +bool CPluginManager::ReloadPlugin(CPlugin *pl)
  1.2578 +{
  1.2579 +	List<CPlugin *>::iterator iter;
  1.2580 +	char filename[PLATFORM_MAX_PATH];
  1.2581 +	bool debug, wasloaded;
  1.2582 +	PluginType ptype;
  1.2583 +	IPlugin *newpl;
  1.2584 +	int id = 1;
  1.2585 +
  1.2586 +	strcpy(filename, pl->m_filename);
  1.2587 +	debug = pl->IsDebugging();
  1.2588 +	ptype = pl->GetType();
  1.2589 +
  1.2590 +	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
  1.2591 +	{
  1.2592 +		if ((*iter) == pl)
  1.2593 +		{
  1.2594 +			break;
  1.2595 +		}
  1.2596 +	}
  1.2597 +
  1.2598 +	if (!UnloadPlugin(pl))
  1.2599 +	{
  1.2600 +		return false;
  1.2601 +	}
  1.2602 +	if (!(newpl=LoadPlugin(filename, debug, ptype, NULL, 0, &wasloaded)))
  1.2603 +	{
  1.2604 +		return false;
  1.2605 +	}
  1.2606 +
  1.2607 +	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1.2608 +	{
  1.2609 +		if ((*iter) == (CPlugin *)newpl)
  1.2610 +		{
  1.2611 +			m_plugins.erase(iter);
  1.2612 +			break;
  1.2613 +		}
  1.2614 +	}
  1.2615 +
  1.2616 +	int i;
  1.2617 +	for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
  1.2618 +	{
  1.2619 +	}
  1.2620 +	m_plugins.insert(iter, (CPlugin *)newpl);
  1.2621 +
  1.2622 +	return true;
  1.2623 +}
  1.2624 +
  1.2625 +void CPluginManager::ReloadOrUnloadPlugins()
  1.2626 +{
  1.2627 +	/* If we're in a load lock, just skip this whole bit. */
  1.2628 +	if (m_LoadingLocked)
  1.2629 +	{
  1.2630 +		return;
  1.2631 +	}
  1.2632 +
  1.2633 +	List<CPlugin *>::iterator iter;
  1.2634 +	List<CPlugin *> tmp_list = m_plugins;
  1.2635 +	CPlugin *pl;
  1.2636 +	time_t t;
  1.2637 +
  1.2638 +	for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
  1.2639 +	{
  1.2640 +		pl = (*iter);
  1.2641 +		if (pl->GetType() ==  PluginType_MapOnly)
  1.2642 +		{
  1.2643 +			UnloadPlugin((IPlugin *)pl);
  1.2644 +		}
  1.2645 +		else if (pl->GetType() == PluginType_MapUpdated)
  1.2646 +		{
  1.2647 +			t = pl->GetFileTimeStamp();
  1.2648 +			if (!t || t > pl->GetTimeStamp())
  1.2649 +			{
  1.2650 +				pl->SetTimeStamp(t);
  1.2651 +				UnloadPlugin((IPlugin *)pl);
  1.2652 +			}
  1.2653 +		}
  1.2654 +	}
  1.2655 +}
  1.2656 +
  1.2657 +void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
  1.2658 +{
  1.2659 +	List<IPluginsListener *>::iterator iter;
  1.2660 +	IPluginsListener *pListener;
  1.2661 +	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
  1.2662 +	{
  1.2663 +		pListener = (*iter);
  1.2664 +		pListener->OnPluginPauseChange(pl, paused);
  1.2665 +	}	
  1.2666 +}
  1.2667 +
  1.2668 +bool CPluginManager::AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func)
  1.2669 +{
  1.2670 +	if (sm_trie_retrieve(m_pNativeLookup, name, NULL))
  1.2671 +	{
  1.2672 +		return false;
  1.2673 +	}
  1.2674 +
  1.2675 +	FakeNative *pNative = new FakeNative;
  1.2676 +
  1.2677 +	pNative->func = g_pVM->CreateFakeNative(func, pNative);
  1.2678 +	if (!pNative->func)
  1.2679 +	{
  1.2680 +		delete pNative;
  1.2681 +		return false;
  1.2682 +	}
  1.2683 +
  1.2684 +	pNative->call = pFunction;
  1.2685 +	pNative->name.assign(name);
  1.2686 +	pNative->ctx = pFunction->GetParentContext();
  1.2687 +
  1.2688 +	m_Natives.push_back(pNative);
  1.2689 +	sm_trie_insert(m_pNativeLookup, name,pNative);
  1.2690 +
  1.2691 +	CPlugin *pPlugin = GetPluginByCtx(pNative->ctx->GetContext());
  1.2692 +	pPlugin->m_fakeNatives.push_back(pNative);
  1.2693 +
  1.2694 +	return true;
  1.2695 +}
  1.2696 +
  1.2697 +void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
  1.2698 +{
  1.2699 +	List<CPlugin *>::iterator iter;
  1.2700 +	CPlugin *pPlugin;
  1.2701 +	IPluginFunction *pFunction;
  1.2702 +
  1.2703 +	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
  1.2704 +	{
  1.2705 +		pPlugin = (*iter);
  1.2706 +
  1.2707 +		if (pPlugin->GetStatus() <= Plugin_Paused)
  1.2708 +		{
  1.2709 +			pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
  1.2710 +
  1.2711 +			if (pFunction)
  1.2712 +			{
  1.2713 +				pForward->AddFunction(pFunction);
  1.2714 +			}
  1.2715 +		}
  1.2716 +	}
  1.2717 +}
  1.2718 +
  1.2719 +CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
  1.2720 +{
  1.2721 +	if (pToken->type != g_PluginIdent)
  1.2722 +	{
  1.2723 +		return NULL;
  1.2724 +	}
  1.2725 +
  1.2726 +	return (CPlugin *)(pToken->ptr);
  1.2727 +}
  1.2728 +
  1.2729 +void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
  1.2730 +{
  1.2731 +	List<CPlugin *>::iterator iter;
  1.2732 +
  1.2733 +	struct _pl
  1.2734 +	{
  1.2735 +		cell_t name;
  1.2736 +		cell_t file;
  1.2737 +		cell_t required;
  1.2738 +	} *plc;
  1.2739 +
  1.2740 +	struct _ext
  1.2741 +	{
  1.2742 +		cell_t name;
  1.2743 +		cell_t file;
  1.2744 +		cell_t autoload;
  1.2745 +		cell_t required;
  1.2746 +	} *ext;
  1.2747 +
  1.2748 +	const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
  1.2749 +
  1.2750 +	for (iter=m_plugins.begin();
  1.2751 +		 iter!=m_plugins.end();
  1.2752 +		 iter++)
  1.2753 +	{
  1.2754 +		CPlugin *pl = (*iter);
  1.2755 +		if (pl->GetStatus() != Plugin_Running)
  1.2756 +		{
  1.2757 +			continue;
  1.2758 +		}
  1.2759 +		IPluginContext *pContext = pl->GetBaseContext();
  1.2760 +		IPluginFunction *pf = pContext->GetFunctionByName(name);
  1.2761 +		if (!pf)
  1.2762 +		{
  1.2763 +			continue;
  1.2764 +		}
  1.2765 +		uint32_t num_vars = pContext->GetPubVarsNum();
  1.2766 +		for (uint32_t i=0; i<num_vars; i++)
  1.2767 +		{
  1.2768 +			sp_pubvar_t *pubvar;
  1.2769 +			if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
  1.2770 +			{
  1.2771 +				continue;
  1.2772 +			}
  1.2773 +			if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
  1.2774 +			{
  1.2775 +				plc = (_pl *)pubvar->offs;
  1.2776 +				if (plc->required)
  1.2777 +				{
  1.2778 +					continue;
  1.2779 +				}
  1.2780 +				char *str;
  1.2781 +				pContext->LocalToString(plc->name, &str);
  1.2782 +				if (strcmp(str, lib) != 0)
  1.2783 +				{
  1.2784 +					continue;
  1.2785 +				}
  1.2786 +				pf->PushString(lib);
  1.2787 +				pf->Execute(NULL);
  1.2788 +			}
  1.2789 +			else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
  1.2790 +			{
  1.2791 +				ext = (_ext *)pubvar->offs;
  1.2792 +				if (ext->required)
  1.2793 +				{
  1.2794 +					continue;
  1.2795 +				}
  1.2796 +				char *str;
  1.2797 +				pContext->LocalToString(ext->name, &str);
  1.2798 +				if (strcmp(str, lib) != 0)
  1.2799 +				{
  1.2800 +					continue;
  1.2801 +				}
  1.2802 +				pf->PushString(lib);
  1.2803 +				pf->Execute(NULL);
  1.2804 +			}
  1.2805 +		}
  1.2806 +	}
  1.2807 +}
  1.2808 +
  1.2809 +bool CPluginManager::LibraryExists(const char *lib)
  1.2810 +{
  1.2811 +	List<CPlugin *>::iterator iter;
  1.2812 +
  1.2813 +	for (iter=m_plugins.begin();
  1.2814 +		 iter!=m_plugins.end();
  1.2815 +		 iter++)
  1.2816 +	{
  1.2817 +		CPlugin *pl = (*iter);
  1.2818 +		if (pl->GetStatus() != Plugin_Running)
  1.2819 +		{
  1.2820 +			continue;
  1.2821 +		}
  1.2822 +		List<String>::iterator s_iter;
  1.2823 +		for (s_iter = pl->m_Libraries.begin();
  1.2824 +			 s_iter != pl->m_Libraries.end();
  1.2825 +			 s_iter++)
  1.2826 +		{
  1.2827 +			if ((*s_iter).compare(lib) == 0)
  1.2828 +			{
  1.2829 +				return true;
  1.2830 +			}
  1.2831 +		}
  1.2832 +	}
  1.2833 +
  1.2834 +	return false;
  1.2835 +}
  1.2836 +
  1.2837 +void CPluginManager::AllPluginsLoaded()
  1.2838 +{
  1.2839 +	List<CPlugin *>::iterator iter;
  1.2840 +	CPlugin *pl;
  1.2841 +
  1.2842 +	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
  1.2843 +	{
  1.2844 +		pl = (*iter);
  1.2845 +		pl->Call_OnAllPluginsLoaded();
  1.2846 +	}
  1.2847 +}
  1.2848 +
  1.2849 +void CPluginManager::UnloadAll()
  1.2850 +{
  1.2851 +	List<CPlugin *>::iterator iter;
  1.2852 +	while ( (iter = m_plugins.begin()) != m_plugins.end() )
  1.2853 +	{
  1.2854 +		UnloadPlugin((*iter));
  1.2855 +	}
  1.2856 +}
  1.2857 +
  1.2858 +int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
  1.2859 +{
  1.2860 +	int id = 1;
  1.2861 +	List<CPlugin *>::iterator iter;
  1.2862 +
  1.2863 +	for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
  1.2864 +	{
  1.2865 +		if ((*iter) == pl)
  1.2866 +		{
  1.2867 +			return id;
  1.2868 +		}
  1.2869 +	}
  1.2870 +
  1.2871 +	return -1;
  1.2872 +}
  1.2873 +
  1.2874 +CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
  1.2875 +{
  1.2876 +	int id;
  1.2877 +	char *end;
  1.2878 +	CPlugin *pl;
  1.2879 +	
  1.2880 +	id = strtol(arg, &end, 10);
  1.2881 +
  1.2882 +	if (*end == '\0')
  1.2883 +	{
  1.2884 +		pl = GetPluginByOrder(id);
  1.2885 +		if (!pl)
  1.2886 +		{
  1.2887 +			return NULL;
  1.2888 +		}
  1.2889 +	}
  1.2890 +	else
  1.2891 +	{
  1.2892 +		char pluginfile[256];
  1.2893 +		const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
  1.2894 +		UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
  1.2895 +
  1.2896 +		if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
  1.2897 +		{
  1.2898 +			return NULL;
  1.2899 +		}
  1.2900 +	}
  1.2901 +
  1.2902 +	return pl;
  1.2903 +}