!Added MOSTLY UNTESTED plugin loading

Fixed a match bug in TestAliasMatch()
Removed pointless implementation of context switching in CPlugin
Redesigned how CPlugins are allocated, deallocated, and instantiated.
Added a basedir function so all code can reference relative paths.  This may be redesigned.
Various other changes

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40214
This commit is contained in:
David Anderson 2006-12-13 11:16:20 +00:00
parent dc60ae49de
commit 23a91de75c
8 changed files with 491 additions and 152 deletions

View File

@ -33,8 +33,8 @@ namespace SourceMod
Plugin_Running=0, /* Plugin is running */ Plugin_Running=0, /* Plugin is running */
Plugin_Loaded, /* Plugin is loaded but not initialized */ Plugin_Loaded, /* Plugin is loaded but not initialized */
Plugin_Paused, /* Plugin is paused */ Plugin_Paused, /* Plugin is paused */
Plugin_Stopped, /* Plugin is paused for map changes, too */
Plugin_Error, /* Plugin has a blocking error */ Plugin_Error, /* Plugin has a blocking error */
Plugin_Uncompiled, /* Plugin is not yet compiled by the JIT */
Plugin_BadLoad, /* Plugin failed to load */ Plugin_BadLoad, /* Plugin failed to load */
}; };

View File

@ -4,6 +4,8 @@
#include "sourcemod.h" #include "sourcemod.h"
SourceMod_Core g_SourceMod_Core; SourceMod_Core g_SourceMod_Core;
IVEngineServer *engine = NULL;
IServerGameDLL *gamedll = NULL;
PLUGIN_EXPOSE(SourceMod, g_SourceMod_Core); PLUGIN_EXPOSE(SourceMod, g_SourceMod_Core);
@ -11,6 +13,8 @@ bool SourceMod_Core::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen
{ {
PLUGIN_SAVEVARS(); PLUGIN_SAVEVARS();
GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
return g_SourceMod.InitializeSourceMod(error, maxlen, late); return g_SourceMod.InitializeSourceMod(error, maxlen, late);
} }

View File

@ -2,6 +2,7 @@
#define _INCLUDE_SOURCEMOD_MM_API_H_ #define _INCLUDE_SOURCEMOD_MM_API_H_
#include <ISmmPlugin.h> #include <ISmmPlugin.h>
#include <eiface.h>
/** /**
* @file Contains wrappers around required Metamod:Source API exports * @file Contains wrappers around required Metamod:Source API exports
@ -27,6 +28,8 @@ public:
}; };
extern SourceMod_Core g_SourceMod_Core; extern SourceMod_Core g_SourceMod_Core;
extern IVEngineServer *engine;
extern IServerGameDLL *gamedll;
PLUGIN_GLOBALVARS(); PLUGIN_GLOBALVARS();

View File

@ -7,6 +7,8 @@
#include "PluginSys.h" #include "PluginSys.h"
#include "ForwardSys.h" #include "ForwardSys.h"
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
SourcePawnEngine g_SourcePawn; SourcePawnEngine g_SourcePawn;
SourceModBase g_SourceMod; SourceModBase g_SourceMod;
@ -31,16 +33,21 @@ void ShutdownJIT()
g_pJIT->CloseLibrary(); g_pJIT->CloseLibrary();
} }
SourceModBase::SourceModBase()
{
m_IsMapLoading = false;
}
bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late)
{ {
//:TODO: we need a localinfo system!
g_BaseDir.assign(g_SMAPI->GetBaseDir()); g_BaseDir.assign(g_SMAPI->GetBaseDir());
g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/addons/sourcemod", g_BaseDir.c_str());
/* Attempt to load the JIT! */ /* Attempt to load the JIT! */
char file[PLATFORM_MAX_PATH]; char file[PLATFORM_MAX_PATH];
char myerror[255]; char myerror[255];
g_SMAPI->PathFormat(file, sizeof(file), "%s/addons/sourcemod/bin/sourcepawn.jit.x86.%s", g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s",
g_BaseDir.c_str(), GetSMBaseDir(),
PLATFORM_LIB_EXT PLATFORM_LIB_EXT
); );
@ -112,21 +119,49 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late)
return false; return false;
} }
g_SMAPI->PathFormat(file, sizeof(file), "%s/addons/sourcemod/plugins/test.smx", g_BaseDir.c_str()); StartSourceMod(late);
IPlugin *pPlugin = g_PluginMngr.LoadPlugin(file, false, PluginType_Global, error, err_max);
IPluginFunction *func = pPlugin->GetFunctionByName("Test");
IPluginFunction *func2 = pPlugin->GetFunctionByName("Test2");
cell_t result = 2;
cell_t val = 6;
ParamType types[] = {Param_Cell, Param_CellByRef};
va_list ap = va_start(ap, late);
CForward *fwd = CForward::CreateForward(NULL, ET_Custom, 2, types, ap);
fwd->AddFunction(func);
fwd->AddFunction(func2);
fwd->PushCell(1);
fwd->PushCellByRef(&val, 0);
fwd->Execute(&result, NULL);
g_PluginMngr.UnloadPlugin(pPlugin);
return true; return true;
} }
void SourceModBase::StartSourceMod(bool late)
{
/* First initialize the global hooks we need */
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
/* If we're late, automatically load plugins now */
DoGlobalPluginLoads();
}
bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
{
m_IsMapLoading = true;
DoGlobalPluginLoads();
m_IsMapLoading = false;
RETURN_META_VALUE(MRES_IGNORED, true);
}
void SourceModBase::DoGlobalPluginLoads()
{
char config_path[PLATFORM_MAX_PATH];
char plugins_path[PLATFORM_MAX_PATH];
g_SMAPI->PathFormat(config_path,
sizeof(config_path),
"%s/configs/plugin_settings.cfg",
GetSMBaseDir());
g_SMAPI->PathFormat(plugins_path,
sizeof(plugins_path),
"%s/plugins",
GetSMBaseDir());
g_PluginMngr.RefreshOrLoadPlugins(config_path, plugins_path);
}
const char *SourceModBase::GetSMBaseDir()
{
return m_SMBaseDir;
}

