diff --git a/core/interfaces/IForwardSys.h b/core/interfaces/IForwardSys.h new file mode 100644 index 00000000..ffeb7ee8 --- /dev/null +++ b/core/interfaces/IForwardSys.h @@ -0,0 +1,211 @@ +#ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_ +#define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_ + +#include +#include + +#define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager" +#define SMINTERFACE_FORWARDMANAGER_VERSION 1 + +namespace SourceMod +{ + enum ResultType + { + Pl_Continue = 0, /* No result */ + Pl_Handled = 1, /* Result was handled, stop at the end */ + Pl_Stop = 2, /* Result was handled, stop now */ + }; + + enum ExecType + { + ET_Ignore = 0, /* Ignore all return values, return 0 */ + ET_Single = 1, /* Only return the first exec, ignore all others */ + ET_Event = 2, /* Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */ + ET_Hook = 3, /* Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */ + }; + + /** + * @brief Abstracts multiple function calling. + * + * NOTE: Parameters should be pushed in forward order, unlike + * the virtual machine/IPluginContext order. + */ + class IForward : public IPluginFunction + { + public: + /** + * @brief Returns the name of the forward. + * + * @return Forward name. + */ + virtual const char *GetForwardName() =0; + + /** + * @brief Pushes a cell onto the current call. + * + * @param cell Parameter value to push. + * @return True if successful, false if type or count mismatch. + */ + virtual bool PushCell(cell_t cell) =0; + + /** + * @brief Pushes a float onto the current call. + * + * @param float Parameter value to push. + * @return True if successful, false if type or count mismatch. + */ + virtual bool PushFloat(float number) =0; + + /** + * @brief Pushes an array of cells onto the current call, each cell individually. + * NOTE: This is essentially a crippled version of PushArray(). + * + * @param array Array of cells. + * @param numcells Number of cells in array. + * @param each Whether or not to push as an array or individual cells. + * @return True if successful, false if too many cells were pushed. + */ + virtual bool PushCells(cell_t array[], unsigned int numcells, bool each) =0; + + /** + * @brief Pushes an array of cells onto the current call. + * + * @param array Array to copy, NULL if no initial array should be copied. + * @param cells Number of cells to allocate and optionally read from the input array. + * @param phys_addr Optional return address for physical array. + * @param copyback Whether or not changes should be copied back to the input array. + * @return True if successful, false otherwise. + */ + virtual bool PushArray(cell_t *inarray, size_t cells, cell_t **phys_addr, bool copyback) =0; + + /** + * @brief Pushes a string onto the current call. + * + * @param string String to push. + * @return True if successful, false if type or count mismatch. + */ + virtual bool PushString(const char *string) =0; + + /** + * @brief Executes the forward, resets the pushed parameter list, and performs any copybacks. + * + * @param result Pointer to store return value in (dependent on forward type). + * @param last_err Pointer to store number of successful executions in. + * @return Error code, if any. + */ + virtual int Execute(cell_t *result, unsigned int *num_functions) =0; + + /** + * @brief Returns the number of functions in this forward. + * + * @return Number of functions in forward. + */ + virtual unsigned int GetFunctionCount() =0; + + /** + * @brief Returns the method of multi-calling this forward has. + * + * @return ResultType of the forward. + */ + virtual ResultType GetResultType() =0; + }; + + + class IChangeableForward : public IForward + { + public: + /** + * @brief Removes a function from the call list. + * + * @param func Function to remove. + */ + virtual void RemoveFunction(IPluginFunction *func) =0; + + /** + * @brief Removes all instances of a plugin from the call list. + * + * @param plugin Plugin to remove instances of. + * @return Number of functions removed therein. + */ + virtual void RemoveFunctionsOfPlugin(IPlugin *plugin) =0; + + /** + * @brief Adds a function to the call list. + * + * @param func Function to add. + */ + virtual void AddFunction(IPluginFunction *func) =0; + }; + + enum ParamType + { + Param_Any = 0, + Param_Cell = 1, + Param_Float = 2, + Param_String = 3, + Param_Array = 4, + Param_VarArgs = 5, + }; + + class IForwardManager : public SMInterface + { + public: + virtual const char *GetInterfaceName() + { + return SMINTERFACE_FORWARDMANAGER_NAME; + } + virtual unsigned int GetInterfaceVersion() + { + return SMINTERFACE_FORWARDMANAGER_VERSION; + } + public: + /** + * @brief Creates a managed forward. This forward exists globally. + * The name used to create the forward is used as its public function in all target plugins. + * As new non-private plugins become loaded or unloaded, they will be automatically added + * or removed. This is ideal for global, static forwards that are never changed. + * + * @param name Name of public function to use in forward. + * @param et Execution type to be used. + * @param num_params Number of parameter this function will have. + * NOTE: For varargs, this should include the vararg parameter. + * @param types Array of type information about each parameter. If NULL, types + * are read off the vararg stream. + * @param ... If types is NULL, num_params ParamTypes should be pushed. + * @return A new IForward on success, NULL if type combination is impossible. + */ + virtual IForward *CreateForward(const char *name, ExecType et, int num_params, ParamType *types, ...) =0; + + /** + * @brief Creates an unmanaged forward. This forward exists privately. + * Unlike managed forwards, no functions are ever added by the Manager. + * However, functions will be removed automatically if their parent plugin is unloaded. + * + * @param name Name of forward (unused except for lookup, can be NULL for anonymous). + * @param et Execution type to be used. + * @param num_params Number of parameter this function will have. + * NOTE: For varargs, this should include the vararg parameter. + * @param types Array of type information about each parameter. If NULL, types + * are read off the vararg stream. + * @param ... If types is NULL, num_params ParamTypes should be pushed. + * @return A new IChangeableForward on success, NULL if type combination is impossible. + */ + virtual IChangeableForward *CreateForwardEx(const char *name, + ExecType et, + int num_params, + ParamType *types, + ...) =0; + + /** + * @brief Finds a forward by name. Does not return anonymous forwards (named NULL or ""). + * + * @param name Name of forward. + * @param ifchng Optionally store either NULL or an IChangeableForward pointer + * depending on type of forward. + * @return IForward pointer, or NULL if none found matching the name. + */ + virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0; + }; +}; + +#endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_ diff --git a/core/interfaces/IPluginSys.h b/core/interfaces/IPluginSys.h index a82164cb..bdd95a92 100644 --- a/core/interfaces/IPluginSys.h +++ b/core/interfaces/IPluginSys.h @@ -7,6 +7,8 @@ #define SMINTERFACE_PLUGINMANAGER_NAME "IPluginManager" #define SMINTERFACE_PLUGINMANAGER_VERSION 1 +#define SM_CONTEXTVAR_USER 3 + namespace SourceMod { /** @@ -34,31 +36,66 @@ namespace SourceMod Plugin_BadLoad, /* Plugin failed to load */ }; + /** * @brief Describes the object lifetime of a plugin. */ - enum PluginLifetime + enum PluginType { - PluginLifetime_Forever, /* Plugin will never be unloaded */ - PluginLifetime_Map /* Plugin will be unloaded at mapchange */ + PluginType_Private, /* Plugin is privately managed and receives no forwards */ + PluginType_MapUpdated, /* Plugin will never be unloaded unless for updates on mapchange */ + PluginType_MapOnly, /* Plugin will be removed at mapchange */ + PluginType_Global, /* Plugin will never be unloaded or updated */ + }; + + + class IPlugin; + + + /** + * @brief Encapsulates a basic function call. + * NOTE: Function calls must be atomic to one execution context. + * NOTE: This object should not be deleted. It lives for the lifetime of the plugin. + */ + class IPluginFunction + { + public: + virtual ~IPluginFunction() + { + } + + /** + * @brief Executes the function with the given parameter array. + * Parameters are read in forward order (i.e. index 0 is parameter #1) + * + * @param param Array of cell parameters. + * @param num_params Number of parameters to push. + * @param result Pointer to store result of function on return. + * @return SourcePawn error code (if any). + */ + virtual int CallFunction(cell_t *params, unsigned int num_params, cell_t *result) =0; + + /** + * @brief Returns which plugin this function belongs to. + */ + virtual IPlugin *GetParentPlugin() =0; }; /** * @brief Encapsulates a run-time plugin as maintained by SourceMod. */ - class IPlugin /*: public IUnloadableParent*/ + class IPlugin { public: - /*UnloadableParentType GetParentType() + virtual ~IPlugin() { - return ParentType_Module; - }*/ - public: + } + /** * @brief Returns the lifetime of a plugin. */ - virtual PluginLifetime GetLifetime() const =0; + virtual PluginType GetType() const =0; /** * @brief Returns the current API context being used in the plugin. @@ -110,20 +147,26 @@ namespace SourceMod */ virtual bool SetPauseState(bool paused) =0; - /** - * @brief Locks or unlocks the plugin from being updated on mapchange. - */ - virtual void SetLockForUpdates(bool lock_status) =0; - - /** - * @brief Returns whether the plugin is locked from being updated on mapchange. - */ - virtual bool GetLockForUpdates() const =0; - /** * @brief Returns the unique serial number of a plugin. */ virtual unsigned int GetSerial() const =0; + + /** + * @brief Returns a function by name. + * + * @param public_name Name of the function. + * @return A new IPluginFunction pointer, NULL if not found. + */ + virtual IPluginFunction *GetFunctionByName(const char *public_name) =0; + + /** + * @brief Returns a function by its id. + * + * @param func_id Function ID. + * @return A new IPluginFunction pointer, NULL if not found. + */ + virtual IPluginFunction *GetFunctionById(funcid_t func_id) =0; }; @@ -159,6 +202,42 @@ namespace SourceMod virtual void Release() =0; }; + /** + * @brief Listens for plugin-oriented events. + */ + class IPluginsListener + { + public: + /** + * @brief Called when a plugin is created/mapped into memory. + */ + virtual void OnPluginCreated(IPlugin *plugin) + { + } + + /** + * @brief Called when a plugin is fully loaded successfully. + */ + virtual void OnPluginLoaded(IPlugin *plugin) + { + } + + /** + * @brief Called when a plugin is unloaded (only if fully loaded). + */ + virtual void OnPluginUnloaded(IPlugin *plugin) + { + } + + /** + * @brief Called when a plugin is destroyed. + * NOTE: Always called if Created, even if load failed. + */ + virtual void OnPluginDestroyed(IPlugin *plugin) + { + } + }; + /** * @brief Manages the runtime loading and unloading of plugins. @@ -188,7 +267,7 @@ namespace SourceMod */ virtual IPlugin *LoadPlugin(const char *path, bool debug, - PluginLifetime lifetime, + PluginType type, char error[], size_t err_max) =0; @@ -221,6 +300,20 @@ namespace SourceMod * Note: This pointer must be freed using EITHER delete OR IPluginIterator::Release(). */ virtual IPluginIterator *GetPluginIterator() =0; + + /** + * @brief Adds a plugin manager listener. + * + * @param listener Pointer to a listener. + */ + virtual void AddPluginsListener(IPluginsListener *listener) =0; + + /** + * @brief Removes a plugin listener. + * + * @param listener Pointer to a listener. + */ + virtual void RemovePluginsListener(IPluginsListener *listener) =0; }; }; diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index d402be95..7b7451ee 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -224,6 +224,10 @@ + + diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 487ebc32..2b7d2cec 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -1,11 +1,15 @@ #include #include "PluginSys.h" -using namespace SourcePawn; +CPluginManager g_PluginMngr; + +CPluginManager::CPluginManager() +{ +} CPlugin *CPlugin::CreatePlugin(const char *file, bool debug_default, - PluginLifetime life, + PluginType type, char *error, size_t maxlen) { @@ -54,14 +58,15 @@ CPlugin *CPlugin::CreatePlugin(const char *file, pPlugin->m_debugging = debug_default; pPlugin->m_ctx_current.base = base; pPlugin->m_ctx_current.ctx = ctx; - pPlugin->m_lifetime = life; - pPlugin->m_lock = false; + 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; + return pPlugin; } @@ -120,14 +125,9 @@ const char *CPlugin::GetFilename() const return m_filename; } -PluginLifetime CPlugin::GetLifetime() const +PluginType CPlugin::GetType() const { - return m_lifetime; -} - -bool CPlugin::GetLockForUpdates() const -{ - return m_lock; + return m_type; } const sm_plugininfo_t *CPlugin::GetPublicInfo() const @@ -150,11 +150,6 @@ bool CPlugin::IsDebugging() const return m_debugging; } -void CPlugin::SetLockForUpdates(bool lock_status) -{ - m_lock = lock_status; -} - bool CPlugin::SetPauseState(bool paused) { if (paused && GetStatus() != Plugin_Paused) @@ -168,3 +163,144 @@ bool CPlugin::SetPauseState(bool paused) return true; } + +/******************* + * PLUGIN ITERATOR * + *******************/ +CPluginManager::CPluginIterator::CPluginIterator(List *_mylist) +{ + mylist = _mylist; +} + +IPlugin *CPluginManager::CPluginIterator::GetPlugin() +{ + return (*current); +} + +bool CPluginManager::CPluginIterator::MorePlugins() +{ + return (current != mylist->end()); +} + +void CPluginManager::CPluginIterator::NextPlugin() +{ + current++; +} + +void CPluginManager::CPluginIterator::Release() +{ + g_PluginMngr.ReleaseIterator(this); +} + +CPluginManager::CPluginIterator::~CPluginIterator() +{ +} + +void CPluginManager::CPluginIterator::Reset() +{ + current = mylist->begin(); +} + +/****************** + * PLUGIN MANAGER * + ******************/ + +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); + + if (!pPlugin) + { + return NULL; + } + + m_plugins.push_back(pPlugin); + + List::iterator iter; + IPluginsListener *pListener; + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginCreated(pPlugin); + } + + /* :TODO: a lot more... */ + + return pPlugin; +} + +bool CPluginManager::UnloadPlugin(IPlugin *plugin) +{ + CPlugin *pPlugin = (CPlugin *)plugin; + + /* :TODO: More */ + + List::iterator iter; + IPluginsListener *pListener; + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginDestroyed(pPlugin); + } + + 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; +} + +IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx) +{ + IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF]; + return pl; +} + +unsigned int CPluginManager::GetPluginCount() +{ + return m_plugins.size(); +} + +void CPluginManager::AddPluginsListener(IPluginsListener *listener) +{ + m_listeners.push_back(listener); +} + +void CPluginManager::RemovePluginsListener(IPluginsListener *listener) +{ + m_listeners.remove(listener); +} + +IPluginIterator *CPluginManager::GetPluginIterator() +{ + if (m_iters.empty()) + { + return new CPluginIterator(&m_plugins); + } else { + CPluginIterator *iter = m_iters.front(); + m_iters.pop(); + return iter; + } +} + +void CPluginManager::ReleaseIterator(CPluginIterator *iter) +{ + m_iters.push(iter); +} diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index c2d24a5e..5705dcc3 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -2,8 +2,14 @@ #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ #include +#include +#include #include "sm_globals.h" +using namespace SourceHook; + +#define SM_CONTEXTVAR_MYSELF 0 + struct ContextPair { ContextPair() : base(NULL), ctx(NULL) @@ -15,8 +21,9 @@ struct ContextPair class CPlugin : public IPlugin { + friend class CPluginManager; public: - virtual PluginLifetime GetLifetime() const; + virtual PluginType GetType() const; virtual SourcePawn::IPluginContext *GetBaseContext() const; virtual sp_context_t *GetContext() const; virtual const sm_plugininfo_t *GetPublicInfo() const; @@ -24,14 +31,14 @@ public: virtual bool IsDebugging() const; virtual PluginStatus GetStatus() const; virtual bool SetPauseState(bool paused); - virtual void SetLockForUpdates(bool lock_status); - virtual bool GetLockForUpdates() const; virtual unsigned int GetSerial() const; virtual const sp_plugin_t *GetPluginStructure() const; + virtual IPluginFunction *GetFunctionByName(const char *public_name); + virtual IPluginFunction *GetFunctionById(funcid_t func_id); public: static CPlugin *CreatePlugin(const char *file, bool debug_default, - PluginLifetime life, + PluginType life, char *error, size_t maxlen); protected: @@ -39,14 +46,56 @@ protected: private: ContextPair m_ctx_current; ContextPair m_ctx_backup; - PluginLifetime m_lifetime; + PluginType m_type; bool m_debugging; char m_filename[PLATFORM_MAX_PATH+1]; PluginStatus m_status; - bool m_lock; unsigned int m_serial; sm_plugininfo_t m_info; sp_plugin_t *m_plugin; }; +class CPluginManager : public IPluginManager +{ +public: + CPluginManager(); +public: + class CPluginIterator : public IPluginIterator + { + public: + CPluginIterator(List *mylist); + virtual ~CPluginIterator(); + virtual bool MorePlugins(); + virtual IPlugin *GetPlugin(); + virtual void NextPlugin(); + virtual void Release(); + public: + void Reset(); + private: + List *mylist; + List::iterator current; + }; + friend class CPluginManager::CPluginIterator; +public: + virtual IPlugin *LoadPlugin(const char *path, + bool debug, + PluginType type, + char error[], + size_t err_max); + virtual bool UnloadPlugin(IPlugin *plugin); + virtual IPlugin *FindPluginByContext(const sp_context_t *ctx); + virtual unsigned int GetPluginCount(); + virtual IPluginIterator *GetPluginIterator(); + virtual void AddPluginsListener(IPluginsListener *listener); + virtual void RemovePluginsListener(IPluginsListener *listener); +protected: + void ReleaseIterator(CPluginIterator *iter); +private: + List m_listeners; + List m_plugins; + CStack m_iters; +}; + +extern CPluginManager g_PluginMngr; + #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_