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