!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
+5 -5
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)
{
IPluginContext *ctx = m_pPlugin->m_ctx_current.base;
IPluginContext *ctx = m_pPlugin->m_ctx.base;
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);
}
IPluginContext *ctx = m_pPlugin->m_ctx_current.base;
IPluginContext *ctx = m_pPlugin->m_ctx.base;
ParamInfo *info = &m_info[m_curparam];
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);
}
IPluginContext *base = m_pPlugin->m_ctx_current.base;
IPluginContext *base = m_pPlugin->m_ctx.base;
ParamInfo *info = &m_info[m_curparam];
size_t cells = (len + sizeof(cell_t) - 1) / sizeof(cell_t);
int err;
@@ -174,7 +174,7 @@ void CFunction::Cancel()
return;
}
IPluginContext *base = m_pPlugin->m_ctx_current.base;
IPluginContext *base = m_pPlugin->m_ctx.base;
while (m_curparam--)
{
@@ -217,7 +217,7 @@ int CFunction::Execute(cell_t *result)
docopies = false;
}
IPluginContext *base = m_pPlugin->m_ctx_current.base;
IPluginContext *base = m_pPlugin->m_ctx.base;
while (numparams--)
{
+321 -110
View File
@@ -2,6 +2,7 @@
#include "PluginSys.h"
#include "LibrarySys.h"
#include "sourcemm_api.h"
#include "sourcemod.h"
#include "CTextParsers.h"
CPluginManager g_PluginMngr;
@@ -10,86 +11,204 @@ CPluginManager::CPluginManager()
{
}
CPlugin *CPlugin::CreatePlugin(const char *file,
bool debug_default,
PluginType type,
char *error,
size_t maxlen)
CPlugin::CPlugin(const char *file)
{
static unsigned int MySerial = 0;
FILE *fp = fopen(file, "rb");
static int MySerial = 0;
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)
{
snprintf(error, maxlen, "Could not open file");
return NULL;
if (error)
{
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;
sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
if (pl == NULL)
{
snprintf(error, maxlen, "Could not load plugin, error %d", err);
return NULL;
fclose(fp);
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);
ICompilation *co = g_pVM->StartCompilation(pl);
if (debug_default)
{
if (!g_pVM->SetCompilationOption(co, "debug", "1"))
{
g_pVM->AbortCompilation(co);
snprintf(error, maxlen, "Could not set plugin to debug mode");
return NULL;
}
}
CPlugin *pPlugin = new CPlugin(file);
pPlugin->m_plugin = pl;
return pPlugin;
}
sp_context_t *ctx = g_pVM->CompileToContext(co, &err);
if (ctx == NULL)
ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
{
if (!m_plugin)
{
snprintf(error, maxlen, "Plugin failed to load, JIT error: %d", err);
return NULL;
}
IPluginContext *base = g_pSourcePawn->CreateBaseContext(ctx);
CPlugin *pPlugin = new CPlugin;
snprintf(pPlugin->m_filename, PLATFORM_MAX_PATH, "%s", file);
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)
/* :NOTICE: We will eventually need to change these natives
* for swapping in new contexts
*/
if (m_ctx.co || m_ctx.ctx)
{
pPlugin->m_priv_funcs = new CFunction *[pPlugin->m_funcsnum];
memset(pPlugin->m_priv_funcs, 0, sizeof(CFunction *) * pPlugin->m_funcsnum);
} else {
pPlugin->m_priv_funcs = NULL;
return 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];
memset(pPlugin->m_pub_funcs, 0, sizeof(CFunction *) * pl->info.publics_num);
} else {
pPlugin->m_pub_funcs = NULL;
return;
}
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)
@@ -113,7 +232,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
} else {
func_id >>= 1;
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;
}
@@ -131,7 +250,7 @@ IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
{
uint32_t index;
IPluginContext *base = m_ctx_current.base;
IPluginContext *base = m_ctx.base;
if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE)
{
@@ -195,12 +314,12 @@ const sp_plugin_t *CPlugin::GetPluginStructure() const
IPluginContext *CPlugin::GetBaseContext() const
{
return m_ctx_current.base;
return m_ctx.base;
}
sp_context_t *CPlugin::GetContext() const
{
return m_ctx_current.ctx;
return m_ctx.ctx;
}
const char *CPlugin::GetFilename() const
@@ -230,7 +349,12 @@ PluginStatus CPlugin::GetStatus() 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)
@@ -299,30 +423,158 @@ void CPluginManager::RefreshOrLoadPlugins(const char *config, const char *basedi
/* :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())
{
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];
g_SMAPI->PathFormat(path, sizeof(path)-1, "%s/%s", basedir, dir->GetEntryName());
RefreshOrLoadPlugins(basedir);
char new_local[PLATFORM_MAX_PATH+1];
if (localpath == NULL)
{
/* 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)
{
CPlugin *pPlugin = CPlugin::CreatePlugin(path, debug, type, error, err_max);
CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, err_max);
if (!pPlugin)
{
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);
List<IPluginsListener *>::iterator iter;
@@ -334,8 +586,6 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ
}
/* :TODO: a lot more... */
return pPlugin;
}
bool CPluginManager::UnloadPlugin(IPlugin *plugin)
@@ -352,45 +602,6 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin)
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;
return true;
@@ -477,7 +688,7 @@ bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
ptr++;
}
if (alias_path_end == alias_len - 1)
if (alias_explicit_paths && alias_path_end == alias_len - 1)
{
/* Trailing slash is totally invalid here */
return false;
+73 -18
View File
@@ -10,21 +10,47 @@
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
struct ContextPair
{
ContextPair() : base(NULL), ctx(NULL)
ContextPair() : base(NULL), ctx(NULL), co(NULL)
{
};
IPluginContext *base;
sp_context_t *ctx;
ICompilation *co;
IVirtualMachine *vm;
};
class CPlugin : public IPlugin
{
friend class CPluginManager;
friend class CFunction;
public:
CPlugin(const char *file);
~CPlugin();
public:
virtual PluginType GetType() const;
virtual SourcePawn::IPluginContext *GetBaseContext() const;
@@ -39,18 +65,34 @@ public:
virtual IPluginFunction *GetFunctionByName(const char *public_name);
virtual IPluginFunction *GetFunctionById(funcid_t func_id);
public:
static CPlugin *CreatePlugin(const char *file,
bool debug_default,
PluginType life,
char *error,
size_t maxlen);
/**
* Creates a plugin object with default values.
* If an error buffer is specified, and an error occurs, the error will be copied to the buffer
* and NULL will be returned.
* 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:
void UpdateInfo();
private:
ContextPair m_ctx_current;
ContextPair m_ctx_backup;
ContextPair m_ctx;
PluginType m_type;
bool m_debugging;
char m_filename[PLATFORM_MAX_PATH+1];
PluginStatus m_status;
unsigned int m_serial;
@@ -59,13 +101,7 @@ private:
unsigned int m_funcsnum;
CFunction **m_priv_funcs;
CFunction **m_pub_funcs;
};
struct PluginDBInfo
{
bool pause;
PluginType lifetime;
char m_errormsg[256];
};
class CPluginManager : public IPluginManager
@@ -108,7 +144,7 @@ public:
/**
* 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.
@@ -119,8 +155,27 @@ public:
* All of these will return true for an alias match.
* 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:
/**
* Caching internal objects
*/
void ReleaseIterator(CPluginIterator *iter);
CFunction *GetFunctionFromPool(funcid_t f, CPlugin *plugin);
void ReleaseFunctionToPool(CFunction *func);