View File

@ -9,11 +9,42 @@
class SourceModBase class SourceModBase
{ {
public:
SourceModBase();
public: public:
/** /**
* @brief Initializes SourceMod, or returns an error on failure. * @brief Initializes SourceMod, or returns an error on failure.
*/ */
bool InitializeSourceMod(char *error, size_t err_max, bool late); bool InitializeSourceMod(char *error, size_t err_max, bool late);
/**
* @brief Starts everything SourceMod needs to run
*/
void StartSourceMod(bool late);
/**
* @brief Map change hook
*/
bool LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background);
/**
* @brief Returns whether or not a mapload is in progress
*/
bool IsMapLoading();
/**
* @brief Returns the base SourceMod folder.
*/
const char *GetSMBaseDir();
private:
/**
* @brief Loading plugins
*/
void DoGlobalPluginLoads();
private:
char m_SMBaseDir[PLATFORM_MAX_PATH+1];
bool m_IsMapLoading;
}; };
extern SourceModBase g_SourceMod; extern SourceModBase g_SourceMod;

View File

@ -15,7 +15,7 @@ void CFunction::Set(funcid_t funcid, CPlugin *plugin)
int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
{ {
IPluginContext *ctx = m_pPlugin->m_ctx_current.base; IPluginContext *ctx = m_pPlugin->m_ctx.base;
while (num_params--) while (num_params--)
{ {
@ -79,7 +79,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr
return SetError(SP_ERROR_PARAMS_MAX); return SetError(SP_ERROR_PARAMS_MAX);
} }
IPluginContext *ctx = m_pPlugin->m_ctx_current.base; IPluginContext *ctx = m_pPlugin->m_ctx.base;
ParamInfo *info = &m_info[m_curparam]; ParamInfo *info = &m_info[m_curparam];
int err; int err;
@ -127,7 +127,7 @@ int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_
return SetError(SP_ERROR_PARAMS_MAX); return SetError(SP_ERROR_PARAMS_MAX);
} }
IPluginContext *base = m_pPlugin->m_ctx_current.base; IPluginContext *base = m_pPlugin->m_ctx.base;
ParamInfo *info = &m_info[m_curparam]; ParamInfo *info = &m_info[m_curparam];
size_t cells = (len + sizeof(cell_t) - 1) / sizeof(cell_t); size_t cells = (len + sizeof(cell_t) - 1) / sizeof(cell_t);
int err; int err;
@ -174,7 +174,7 @@ void CFunction::Cancel()
return; return;
} }
IPluginContext *base = m_pPlugin->m_ctx_current.base; IPluginContext *base = m_pPlugin->m_ctx.base;
while (m_curparam--) while (m_curparam--)
{ {
@ -217,7 +217,7 @@ int CFunction::Execute(cell_t *result)
docopies = false; docopies = false;
} }
IPluginContext *base = m_pPlugin->m_ctx_current.base; IPluginContext *base = m_pPlugin->m_ctx.base;
while (numparams--) while (numparams--)
{ {

View File

@ -2,6 +2,7 @@
#include "PluginSys.h" #include "PluginSys.h"
#include "LibrarySys.h" #include "LibrarySys.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "sourcemod.h"
#include "CTextParsers.h" #include "CTextParsers.h"
CPluginManager g_PluginMngr; CPluginManager g_PluginMngr;
@ -10,86 +11,204 @@ CPluginManager::CPluginManager()
{ {
} }
CPlugin *CPlugin::CreatePlugin(const char *file, CPlugin::CPlugin(const char *file)
bool debug_default,
PluginType type,
char *error,
size_t maxlen)
{ {
static unsigned int MySerial = 0; static int MySerial = 0;
FILE *fp = fopen(file, "rb");
m_type = PluginType_Private;
m_status = Plugin_Uncompiled;
m_serial = ++MySerial;
m_plugin = NULL;
m_funcsnum = 0;
m_priv_funcs = NULL;
m_pub_funcs = NULL;
m_errormsg[256] = '\0';
snprintf(m_filename, sizeof(m_filename), "%s", file);
}
CPlugin::~CPlugin()
{
if (m_ctx.base)
{
g_pSourcePawn->FreeBaseContext(m_ctx.base);
m_ctx.base = NULL;
}
if (m_ctx.ctx)
{
m_ctx.vm->FreeContext(m_ctx.ctx);
m_ctx.ctx = NULL;
}
if (m_ctx.co)
{
m_ctx.vm->AbortCompilation(m_ctx.co);
m_ctx.co = NULL;
}
if (m_plugin)
{
g_pSourcePawn->FreeFromMemory(m_plugin);
m_plugin = NULL;
}
if (m_pub_funcs)
{
for (uint32_t i=0; i<m_plugin->info.publics_num; i++)
{
g_PluginMngr.ReleaseFunctionToPool(m_pub_funcs[i]);
}
delete [] m_pub_funcs;
m_pub_funcs = NULL;
}
if (m_priv_funcs)
{
for (unsigned int i=0; i<m_funcsnum; i++)
{
g_PluginMngr.ReleaseFunctionToPool(m_priv_funcs[i]);
}
delete [] m_priv_funcs;
m_priv_funcs = NULL;
}
}
CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
{
char fullpath[PLATFORM_MAX_PATH+1];
g_LibSys.PathFormat(fullpath, sizeof(fullpath), "%s/plugins/%s", g_SourceMod.GetSMBaseDir(), file);
FILE *fp = fopen(fullpath, "rb");
if (!fp) if (!fp)
{ {
snprintf(error, maxlen, "Could not open file"); if (error)
return NULL; {
snprintf(error, maxlength, "Unable to open file");
return NULL;
} else {
CPlugin *pPlugin = new CPlugin(file);
snprintf(pPlugin->m_errormsg, sizeof(pPlugin->m_errormsg), "Unable to open file");
pPlugin->m_status = Plugin_BadLoad;
return pPlugin;
}
} }
int err; int err;
sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err); sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
if (pl == NULL) if (pl == NULL)
{ {
snprintf(error, maxlen, "Could not load plugin, error %d", err); fclose(fp);
return NULL; if (error)
{
snprintf(error, maxlength, "Error %d while parsing plugin", err);
return NULL;
} else {
CPlugin *pPlugin = new CPlugin(file);
snprintf(pPlugin->m_errormsg, sizeof(pPlugin->m_errormsg), "Error %d while parsing plugin", err);
pPlugin->m_status = Plugin_BadLoad;
return pPlugin;
}
} }
fclose(fp); fclose(fp);
ICompilation *co = g_pVM->StartCompilation(pl); CPlugin *pPlugin = new CPlugin(file);
pPlugin->m_plugin = pl;
return pPlugin;
}
if (debug_default) ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
{
if (!m_plugin)
{ {
if (!g_pVM->SetCompilationOption(co, "debug", "1"))
{
g_pVM->AbortCompilation(co);
snprintf(error, maxlen, "Could not set plugin to debug mode");
return NULL;
}
}
sp_context_t *ctx = g_pVM->CompileToContext(co, &err);
if (ctx == NULL)
{
snprintf(error, maxlen, "Plugin failed to load, JIT error: %d", err);
return NULL; return NULL;
} }
IPluginContext *base = g_pSourcePawn->CreateBaseContext(ctx); /* :NOTICE: We will eventually need to change these natives
CPlugin *pPlugin = new CPlugin; * for swapping in new contexts
*/
snprintf(pPlugin->m_filename, PLATFORM_MAX_PATH, "%s", file); if (m_ctx.co || m_ctx.ctx)
pPlugin->m_debugging = debug_default;
pPlugin->m_ctx_current.base = base;
pPlugin->m_ctx_current.ctx = ctx;
pPlugin->m_type = type;
pPlugin->m_serial = ++MySerial;
pPlugin->m_status = Plugin_Loaded;
pPlugin->m_plugin = pl;
pPlugin->UpdateInfo();
ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)(IPlugin *)pPlugin;
/* Build function information loosely */
pPlugin->m_funcsnum = g_pVM->FunctionCount(ctx);
if (pPlugin->m_funcsnum)
{ {
pPlugin->m_priv_funcs = new CFunction *[pPlugin->m_funcsnum]; return NULL;
memset(pPlugin->m_priv_funcs, 0, sizeof(CFunction *) * pPlugin->m_funcsnum);
} else {
pPlugin->m_priv_funcs = NULL;
} }
if (pl->info.publics_num) m_status = Plugin_Uncompiled;
m_ctx.vm = vm;
m_ctx.co = vm->StartCompilation(m_plugin);
return m_ctx.co;
}
void CPlugin::CancelMyCompile()
{
if (!m_ctx.co)
{ {
pPlugin->m_pub_funcs = new CFunction *[pl->info.publics_num]; return;
memset(pPlugin->m_pub_funcs, 0, sizeof(CFunction *) * pl->info.publics_num);
} else {
pPlugin->m_pub_funcs = NULL;
} }
return pPlugin; m_ctx.vm->AbortCompilation(m_ctx.co);
m_ctx.co = NULL;
m_ctx.vm = NULL;
}
bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
{
if (!m_ctx.co)
{
return false;
}
int err;
m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
if (!m_ctx.ctx)
{
memset(&m_ctx, 0, sizeof(m_ctx));
if (!error)
{
SetErrorState(Plugin_Error, "Failed to compile (error %d)", err);
} else {
snprintf(error, maxlength, "Failed to compile (error %d)", err);
}
return false;
}
m_ctx.base = g_pSourcePawn->CreateBaseContext(m_ctx.ctx);
m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
m_funcsnum = m_ctx.vm->FunctionCount(m_ctx.ctx);
/**
* Note: Since the m_plugin member will never change,
* it is safe to assume the function count will never change
*/
if (m_funcsnum && m_priv_funcs == NULL)
{
m_priv_funcs = new CFunction *[m_funcsnum];
memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
} else {
m_priv_funcs = NULL;
}
if (m_plugin->info.publics_num && m_pub_funcs == NULL)
{
m_pub_funcs = new CFunction *[m_plugin->info.publics_num];
memset(m_pub_funcs, 0, sizeof(CFunction *) * m_plugin->info.publics_num);
} else {
m_pub_funcs = NULL;
}
UpdateInfo();
return true;
}
void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
{
m_status = status;
va_list ap;
va_start(ap, error_fmt);
vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
va_end(ap);
} }
IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id) IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
@ -113,7 +232,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
} else { } else {
func_id >>= 1; func_id >>= 1;
unsigned int index; unsigned int index;
if (!g_pVM->FunctionLookup(m_ctx_current.ctx, func_id, &index)) if (!g_pVM->FunctionLookup(m_ctx.ctx, func_id, &index))
{ {
return NULL; return NULL;
} }
@ -131,7 +250,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
IPluginFunction *CPlugin::GetFunctionByName(const char *public_name) IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
{ {
uint32_t index; uint32_t index;
IPluginContext *base = m_ctx_current.base; IPluginContext *base = m_ctx.base;
if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE) if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE)
{ {
@ -195,12 +314,12 @@ const sp_plugin_t *CPlugin::GetPluginStructure() const
IPluginContext *CPlugin::GetBaseContext() const IPluginContext *CPlugin::GetBaseContext() const
{ {
return m_ctx_current.base; return m_ctx.base;
} }
sp_context_t *CPlugin::GetContext() const sp_context_t *CPlugin::GetContext() const
{ {
return m_ctx_current.ctx; return m_ctx.ctx;
} }
const char *CPlugin::GetFilename() const const char *CPlugin::GetFilename() const
@ -230,7 +349,12 @@ PluginStatus CPlugin::GetStatus() const
bool CPlugin::IsDebugging() const bool CPlugin::IsDebugging() const
{ {
return m_debugging; if (!m_ctx.ctx)
{
return false;
}
return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
} }
bool CPlugin::SetPauseState(bool paused) bool CPlugin::SetPauseState(bool paused)
@ -299,30 +423,158 @@ void CPluginManager::RefreshOrLoadPlugins(const char *config, const char *basedi
/* :TODO: log the error, don't bail out though */ /* :TODO: log the error, don't bail out though */
} }
LoadPluginsFromDir(basedir, NULL);
}
void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
{
char base_path[PLATFORM_MAX_PATH+1];
/* Form the current path to start reading from */
if (localpath == NULL)
{
g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
} else {
g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
}
IDirectory *dir = g_LibSys.OpenDirectory(base_path);
if (!dir)
{
//:TODO: write a logger and LOG THIS UP, BABY
//g_LibSys.GetPlatformError(error, err_max);
return;
}
//:TODO: move this to a separate recursive function and do stuff
/*IDirectory *dir = g_LibSys.OpenDirectory(basedir);
while (dir->MoreFiles()) while (dir->MoreFiles())
{ {
if (dir->IsEntryDirectory() && (strcmp(dir->GetEntryName(), "disabled") != 0)) if (dir->IsEntryDirectory()
&& (strcmp(dir->GetEntryName(), ".") != 0)
&& (strcmp(dir->GetEntryName(), "..") != 0)
&& (strcmp(dir->GetEntryName(), "disabled") != 0)
&& (strcmp(dir->GetEntryName(), "optional") != 0))
{ {
char path[PLATFORM_MAX_PATH+1]; char new_local[PLATFORM_MAX_PATH+1];
g_SMAPI->PathFormat(path, sizeof(path)-1, "%s/%s", basedir, dir->GetEntryName()); if (localpath == NULL)
RefreshOrLoadPlugins(basedir); {
/* If no path yet, don't add a former slash */
snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
} else {
g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
}
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)
{
continue;
}
/* If the filename matches, load the plugin */
char plugin[PLATFORM_MAX_PATH+1];
if (localpath == NULL)
{
snprintf(plugin, sizeof(plugin), "%s", name);
} else {
g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
}
LoadAutoPlugin(plugin);
}
dir->NextEntry();
}
g_LibSys.CloseDirectory(dir);
}
void CPluginManager::LoadAutoPlugin(const char *file)
{
CPlugin *pPlugin = CPlugin::CreatePlugin(file, NULL, 0);
assert(pPlugin != NULL);
pPlugin->m_type = PluginType_MapUpdated;
ICompilation *co = NULL;
if (pPlugin->m_status == Plugin_Uncompiled)
{
co = pPlugin->StartMyCompile(g_pVM);
}
PluginSettings *pset;
unsigned int setcount = m_PluginInfo.GetSettingsNum();
for (unsigned int i=0; i<setcount; i++)
{
pset = m_PluginInfo.GetSettingsIfMatch(i, file);
if (!pset)
{
continue;
}
pPlugin->m_type = pset->type_val;
if (co)
{
for (unsigned int j=0; j<pset->opts_num; j++)
{
const char *key, *val;
m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
if (!key || !val)
{
continue;
}
if (!g_pVM->SetCompilationOption(co, key, val))
{
pPlugin->SetErrorState(Plugin_Error, "Unable to set option (key \"%s\") (value \"%s\")", key, val);
pPlugin->CancelMyCompile();
co = NULL;
break;
}
}
} }
} }
g_LibSys.CloseDirectory(dir);*/
if (co)
{
pPlugin->FinishMyCompile(NULL, 0);
co = NULL;
}
InitAndAddPlugin(pPlugin);
} }
IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max) IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
{ {
CPlugin *pPlugin = CPlugin::CreatePlugin(path, debug, type, error, err_max); CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, err_max);
if (!pPlugin) if (!pPlugin)
{ {
return NULL; return NULL;
} }
ICompilation *co = pPlugin->StartMyCompile(g_pVM);
if (!co || (debug && !g_pVM->SetCompilationOption(co, "debug", "1")))
{
snprintf(error, err_max, "Unable to start%s compilation", debug ? " debug" : "");
pPlugin->CancelMyCompile();
delete pPlugin;
return NULL;
}
if (!pPlugin->FinishMyCompile(error, err_max))
{
delete pPlugin;
return NULL;
}
pPlugin->m_type = type;
InitAndAddPlugin(pPlugin);
return pPlugin;
}
void CPluginManager::InitAndAddPlugin(CPlugin *pPlugin)
{
m_plugins.push_back(pPlugin); m_plugins.push_back(pPlugin);
List<IPluginsListener *>::iterator iter; List<IPluginsListener *>::iterator iter;
@ -334,8 +586,6 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ
} }
/* :TODO: a lot more... */ /* :TODO: a lot more... */
return pPlugin;
} }
bool CPluginManager::UnloadPlugin(IPlugin *plugin) bool CPluginManager::UnloadPlugin(IPlugin *plugin)
@ -352,45 +602,6 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin)
pListener->OnPluginDestroyed(pPlugin); pListener->OnPluginDestroyed(pPlugin);
} }
if (pPlugin->m_pub_funcs)
{
for (uint32_t i=0; i<pPlugin->m_plugin->info.publics_num; i++)
{
g_PluginMngr.ReleaseFunctionToPool(pPlugin->m_pub_funcs[i]);
}
delete [] pPlugin->m_pub_funcs;
pPlugin->m_pub_funcs = NULL;
}
if (pPlugin->m_priv_funcs)
{
for (unsigned int i=0; i<pPlugin->m_funcsnum; i++)
{
g_PluginMngr.ReleaseFunctionToPool(pPlugin->m_priv_funcs[i]);
}
delete [] pPlugin->m_priv_funcs;
pPlugin->m_priv_funcs = NULL;
}
if (pPlugin->m_ctx_current.base)
{
g_pSourcePawn->FreeBaseContext(pPlugin->m_ctx_current.base);
}
if (pPlugin->m_ctx_backup.base)
{
g_pSourcePawn->FreeBaseContext(pPlugin->m_ctx_backup.base);
}
if (pPlugin->m_ctx_current.ctx)
{
pPlugin->m_ctx_current.ctx->vmbase->FreeContext(pPlugin->m_ctx_current.ctx);
}
if (pPlugin->m_ctx_backup.ctx)
{
pPlugin->m_ctx_backup.ctx->vmbase->FreeContext(pPlugin->m_ctx_backup.ctx);
}
g_pSourcePawn->FreeFromMemory(pPlugin->m_plugin);
delete pPlugin; delete pPlugin;
return true; return true;
@ -477,7 +688,7 @@ bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
ptr++; ptr++;
} }
if (alias_path_end == alias_len - 1) if (alias_explicit_paths && alias_path_end == alias_len - 1)
{ {
/* Trailing slash is totally invalid here */ /* Trailing slash is totally invalid here */
return false; return false;

View File

@ -10,21 +10,47 @@
using namespace SourceHook; using namespace SourceHook;
/**
* NOTES:
*
* Currently this system needs a lot of work but it's good skeletally. Plugin creation
* is done without actually compiling anything. This is done by Load functions in the
* manager. This will need a rewrite when we add context switching.
*
* The plugin object itself has a few things to note. The most important is that it stores
* a table of function objects. The manager marshals allocation and freeing of these objects.
* The plugin object can be in erroneous states, they are:
* Plugin_Error --> Some error occurred any time during or after compilation.
* This error can be cleared since the plugin itself is valid.
* However, the state itself being set prevents any runtime action.
* Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it.
*
* If a plugin fails to load externally, it is never added to the internal tracker. However,
* plugins that failed to load from the internal loading mechanism are always tracked. This
* allows users to see which automatically loaded plugins failed, and makes the interface a bit
* more flexible.
*/
#define SM_CONTEXTVAR_MYSELF 0 #define SM_CONTEXTVAR_MYSELF 0
struct ContextPair struct ContextPair
{ {
ContextPair() : base(NULL), ctx(NULL) ContextPair() : base(NULL), ctx(NULL), co(NULL)
{ {
}; };
IPluginContext *base; IPluginContext *base;
sp_context_t *ctx; sp_context_t *ctx;
ICompilation *co;
IVirtualMachine *vm;
}; };
class CPlugin : public IPlugin class CPlugin : public IPlugin
{ {
friend class CPluginManager; friend class CPluginManager;
friend class CFunction; friend class CFunction;
public:
CPlugin(const char *file);
~CPlugin();
public: public:
virtual PluginType GetType() const; virtual PluginType GetType() const;
virtual SourcePawn::IPluginContext *GetBaseContext() const; virtual SourcePawn::IPluginContext *GetBaseContext() const;
@ -39,18 +65,34 @@ public:
virtual IPluginFunction *GetFunctionByName(const char *public_name); virtual IPluginFunction *GetFunctionByName(const char *public_name);
virtual IPluginFunction *GetFunctionById(funcid_t func_id); virtual IPluginFunction *GetFunctionById(funcid_t func_id);
public: public:
static CPlugin *CreatePlugin(const char *file, /**
bool debug_default, * Creates a plugin object with default values.
PluginType life, * If an error buffer is specified, and an error occurs, the error will be copied to the buffer
char *error, * and NULL will be returned.
size_t maxlen); * If an error buffer is not specified, the error will be copied to an internal buffer and
* a valid (but error-stated) CPlugin will be returned.
*/
static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
public:
/**
* Starts the initial compilation of a plugin.
* Returns false if another compilation exists or there is a current context set.
*/
ICompilation *StartMyCompile(IVirtualMachine *vm);
/**
* Finalizes a compilation. If error buffer is NULL, the error is saved locally.
*/
bool FinishMyCompile(char *error, size_t maxlength);
void CancelMyCompile();
/**
* Sets an error state on the plugin
*/
void SetErrorState(PluginStatus status, const char *error_fmt, ...);
protected: protected:
void UpdateInfo(); void UpdateInfo();
private: private:
ContextPair m_ctx_current; ContextPair m_ctx;
ContextPair m_ctx_backup;
PluginType m_type; PluginType m_type;
bool m_debugging;
char m_filename[PLATFORM_MAX_PATH+1]; char m_filename[PLATFORM_MAX_PATH+1];
PluginStatus m_status; PluginStatus m_status;
unsigned int m_serial; unsigned int m_serial;
@ -59,13 +101,7 @@ private:
unsigned int m_funcsnum; unsigned int m_funcsnum;
CFunction **m_priv_funcs; CFunction **m_priv_funcs;
CFunction **m_pub_funcs; CFunction **m_pub_funcs;
}; char m_errormsg[256];
struct PluginDBInfo
{
bool pause;
PluginType lifetime;
}; };
class CPluginManager : public IPluginManager class CPluginManager : public IPluginManager
@ -108,7 +144,7 @@ public:
/** /**
* Refreshes and loads plugins, usually used on mapchange * Refreshes and loads plugins, usually used on mapchange
*/ */
virtual void RefreshOrLoadPlugins(const char *config, const char *basedir); void RefreshOrLoadPlugins(const char *config, const char *basedir);
/** /**
* Tests a plugin file mask against a local folder. * Tests a plugin file mask against a local folder.
@ -119,8 +155,27 @@ public:
* All of these will return true for an alias match. * All of these will return true for an alias match.
* Wildcards are allowed in the filename. * Wildcards are allowed in the filename.
*/ */
virtual bool TestAliasMatch(const char *alias, const char *localdir); bool TestAliasMatch(const char *alias, const char *localdir);
private:
/**
* Recursively loads all plugins in the given directory.
*/
void LoadPluginsFromDir(const char *basedir, const char *localdir);
/**
* Loads a plugin using automatic information.
* The file must be relative to the plugins folder.
*/
void LoadAutoPlugin(const char *file);
/**
* Adds and initializes a plugin object. This is wrapped by LoadPlugin functions.
*/
void InitAndAddPlugin(CPlugin *pPlugin);
protected: protected:
/**
* Caching internal objects
*/
void ReleaseIterator(CPluginIterator *iter); void ReleaseIterator(CPluginIterator *iter);
CFunction *GetFunctionFromPool(funcid_t f, CPlugin *plugin); CFunction *GetFunctionFromPool(funcid_t f, CPlugin *plugin);
void ReleaseFunctionToPool(CFunction *func); void ReleaseFunctionToPool(CFunction *func);