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