From 90d1f4495e48d55bff2464aa6aa6f1a321e85353 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 15 Dec 2006 13:38:04 +0000 Subject: [PATCH] Added global class initialization automation Finalized basics of plugin loading Began redoing how dependencies will be tracked Renamed some bad names Finished some stuff in ForwardSys --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40216 --- core/interfaces/IModuleSys.h | 17 +- core/interfaces/IPluginFunction.h | 6 +- core/interfaces/IPluginSys.h | 8 +- core/interfaces/IShareSys.h | 80 ++------- core/msvc8/sourcemod_mm.vcproj | 8 + core/sm_platform.h | 3 + core/sm_trie.cpp | 50 ++++++ core/sm_trie.h | 1 + core/sourcemod.cpp | 22 ++- core/sourcemod.h | 39 +++++ core/systems/CFunction.cpp | 6 +- core/systems/ForwardSys.cpp | 59 ++++++- core/systems/ForwardSys.h | 14 +- core/systems/PluginInfoDatabase.cpp | 2 +- core/systems/PluginSys.cpp | 241 ++++++++++++++++++++++++---- core/systems/PluginSys.h | 84 +++++++++- 16 files changed, 509 insertions(+), 131 deletions(-) diff --git a/core/interfaces/IModuleSys.h b/core/interfaces/IModuleSys.h index 6b4eabe4..fa13f3ba 100644 --- a/core/interfaces/IModuleSys.h +++ b/core/interfaces/IModuleSys.h @@ -8,16 +8,12 @@ namespace SourceMod { class IModuleInterface; - class IModule : public IUnloadableParent + class IModule { - public: - virtual UnloadableParentType GetParentType() - { - return ParentType_Module; - } public: virtual IModuleInterface *GetModuleInfo() =0; virtual const char *GetFilename() =0; + virtual IdentityToken_t GetIdentityToken() =0; }; class IModuleInterface @@ -27,13 +23,19 @@ namespace SourceMod * @brief Called when the module is loaded. * * @param me Pointer back to module. + * @param token Identity token handle. * @param sys Pointer to interface sharing system of SourceMod. * @param error Error buffer to print back to, if any. * @param err_max Maximum size of error buffer. * @param late If this module was loaded "late" (i.e. manually). * @return True if load should continue, false otherwise. */ - virtual bool OnModuleLoad(IModule *me, IShareSys *sys, char *error, size_t err_max, bool late) =0; + virtual bool OnModuleLoad(IModule *me, + IdentityToken_t token, + IShareSys *sys, + char *error, + size_t err_max, + bool late) =0; /** * @brief Called when the module is unloaded. @@ -51,7 +53,6 @@ namespace SourceMod * @param pause True if pausing, false if unpausing. */ virtual void OnPauseChange(bool pause) =0; - public: virtual const char *GetModuleName() =0; virtual const char *GetModuleVersion() =0; diff --git a/core/interfaces/IPluginFunction.h b/core/interfaces/IPluginFunction.h index e0716c79..7174baf6 100644 --- a/core/interfaces/IPluginFunction.h +++ b/core/interfaces/IPluginFunction.h @@ -5,10 +5,10 @@ namespace SourceMod { - #define SM_FUNCFLAG_COPYBACK (1<<0) /* Copy an array/reference back after call */ + #define SM_PARAM_COPYBACK (1<<0) /* Copy an array/reference back after call */ - #define SP_STRING_UTF8 (1<<0) /* String should be UTF-8 handled */ - #define SP_STRING_COPY (1<<1) /* String should be copied into the plugin */ + #define SM_PARAM_STRING_UTF8 (1<<0) /* String should be UTF-8 handled */ + #define SM_PARAM_STRING_COPY (1<<1) /* String should be copied into the plugin */ /** * @brief Represents what a function needs to implement in order to be callable. diff --git a/core/interfaces/IPluginSys.h b/core/interfaces/IPluginSys.h index 6868d176..b3eac81a 100644 --- a/core/interfaces/IPluginSys.h +++ b/core/interfaces/IPluginSys.h @@ -25,15 +25,19 @@ namespace SourceMod const char *url; } sm_plugininfo_t; + /** * @brief Describes the usability status of a plugin. */ enum PluginStatus { Plugin_Running=0, /* Plugin is running */ - Plugin_Loaded, /* Plugin is loaded but not initialized */ - Plugin_Paused, /* Plugin is paused */ + /* All states below are unexecutable */ + Plugin_Paused, /* Plugin is loaded but paused */ + /* All states below do not have all natives */ + Plugin_Loaded, /* Plugin has passed loading and can be finalized */ Plugin_Error, /* Plugin has a blocking error */ + Plugin_Created, /* Plugin is created but not initialized */ Plugin_Uncompiled, /* Plugin is not yet compiled by the JIT */ Plugin_BadLoad, /* Plugin failed to load */ }; diff --git a/core/interfaces/IShareSys.h b/core/interfaces/IShareSys.h index 7e08f72c..d81a0fee 100644 --- a/core/interfaces/IShareSys.h +++ b/core/interfaces/IShareSys.h @@ -1,8 +1,11 @@ #ifndef _INCLUDE_SOURCEMOD_IFACE_SHARE_SYS_H_ #define _INCLUDE_SOURCEMOD_IFACE_SHARE_SYS_H_ +#include + namespace SourceMod { + typedef unsigned int IdentityToken_t; /** * @brief Defines the base functionality required by a shared interface. */ @@ -37,48 +40,6 @@ namespace SourceMod } }; - enum UnloadableParentType - { - ParentType_Module, - ParentType_Plugin - }; - - /** - * @brief Denotes a top-level unloadable object. - */ - class IUnloadableParent - { - public: - virtual UnloadableParentType GetParentType() =0; - - virtual void *GetParentToken() =0; - - /** - * @brief Called when an interface this object has requested is removed. - * - * @param pIface Interface being removed. - */ - virtual void OnInterfaceUnlink(SMInterface *pIface) =0; - protected: - void *m_parent_token; - }; - - /** - * @brief Listens for unlinked objects. - */ - class IUnlinkListener - { - public: - /** - * @brief Called when a parent object is unloaded. - * - * @param parent The parent object which is dying. - */ - virtual void OnParentUnlink(IUnloadableParent *parent) - { - } - }; - /** * @brief Tracks dependencies and fires dependency listeners. */ @@ -89,9 +50,9 @@ namespace SourceMod * @brief Adds an interface to the global interface system * * @param iface Interface pointer (must be unique). - * @param parent Parent unloadable token given to the module/interface. + * @param token Parent token of the module/interface. */ - virtual bool AddInterface(SMInterface *iface, IUnloadableParent *parent) =0; + virtual bool AddInterface(SMInterface *iface, IdentityToken_t token) =0; /** * @brief Requests an interface from the global interface system. @@ -99,36 +60,21 @@ namespace SourceMod * * @param iface_name Interface name. * @param iface_vers Interface version to attempt to match. - * @param me Object requesting this interface, in order to track dependencies. + * @param token Object requesting this interface, in order to track dependencies. * @param pIface Pointer to store the return value in. */ virtual bool RequestInterface(const char *iface_name, - unsigned int iface_vers, - IUnloadableParent *me, + unsigned int iface_vers, + IdentityToken_t token, void **pIface) =0; /** - * @brief Unloads an interface. - * - * @param iface Interface pointer. - * @param parent Security token, trivial measure to prevent accidental unloads. - * This token must match the one used with AddInterface(). + * @brief Adds a list of natives to the global native pool. + * + * @param token Identity token of parent object. + * @param natives Array of natives to add, NULL terminated. */ - virtual void RemoveInterface(SMInterface *iface, IUnloadableParent *parent) =0; - - /** - * @brief Adds an unlink listener. - * - * @param pListener Listener pointer. - */ - virtual void AddUnlinkListener(IUnlinkListener *pListener) =0; - - /** - * @brief Removes an unlink listener. - * - * @param pListener Listener pointer. - */ - virtual void RemoveUnlinkListener(IUnlinkListener *pListener) =0; + virtual void AddNatives(IdentityToken_t token, const sp_nativeinfo_t *natives[]) =0; }; }; diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 0a482bdd..59d92a39 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -186,6 +186,10 @@ RelativePath="..\sm_memtable.cpp" > + + @@ -216,6 +220,10 @@ RelativePath="..\sm_platform.h" > + + diff --git a/core/sm_platform.h b/core/sm_platform.h index 5a6ea71f..2487611f 100644 --- a/core/sm_platform.h +++ b/core/sm_platform.h @@ -13,6 +13,9 @@ #if !defined snprintf #define snprintf _snprintf #endif +#if !defined stat +#define stat _stat +#endif #define strcasecmp strcmpi #include #include diff --git a/core/sm_trie.cpp b/core/sm_trie.cpp index b0dbd8d5..7e92c5d9 100644 --- a/core/sm_trie.cpp +++ b/core/sm_trie.cpp @@ -266,6 +266,56 @@ void sm_trie_destroy(Trie *trie) delete trie; } +bool sm_trie_delete(Trie *trie, const char *key) +{ + unsigned int lastidx = 1; /* the last node index */ + unsigned int curidx; /* current node index */ + const char *keyptr = key; /* input stream at current token */ + TrieNode *node = NULL; /* current node being processed */ + TrieNode *base = trie->base; + + if (!*key) + { + return false; + } + + /* Start traversing at the root node */ + do + { + /* Find where the next character is, then advance */ + curidx = base[lastidx].idx; + node = &base[curidx]; + curidx += charval(*keyptr); + node = &base[curidx]; + keyptr++; + + /* Check if this slot is supposed to be empty or is a collision */ + if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx) + { + return false; + } else if (node->mode == Node_Term) { + char *term = &trie->stringtab[node->idx]; + if (strcmp(keyptr, term) == 0) + { + break; + } + } + lastidx = curidx; + } while (*keyptr != '\0'); + + assert(node != NULL); + + if (!node->valset) + { + return false; + } + + node->valset = false; + node->value = NULL; + + return true; +} + bool sm_trie_retrieve(Trie *trie, const char *key, void **value) { unsigned int lastidx = 1; /* the last node index */ diff --git a/core/sm_trie.h b/core/sm_trie.h index 9193f59e..a6e0c0fe 100644 --- a/core/sm_trie.h +++ b/core/sm_trie.h @@ -7,5 +7,6 @@ Trie *sm_trie_create(); void sm_trie_destroy(Trie *trie); bool sm_trie_insert(Trie *trie, const char *key, void *value); bool sm_trie_retrieve(Trie *trie, const char *key, void **value); +bool sm_trie_delete(Trie *trie, const char *key); #endif //_INCLUDE_SOURCEMOD_SIMPLE_TRIE_H_ diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index b2759613..f8d544dd 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -130,12 +130,17 @@ void SourceModBase::StartSourceMod(bool late) SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); /* If we're late, automatically load plugins now */ - DoGlobalPluginLoads(); + if (late) + { + m_IsLateLoadInMap = late; + DoGlobalPluginLoads(); + } } bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) { m_IsMapLoading = true; + m_IsLateLoadInMap = false; DoGlobalPluginLoads(); @@ -158,10 +163,23 @@ void SourceModBase::DoGlobalPluginLoads() "%s/plugins", GetSMBaseDir()); - g_PluginMngr.RefreshOrLoadPlugins(config_path, plugins_path); + g_PluginSys.LoadAll_FirstPass(config_path, plugins_path); +} + +bool SourceModBase::IsLateLoadInMap() +{ + return m_IsLateLoadInMap; } const char *SourceModBase::GetSMBaseDir() { return m_SMBaseDir; } + +SMGlobalClass *SMGlobalClass::head = NULL; + +SMGlobalClass::SMGlobalClass() +{ + m_pGlobalClassNext = SMGlobalClass::head; + SMGlobalClass::head = this; +} diff --git a/core/sourcemod.h b/core/sourcemod.h index c7ecd7e5..238531c4 100644 --- a/core/sourcemod.h +++ b/core/sourcemod.h @@ -37,6 +37,10 @@ public: */ const char *GetSMBaseDir(); + /** + * @brief Returns whether our load in this map is late. + */ + bool IsLateLoadInMap(); private: /** * @brief Loading plugins @@ -45,6 +49,41 @@ private: private: char m_SMBaseDir[PLATFORM_MAX_PATH+1]; bool m_IsMapLoading; + bool m_IsLateLoadInMap; +}; + +/** + * @brief Any class deriving from this will be automatically initiated/shutdown by SourceMod + */ +class SMGlobalClass +{ + friend class SourceModBase; +public: + SMGlobalClass(); +public: + /** + * @brief Called when SourceMod is initially loading + */ + virtual void OnSourceModStartup(bool late) + { + } + + /** + * @brief Called after all global classes have initialized + */ + virtual void OnSourceModAllInitialized() + { + } + + /** + * @brief Called when SourceMod is shutting down + */ + virtual void OnSourceModShutdown() + { + } +private: + SMGlobalClass *m_pGlobalClassNext; + static SMGlobalClass *head; }; extern SourceModBase g_SourceMod; diff --git a/core/systems/CFunction.cpp b/core/systems/CFunction.cpp index abb27173..1d6bf769 100644 --- a/core/systems/CFunction.cpp +++ b/core/systems/CFunction.cpp @@ -112,7 +112,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr int CFunction::PushString(const char *string) { - return _PushString(string, SP_STRING_COPY, 0, strlen(string)+1); + return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1); } int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags) @@ -141,12 +141,12 @@ int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_ m_params[m_curparam] = info->local_addr; m_curparam++; /* Prevent a leak */ - if (!(sz_flags & SP_STRING_COPY)) + if (!(sz_flags & SM_PARAM_STRING_COPY)) { goto skip_localtostr; } - if (sz_flags & SP_STRING_UTF8) + if (sz_flags & SM_PARAM_STRING_UTF8) { if ((err=base->StringToLocalUTF8(info->local_addr, len, string, NULL)) != SP_ERROR_NONE) { diff --git a/core/systems/ForwardSys.cpp b/core/systems/ForwardSys.cpp index 7e3feb67..d0dd7b26 100644 --- a/core/systems/ForwardSys.cpp +++ b/core/systems/ForwardSys.cpp @@ -28,6 +28,16 @@ CForwardManager g_Forwards; * X Push vararg strings (copyback tested = :TODO:) */ +void CForwardManager::OnSourceModAllInitialized() +{ + g_PluginSys.AddPluginsListener(this); +} + +void CForwardManager::OnSourceModShutdown() +{ + g_PluginSys.RemovePluginsListener(this); +} + IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, ParamType *types, ...) { CForward *fwd; @@ -38,6 +48,11 @@ IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned va_end(ap); + if (fwd) + { + m_managed.push_back(fwd); + } + return fwd; } @@ -51,9 +66,43 @@ IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType va_end(ap); + if (fwd) + { + m_unmanaged.push_back(fwd); + } + return fwd; } +void CForwardManager::OnPluginLoaded(IPlugin *plugin) +{ + /* Attach any globally managed forwards */ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + IPluginFunction *pFunc = plugin->GetFunctionByName(fwd->GetForwardName()); + if (pFunc) + { + fwd->AddFunction(pFunc); + } + } +} + +void CForwardManager::OnPluginUnloaded(IPlugin *plugin) +{ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + fwd->RemoveFunctionsOfPlugin(plugin); + } +} + IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng) { List::iterator iter; @@ -243,9 +292,7 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter) } } - /* Call the function and deal with the return value. - * :TODO: only pass reader if we know we have an array in the list - */ + /* Call the function and deal with the return value. */ if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE) { bool handled = false; @@ -478,7 +525,7 @@ int CForward::PushString(const char *string) m_params[m_curparam].pushedas = Param_String; } - _Int_PushString((cell_t *)string, strlen(string)+1, SP_STRING_COPY, 0); + _Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0); m_curparam++; return SP_ERROR_NONE; @@ -521,7 +568,7 @@ void CForward::Cancel() bool CForward::AddFunction(sp_context_t *ctx, funcid_t index) { - IPlugin *pPlugin = g_PluginMngr.FindPluginByContext(ctx); + IPlugin *pPlugin = g_PluginSys.FindPluginByContext(ctx); if (!pPlugin) { return false; @@ -590,7 +637,7 @@ bool CForward::AddFunction(IPluginFunction *func) return false; } - //:TODO: eventually we will tell the plugin we're using it + //:TODO: eventually we will tell the plugin we're using it [?] m_functions.push_back(func); return true; diff --git a/core/systems/ForwardSys.h b/core/systems/ForwardSys.h index a3dfd5c9..4748e9fb 100644 --- a/core/systems/ForwardSys.h +++ b/core/systems/ForwardSys.h @@ -6,6 +6,7 @@ #include "sm_globals.h" #include #include +#include "sourcemod.h" using namespace SourceHook; @@ -83,10 +84,13 @@ protected: int m_errstate; }; -class CForwardManager : public IForwardManager +class CForwardManager : + public IForwardManager, + public IPluginsListener, + public SMGlobalClass { friend class CForward; -public: +public: //IForwardManager virtual IForward *CreateForward(const char *name, ExecType et, unsigned int num_params, @@ -99,6 +103,12 @@ public: ...); virtual IForward *FindForward(const char *name, IChangeableForward **ifchng); virtual void ReleaseForward(IForward *forward); +public: //IPluginsListener + virtual void OnPluginLoaded(IPlugin *plugin); + virtual void OnPluginUnloaded(IPlugin *plugin); +public: //SMGlobalClass + virtual void OnSourceModAllInitialized(); + virtual void OnSourceModShutdown(); protected: CForward *ForwardMake(); void ForwardFree(CForward *fwd); diff --git a/core/systems/PluginInfoDatabase.cpp b/core/systems/PluginInfoDatabase.cpp index fb78b69d..5b7d1ac6 100644 --- a/core/systems/PluginInfoDatabase.cpp +++ b/core/systems/PluginInfoDatabase.cpp @@ -85,7 +85,7 @@ PluginSettings *CPluginInfoDatabase::GetSettingsIfMatch(unsigned int index, cons return NULL; } - if (!g_PluginMngr.TestAliasMatch(name, filename)) + if (!g_PluginSys.TestAliasMatch(name, filename)) { return NULL; } diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 2937a103..9cbc5f74 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -5,11 +5,7 @@ #include "sourcemod.h" #include "CTextParsers.h" -CPluginManager g_PluginMngr; - -CPluginManager::CPluginManager() -{ -} +CPluginManager g_PluginSys; CPlugin::CPlugin(const char *file) { @@ -54,7 +50,7 @@ CPlugin::~CPlugin() { for (uint32_t i=0; iinfo.publics_num; i++) { - g_PluginMngr.ReleaseFunctionToPool(m_pub_funcs[i]); + g_PluginSys.ReleaseFunctionToPool(m_pub_funcs[i]); } delete [] m_pub_funcs; m_pub_funcs = NULL; @@ -64,7 +60,7 @@ CPlugin::~CPlugin() { for (unsigned int i=0; iGetPublicByIndex(index, &pub); if (pub) { - pFunc = g_PluginMngr.GetFunctionFromPool(pub->funcid, this); + pFunc = g_PluginSys.GetFunctionFromPool(pub->funcid, this); m_pub_funcs[index] = pFunc; } } @@ -307,6 +305,77 @@ void CPlugin::UpdateInfo() m_info.version = m_info.version ? m_info.version : ""; } +void CPlugin::Call_OnPluginInit() +{ + if (m_status != Plugin_Loaded) + { + return; + } + + m_status = Plugin_Running; + + int err; + cell_t result; + IPluginFunction *pFunction = GetFunctionByName("OnPluginInit"); + if (!pFunction) + { + return; + } + + /* :TODO: push our own handle */ + pFunction->PushCell(0); + if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) + { + /* :TODO: log into debugger instead */ + SetErrorState(Plugin_Error, "Runtime error %d", err); + } +} + +bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) +{ + if (m_status != Plugin_Created) + { + return false; + } + + if (!error) + { + error = m_errormsg; + maxlength = sizeof(m_errormsg); + } + + int err; + cell_t result; + IPluginFunction *pFunction = GetFunctionByName("AskPluginLoad"); + + if (!pFunction) + { + return true; + } + + pFunction->PushCell(0); //:TODO: handle to ourself + 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) + { + /* :TODO: debugging system */ + snprintf(error, maxlength, "Plugin load returned run time error %d", err); + m_status = Plugin_Error; + return false; + } + + if (!result) + { + m_status = Plugin_Error; + return false; + } + + m_status = Plugin_Loaded; + + return true; +} + const sp_plugin_t *CPlugin::GetPluginStructure() const { return m_plugin; @@ -397,7 +466,7 @@ void CPluginManager::CPluginIterator::NextPlugin() void CPluginManager::CPluginIterator::Release() { - g_PluginMngr.ReleaseIterator(this); + g_PluginSys.ReleaseIterator(this); } CPluginManager::CPluginIterator::~CPluginIterator() @@ -413,7 +482,20 @@ void CPluginManager::CPluginIterator::Reset() * PLUGIN MANAGER * ******************/ -void CPluginManager::RefreshOrLoadPlugins(const char *config, const char *basedir) +CPluginManager::CPluginManager() +{ + m_LoadLookup = sm_trie_create(); + m_AllPluginsLoaded = false; +} + +CPluginManager::~CPluginManager() +{ + //:TODO: we need a good way to free what we're holding + sm_trie_destroy(m_LoadLookup); +} + + +void CPluginManager::LoadAll_FirstPass( const char *config, const char *basedir ) { /* First read in the database of plugin settings */ SMCParseError err; @@ -467,29 +549,51 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa } else if (dir->IsEntryFile()) { const char *name = dir->GetEntryName(); size_t len = strlen(name); - if (len < 4 - || strcmp(&name[len-4], ".smx") != 0) + 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); } - /* 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); } +//well i have discovered that gabe newell is very fat, so i wrote this comment now +//:TODO: remove this function, create a better wrapper for LoadPlugin()/LoadAutoPlugin() void CPluginManager::LoadAutoPlugin(const char *file) { - CPlugin *pPlugin = CPlugin::CreatePlugin(file, NULL, 0); + /** + * Does this plugin already exist? + */ + CPlugin *pPlugin; + if (sm_trie_retrieve(m_LoadLookup, file, (void **)&pPlugin)) + { + /* First check the type */ + PluginType type = pPlugin->GetType(); + if (type == PluginType_Private + || type == PluginType_Global) + { + return; + } + /* Check to see if we should try reloading it */ + if (pPlugin->GetStatus() == Plugin_BadLoad + || pPlugin->GetStatus() == Plugin_Error) + { + UnloadPlugin(pPlugin); + } + } + + pPlugin = CPlugin::CreatePlugin(file, NULL, 0); assert(pPlugin != NULL); @@ -539,12 +643,36 @@ void CPluginManager::LoadAutoPlugin(const char *file) co = NULL; } - InitAndAddPlugin(pPlugin); + /* We don't care about the return value */ + if (pPlugin->GetStatus() == Plugin_Created) + { + AddCoreNativesToPlugin(pPlugin); + pPlugin->Call_AskPluginLoad(NULL, 0); + } + + AddPlugin(pPlugin); } IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max) { - CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, err_max); + /* See if this plugin is already loaded... reformat to get sep chars right */ + char checkpath[PLATFORM_MAX_PATH+1]; + g_LibSys.PathFormat(checkpath, sizeof(checkpath), "%s", path); + + /** + * In manually loading a plugin, any sort of load error causes a deletion. + * This is because it's assumed manually loaded plugins will not be managed. + * For managed plugins, we need the UI to report them properly. + */ + + CPlugin *pPlugin; + if (sm_trie_retrieve(m_LoadLookup, checkpath, (void **)&pPlugin)) + { + snprintf(error, err_max, "Plugin file is alread loaded"); + return NULL; + } + + pPlugin = CPlugin::CreatePlugin(path, error, err_max); if (!pPlugin) { @@ -568,14 +696,24 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ pPlugin->m_type = type; - InitAndAddPlugin(pPlugin); + AddCoreNativesToPlugin(pPlugin); + + /* Finally, ask the plugin if it wants to be loaded */ + if (!pPlugin->Call_AskPluginLoad(error, err_max)) + { + delete pPlugin; + return NULL; + } + + AddPlugin(pPlugin); return pPlugin; } -void CPluginManager::InitAndAddPlugin(CPlugin *pPlugin) +void CPluginManager::AddPlugin(CPlugin *pPlugin) { m_plugins.push_back(pPlugin); + sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin); List::iterator iter; IPluginsListener *pListener; @@ -585,7 +723,43 @@ void CPluginManager::InitAndAddPlugin(CPlugin *pPlugin) pListener->OnPluginCreated(pPlugin); } - /* :TODO: a lot more... */ + /* If the second pass was already completed, we have to run the pass on this plugin */ + if (m_AllPluginsLoaded && pPlugin->GetStatus() == Plugin_Loaded) + { + RunSecondPass(pPlugin); + } +} + +void CPluginManager::RunSecondPass(CPlugin *pPlugin) +{ + /* Tell this plugin to finish initializing itself */ + pPlugin->Call_OnPluginInit(); + + /* Finish by telling all listeners */ + List::iterator iter; + IPluginsListener *pListener; + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginLoaded(pPlugin); + } +} + +void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) +{ + List::iterator iter; + + for (iter=m_natives.begin(); iter!=m_natives.end(); iter++) + { + sp_nativeinfo_t *natives = (*iter); + IPluginContext *ctx = pPlugin->GetBaseContext(); + unsigned int i=0; + /* Attempt to bind every native! */ + while (natives[i].func != NULL) + { + ctx->BindNative(&natives[i++]); + } + } } bool CPluginManager::UnloadPlugin(IPlugin *plugin) @@ -601,6 +775,9 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin) pListener = (*iter); pListener->OnPluginDestroyed(pPlugin); } + + m_plugins.remove(plugin); + sm_trie_delete(m_LoadLookup, pPlugin->m_filename); delete pPlugin; @@ -877,7 +1054,7 @@ bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath) return true; } -CPluginManager::~CPluginManager() +bool CPluginManager::IsLateLoadTime() { - //:TODO: we need a good way to free what we're holding + return (m_AllPluginsLoaded || g_SourceMod.IsLateLoadInMap()); } diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 4791ef2d..44856ce9 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -1,12 +1,14 @@ #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ +#include #include #include #include #include "sm_globals.h" #include "CFunction.h" #include "PluginInfoDatabase.h" +#include "sm_trie.h" using namespace SourceHook; @@ -29,6 +31,31 @@ using namespace SourceHook; * 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. + * + * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid + * for execution. SourceMod is a two pass system, and even though the second pass is not implemented + * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When + * implemented, a Created plugin will be switched to Error in the second pass if it not loadable. + * + * The two pass loading mechanism is described below. Modules/natives are not implemented yet. + * PASS ONE: All loadable plugins are found and have the following steps performed: + * 1. Loading and compilation is attempted. + * 2. If successful, all natives from Core are added. + * 3. OnPluginLoad() is called. + * 4. If failed, any user natives are scrapped and the process halts here. + * 5. If successful, the plugin is ready for Pass 2. + * INTERMEDIATE: + * 1. All forced modules are loaded. + * PASS TWO: All loaded plugins are found and have these steps performed: + * 1. Any modules referenced in the plugin that are not already loaded, are loaded. + * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6. + * 3. If any natives are unresolved, check if they are found in the user-natives pool. + * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter. + * 5. If the filter fails, the plugin is marked as failed. + * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped. + * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran. + * PASS THREE (not a real pass): + * 7. Once all plugins are deemed to be loaded, OnPluginInit() is called */ #define SM_CONTEXTVAR_MYSELF 0 @@ -88,6 +115,24 @@ public: * Sets an error state on the plugin */ void SetErrorState(PluginStatus status, const char *error_fmt, ...); + + /** + * Calls the OnPluginLoad function, and sets any failed states if necessary. + * NOTE: Valid pre-states are: Plugin_Created + * If validated, plugin state is changed to Plugin_Loaded + * + * If the error buffer is NULL, the error message is cached locally. + */ + bool Call_AskPluginLoad(char *error, size_t maxlength); + + /** + * Calls the OnPluginInit function. + * NOTE: Valid pre-states are: Plugin_Created + * NOTE: Pre-state will be changed to Plugin_Running + */ + void Call_OnPluginInit(); +public: + time_t HasUpdatedFile(); protected: void UpdateInfo(); private: @@ -102,6 +147,7 @@ private: CFunction **m_priv_funcs; CFunction **m_pub_funcs; char m_errormsg[256]; + time_t m_LastAccess; }; class CPluginManager : public IPluginManager @@ -142,9 +188,14 @@ public: //IPluginManager virtual void RemovePluginsListener(IPluginsListener *listener); public: /** - * Refreshes and loads plugins, usually used on mapchange + * Loads all plugins not yet loaded */ - void RefreshOrLoadPlugins(const char *config, const char *basedir); + void LoadAll_FirstPass(const char *config, const char *basedir); + + /** + * Runs the second loading pass for all plugins + */ + void LoadAll_SecondPass(); /** * Tests a plugin file mask against a local folder. @@ -156,6 +207,16 @@ public: * Wildcards are allowed in the filename. */ bool TestAliasMatch(const char *alias, const char *localdir); + + /** + * Registers natives in core itself ONLY. + */ + void RegisterGlobalNatives(sp_nativeinfo_t *info[]); + + /** + * Returns whether anything loaded will be a late load. + */ + bool IsLateLoadTime(); private: /** * Recursively loads all plugins in the given directory. @@ -169,9 +230,19 @@ private: void LoadAutoPlugin(const char *file); /** - * Adds and initializes a plugin object. This is wrapped by LoadPlugin functions. + * Adds a plugin object. This is wrapped by LoadPlugin functions. */ - void InitAndAddPlugin(CPlugin *pPlugin); + void AddPlugin(CPlugin *pPlugin); + + /** + * Runs the second loading pass on a plugin. + */ + void RunSecondPass(CPlugin *pPlugin); + + /** + * Adds any globally registered natives to a plugin + */ + void AddCoreNativesToPlugin(CPlugin *pPlugin); protected: /** * Caching internal objects @@ -182,11 +253,14 @@ protected: private: List m_listeners; List m_plugins; + List m_natives; CStack m_iters; CStack m_funcpool; CPluginInfoDatabase m_PluginInfo; + Trie *m_LoadLookup; + bool m_AllPluginsLoaded; }; -extern CPluginManager g_PluginMngr; +extern CPluginManager g_PluginSys; #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_