From 23a91de75c87ef2d48299f291c26d41a5ffdb05a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 13 Dec 2006 11:16:20 +0000 Subject: [PATCH] !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 --- core/interfaces/IPluginSys.h | 2 +- core/sourcemm_api.cpp | 4 + core/sourcemm_api.h | 3 + core/sourcemod.cpp | 71 ++++-- core/sourcemod.h | 31 +++ core/systems/CFunction.cpp | 10 +- core/systems/PluginSys.cpp | 431 ++++++++++++++++++++++++++--------- core/systems/PluginSys.h | 91 ++++++-- 8 files changed, 491 insertions(+), 152 deletions(-) diff --git a/core/interfaces/IPluginSys.h b/core/interfaces/IPluginSys.h index 9e0a86a3..6868d176 100644 --- a/core/interfaces/IPluginSys.h +++ b/core/interfaces/IPluginSys.h @@ -33,8 +33,8 @@ namespace SourceMod Plugin_Running=0, /* Plugin is running */ Plugin_Loaded, /* Plugin is loaded but not initialized */ Plugin_Paused, /* Plugin is paused */ - Plugin_Stopped, /* Plugin is paused for map changes, too */ Plugin_Error, /* Plugin has a blocking error */ + Plugin_Uncompiled, /* Plugin is not yet compiled by the JIT */ Plugin_BadLoad, /* Plugin failed to load */ }; diff --git a/core/sourcemm_api.cpp b/core/sourcemm_api.cpp index 2e862a26..75652a00 100644 --- a/core/sourcemm_api.cpp +++ b/core/sourcemm_api.cpp @@ -4,6 +4,8 @@ #include "sourcemod.h" SourceMod_Core g_SourceMod_Core; +IVEngineServer *engine = NULL; +IServerGameDLL *gamedll = NULL; 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(); + GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + return g_SourceMod.InitializeSourceMod(error, maxlen, late); } diff --git a/core/sourcemm_api.h b/core/sourcemm_api.h index 3089da9e..dff0a5a8 100644 --- a/core/sourcemm_api.h +++ b/core/sourcemm_api.h @@ -2,6 +2,7 @@ #define _INCLUDE_SOURCEMOD_MM_API_H_ #include +#include /** * @file Contains wrappers around required Metamod:Source API exports @@ -27,6 +28,8 @@ public: }; extern SourceMod_Core g_SourceMod_Core; +extern IVEngineServer *engine; +extern IServerGameDLL *gamedll; PLUGIN_GLOBALVARS(); diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 126d805d..b2759613 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -7,6 +7,8 @@ #include "PluginSys.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; SourceModBase g_SourceMod; @@ -31,16 +33,21 @@ void ShutdownJIT() g_pJIT->CloseLibrary(); } +SourceModBase::SourceModBase() +{ + m_IsMapLoading = false; +} + bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) { - //:TODO: we need a localinfo system! 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! */ char file[PLATFORM_MAX_PATH]; char myerror[255]; - g_SMAPI->PathFormat(file, sizeof(file), "%s/addons/sourcemod/bin/sourcepawn.jit.x86.%s", - g_BaseDir.c_str(), + g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s", + GetSMBaseDir(), PLATFORM_LIB_EXT ); @@ -112,21 +119,49 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) return false; } - g_SMAPI->PathFormat(file, sizeof(file), "%s/addons/sourcemod/plugins/test.smx", g_BaseDir.c_str()); - 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); + StartSourceMod(late); 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; +} diff --git a/core/sourcemod.h b/core/sourcemod.h index f053de65..c7ecd7e5 100644 --- a/core/sourcemod.h +++ b/core/sourcemod.h @@ -9,11 +9,42 @@ class SourceModBase { +public: + SourceModBase(); public: /** * @brief Initializes SourceMod, or returns an error on failure. */ 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; diff --git a/core/systems/CFunction.cpp b/core/systems/CFunction.cpp index 1ad5a5d1..abb27173 100644 --- a/core/systems/CFunction.cpp +++ b/core/systems/CFunction.cpp @@ -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--) { diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 7b4769ec..2937a103 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -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; iinfo.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; im_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; im_type = pset->type_val; + if (co) + { + for (unsigned int j=0; jopts_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::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; im_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; im_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; diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index ced80618..4791ef2d 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -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);