2008-03-30 09:00:22 +02:00
|
|
|
/**
|
2013-08-25 20:59:46 +02:00
|
|
|
* vim: set ts=4 sw=4 tw=99 noet :
|
2008-03-30 09:00:22 +02:00
|
|
|
* =============================================================================
|
|
|
|
* SourceMod
|
|
|
|
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
|
|
|
* =============================================================================
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
|
|
|
* Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* As a special exception, AlliedModders LLC gives you permission to link the
|
|
|
|
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
|
|
|
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
|
|
|
* by the Valve Corporation. You must obey the GNU General Public License in
|
|
|
|
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
|
|
|
* this exception to all derivative works. AlliedModders LLC defines further
|
|
|
|
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
|
|
|
* or <http://www.sourcemod.net/license.php>.
|
|
|
|
*
|
|
|
|
* Version: $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2013-03-29 21:06:12 +01:00
|
|
|
#include <stdarg.h>
|
2008-03-30 09:00:22 +02:00
|
|
|
#include "PluginSys.h"
|
|
|
|
#include "ShareSys.h"
|
2013-03-29 19:37:29 +01:00
|
|
|
#include <ILibrarySys.h>
|
|
|
|
#include <ISourceMod.h>
|
|
|
|
#include <IHandleSys.h>
|
|
|
|
#include <IForwardSys.h>
|
|
|
|
#include <IPlayerHelpers.h>
|
2008-03-30 09:00:22 +02:00
|
|
|
#include "ExtensionSys.h"
|
2013-03-29 19:37:29 +01:00
|
|
|
#include "GameConfigs.h"
|
|
|
|
#include "common_logic.h"
|
|
|
|
#include "Translator.h"
|
2014-08-17 17:06:43 +02:00
|
|
|
#include "Logger.h"
|
2015-08-17 04:10:50 +02:00
|
|
|
#include "frame_tasks.h"
|
2015-08-26 05:58:43 +02:00
|
|
|
#include <am-string.h>
|
2015-08-31 06:27:32 +02:00
|
|
|
#include <bridge/include/IVEngineServerBridge.h>
|
|
|
|
#include <bridge/include/CoreProvider.h>
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
CPluginManager g_PluginSys;
|
|
|
|
HandleType_t g_PluginType = 0;
|
|
|
|
IdentityType_t g_PluginIdent = 0;
|
|
|
|
|
|
|
|
CPlugin::CPlugin(const char *file)
|
2015-08-16 22:08:18 +02:00
|
|
|
: m_serial(0),
|
|
|
|
m_status(Plugin_Uncompiled),
|
2015-08-17 04:10:50 +02:00
|
|
|
m_WaitingToUnload(false),
|
2015-08-16 22:08:18 +02:00
|
|
|
m_SilentFailure(false),
|
|
|
|
m_FakeNativesMissing(false),
|
|
|
|
m_LibraryMissing(false),
|
|
|
|
m_pContext(nullptr),
|
|
|
|
m_MaxClientsVar(nullptr),
|
|
|
|
m_ident(nullptr),
|
|
|
|
m_bGotAllLoaded(false),
|
|
|
|
m_FileVersion(0),
|
|
|
|
m_LastAccess(0),
|
|
|
|
m_handle(BAD_HANDLE)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
static int MySerial = 0;
|
|
|
|
|
|
|
|
m_serial = ++MySerial;
|
2015-08-16 22:08:18 +02:00
|
|
|
m_errormsg[0] = '\0';
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(m_filename, sizeof(m_filename), "%s", file);
|
2015-08-16 22:08:18 +02:00
|
|
|
|
2015-08-16 22:20:47 +02:00
|
|
|
memset(&m_info, 0, sizeof(m_info));
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
m_pPhrases = g_Translator.CreatePhraseCollection();
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin::~CPlugin()
|
|
|
|
{
|
|
|
|
if (m_handle)
|
|
|
|
{
|
|
|
|
HandleSecurity sec;
|
|
|
|
sec.pOwner = g_PluginSys.GetIdentity();
|
|
|
|
sec.pIdentity = sec.pOwner;
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
handlesys->FreeHandle(m_handle, &sec);
|
2008-03-30 09:00:22 +02:00
|
|
|
g_ShareSys.DestroyIdentity(m_ident);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i=0; i<m_configs.size(); i++)
|
|
|
|
delete m_configs[i];
|
|
|
|
m_configs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::InitIdentity()
|
|
|
|
{
|
|
|
|
if (!m_handle)
|
|
|
|
{
|
|
|
|
m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
|
2013-03-29 19:37:29 +01:00
|
|
|
m_handle = handlesys->CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
|
2008-07-11 10:18:43 +02:00
|
|
|
m_pRuntime->GetDefaultContext()->SetKey(1, m_ident);
|
|
|
|
m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int CPlugin::CalcMemUsage()
|
|
|
|
{
|
|
|
|
unsigned int base_size =
|
|
|
|
sizeof(CPlugin)
|
|
|
|
+ sizeof(IdentityToken_t)
|
|
|
|
+ (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
|
2013-08-25 20:59:46 +02:00
|
|
|
+ m_Props.mem_usage();
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2015-08-16 22:08:18 +02:00
|
|
|
for (unsigned int i = 0; i < m_configs.size(); i++) {
|
2008-03-30 09:00:22 +02:00
|
|
|
base_size += m_configs[i]->autocfg.size();
|
|
|
|
base_size += m_configs[i]->folder.size();
|
|
|
|
}
|
|
|
|
|
2015-08-16 22:08:18 +02:00
|
|
|
for (auto i = m_Libraries.begin(); i != m_Libraries.end(); i++)
|
2008-03-30 09:00:22 +02:00
|
|
|
base_size += (*i).size();
|
|
|
|
|
2015-08-16 22:08:18 +02:00
|
|
|
for (auto i = m_RequiredLibs.begin(); i != m_RequiredLibs.end(); i++)
|
2008-03-30 09:00:22 +02:00
|
|
|
base_size += (*i).size();
|
|
|
|
|
|
|
|
return base_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle_t CPlugin::GetMyHandle()
|
|
|
|
{
|
|
|
|
return m_handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
char fullpath[PLATFORM_MAX_PATH];
|
2013-03-29 19:37:29 +01:00
|
|
|
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
|
2008-03-30 09:00:22 +02:00
|
|
|
FILE *fp = fopen(fullpath, "rb");
|
|
|
|
|
|
|
|
CPlugin *pPlugin = new CPlugin(file);
|
|
|
|
|
2015-08-16 22:08:18 +02:00
|
|
|
if (!fp) {
|
2008-03-30 09:00:22 +02:00
|
|
|
if (error)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Unable to open file");
|
2008-03-30 09:00:22 +02:00
|
|
|
pPlugin->m_status = Plugin_BadLoad;
|
|
|
|
return pPlugin;
|
|
|
|
}
|
|
|
|
|
2008-07-31 23:08:00 +02:00
|
|
|
fclose(fp);
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
return pPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
|
|
|
|
{
|
2013-08-25 20:59:46 +02:00
|
|
|
StringHashMap<void *>::Result r = m_Props.find(prop);
|
|
|
|
if (!r.found())
|
|
|
|
return false;
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (ptr)
|
|
|
|
*ptr = r->value;
|
|
|
|
|
|
|
|
if (remove)
|
|
|
|
m_Props.remove(r);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
return true;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CPlugin::SetProperty(const char *prop, void *ptr)
|
|
|
|
{
|
2013-08-25 20:59:46 +02:00
|
|
|
return m_Props.insert(prop, ptr);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2008-07-11 10:18:43 +02:00
|
|
|
IPluginRuntime *CPlugin::GetRuntime()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
return m_pRuntime;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
|
|
|
|
{
|
2014-07-13 13:56:47 +02:00
|
|
|
if (m_status == Plugin_Running)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
/* Tell everyone we're now paused */
|
2014-07-13 13:56:47 +02:00
|
|
|
SetPauseState(true);
|
|
|
|
/* If we won't recover from this error, drop everything and pause dependent plugins too! */
|
|
|
|
if (status == Plugin_Failed)
|
|
|
|
{
|
|
|
|
DropEverything();
|
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2014-07-13 13:56:47 +02:00
|
|
|
/* SetPauseState sets the status to Plugin_Paused, but we might want to see some other status set. */
|
|
|
|
m_status = status;
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, error_fmt);
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeVsprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
|
2008-03-30 09:00:22 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
2008-07-11 10:18:43 +02:00
|
|
|
if (m_pRuntime != NULL)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
m_pRuntime->SetPauseState(true);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-17 05:53:28 +02:00
|
|
|
bool CPlugin::ReadInfo()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
/* Now grab the info */
|
|
|
|
uint32_t idx;
|
|
|
|
IPluginContext *base = GetBaseContext();
|
|
|
|
int err = base->FindPubvarByName("myinfo", &idx);
|
|
|
|
|
2015-08-16 22:20:47 +02:00
|
|
|
if (err == SP_ERROR_NONE) {
|
2008-03-30 09:00:22 +02:00
|
|
|
struct sm_plugininfo_c_t
|
|
|
|
{
|
|
|
|
cell_t name;
|
|
|
|
cell_t description;
|
|
|
|
cell_t author;
|
|
|
|
cell_t version;
|
|
|
|
cell_t url;
|
|
|
|
};
|
|
|
|
sm_plugininfo_c_t *cinfo;
|
|
|
|
cell_t local_addr;
|
|
|
|
|
2015-08-16 22:20:47 +02:00
|
|
|
auto update_field = [base](cell_t addr, ke::AString *dest) {
|
|
|
|
const char* ptr;
|
|
|
|
if (base->LocalToString(addr, (char **)&ptr) == SP_ERROR_NONE)
|
|
|
|
*dest = ptr;
|
|
|
|
else
|
|
|
|
*dest = "";
|
|
|
|
};
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
|
2015-08-16 22:20:47 +02:00
|
|
|
update_field(cinfo->name, &info_name_);
|
|
|
|
update_field(cinfo->description, &info_description_);
|
|
|
|
update_field(cinfo->author, &info_author_);
|
|
|
|
update_field(cinfo->version, &info_version_);
|
|
|
|
update_field(cinfo->url, &info_url_);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2015-08-16 22:20:47 +02:00
|
|
|
ke::SafeStrcpy(m_DateTime, sizeof(m_DateTime), "unknown");
|
|
|
|
if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE) {
|
2008-06-02 07:03:27 +02:00
|
|
|
struct __version_info
|
|
|
|
{
|
|
|
|
cell_t version;
|
|
|
|
cell_t filevers;
|
|
|
|
cell_t date;
|
|
|
|
cell_t time;
|
|
|
|
};
|
|
|
|
__version_info *info;
|
|
|
|
cell_t local_addr;
|
2008-10-06 00:20:17 +02:00
|
|
|
const char *pDate, *pTime, *pFileVers;
|
2008-06-02 07:03:27 +02:00
|
|
|
|
|
|
|
pDate = "";
|
|
|
|
pTime = "";
|
|
|
|
|
|
|
|
base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
|
|
|
|
m_FileVersion = info->version;
|
2015-08-16 22:20:47 +02:00
|
|
|
if (m_FileVersion >= 4) {
|
2008-06-02 07:03:27 +02:00
|
|
|
base->LocalToString(info->date, (char **)&pDate);
|
|
|
|
base->LocalToString(info->time, (char **)&pTime);
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
|
2008-06-02 07:03:27 +02:00
|
|
|
}
|
2015-08-16 22:20:47 +02:00
|
|
|
if (m_FileVersion > 5) {
|
2008-10-06 00:20:17 +02:00
|
|
|
base->LocalToString(info->filevers, (char **)&pFileVers);
|
|
|
|
SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers);
|
|
|
|
return false;
|
|
|
|
}
|
2015-08-16 22:20:47 +02:00
|
|
|
} else {
|
2008-06-02 07:03:27 +02:00
|
|
|
m_FileVersion = 0;
|
|
|
|
}
|
2008-10-06 00:20:17 +02:00
|
|
|
|
|
|
|
if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE)
|
|
|
|
base->GetPubvarByIndex(idx, &m_MaxClientsVar);
|
2015-08-16 22:20:47 +02:00
|
|
|
else
|
|
|
|
m_MaxClientsVar = nullptr;
|
2008-10-06 00:20:17 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::SyncMaxClients(int max_clients)
|
|
|
|
{
|
|
|
|
if (m_MaxClientsVar == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*m_MaxClientsVar->offs = max_clients;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::Call_OnPluginStart()
|
|
|
|
{
|
|
|
|
if (m_status != Plugin_Loaded)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_status = Plugin_Running;
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
SyncMaxClients(playerhelpers->GetMaxClients());
|
2008-10-06 00:20:17 +02:00
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
cell_t result;
|
2008-07-11 10:18:43 +02:00
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart");
|
2008-03-30 09:00:22 +02:00
|
|
|
if (!pFunction)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int err;
|
|
|
|
if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
|
|
|
|
{
|
|
|
|
SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::Call_OnPluginEnd()
|
|
|
|
{
|
|
|
|
if (m_status > Plugin_Paused)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cell_t result;
|
2008-07-11 10:18:43 +02:00
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd");
|
2008-03-30 09:00:22 +02:00
|
|
|
if (!pFunction)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pFunction->Execute(&result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::Call_OnAllPluginsLoaded()
|
|
|
|
{
|
|
|
|
if (m_status > Plugin_Paused)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_bGotAllLoaded)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bGotAllLoaded = true;
|
|
|
|
|
|
|
|
cell_t result;
|
2008-07-11 10:18:43 +02:00
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded");
|
2008-03-30 09:00:22 +02:00
|
|
|
if (pFunction != NULL)
|
|
|
|
{
|
|
|
|
pFunction->Execute(&result);
|
|
|
|
}
|
|
|
|
|
2015-08-30 01:23:50 +02:00
|
|
|
if (bridge->IsMapRunning())
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
pFunction->Execute(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-30 01:23:50 +02:00
|
|
|
if (bridge->AreConfigsExecuted())
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2015-08-30 01:23:50 +02:00
|
|
|
bridge->ExecuteConfigs(GetBaseContext());
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
APLRes CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
if (m_status != Plugin_Created)
|
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
return APLRes_Failure;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_status = Plugin_Loaded;
|
|
|
|
|
|
|
|
int err;
|
|
|
|
cell_t result;
|
2009-03-19 03:20:40 +01:00
|
|
|
bool haveNewAPL = false;
|
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad2");
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
if (pFunction)
|
|
|
|
{
|
|
|
|
haveNewAPL = true;
|
|
|
|
}
|
|
|
|
else if (!(pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad")))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
return APLRes_Success;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pFunction->PushCell(m_handle);
|
|
|
|
pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
|
|
|
|
pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
|
|
|
|
pFunction->PushCell(maxlength);
|
|
|
|
if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
|
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
return APLRes_Failure;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
if (haveNewAPL)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
return (APLRes)result;
|
|
|
|
}
|
|
|
|
else if (result)
|
|
|
|
{
|
|
|
|
return APLRes_Success;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return APLRes_Failure;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-28 04:26:58 +02:00
|
|
|
void CPlugin::Call_OnLibraryAdded(const char *lib)
|
|
|
|
{
|
|
|
|
if (m_status > Plugin_Paused)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cell_t result;
|
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnLibraryAdded");
|
|
|
|
if (!pFunction)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pFunction->PushString(lib);
|
|
|
|
pFunction->Execute(&result);
|
|
|
|
}
|
|
|
|
|
2008-07-11 10:18:43 +02:00
|
|
|
void *CPlugin::GetPluginStructure()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
return NULL;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2015-09-17 05:53:28 +02:00
|
|
|
// Only called during plugin construction.
|
|
|
|
bool CPlugin::TryCompile(char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
char fullpath[PLATFORM_MAX_PATH];
|
|
|
|
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", m_filename);
|
|
|
|
|
|
|
|
char loadmsg[255];
|
|
|
|
m_pRuntime = g_pSourcePawn2->LoadBinaryFromFile(fullpath, loadmsg, sizeof(loadmsg));
|
|
|
|
if (!m_pRuntime) {
|
|
|
|
ke::SafeSprintf(error, maxlength, "Unable to load plugin (%s)", loadmsg);
|
|
|
|
m_status = Plugin_BadLoad;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadInfo() sets its own error state.
|
|
|
|
if (!ReadInfo()) {
|
|
|
|
ke::SafeSprintf(error, maxlength, "%s", m_errormsg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_status = Plugin_Created;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
IPluginContext *CPlugin::GetBaseContext()
|
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
if (m_pRuntime == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_pRuntime->GetDefaultContext();
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sp_context_t *CPlugin::GetContext()
|
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
return NULL;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *CPlugin::GetFilename()
|
|
|
|
{
|
|
|
|
return m_filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginType CPlugin::GetType()
|
|
|
|
{
|
2015-08-16 22:08:18 +02:00
|
|
|
return PluginType_MapUpdated;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const sm_plugininfo_t *CPlugin::GetPublicInfo()
|
|
|
|
{
|
2008-10-06 00:20:17 +02:00
|
|
|
if (GetStatus() >= Plugin_Created)
|
2015-08-16 22:20:47 +02:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
m_info.author = info_author_.chars();
|
|
|
|
m_info.description = info_description_.chars();
|
|
|
|
m_info.name = info_name_.chars();
|
|
|
|
m_info.url = info_url_.chars();
|
|
|
|
m_info.version = info_version_.chars();
|
2008-03-30 09:00:22 +02:00
|
|
|
return &m_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int CPlugin::GetSerial()
|
|
|
|
{
|
|
|
|
return m_serial;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginStatus CPlugin::GetStatus()
|
|
|
|
{
|
|
|
|
return m_status;
|
|
|
|
}
|
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
bool CPlugin::IsSilentlyFailed()
|
|
|
|
{
|
2015-08-16 22:08:18 +02:00
|
|
|
return m_SilentFailure;
|
2009-03-19 03:20:40 +01:00
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
bool CPlugin::IsDebugging()
|
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
if (m_pRuntime == NULL)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-15 07:22:26 +02:00
|
|
|
return true;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2015-08-16 22:08:18 +02:00
|
|
|
void CPlugin::SetSilentlyFailed()
|
2009-03-19 03:20:40 +01:00
|
|
|
{
|
2015-08-16 22:08:18 +02:00
|
|
|
m_SilentFailure = true;
|
2009-03-19 03:20:40 +01:00
|
|
|
}
|
|
|
|
|
2012-12-14 21:15:12 +01:00
|
|
|
void CPlugin::LibraryActions(LibraryAction action)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
List<String>::iterator iter;
|
|
|
|
for (iter = m_Libraries.begin();
|
|
|
|
iter != m_Libraries.end();
|
|
|
|
iter++)
|
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
g_PluginSys.OnLibraryAction((*iter).c_str(), action);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPlugin::SetPauseState(bool paused)
|
|
|
|
{
|
|
|
|
if (paused && GetStatus() != Plugin_Running)
|
|
|
|
{
|
|
|
|
return false;
|
2014-07-13 13:56:47 +02:00
|
|
|
}
|
|
|
|
else if (!paused && GetStatus() != Plugin_Paused && GetStatus() != Plugin_Error) {
|
2008-03-30 09:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paused)
|
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
LibraryActions(LibraryAction_Removed);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2014-07-13 13:56:47 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Set to running again BEFORE trying to call OnPluginPauseChange ;)
|
|
|
|
m_status = Plugin_Running;
|
|
|
|
m_pRuntime->SetPauseState(false);
|
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2008-07-11 10:18:43 +02:00
|
|
|
IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange");
|
2008-03-30 09:00:22 +02:00
|
|
|
if (pFunction)
|
|
|
|
{
|
|
|
|
cell_t result;
|
|
|
|
pFunction->PushCell(paused ? 1 : 0);
|
|
|
|
pFunction->Execute(&result);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paused)
|
|
|
|
{
|
|
|
|
m_status = Plugin_Paused;
|
2008-07-11 10:18:43 +02:00
|
|
|
m_pRuntime->SetPauseState(true);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_PluginSys._SetPauseState(this, paused);
|
|
|
|
|
|
|
|
if (!paused)
|
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
LibraryActions(LibraryAction_Added);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
IdentityToken_t *CPlugin::GetIdentity()
|
|
|
|
{
|
|
|
|
return m_ident;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPlugin::IsRunnable()
|
|
|
|
{
|
|
|
|
return (m_status <= Plugin_Paused) ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t CPlugin::GetFileTimeStamp()
|
|
|
|
{
|
|
|
|
char path[PLATFORM_MAX_PATH];
|
2013-03-29 19:37:29 +01:00
|
|
|
g_pSM->BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
|
2008-03-30 09:00:22 +02:00
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
|
|
struct _stat s;
|
|
|
|
if (_stat(path, &s) != 0)
|
|
|
|
#elif defined PLATFORM_POSIX
|
|
|
|
struct stat s;
|
|
|
|
if (stat(path, &s) != 0)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return s.st_mtime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t CPlugin::GetTimeStamp()
|
|
|
|
{
|
|
|
|
return m_LastAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::SetTimeStamp(time_t t)
|
|
|
|
{
|
|
|
|
m_LastAccess = t;
|
|
|
|
}
|
|
|
|
|
2008-05-10 11:23:55 +02:00
|
|
|
IPhraseCollection *CPlugin::GetPhrases()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-05-10 11:23:55 +02:00
|
|
|
return m_pPhrases;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::DependencyDropped(CPlugin *pOwner)
|
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
if (!m_pRuntime)
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
|
2015-08-15 04:16:28 +02:00
|
|
|
for (auto lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++) {
|
|
|
|
if (m_RequiredLibs.find(*lib_iter) != m_RequiredLibs.end()) {
|
|
|
|
m_LibraryMissing = true;
|
|
|
|
break;
|
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int unbound = 0;
|
2013-08-30 19:16:28 +02:00
|
|
|
for (size_t i = 0; i < pOwner->m_fakes.length(); i++)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-08-30 19:16:28 +02:00
|
|
|
ke::Ref<Native> entry(pOwner->m_fakes[i]);
|
|
|
|
|
|
|
|
uint32_t idx;
|
|
|
|
if (m_pRuntime->FindNativeByName(entry->name(), &idx) != SP_ERROR_NONE)
|
2008-03-30 09:00:22 +02:00
|
|
|
continue;
|
2013-08-30 19:16:28 +02:00
|
|
|
|
2015-02-25 07:58:31 +01:00
|
|
|
m_pRuntime->UpdateNativeBinding(idx, nullptr, 0, nullptr);
|
2008-03-30 09:00:22 +02:00
|
|
|
unbound++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unbound)
|
|
|
|
{
|
|
|
|
m_FakeNativesMissing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* :IDEA: in the future, add native trapping? */
|
|
|
|
if (m_FakeNativesMissing || m_LibraryMissing)
|
|
|
|
{
|
|
|
|
SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-29 21:10:51 +01:00
|
|
|
size_t CPlugin::GetConfigCount()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
return (unsigned int)m_configs.size();
|
|
|
|
}
|
|
|
|
|
2013-03-29 21:10:51 +01:00
|
|
|
AutoConfig *CPlugin::GetConfig(size_t i)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
if (i >= GetConfigCount())
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_configs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
|
|
|
|
{
|
|
|
|
/* Do a check for duplicates to prevent double-execution */
|
|
|
|
for (size_t i = 0; i < m_configs.size(); i++)
|
|
|
|
{
|
|
|
|
if (m_configs[i]->autocfg.compare(cfg) == 0
|
|
|
|
&& m_configs[i]->folder.compare(folder) == 0
|
|
|
|
&& m_configs[i]->create == autoCreate)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoConfig *c = new AutoConfig;
|
|
|
|
|
|
|
|
c->autocfg = cfg;
|
|
|
|
c->folder = folder;
|
|
|
|
c->create = autoCreate;
|
|
|
|
|
|
|
|
m_configs.push_back(c);
|
|
|
|
}
|
|
|
|
|
2008-05-26 09:51:11 +02:00
|
|
|
void CPlugin::DropEverything()
|
|
|
|
{
|
|
|
|
CPlugin *pOther;
|
|
|
|
List<WeakNative>::iterator wk_iter;
|
|
|
|
|
|
|
|
/* Tell everyone that depends on us that we're about to drop */
|
2013-03-29 19:37:29 +01:00
|
|
|
for (List<CPlugin *>::iterator iter = m_Dependents.begin();
|
2008-05-26 09:51:11 +02:00
|
|
|
iter != m_Dependents.end();
|
|
|
|
iter++)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
pOther = static_cast<CPlugin *>(*iter);
|
2008-05-26 09:51:11 +02:00
|
|
|
pOther->DependencyDropped(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note: we don't care about things we depend on.
|
|
|
|
* The reason is that extensions have their own cleanup
|
|
|
|
* code for plugins. Although the "right" design would be
|
|
|
|
* to centralize that here, i'm omitting it for now. Thus,
|
|
|
|
* the code below to walk the plugins list will suffice.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Other plugins could be holding weak references that were
|
|
|
|
* added by us. We need to clean all of those up now.
|
|
|
|
*/
|
2013-03-29 19:37:29 +01:00
|
|
|
for (List<CPlugin *>::iterator iter = g_PluginSys.m_plugins.begin();
|
2008-05-26 09:51:11 +02:00
|
|
|
iter != g_PluginSys.m_plugins.end();
|
|
|
|
iter++)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
(*iter)->ToNativeOwner()->DropRefsTo(this);
|
2008-05-26 09:51:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Proceed with the rest of the necessities. */
|
|
|
|
CNativeOwner::DropEverything();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
|
|
|
|
{
|
2013-08-30 19:16:28 +02:00
|
|
|
ke::Ref<Native> entry = g_ShareSys.AddFakeNative(pFunc, name, func);
|
|
|
|
if (!entry)
|
2008-05-26 09:51:11 +02:00
|
|
|
return false;
|
|
|
|
|
2013-08-30 19:16:28 +02:00
|
|
|
m_fakes.append(entry);
|
2008-05-26 09:51:11 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
/*******************
|
|
|
|
* PLUGIN ITERATOR *
|
|
|
|
*******************/
|
|
|
|
|
|
|
|
CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
|
|
|
|
{
|
|
|
|
mylist = _mylist;
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
IPlugin *CPluginManager::CPluginIterator::GetPlugin()
|
|
|
|
{
|
|
|
|
return (*current);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::CPluginIterator::MorePlugins()
|
|
|
|
{
|
|
|
|
return (current != mylist->end());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::CPluginIterator::NextPlugin()
|
|
|
|
{
|
|
|
|
current++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::CPluginIterator::Release()
|
|
|
|
{
|
|
|
|
g_PluginSys.ReleaseIterator(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::CPluginIterator::~CPluginIterator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::CPluginIterator::Reset()
|
|
|
|
{
|
|
|
|
current = mylist->begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************
|
|
|
|
* PLUGIN MANAGER *
|
|
|
|
******************/
|
|
|
|
|
|
|
|
CPluginManager::CPluginManager()
|
|
|
|
{
|
|
|
|
m_AllPluginsLoaded = false;
|
|
|
|
m_MyIdent = NULL;
|
|
|
|
m_LoadingLocked = false;
|
2012-08-09 02:54:43 +02:00
|
|
|
|
|
|
|
m_bBlockBadPlugins = true;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::~CPluginManager()
|
|
|
|
{
|
|
|
|
CStack<CPluginManager::CPluginIterator *>::iterator iter;
|
|
|
|
for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
|
|
|
|
{
|
|
|
|
delete (*iter);
|
|
|
|
}
|
|
|
|
m_iters.popall();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::Shutdown()
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
|
|
|
|
while ((iter = m_plugins.begin()) != m_plugins.end())
|
|
|
|
{
|
|
|
|
UnloadPlugin(*iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
void CPluginManager::LoadAll(const char *config_path, const char *plugins_path)
|
|
|
|
{
|
|
|
|
LoadAll_FirstPass(config_path, plugins_path);
|
|
|
|
g_Extensions.MarkAllLoaded();
|
|
|
|
LoadAll_SecondPass();
|
|
|
|
g_Extensions.MarkAllLoaded();
|
|
|
|
AllPluginsLoaded();
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
|
|
|
|
{
|
|
|
|
/* First read in the database of plugin settings */
|
|
|
|
m_AllPluginsLoaded = false;
|
|
|
|
LoadPluginsFromDir(basedir, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
|
|
|
|
{
|
|
|
|
char base_path[PLATFORM_MAX_PATH];
|
|
|
|
|
|
|
|
/* Form the current path to start reading from */
|
|
|
|
if (localpath == NULL)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(base_path, sizeof(base_path), "%s", basedir);
|
2008-03-30 09:00:22 +02:00
|
|
|
} else {
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
IDirectory *dir = libsys->OpenDirectory(base_path);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
if (!dir)
|
|
|
|
{
|
|
|
|
char error[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->GetPlatformError(error, sizeof(error));
|
2014-08-17 17:06:43 +02:00
|
|
|
g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
|
|
|
|
g_Logger.LogError("[SM] Platform returned error: %s", error);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (dir->MoreFiles())
|
|
|
|
{
|
|
|
|
if (dir->IsEntryDirectory()
|
|
|
|
&& (strcmp(dir->GetEntryName(), ".") != 0)
|
|
|
|
&& (strcmp(dir->GetEntryName(), "..") != 0)
|
|
|
|
&& (strcmp(dir->GetEntryName(), "disabled") != 0)
|
|
|
|
&& (strcmp(dir->GetEntryName(), "optional") != 0))
|
|
|
|
{
|
|
|
|
char new_local[PLATFORM_MAX_PATH];
|
|
|
|
if (localpath == NULL)
|
|
|
|
{
|
|
|
|
/* If no path yet, don't add a former slash */
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
|
2008-03-30 09:00:22 +02:00
|
|
|
} else {
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
LoadPluginsFromDir(basedir, new_local);
|
|
|
|
} else if (dir->IsEntryFile()) {
|
|
|
|
const char *name = dir->GetEntryName();
|
|
|
|
size_t len = strlen(name);
|
|
|
|
if (len >= 4
|
|
|
|
&& strcmp(&name[len-4], ".smx") == 0)
|
|
|
|
{
|
|
|
|
/* If the filename matches, load the plugin */
|
|
|
|
char plugin[PLATFORM_MAX_PATH];
|
|
|
|
if (localpath == NULL)
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(plugin, sizeof(plugin), "%s", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
} else {
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
LoadAutoPlugin(plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dir->NextEntry();
|
|
|
|
}
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->CloseDirectory(dir);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
LoadRes CPluginManager::_LoadPlugin(CPlugin **aResult, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
if (m_LoadingLocked)
|
|
|
|
return LoadRes_NeverLoad;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does this plugin already exist?
|
|
|
|
*/
|
2013-08-25 20:59:46 +02:00
|
|
|
CPlugin *pPlugin;
|
|
|
|
if (m_LoadLookup.retrieve(path, &pPlugin))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
/* Check to see if we should try reloading it */
|
|
|
|
if (pPlugin->GetStatus() == Plugin_BadLoad
|
|
|
|
|| pPlugin->GetStatus() == Plugin_Error
|
2013-02-17 00:15:22 +01:00
|
|
|
|| pPlugin->GetStatus() == Plugin_Failed)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
UnloadPlugin(pPlugin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-08-25 20:59:46 +02:00
|
|
|
if (aResult)
|
|
|
|
*aResult = pPlugin;
|
2008-03-30 09:00:22 +02:00
|
|
|
return LoadRes_AlreadyLoaded;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
|
2008-03-30 09:00:22 +02:00
|
|
|
assert(pPlugin != NULL);
|
|
|
|
|
2015-09-17 05:53:28 +02:00
|
|
|
if (pPlugin->m_status == Plugin_Uncompiled) {
|
|
|
|
pPlugin->TryCompile(error, maxlength);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2012-08-09 02:54:43 +02:00
|
|
|
|
|
|
|
if (pPlugin->GetStatus() == Plugin_Created)
|
|
|
|
{
|
|
|
|
unsigned char *pCodeHash = pPlugin->m_pRuntime->GetCodeHash();
|
|
|
|
|
|
|
|
char codeHashBuf[40];
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(codeHashBuf, 40, "plugin_");
|
2012-08-09 02:54:43 +02:00
|
|
|
for (int i = 0; i < 16; i++)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]);
|
2012-08-09 02:54:43 +02:00
|
|
|
|
|
|
|
const char *bulletinUrl = g_pGameConf->GetKeyValue(codeHashBuf);
|
|
|
|
if (bulletinUrl != NULL)
|
|
|
|
{
|
|
|
|
if (m_bBlockBadPlugins)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
if (bulletinUrl[0] != '\0')
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Known malware detected and blocked. See %s for more info", bulletinUrl);
|
2012-08-09 02:54:43 +02:00
|
|
|
} else {
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Possible malware or illegal plugin detected and blocked");
|
2012-08-09 02:54:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pPlugin->m_status = Plugin_BadLoad;
|
|
|
|
} else {
|
|
|
|
if (bulletinUrl[0] != '\0')
|
|
|
|
{
|
2014-08-17 17:06:43 +02:00
|
|
|
g_Logger.LogMessage("%s: Known malware detected. See %s for more info, blocking disabled in core.cfg", pPlugin->GetFilename(), bulletinUrl);
|
2012-08-09 02:54:43 +02:00
|
|
|
} else {
|
2014-08-17 17:06:43 +02:00
|
|
|
g_Logger.LogMessage("%s: Possible malware or illegal plugin detected, blocking disabled in core.cfg", pPlugin->GetFilename());
|
2012-08-09 02:54:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
LoadRes loadFailure = LoadRes_Failure;
|
2008-03-30 09:00:22 +02:00
|
|
|
/* Get the status */
|
|
|
|
if (pPlugin->GetStatus() == Plugin_Created)
|
|
|
|
{
|
2008-05-26 09:51:11 +02:00
|
|
|
/* First native pass - add anything from Core */
|
|
|
|
g_ShareSys.BindNativesToPlugin(pPlugin, true);
|
2008-03-30 09:00:22 +02:00
|
|
|
pPlugin->InitIdentity();
|
2009-03-19 03:20:40 +01:00
|
|
|
APLRes result = pPlugin->Call_AskPluginLoad(error, maxlength);
|
|
|
|
switch (result)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
case APLRes_Success:
|
2008-03-30 09:00:22 +02:00
|
|
|
/* Autoload any modules */
|
|
|
|
LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
|
2009-03-19 03:20:40 +01:00
|
|
|
break;
|
|
|
|
case APLRes_Failure:
|
2008-03-30 09:00:22 +02:00
|
|
|
pPlugin->SetErrorState(Plugin_Failed, "%s", error);
|
2009-03-19 03:20:40 +01:00
|
|
|
loadFailure = LoadRes_Failure;
|
|
|
|
break;
|
|
|
|
case APLRes_SilentFailure:
|
|
|
|
pPlugin->SetErrorState(Plugin_Failed, "%s", error);
|
|
|
|
loadFailure = LoadRes_SilentFailure;
|
2015-08-16 22:08:18 +02:00
|
|
|
pPlugin->SetSilentlyFailed();
|
2009-03-19 03:20:40 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the time stamp */
|
|
|
|
time_t t = pPlugin->GetFileTimeStamp();
|
|
|
|
pPlugin->SetTimeStamp(t);
|
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (aResult)
|
|
|
|
*aResult = pPlugin;
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : loadFailure;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
|
|
|
|
{
|
|
|
|
CPlugin *pl;
|
|
|
|
LoadRes res;
|
|
|
|
|
|
|
|
*wasloaded = false;
|
2015-09-16 10:22:12 +02:00
|
|
|
if ((res=_LoadPlugin(&pl, path, true, PluginType_MapUpdated, error, maxlength)) == LoadRes_Failure)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
delete pl;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res == LoadRes_AlreadyLoaded)
|
|
|
|
{
|
|
|
|
*wasloaded = true;
|
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res == LoadRes_NeverLoad)
|
|
|
|
{
|
2009-10-02 12:33:59 +02:00
|
|
|
if (error)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-10-02 12:33:59 +02:00
|
|
|
if (m_LoadingLocked)
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "There is a global plugin loading lock in effect");
|
2009-10-02 12:33:59 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
|
2009-10-02 12:33:59 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddPlugin(pl);
|
|
|
|
|
|
|
|
/* Run second pass if we need to */
|
|
|
|
if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
|
|
|
|
{
|
|
|
|
if (!RunSecondPass(pl, error, maxlength))
|
|
|
|
{
|
|
|
|
UnloadPlugin(pl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pl->Call_OnAllPluginsLoaded();
|
|
|
|
}
|
|
|
|
|
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::LoadAutoPlugin(const char *plugin)
|
|
|
|
{
|
|
|
|
CPlugin *pl = NULL;
|
|
|
|
LoadRes res;
|
|
|
|
char error[255] = "Unknown error";
|
|
|
|
|
|
|
|
if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
|
|
|
|
{
|
2014-08-17 17:06:43 +02:00
|
|
|
g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s.", plugin, error);
|
2008-10-06 00:20:17 +02:00
|
|
|
pl->SetErrorState(
|
2009-12-20 03:19:28 +01:00
|
|
|
pl->GetStatus() <= Plugin_Created ? Plugin_BadLoad : pl->GetStatus(),
|
2008-10-06 00:20:17 +02:00
|
|
|
"%s",
|
|
|
|
error);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2009-03-19 03:20:40 +01:00
|
|
|
if (res == LoadRes_Successful || res == LoadRes_Failure || res == LoadRes_SilentFailure)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
AddPlugin(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AddPlugin(CPlugin *pPlugin)
|
|
|
|
{
|
|
|
|
List<IPluginsListener *>::iterator iter;
|
|
|
|
IPluginsListener *pListener;
|
|
|
|
|
|
|
|
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
|
|
|
|
{
|
|
|
|
pListener = (*iter);
|
|
|
|
pListener->OnPluginCreated(pPlugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_plugins.push_back(pPlugin);
|
2013-03-29 19:37:29 +01:00
|
|
|
m_LoadLookup.insert(pPlugin->m_filename, pPlugin);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::LoadAll_SecondPass()
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
CPlugin *pPlugin;
|
|
|
|
|
|
|
|
char error[256];
|
|
|
|
for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
|
|
|
|
{
|
|
|
|
pPlugin = (*iter);
|
|
|
|
if (pPlugin->GetStatus() == Plugin_Loaded)
|
|
|
|
{
|
|
|
|
error[0] = '\0';
|
|
|
|
if (!RunSecondPass(pPlugin, error, sizeof(error)))
|
|
|
|
{
|
2014-08-17 17:06:43 +02:00
|
|
|
g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
|
2014-10-29 17:13:43 +01:00
|
|
|
pPlugin->SetErrorState(Plugin_BadLoad, "%s", error);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_AllPluginsLoaded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
struct _pl
|
|
|
|
{
|
|
|
|
cell_t name;
|
|
|
|
cell_t file;
|
|
|
|
cell_t required;
|
|
|
|
} *pl;
|
|
|
|
|
|
|
|
IPluginContext *pBase = pPlugin->GetBaseContext();
|
|
|
|
uint32_t num = pBase->GetPubVarsNum();
|
|
|
|
sp_pubvar_t *pubvar;
|
|
|
|
char *name, *file;
|
|
|
|
char pathfile[PLATFORM_MAX_PATH];
|
|
|
|
|
2015-08-15 04:16:28 +02:00
|
|
|
for (uint32_t i=0; i<num; i++) {
|
2008-03-30 09:00:22 +02:00
|
|
|
if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
|
|
|
|
continue;
|
2015-08-15 04:16:28 +02:00
|
|
|
if (strncmp(pubvar->name, "__pl_", 5) == 0) {
|
2008-03-30 09:00:22 +02:00
|
|
|
pl = (_pl *)pubvar->offs;
|
|
|
|
if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
|
|
|
|
continue;
|
|
|
|
if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
|
|
|
|
continue;
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
|
2008-03-30 09:00:22 +02:00
|
|
|
if (strcmp(pathfile, file) == 0)
|
|
|
|
continue;
|
2015-08-15 04:16:28 +02:00
|
|
|
if (pl->required == false) {
|
2008-03-30 09:00:22 +02:00
|
|
|
IPluginFunction *pFunc;
|
|
|
|
char buffer[64];
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
|
2015-08-15 04:16:28 +02:00
|
|
|
if ((pFunc=pBase->GetFunctionByName(buffer))) {
|
2008-03-30 09:00:22 +02:00
|
|
|
cell_t res;
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
if (pFunc->Execute(&res) != SP_ERROR_NONE) {
|
2008-03-30 09:00:22 +02:00
|
|
|
if (error)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Fatal error during initializing plugin load");
|
2008-03-30 09:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-08-15 04:16:28 +02:00
|
|
|
} else {
|
2008-03-30 09:00:22 +02:00
|
|
|
/* Check that we aren't registering the same library twice */
|
2015-08-15 04:16:28 +02:00
|
|
|
if (pPlugin->m_RequiredLibs.find(name) != pPlugin->m_RequiredLibs.end())
|
2008-03-30 09:00:22 +02:00
|
|
|
continue;
|
2015-08-15 04:16:28 +02:00
|
|
|
|
|
|
|
pPlugin->m_RequiredLibs.push_back(name);
|
|
|
|
|
|
|
|
CPlugin *found;
|
|
|
|
for (auto iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) {
|
|
|
|
CPlugin *pl = (*iter);
|
|
|
|
if (pl->m_Libraries.find(name) != pl->m_Libraries.end()) {
|
|
|
|
found = pl;
|
2008-03-30 09:00:22 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-08-15 04:16:28 +02:00
|
|
|
if (!found) {
|
2008-03-30 09:00:22 +02:00
|
|
|
if (error)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2015-08-15 04:16:28 +02:00
|
|
|
|
|
|
|
found->AddDependent(pPlugin);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
/* Find any extensions this plugin needs */
|
|
|
|
struct _ext
|
|
|
|
{
|
|
|
|
cell_t name;
|
|
|
|
cell_t file;
|
|
|
|
cell_t autoload;
|
|
|
|
cell_t required;
|
|
|
|
} *ext;
|
|
|
|
|
|
|
|
IPluginContext *pBase = pPlugin->GetBaseContext();
|
|
|
|
uint32_t num = pBase->GetPubVarsNum();
|
|
|
|
sp_pubvar_t *pubvar;
|
|
|
|
IExtension *pExt;
|
|
|
|
char path[PLATFORM_MAX_PATH];
|
|
|
|
char *file, *name;
|
|
|
|
for (uint32_t i=0; i<num; i++)
|
|
|
|
{
|
|
|
|
if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strncmp(pubvar->name, "__ext_", 6) == 0)
|
|
|
|
{
|
|
|
|
ext = (_ext *)pubvar->offs;
|
|
|
|
if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (pass == 1)
|
|
|
|
{
|
|
|
|
/* Attempt to auto-load if necessary */
|
|
|
|
if (ext->autoload)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
|
2011-10-12 04:51:24 +02:00
|
|
|
bool bErrorOnMissing = ext->required ? true : false;
|
2013-08-24 06:12:21 +02:00
|
|
|
g_Extensions.LoadAutoExtension(path, bErrorOnMissing);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pass == 2)
|
|
|
|
{
|
|
|
|
/* Is this required? */
|
|
|
|
if (ext->required)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
|
2008-03-30 09:00:22 +02:00
|
|
|
if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
|
|
|
|
{
|
|
|
|
pExt = g_Extensions.FindExtensionByName(name);
|
|
|
|
}
|
|
|
|
/* :TODO: should we bind to unloaded extensions?
|
|
|
|
* Currently the extension manager will ignore this.
|
|
|
|
*/
|
|
|
|
if (!pExt || !pExt->IsRunning(NULL, 0))
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_Extensions.BindChildPlugin(pExt, pPlugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IPluginFunction *pFunc;
|
|
|
|
char buffer[64];
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
|
|
|
|
{
|
|
|
|
cell_t res;
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
if (pFunc->Execute(&res) != SP_ERROR_NONE) {
|
2008-03-30 09:00:22 +02:00
|
|
|
if (error)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Fatal error during plugin initialization (ext req)");
|
2008-03-30 09:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
/* Second pass for extension requirements */
|
|
|
|
if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-26 09:51:11 +02:00
|
|
|
/* Run another binding pass */
|
|
|
|
g_ShareSys.BindNativesToPlugin(pPlugin, false);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2008-05-26 09:51:11 +02:00
|
|
|
/* Find any unbound natives. Right now, these are not allowed. */
|
2008-03-30 09:00:22 +02:00
|
|
|
IPluginContext *pContext = pPlugin->GetBaseContext();
|
|
|
|
uint32_t num = pContext->GetNativesNum();
|
|
|
|
for (unsigned int i=0; i<num; i++)
|
|
|
|
{
|
2015-02-25 07:58:31 +01:00
|
|
|
const sp_native_t *native = pContext->GetRuntime()->GetNative(i);
|
|
|
|
if (!native)
|
2008-03-30 09:00:22 +02:00
|
|
|
break;
|
2015-02-25 07:58:31 +01:00
|
|
|
if (native->status == SP_NATIVE_UNBOUND &&
|
|
|
|
native->name[0] != '@' &&
|
|
|
|
!(native->flags & SP_NTVFLAG_OPTIONAL))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Native \"%s\" was not found", native->name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish by telling all listeners */
|
|
|
|
List<IPluginsListener *>::iterator iter;
|
|
|
|
IPluginsListener *pListener;
|
|
|
|
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
|
|
|
|
{
|
|
|
|
pListener = (*iter);
|
|
|
|
pListener->OnPluginLoaded(pPlugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell this plugin to finish initializing itself */
|
|
|
|
pPlugin->Call_OnPluginStart();
|
|
|
|
|
|
|
|
/* Now, if we have fake natives, go through all plugins that might need rebinding */
|
2013-08-30 19:16:28 +02:00
|
|
|
if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_fakes.length())
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator pl_iter;
|
|
|
|
for (pl_iter = m_plugins.begin();
|
|
|
|
pl_iter != m_plugins.end();
|
|
|
|
pl_iter++)
|
|
|
|
{
|
2013-08-30 19:16:28 +02:00
|
|
|
CPlugin *pOther = (*pl_iter);
|
2008-03-30 09:00:22 +02:00
|
|
|
if ((pOther->GetStatus() == Plugin_Error
|
|
|
|
&& (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
|
|
|
|
|| pOther->m_FakeNativesMissing)
|
|
|
|
{
|
|
|
|
TryRefreshDependencies(pOther);
|
|
|
|
}
|
2008-05-26 09:51:11 +02:00
|
|
|
else if ((pOther->GetStatus() == Plugin_Running
|
|
|
|
|| pOther->GetStatus() == Plugin_Paused)
|
|
|
|
&& pOther != pPlugin)
|
|
|
|
{
|
2015-08-15 03:19:36 +02:00
|
|
|
g_ShareSys.BeginBindingFor(pPlugin);
|
2013-08-30 19:16:28 +02:00
|
|
|
for (size_t i = 0; i < pPlugin->m_fakes.length(); i++)
|
|
|
|
g_ShareSys.BindNativeToPlugin(pOther, pPlugin->m_fakes[i]);
|
2008-05-26 09:51:11 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go through our libraries and tell other plugins they're added */
|
|
|
|
List<String>::iterator s_iter;
|
|
|
|
for (s_iter = pPlugin->m_Libraries.begin();
|
|
|
|
s_iter != pPlugin->m_Libraries.end();
|
|
|
|
s_iter++)
|
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
OnLibraryAction((*s_iter).c_str(), LibraryAction_Added);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2008-05-10 11:23:55 +02:00
|
|
|
/* :TODO: optimize? does this even matter? */
|
|
|
|
pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
|
2014-05-28 04:26:58 +02:00
|
|
|
|
|
|
|
/* Go through all other already loaded plugins and tell this plugin, that their libraries are loaded */
|
|
|
|
for (List<CPlugin *>::iterator pl_iter = m_plugins.begin(); pl_iter != m_plugins.end(); pl_iter++)
|
|
|
|
{
|
|
|
|
CPlugin *pl = (*pl_iter);
|
|
|
|
/* Don't call our own libraries again and only care for already loaded plugins */
|
|
|
|
if (pl == pPlugin || pl->GetStatus() != Plugin_Running)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (s_iter=pl->m_Libraries.begin(); s_iter!=pl->m_Libraries.end(); s_iter++)
|
|
|
|
{
|
|
|
|
pPlugin->Call_OnLibraryAdded((*s_iter).c_str());
|
|
|
|
}
|
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
|
|
|
|
{
|
|
|
|
assert(pPlugin->GetBaseContext() != NULL);
|
|
|
|
|
2008-05-26 09:51:11 +02:00
|
|
|
g_ShareSys.BindNativesToPlugin(pPlugin, false);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2015-08-15 04:16:28 +02:00
|
|
|
for (auto req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++) {
|
|
|
|
CPlugin *found = nullptr;
|
|
|
|
for (auto pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++) {
|
|
|
|
CPlugin *search = (*pl_iter);
|
|
|
|
if (search->m_Libraries.find(*req_iter) != search->m_Libraries.end()) {
|
|
|
|
found = search;
|
|
|
|
break;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
2015-08-15 04:16:28 +02:00
|
|
|
if (!found) {
|
2008-03-30 09:00:22 +02:00
|
|
|
pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
|
|
|
|
return;
|
|
|
|
}
|
2015-08-15 04:16:28 +02:00
|
|
|
|
|
|
|
found->AddDependent(pPlugin);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find any unbound natives
|
|
|
|
* Right now, these are not allowed
|
|
|
|
*/
|
|
|
|
IPluginContext *pContext = pPlugin->GetBaseContext();
|
|
|
|
uint32_t num = pContext->GetNativesNum();
|
|
|
|
for (unsigned int i=0; i<num; i++)
|
|
|
|
{
|
2015-02-25 07:58:31 +01:00
|
|
|
const sp_native_t *native = pContext->GetRuntime()->GetNative(i);
|
|
|
|
if (!native)
|
2008-03-30 09:00:22 +02:00
|
|
|
break;
|
2015-02-25 07:58:31 +01:00
|
|
|
if (native->status == SP_NATIVE_UNBOUND &&
|
|
|
|
native->name[0] != '@' &&
|
|
|
|
!(native->flags & SP_NTVFLAG_OPTIONAL))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pPlugin->GetStatus() == Plugin_Error)
|
|
|
|
{
|
|
|
|
/* If we got here, all natives are okay again! */
|
2008-07-11 10:18:43 +02:00
|
|
|
if (pPlugin->m_pRuntime->IsPaused())
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2014-07-13 13:56:47 +02:00
|
|
|
pPlugin->SetPauseState(false);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::UnloadPlugin(IPlugin *plugin)
|
|
|
|
{
|
2015-08-17 04:10:50 +02:00
|
|
|
CPlugin *pPlugin = (CPlugin *)plugin;
|
|
|
|
|
|
|
|
// If we're already in the unload queue, just wait.
|
|
|
|
if (pPlugin->WaitingToUnload())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return ScheduleUnload(pPlugin);
|
2015-08-15 02:55:58 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2015-08-15 02:55:58 +02:00
|
|
|
bool CPluginManager::ScheduleUnload(CPlugin *pPlugin)
|
|
|
|
{
|
|
|
|
// Should not be recursively removing.
|
|
|
|
assert(m_plugins.find(pPlugin) != m_plugins.end());
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2015-08-15 02:55:58 +02:00
|
|
|
IPluginContext *pContext = pPlugin->GetBaseContext();
|
2015-08-17 04:10:50 +02:00
|
|
|
if (pContext && pContext->IsInExec()) {
|
|
|
|
ke::Lambda<void()> callback = [this, pPlugin]() {
|
|
|
|
this->ScheduleUnload(pPlugin);
|
|
|
|
};
|
|
|
|
pPlugin->SetWaitingToUnload();
|
|
|
|
ScheduleTaskForNextFrame(ke::Move(callback));
|
2008-03-30 09:00:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-15 02:55:58 +02:00
|
|
|
// No need to schedule an unload, we can unload immediately.
|
|
|
|
UnloadPluginImpl(pPlugin);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::UnloadPluginImpl(CPlugin *pPlugin)
|
|
|
|
{
|
2008-03-30 09:00:22 +02:00
|
|
|
/* Remove us from the lookup table and linked list */
|
|
|
|
m_plugins.remove(pPlugin);
|
2013-03-29 19:37:29 +01:00
|
|
|
m_LoadLookup.remove(pPlugin->m_filename);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
/* Go through our libraries and tell other plugins they're gone */
|
|
|
|
List<String>::iterator s_iter;
|
|
|
|
for (s_iter = pPlugin->m_Libraries.begin();
|
|
|
|
s_iter != pPlugin->m_Libraries.end();
|
|
|
|
s_iter++)
|
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
List<IPluginsListener *>::iterator iter;
|
|
|
|
IPluginsListener *pListener;
|
|
|
|
|
2015-04-13 02:25:07 +02:00
|
|
|
if (pPlugin->GetStatus() <= Plugin_Error || pPlugin->GetStatus() == Plugin_Failed)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2012-12-14 21:05:18 +01:00
|
|
|
/* Notify plugin */
|
|
|
|
pPlugin->Call_OnPluginEnd();
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
/* Notify listeners of unloading */
|
|
|
|
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
|
|
|
|
{
|
|
|
|
pListener = (*iter);
|
|
|
|
pListener->OnPluginUnloaded(pPlugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-26 09:51:11 +02:00
|
|
|
pPlugin->DropEverything();
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
|
|
|
|
{
|
|
|
|
/* Notify listeners of destruction */
|
|
|
|
pListener = (*iter);
|
|
|
|
pListener->OnPluginDestroyed(pPlugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell the plugin to delete itself */
|
|
|
|
delete pPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
|
|
|
|
{
|
2008-07-11 10:18:43 +02:00
|
|
|
IPlugin *pPlugin;
|
|
|
|
IPluginContext *pContext;
|
|
|
|
|
|
|
|
pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
|
|
|
|
|
|
|
|
if (pContext->GetKey(2, (void **)&pPlugin))
|
|
|
|
{
|
|
|
|
return pPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
|
|
|
|
{
|
|
|
|
return (CPlugin *)FindPluginByContext(ctx);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int CPluginManager::GetPluginCount()
|
|
|
|
{
|
|
|
|
return m_plugins.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AddPluginsListener(IPluginsListener *listener)
|
|
|
|
{
|
|
|
|
m_listeners.push_back(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
|
|
|
|
{
|
|
|
|
m_listeners.remove(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
IPluginIterator *CPluginManager::GetPluginIterator()
|
|
|
|
{
|
|
|
|
if (m_iters.empty())
|
|
|
|
{
|
|
|
|
return new CPluginIterator(&m_plugins);
|
|
|
|
} else {
|
|
|
|
CPluginIterator *iter = m_iters.front();
|
|
|
|
m_iters.pop();
|
|
|
|
iter->Reset();
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::ReleaseIterator(CPluginIterator *iter)
|
|
|
|
{
|
|
|
|
m_iters.push(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
|
|
|
|
{
|
|
|
|
/* As an optimization, we do not call strlen, but compute the length in the first pass */
|
|
|
|
size_t alias_len = 0;
|
|
|
|
size_t local_len = 0;
|
|
|
|
|
|
|
|
const char *ptr = alias;
|
|
|
|
unsigned int alias_explicit_paths = 0;
|
|
|
|
unsigned int alias_path_end = 0;
|
|
|
|
while (*ptr != '\0')
|
|
|
|
{
|
|
|
|
if (*ptr == '\\' || *ptr == '/')
|
|
|
|
{
|
|
|
|
alias_explicit_paths++;
|
|
|
|
alias_path_end = alias_len;
|
|
|
|
}
|
|
|
|
alias_len++;
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alias_explicit_paths && alias_path_end == alias_len - 1)
|
|
|
|
{
|
|
|
|
/* Trailing slash is totally invalid here */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = localpath;
|
|
|
|
unsigned int local_explicit_paths = 0;
|
|
|
|
unsigned int local_path_end = 0;
|
|
|
|
while (*ptr != '\0')
|
|
|
|
{
|
|
|
|
if (*ptr == '\\' || *ptr == '/')
|
|
|
|
{
|
|
|
|
local_explicit_paths++;
|
|
|
|
local_path_end = local_len;
|
|
|
|
}
|
|
|
|
local_len++;
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the alias has more explicit paths than the real path,
|
|
|
|
* no match will be possible.
|
|
|
|
*/
|
|
|
|
if (alias_explicit_paths > local_explicit_paths)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alias_explicit_paths)
|
|
|
|
{
|
|
|
|
/* We need to find if the paths match now. For example, these should all match:
|
|
|
|
* csdm csdm
|
|
|
|
* csdm optional/csdm
|
|
|
|
* csdm/ban optional/crab/csdm/ban
|
|
|
|
*/
|
|
|
|
const char *aliasptr = alias;
|
|
|
|
const char *localptr = localpath;
|
|
|
|
bool match = true;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (*aliasptr != *localptr)
|
|
|
|
{
|
|
|
|
/* We have to knock one path off */
|
|
|
|
local_explicit_paths--;
|
|
|
|
if (alias_explicit_paths > local_explicit_paths)
|
|
|
|
{
|
|
|
|
/* Skip out if we're gonna have an impossible match */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Eat up localptr tokens until we get a result */
|
|
|
|
while (((localptr - localpath) < (int)local_path_end)
|
|
|
|
&& *localptr != '/'
|
|
|
|
&& *localptr != '\\')
|
|
|
|
{
|
|
|
|
localptr++;
|
|
|
|
}
|
|
|
|
/* Check if we hit the end of our searchable area.
|
|
|
|
* This probably isn't possible because of the path
|
|
|
|
* count check, but it's a good idea anyway.
|
|
|
|
*/
|
|
|
|
if ((localptr - localpath) >= (int)local_path_end)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
/* Consume the slash token */
|
|
|
|
localptr++;
|
|
|
|
}
|
|
|
|
/* Reset the alias pointer so we can continue consuming */
|
|
|
|
aliasptr = alias;
|
|
|
|
match = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Note:
|
|
|
|
* This is safe because if localptr terminates early, aliasptr will too
|
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* We should never reach the end of the string because of this check. */
|
|
|
|
bool aliasend = (aliasptr - alias) > (int)alias_path_end;
|
|
|
|
bool localend = (localptr - localpath) > (int)local_path_end;
|
|
|
|
if (aliasend || localend)
|
|
|
|
{
|
|
|
|
if (aliasend && localend)
|
|
|
|
{
|
|
|
|
/* we matched, and we can break out now */
|
|
|
|
match = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
|
|
|
|
match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we got here, it's safe to compare the next two tokens */
|
|
|
|
if (*localptr != *aliasptr)
|
|
|
|
{
|
|
|
|
match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
localptr++;
|
|
|
|
aliasptr++;
|
|
|
|
} while (true);
|
|
|
|
} while (!match);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we got here, it's time to compare filenames */
|
|
|
|
const char *aliasptr = alias;
|
|
|
|
const char *localptr = localpath;
|
|
|
|
|
|
|
|
if (alias_explicit_paths)
|
|
|
|
{
|
|
|
|
aliasptr = &alias[alias_path_end + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local_explicit_paths)
|
|
|
|
{
|
|
|
|
localptr = &localpath[local_path_end + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (*aliasptr == '*')
|
|
|
|
{
|
|
|
|
/* First, see if this is the last character */
|
|
|
|
if ((unsigned)(aliasptr - alias) == alias_len - 1)
|
|
|
|
{
|
|
|
|
/* If so, there's no need to match anything else */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/* Otherwise, we need to search for an appropriate matching sequence in local.
|
|
|
|
* Note that we only need to search up to the next asterisk.
|
|
|
|
*/
|
|
|
|
aliasptr++;
|
|
|
|
bool match = true;
|
|
|
|
const char *local_orig = localptr;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
match = true;
|
|
|
|
while (*aliasptr != '\0' && *aliasptr != '*')
|
|
|
|
{
|
|
|
|
/* Since aliasptr is never '\0', localptr hitting the end will fail */
|
|
|
|
if (*aliasptr != *localptr)
|
|
|
|
{
|
|
|
|
match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
aliasptr++;
|
|
|
|
localptr++;
|
|
|
|
}
|
|
|
|
if (!match)
|
|
|
|
{
|
|
|
|
/* If we didn't get a match, we need to advance the search stream.
|
|
|
|
* This will let us skip tokens while still searching for another match.
|
|
|
|
*/
|
|
|
|
localptr = ++local_orig;
|
|
|
|
/* Make sure we don't go out of bounds */
|
|
|
|
if (*localptr == '\0')
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (!match);
|
|
|
|
|
|
|
|
if (!match)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
/* If we got a match, move on to the next token */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (*aliasptr == '\0') {
|
|
|
|
if (*localptr == '\0'
|
|
|
|
||
|
|
|
|
strcmp(localptr, ".smx") == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (*aliasptr != *localptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aliasptr++;
|
|
|
|
localptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::IsLateLoadTime() const
|
|
|
|
{
|
2015-08-30 01:23:50 +02:00
|
|
|
return (m_AllPluginsLoaded || !bridge->IsMapLoading());
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::OnSourceModAllInitialized()
|
|
|
|
{
|
|
|
|
m_MyIdent = g_ShareSys.CreateCoreIdentity();
|
|
|
|
|
|
|
|
HandleAccess sec;
|
2013-03-29 19:37:29 +01:00
|
|
|
handlesys->InitAccessDefaults(NULL, &sec);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
|
|
|
|
sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
g_PluginType = handlesys->CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
|
2008-03-30 09:00:22 +02:00
|
|
|
g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
|
|
|
|
|
2015-08-28 04:59:04 +02:00
|
|
|
rootmenu->AddRootConsoleCommand3("plugins", "Manage Plugins", this);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
g_ShareSys.AddInterface(NULL, GetOldAPI());
|
2012-12-14 21:15:12 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
m_pOnLibraryAdded = forwardsys->CreateForward("OnLibraryAdded", ET_Ignore, 1, NULL, Param_String);
|
|
|
|
m_pOnLibraryRemoved = forwardsys->CreateForward("OnLibraryRemoved", ET_Ignore, 1, NULL, Param_String);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::OnSourceModShutdown()
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->RemoveRootConsoleCommand("plugins", this);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
UnloadAll();
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
handlesys->RemoveType(g_PluginType, m_MyIdent);
|
2008-03-30 09:00:22 +02:00
|
|
|
g_ShareSys.DestroyIdentType(g_PluginIdent);
|
|
|
|
g_ShareSys.DestroyIdentity(m_MyIdent);
|
2012-12-14 21:15:12 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
forwardsys->ReleaseForward(m_pOnLibraryAdded);
|
|
|
|
forwardsys->ReleaseForward(m_pOnLibraryRemoved);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2012-08-09 02:54:43 +02:00
|
|
|
ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
|
|
|
|
const char *value,
|
|
|
|
ConfigSource source,
|
|
|
|
char *error,
|
|
|
|
size_t maxlength)
|
|
|
|
{
|
|
|
|
if (strcmp(key, "BlockBadPlugins") == 0) {
|
|
|
|
if (strcasecmp(value, "yes") == 0)
|
|
|
|
{
|
|
|
|
m_bBlockBadPlugins = true;
|
|
|
|
} else if (strcasecmp(value, "no") == 0) {
|
|
|
|
m_bBlockBadPlugins = false;
|
|
|
|
} else {
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
|
2012-08-09 02:54:43 +02:00
|
|
|
return ConfigResult_Reject;
|
|
|
|
}
|
|
|
|
return ConfigResult_Accept;
|
|
|
|
}
|
|
|
|
return ConfigResult_Ignore;
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
|
|
|
|
{
|
|
|
|
/* We don't care about the internal object, actually */
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
|
|
|
{
|
|
|
|
*pSize = ((CPlugin *)object)->CalcMemUsage();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
|
|
|
|
{
|
|
|
|
IPlugin *pPlugin;
|
|
|
|
HandleError _err;
|
|
|
|
|
|
|
|
HandleSecurity sec;
|
|
|
|
|
|
|
|
sec.pOwner = NULL;
|
|
|
|
sec.pIdentity = m_MyIdent;
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
if ((_err=handlesys->ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
pPlugin = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
*err = _err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *CPluginManager::GetPluginByOrder(int num)
|
|
|
|
{
|
|
|
|
if (num < 1 || num > (int)GetPluginCount())
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *pl;
|
|
|
|
int id = 1;
|
|
|
|
|
2008-06-11 10:28:34 +02:00
|
|
|
SourceHook::List<CPlugin *>::iterator iter;
|
|
|
|
for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2008-06-11 10:28:34 +02:00
|
|
|
pl = *iter;
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *CPluginManager::GetStatusText(PluginStatus st)
|
|
|
|
{
|
|
|
|
switch (st)
|
|
|
|
{
|
|
|
|
case Plugin_Running:
|
|
|
|
return "Running";
|
|
|
|
case Plugin_Paused:
|
|
|
|
return "Paused";
|
|
|
|
case Plugin_Error:
|
|
|
|
return "Error";
|
|
|
|
case Plugin_Uncompiled:
|
|
|
|
return "Uncompiled";
|
|
|
|
case Plugin_BadLoad:
|
|
|
|
return "Bad Load";
|
|
|
|
case Plugin_Failed:
|
|
|
|
return "Failed";
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return "-";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
static inline bool IS_STR_FILLED(const char *text)
|
|
|
|
{
|
|
|
|
return text[0] != '\0';
|
|
|
|
}
|
|
|
|
|
2015-08-28 04:59:04 +02:00
|
|
|
void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2015-08-28 04:59:04 +02:00
|
|
|
int argcount = command->ArgC();
|
2008-03-30 09:00:22 +02:00
|
|
|
if (argcount >= 3)
|
|
|
|
{
|
2015-08-28 04:59:04 +02:00
|
|
|
const char *cmd = command->Arg(2);
|
2008-03-30 09:00:22 +02:00
|
|
|
if (strcmp(cmd, "list") == 0)
|
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
unsigned int id = 1;
|
|
|
|
int plnum = GetPluginCount();
|
|
|
|
|
|
|
|
if (!plnum)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] No plugins loaded");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2008-06-02 07:03:27 +02:00
|
|
|
CPlugin *pl;
|
|
|
|
SourceHook::List<CPlugin *>::iterator iter;
|
|
|
|
SourceHook::List<CPlugin *> m_FailList;
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2008-06-11 10:28:34 +02:00
|
|
|
for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2008-06-02 07:03:27 +02:00
|
|
|
pl = (*iter);
|
2008-03-30 09:00:22 +02:00
|
|
|
assert(pl->GetStatus() != Plugin_Created);
|
|
|
|
int len = 0;
|
|
|
|
const sm_plugininfo_t *info = pl->GetPublicInfo();
|
2009-03-19 03:20:40 +01:00
|
|
|
if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
len += ke::SafeSprintf(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
if (pl->GetStatus() <= Plugin_Error)
|
|
|
|
{
|
|
|
|
/* Plugin has failed to load. */
|
|
|
|
m_FailList.push_back(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
len += ke::SafeSprintf(buffer, sizeof(buffer), " %02d", id);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2008-10-06 00:20:17 +02:00
|
|
|
if (pl->GetStatus() < Plugin_Created)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-03-19 03:20:40 +01:00
|
|
|
if (pl->IsSilentlyFailed())
|
2015-08-26 05:58:43 +02:00
|
|
|
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " Disabled:");
|
|
|
|
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
|
2008-10-06 00:20:17 +02:00
|
|
|
if (IS_STR_FILLED(info->version))
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
|
2008-10-06 00:20:17 +02:00
|
|
|
}
|
|
|
|
if (IS_STR_FILLED(info->author))
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
|
2008-10-06 00:20:17 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2008-10-06 00:20:17 +02:00
|
|
|
else
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("%s", buffer);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_FailList.empty())
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("Load Errors:");
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2008-06-02 07:03:27 +02:00
|
|
|
SourceHook::List<CPlugin *>::iterator _iter;
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
CPlugin *pl;
|
|
|
|
|
|
|
|
for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
|
|
|
|
{
|
|
|
|
pl = (CPlugin *)*_iter;
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "load") == 0)
|
|
|
|
{
|
|
|
|
if (argcount < 4)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Usage: sm plugins load <file>");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char error[128];
|
|
|
|
bool wasloaded;
|
2015-08-28 04:59:04 +02:00
|
|
|
const char *filename = command->Arg(3);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
char pluginfile[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
const char *ext = libsys->GetFileExtension(filename) ? "" : ".smx";
|
|
|
|
g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
|
|
|
|
|
|
|
|
if (wasloaded)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "unload") == 0)
|
|
|
|
{
|
|
|
|
if (argcount < 4)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *pl;
|
|
|
|
char *end;
|
2015-08-28 04:59:04 +02:00
|
|
|
const char *arg = command->Arg(3);
|
2008-03-30 09:00:22 +02:00
|
|
|
int id = strtol(arg, &end, 10);
|
|
|
|
|
|
|
|
if (*end == '\0')
|
|
|
|
{
|
|
|
|
pl = GetPluginByOrder(id);
|
|
|
|
if (!pl)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char pluginfile[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
|
|
|
|
g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (!m_LoadLookup.retrieve(pluginfile, &pl))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char name[PLATFORM_MAX_PATH];
|
2008-10-06 00:20:17 +02:00
|
|
|
|
|
|
|
if (pl->GetStatus() < Plugin_Created)
|
|
|
|
{
|
|
|
|
const sm_plugininfo_t *info = pl->GetPublicInfo();
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
|
2008-10-06 00:20:17 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(name, sizeof(name), "%s", pl->GetFilename());
|
2008-10-06 00:20:17 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
if (UnloadPlugin(pl))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Failed to unload plugin %s.", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "unload_all") == 0)
|
|
|
|
{
|
|
|
|
g_PluginSys.UnloadAll();
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] All plugins have been unloaded.");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "load_lock") == 0)
|
|
|
|
{
|
|
|
|
if (m_LoadingLocked)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] There is already a loading lock in effect.");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_LoadingLocked = true;
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "load_unlock") == 0)
|
|
|
|
{
|
|
|
|
if (m_LoadingLocked)
|
|
|
|
{
|
|
|
|
m_LoadingLocked = false;
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] The loading lock is no longer in effect.");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] There was no loading lock in effect.");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "info") == 0)
|
|
|
|
{
|
|
|
|
if (argcount < 4)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Usage: sm plugins info <#>");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *pl;
|
|
|
|
char *end;
|
2015-08-28 04:59:04 +02:00
|
|
|
const char *arg = command->Arg(3);
|
2008-03-30 09:00:22 +02:00
|
|
|
int id = strtol(arg, &end, 10);
|
|
|
|
|
|
|
|
if (*end == '\0')
|
|
|
|
{
|
|
|
|
pl = GetPluginByOrder(id);
|
|
|
|
if (!pl)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char pluginfile[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
|
|
|
|
g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (!m_LoadLookup.retrieve(pluginfile, &pl))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const sm_plugininfo_t *info = pl->GetPublicInfo();
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Filename: %s", pl->GetFilename());
|
2014-10-29 17:13:43 +01:00
|
|
|
if (pl->GetStatus() <= Plugin_Error || pl->GetStatus() == Plugin_Failed)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
if (IS_STR_FILLED(info->name))
|
|
|
|
{
|
|
|
|
if (IS_STR_FILLED(info->description))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Title: %s (%s)", info->name, info->description);
|
2008-03-30 09:00:22 +02:00
|
|
|
} else {
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Title: %s", info->name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (IS_STR_FILLED(info->author))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Author: %s", info->author);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
if (IS_STR_FILLED(info->version))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Version: %s", info->version);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
if (IS_STR_FILLED(info->url))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" URL: %s", info->url);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2014-10-29 17:13:43 +01:00
|
|
|
if (pl->GetStatus() == Plugin_Error || pl->GetStatus() == Plugin_Failed)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Error: %s", pl->m_errormsg);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-06-02 07:03:27 +02:00
|
|
|
if (pl->GetStatus() == Plugin_Running)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Status: running");
|
2008-06-02 07:03:27 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Status: not running");
|
2008-06-02 07:03:27 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
2008-06-02 07:03:27 +02:00
|
|
|
if (pl->m_FileVersion >= 3)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Timestamp: %s", pl->m_DateTime);
|
2008-06-02 07:03:27 +02:00
|
|
|
}
|
2012-08-09 02:54:43 +02:00
|
|
|
|
|
|
|
unsigned char *pCodeHash = pl->m_pRuntime->GetCodeHash();
|
|
|
|
unsigned char *pDataHash = pl->m_pRuntime->GetDataHash();
|
|
|
|
|
|
|
|
char combinedHash[33];
|
|
|
|
for (int i = 0; i < 16; i++)
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]);
|
2012-08-09 02:54:43 +02:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Hash: %s", combinedHash);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" Load error: %s", pl->m_errormsg);
|
2008-10-06 00:20:17 +02:00
|
|
|
if (pl->GetStatus() < Plugin_Created)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
|
2008-10-06 00:20:17 +02:00
|
|
|
info->name ? info->name : "<none>",
|
|
|
|
info->version ? info->version : "<none>");
|
|
|
|
if (IS_STR_FILLED(info->url))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint(" File URL: %s", info->url);
|
2008-10-06 00:20:17 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "refresh") == 0)
|
|
|
|
{
|
2015-01-31 21:28:21 +01:00
|
|
|
RefreshAll();
|
2015-08-30 01:23:50 +02:00
|
|
|
bridge->DoGlobalPluginLoads();
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (strcmp(cmd, "reload") == 0)
|
|
|
|
{
|
|
|
|
if (argcount < 4)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *pl;
|
|
|
|
char *end;
|
2015-08-28 04:59:04 +02:00
|
|
|
const char *arg = command->Arg(3);
|
2008-03-30 09:00:22 +02:00
|
|
|
int id = strtol(arg, &end, 10);
|
|
|
|
|
|
|
|
if (*end == '\0')
|
|
|
|
{
|
|
|
|
pl = GetPluginByOrder(id);
|
|
|
|
if (!pl)
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char pluginfile[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
|
|
|
|
g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (!m_LoadLookup.retrieve(pluginfile, &pl))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
|
2008-03-30 09:00:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char name[PLATFORM_MAX_PATH];
|
|
|
|
const sm_plugininfo_t *info = pl->GetPublicInfo();
|
2009-12-20 03:25:56 +01:00
|
|
|
if (pl->GetStatus() <= Plugin_Paused)
|
2009-12-20 03:19:28 +01:00
|
|
|
strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
|
|
|
|
else
|
|
|
|
strcpy(name, pl->GetFilename());
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
if (ReloadPlugin(pl))
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("[SM] Failed to reload plugin %s.", name);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw the main menu */
|
2013-03-29 19:37:29 +01:00
|
|
|
rootmenu->ConsolePrint("SourceMod Plugins Menu:");
|
|
|
|
rootmenu->DrawGenericOption("info", "Information about a plugin");
|
|
|
|
rootmenu->DrawGenericOption("list", "Show loaded plugins");
|
|
|
|
rootmenu->DrawGenericOption("load", "Load a plugin");
|
|
|
|
rootmenu->DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
|
|
|
|
rootmenu->DrawGenericOption("load_unlock", "Re-enables plugin loading");
|
|
|
|
rootmenu->DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
|
|
|
|
rootmenu->DrawGenericOption("reload", "Reloads a plugin");
|
|
|
|
rootmenu->DrawGenericOption("unload", "Unload a plugin");
|
|
|
|
rootmenu->DrawGenericOption("unload_all", "Unloads all plugins");
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::ReloadPlugin(CPlugin *pl)
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
char filename[PLATFORM_MAX_PATH];
|
2008-08-15 07:22:26 +02:00
|
|
|
bool wasloaded;
|
2008-03-30 09:00:22 +02:00
|
|
|
PluginType ptype;
|
|
|
|
IPlugin *newpl;
|
|
|
|
int id = 1;
|
|
|
|
|
|
|
|
strcpy(filename, pl->m_filename);
|
|
|
|
ptype = pl->GetType();
|
|
|
|
|
|
|
|
for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
|
|
|
|
{
|
|
|
|
if ((*iter) == pl)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!UnloadPlugin(pl))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-15 07:22:26 +02:00
|
|
|
if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
|
|
|
|
{
|
|
|
|
if ((*iter) == (CPlugin *)newpl)
|
|
|
|
{
|
|
|
|
m_plugins.erase(iter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
m_plugins.insert(iter, (CPlugin *)newpl);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
void CPluginManager::RefreshAll()
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
/* If we're in a load lock, just skip this whole bit. */
|
|
|
|
if (m_LoadingLocked)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<CPlugin *> tmp_list = m_plugins;
|
2015-09-16 10:22:12 +02:00
|
|
|
for (auto iter = tmp_list.begin(); iter != tmp_list.end(); iter++)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2015-09-16 10:22:12 +02:00
|
|
|
CPlugin *pl = (*iter);
|
|
|
|
time_t t = pl->GetFileTimeStamp();
|
|
|
|
if (!t || t > pl->GetTimeStamp()) {
|
|
|
|
pl->SetTimeStamp(t);
|
|
|
|
UnloadPlugin(pl);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
|
|
|
|
{
|
|
|
|
List<IPluginsListener *>::iterator iter;
|
|
|
|
IPluginsListener *pListener;
|
|
|
|
for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
|
|
|
|
{
|
|
|
|
pListener = (*iter);
|
|
|
|
pListener->OnPluginPauseChange(pl, paused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
CPlugin *pPlugin;
|
|
|
|
IPluginFunction *pFunction;
|
|
|
|
|
|
|
|
for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
|
|
|
|
{
|
|
|
|
pPlugin = (*iter);
|
|
|
|
|
|
|
|
if (pPlugin->GetStatus() <= Plugin_Paused)
|
|
|
|
{
|
|
|
|
pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
|
|
|
|
|
|
|
|
if (pFunction)
|
|
|
|
{
|
|
|
|
pForward->AddFunction(pFunction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
|
|
|
|
{
|
|
|
|
if (pToken->type != g_PluginIdent)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (CPlugin *)(pToken->ptr);
|
|
|
|
}
|
|
|
|
|
2012-12-14 21:15:12 +01:00
|
|
|
void CPluginManager::OnLibraryAction(const char *lib, LibraryAction action)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
switch (action)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2012-12-14 21:15:12 +01:00
|
|
|
case LibraryAction_Removed:
|
|
|
|
m_pOnLibraryRemoved->PushString(lib);
|
|
|
|
m_pOnLibraryRemoved->Execute(NULL);
|
|
|
|
break;
|
|
|
|
case LibraryAction_Added:
|
|
|
|
m_pOnLibraryAdded->PushString(lib);
|
|
|
|
m_pOnLibraryAdded->Execute(NULL);
|
|
|
|
break;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::LibraryExists(const char *lib)
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
|
|
|
|
for (iter=m_plugins.begin();
|
|
|
|
iter!=m_plugins.end();
|
|
|
|
iter++)
|
|
|
|
{
|
|
|
|
CPlugin *pl = (*iter);
|
|
|
|
if (pl->GetStatus() != Plugin_Running)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
List<String>::iterator s_iter;
|
|
|
|
for (s_iter = pl->m_Libraries.begin();
|
|
|
|
s_iter != pl->m_Libraries.end();
|
|
|
|
s_iter++)
|
|
|
|
{
|
|
|
|
if ((*s_iter).compare(lib) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AllPluginsLoaded()
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
CPlugin *pl;
|
|
|
|
|
|
|
|
for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
|
|
|
|
{
|
|
|
|
pl = (*iter);
|
|
|
|
pl->Call_OnAllPluginsLoaded();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::UnloadAll()
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
while ( (iter = m_plugins.begin()) != m_plugins.end() )
|
|
|
|
{
|
|
|
|
UnloadPlugin((*iter));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
|
|
|
|
{
|
|
|
|
int id = 1;
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
|
|
|
|
for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
|
|
|
|
{
|
|
|
|
if ((*iter) == pl)
|
|
|
|
{
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
SMPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
|
|
|
int id;
|
|
|
|
char *end;
|
|
|
|
CPlugin *pl;
|
|
|
|
|
|
|
|
id = strtol(arg, &end, 10);
|
|
|
|
|
|
|
|
if (*end == '\0')
|
|
|
|
{
|
|
|
|
pl = GetPluginByOrder(id);
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char pluginfile[256];
|
2013-03-29 19:37:29 +01:00
|
|
|
const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
|
2015-08-26 05:58:43 +02:00
|
|
|
ke::SafeSprintf(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
2013-08-25 20:59:46 +02:00
|
|
|
if (!m_LoadLookup.retrieve(pluginfile, &pl))
|
2008-03-30 09:00:22 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pl;
|
|
|
|
}
|
2008-10-06 00:20:17 +02:00
|
|
|
|
|
|
|
void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue)
|
|
|
|
{
|
|
|
|
SyncMaxClients(newvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::SyncMaxClients(int max_clients)
|
|
|
|
{
|
|
|
|
List<CPlugin *>::iterator iter;
|
|
|
|
|
|
|
|
for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
|
|
|
|
{
|
|
|
|
(*iter)->SyncMaxClients(max_clients);
|
|
|
|
}
|
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-31 22:30:22 +02:00
|
|
|
const CVector<SMPlugin *> *CPluginManager::ListPlugins()
|
2009-02-01 08:33:31 +01:00
|
|
|
{
|
2013-03-31 22:30:22 +02:00
|
|
|
CVector<SMPlugin *> *list = new CVector<SMPlugin *>();
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-31 22:30:22 +02:00
|
|
|
for (List<CPlugin *>::iterator iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
|
2013-03-29 19:37:29 +01:00
|
|
|
list->push_back((*iter));
|
2013-03-31 22:30:22 +02:00
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::FreePluginList(const CVector<SMPlugin *> *list)
|
|
|
|
{
|
|
|
|
delete const_cast<CVector<SMPlugin *> *>(list);
|
2013-03-29 19:37:29 +01:00
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
class OldPluginAPI : public IPluginManager
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
IPlugin *LoadPlugin(const char *path,
|
|
|
|
bool debug,
|
|
|
|
PluginType type,
|
|
|
|
char error[],
|
|
|
|
size_t maxlength,
|
|
|
|
bool *wasloaded)
|
2009-02-01 08:33:31 +01:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
return g_PluginSys.LoadPlugin(path, debug, type, error, maxlength, wasloaded);
|
2009-02-01 08:33:31 +01:00
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
bool UnloadPlugin(IPlugin *plugin)
|
2009-02-01 08:33:31 +01:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
return g_PluginSys.UnloadPlugin(plugin);
|
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
IPlugin *FindPluginByContext(const sp_context_t *ctx)
|
|
|
|
{
|
|
|
|
return g_PluginSys.FindPluginByContext(ctx);
|
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
unsigned int GetPluginCount()
|
|
|
|
{
|
|
|
|
return g_PluginSys.GetPluginCount();
|
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
IPluginIterator *GetPluginIterator()
|
|
|
|
{
|
|
|
|
return g_PluginSys.GetPluginIterator();
|
|
|
|
}
|
2009-02-01 08:33:31 +01:00
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
void AddPluginsListener(IPluginsListener *listener)
|
|
|
|
{
|
|
|
|
g_PluginSys.AddPluginsListener(listener);
|
2009-02-01 08:33:31 +01:00
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
void RemovePluginsListener(IPluginsListener *listener)
|
2009-02-01 08:33:31 +01:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
g_PluginSys.RemovePluginsListener(listener);
|
2009-02-01 08:33:31 +01:00
|
|
|
}
|
|
|
|
|
2013-03-29 19:37:29 +01:00
|
|
|
IPlugin *PluginFromHandle(Handle_t handle, HandleError *err)
|
2009-02-01 08:33:31 +01:00
|
|
|
{
|
2013-03-29 19:37:29 +01:00
|
|
|
return g_PluginSys.PluginFromHandle(handle, err);
|
2009-02-01 08:33:31 +01:00
|
|
|
}
|
2013-03-29 19:37:29 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static OldPluginAPI sOldPluginAPI;
|
|
|
|
|
|
|
|
IPluginManager *CPluginManager::GetOldAPI()
|
|
|
|
{
|
|
|
|
return &sOldPluginAPI;
|
2009-02-01 08:33:31 +01:00
|
|
|
}
|