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