dvander@372: /** dvander@375: * vim: set ts=4 : dvander@372: * =============================================================== dvander@376: * SourceMod, Copyright (C) 2004-2007 AlliedModders LLC. dvander@376: * All rights reserved. dvander@372: * =============================================================== dvander@372: * dvander@379: * This file is part of the SourceMod/SourcePawn SDK. This file may only be dvander@379: * used or modified under the Terms and Conditions of its License Agreement, dvander@379: * which is found in public/licenses/LICENSE.txt. As of this notice, derivative dvander@379: * works must be licensed under the GNU General Public License (version 2 or dvander@379: * greater). A copy of the GPL is included under public/licenses/GPL.txt. dvander@379: * dvander@379: * To view the latest information, see: http://www.sourcemod.net/license.php dvander@372: * dvander@372: * Version: $Id$ dvander@372: */ dvander@372: dvander@324: #ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_ dvander@324: #define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_ dvander@324: dvander@372: /** dvander@372: * @file IForwardSys.h dvander@372: * @brief Defines the interface for managing collections ("forwards") of plugin calls. dvander@372: * dvander@372: * The Forward System is responsible for managing automated collections of IPluginFunctions. dvander@372: * It thus provides wrappers to calling many functions at once. There are two types of such dvander@372: * wrappers: Managed and Unmanaged. Confusingly, these terms refer to whether the user manages dvander@372: * the forwards, not Core. Managed forwards are completely managed by the user, and are custom dvander@372: * editable collections. Unmanaged forwards are the opposite, and will only work on a single global dvander@372: * function name in all plugins. dvander@372: */ dvander@372: dvander@324: #include dvander@327: #include dvander@327: dvander@327: using namespace SourcePawn; dvander@324: dvander@324: #define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager" dvander@324: #define SMINTERFACE_FORWARDMANAGER_VERSION 1 dvander@324: dvander@372: /* dvander@324: * There is some very important documentation at the bottom of this file. dvander@324: * Readers interested in knowing more about the forward system, scrolling down is a must! dvander@324: */ dvander@324: dvander@324: namespace SourceMod dvander@324: { dvander@372: /** dvander@372: * @brief Defines the event hook result types plugins can return. dvander@372: */ dvander@324: enum ResultType dvander@324: { dvander@372: Pl_Continue = 0, /**< No result */ dvander@481: Pl_Handled = 3, /**< Result was handled, stop at the end */ dvander@481: Pl_Stop = 4, /**< Result was handled, stop now */ dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Defines how a forward iterates through plugin functions. dvander@372: */ dvander@324: enum ExecType dvander@324: { dvander@372: ET_Ignore = 0, /**< Ignore all return values, return 0 */ dvander@372: ET_Single = 1, /**< Only return the last exec, ignore all others */ dvander@372: ET_Event = 2, /**< Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */ dvander@372: ET_Hook = 3, /**< Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */ dvander@372: ET_Custom = 4, /**< Ignored or handled by an IForwardFilter */ dvander@324: }; dvander@324: dvander@324: class IForward; dvander@324: dvander@372: /** dvander@372: * @brief Allows interception of how the Forward System executes functions. dvander@372: */ dvander@324: class IForwardFilter dvander@324: { dvander@324: public: dvander@324: /** dvander@324: * @brief Called when an error occurs executing a plugin. dvander@324: * dvander@324: * @param fwd IForward pointer. dvander@324: * @param func IPluginFunction pointer to the failed function. dvander@324: * @param err Error code. dvander@324: * @return True to handle, false to pass to global error reporter. dvander@324: */ dvander@324: virtual bool OnErrorReport(IForward *fwd, dvander@324: IPluginFunction *func, dvander@324: int err) dvander@324: { dvander@324: return false; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called after each function return during execution. dvander@324: * NOTE: Only used for ET_Custom. dvander@324: * dvander@324: * @param fwd IForward pointer. dvander@324: * @param func IPluginFunction pointer to the executed function. dvander@324: * @param retval Pointer to current return value (can be modified). dvander@324: * @return ResultType denoting the next action to take. dvander@324: */ dvander@324: virtual ResultType OnFunctionReturn(IForward *fwd, dvander@324: IPluginFunction *func, dvander@324: cell_t *retval) dvander@324: { dvander@324: return Pl_Continue; dvander@324: } dvander@324: dvander@324: /** dvander@324: * @brief Called when execution begins. dvander@324: */ dvander@324: virtual void OnExecuteBegin() dvander@324: { dvander@324: }; dvander@324: dvander@324: /** dvander@324: * @brief Called when execution ends. dvander@324: * dvander@324: * @param final_ret Final return value (modifiable). dvander@324: * @param success Number of successful execs. dvander@324: * @param failed Number of failed execs. dvander@324: */ dvander@324: virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed) dvander@324: { dvander@324: } dvander@324: }; dvander@324: dvander@324: /** dvander@372: * @brief Unmanaged Forward, abstracts calling multiple functions as "forwards," or collections of functions. dvander@324: * dvander@372: * Parameters should be pushed in forward order, unlike the virtual machine/IPluginContext order. dvander@372: * Some functions are repeated in here because their documentation differs from their IPluginFunction equivalents. dvander@372: * Missing are the Push functions, whose only doc change is that they throw SP_ERROR_PARAM on type mismatches. dvander@324: */ dvander@324: class IForward : public ICallable dvander@324: { dvander@324: public: dvander@372: /** Virtual Destructor */ dvander@324: virtual ~IForward() dvander@324: { dvander@324: } dvander@324: public: dvander@324: /** dvander@324: * @brief Returns the name of the forward. dvander@324: * dvander@324: * @return Forward name. dvander@324: */ faluco@430: virtual const char *GetForwardName() const =0; dvander@324: dvander@324: /** dvander@324: * @brief Returns the number of functions in this forward. dvander@324: * dvander@324: * @return Number of functions in forward. dvander@324: */ faluco@430: virtual unsigned int GetFunctionCount() const =0; dvander@324: dvander@324: /** dvander@324: * @brief Returns the method of multi-calling this forward has. dvander@324: * dvander@324: * @return ExecType of the forward. dvander@324: */ faluco@430: virtual ExecType GetExecType() const =0; dvander@324: dvander@324: /** dvander@324: * @brief Executes the forward. dvander@324: * dvander@447: * @param result Optional pointer to store result in. dvander@324: * @param filter Optional pointer to an IForwardFilter. dvander@324: * @return Error code, if any. dvander@324: */ dvander@324: virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0; dvander@324: dvander@324: /** dvander@324: * @brief Pushes an array of cells onto the current call. Different rules than ICallable. dvander@324: * NOTE: On Execute, the pointer passed will be modified according to the copyback rule. dvander@324: * dvander@324: * @param inarray Array to copy. Cannot be NULL, unlike ICallable's version. dvander@324: * @param cells Number of cells to allocate and optionally read from the input array. dvander@324: * @param phys_addr Unused. If a value is passed, it will be filled with NULL. dvander@324: * @param flags Whether or not changes should be copied back to the input array. dvander@324: * @return Error code, if any. dvander@324: */ dvander@324: virtual int PushArray(cell_t *inarray, dvander@324: unsigned int cells, dvander@324: cell_t **phys_addr, dvander@324: int flags=0) =0; dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Managed Forward, same as IForward, except the collection can be modified. dvander@372: */ dvander@324: class IChangeableForward : public IForward dvander@324: { dvander@324: public: dvander@324: /** dvander@324: * @brief Removes a function from the call list. dvander@324: * NOTE: Only removes one instance. dvander@324: * dvander@324: * @param func Function to remove. dvander@324: * @return Whether or not the function was removed. dvander@324: */ dvander@324: virtual bool RemoveFunction(IPluginFunction *func) =0; dvander@324: dvander@324: /** dvander@324: * @brief Removes all instances of a plugin from the call list. dvander@324: * dvander@324: * @param plugin Plugin to remove instances of. dvander@324: * @return Number of functions removed therein. dvander@324: */ dvander@324: virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0; dvander@324: dvander@324: /** dvander@324: * @brief Adds a function to the call list. dvander@324: * NOTE: Cannot be used during an incompleted call. dvander@324: * NOTE: If used during a call, function is temporarily queued until calls are over. dvander@324: * NOTE: Adding mulitple copies of the same function is illegal. dvander@324: * dvander@324: * @param func Function to add. dvander@324: * @return True on success, otherwise false. dvander@324: */ dvander@324: virtual bool AddFunction(IPluginFunction *func) =0; dvander@324: dvander@324: /** dvander@324: * @brief Adds a function to the call list. dvander@324: * NOTE: Cannot be used during an incompleted call. dvander@324: * NOTE: If used during a call, function is temporarily queued until calls are over. dvander@324: * dvander@324: * @param ctx Context to use as a look-up. dvander@372: * @param index Function id to add. dvander@324: * @return True on success, otherwise false. dvander@324: */ dvander@327: virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0; dvander@324: }; dvander@324: dvander@324: #define SP_PARAMTYPE_ANY 0 dvander@324: #define SP_PARAMFLAG_BYREF (1<<0) dvander@324: #define SP_PARAMTYPE_CELL (1<<1) dvander@324: #define SP_PARAMTYPE_FLOAT (2<<1) dvander@324: #define SP_PARAMTYPE_STRING (3<<1)|SP_PARAMFLAG_BYREF dvander@324: #define SP_PARAMTYPE_ARRAY (4<<1)|SP_PARAMFLAG_BYREF dvander@324: #define SP_PARAMTYPE_VARARG (5<<1) dvander@324: dvander@372: /** dvander@372: * @brief Describes the various ways to pass parameters to plugins. dvander@372: */ dvander@324: enum ParamType dvander@324: { dvander@372: Param_Any = SP_PARAMTYPE_ANY, /**< Any data type can be pushed */ dvander@372: Param_Cell = SP_PARAMTYPE_CELL, /**< Only basic cells can be pushed */ dvander@372: Param_Float = SP_PARAMTYPE_FLOAT, /**< Only floats can be pushed */ dvander@372: Param_String = SP_PARAMTYPE_STRING, /**< Only strings can be pushed */ dvander@372: Param_Array = SP_PARAMTYPE_ARRAY, /**< Only arrays can be pushed */ dvander@372: Param_VarArgs = SP_PARAMTYPE_VARARG, /**< Same as "..." in plugins, anything can be pushed, but it will always be byref */ dvander@372: Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF, /**< Only a cell by reference can be pushed */ dvander@372: Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF, /**< Only a float by reference can be pushed */ dvander@324: }; dvander@324: dvander@372: /** dvander@372: * @brief Provides functions for creating/destroying managed and unmanaged forwards. dvander@372: */ dvander@324: class IForwardManager : public SMInterface dvander@324: { dvander@324: public: dvander@324: virtual const char *GetInterfaceName() dvander@324: { dvander@324: return SMINTERFACE_FORWARDMANAGER_NAME; dvander@324: } dvander@324: virtual unsigned int GetInterfaceVersion() dvander@324: { dvander@324: return SMINTERFACE_FORWARDMANAGER_VERSION; dvander@324: } dvander@324: public: dvander@324: /** dvander@324: * @brief Creates a managed forward. This forward exists globally. dvander@324: * The name used to create the forward is used as its public function in all target plugins. dvander@324: * As new non-private plugins become loaded or unloaded, they will be automatically added dvander@324: * or removed. This is ideal for global, static forwards that are never changed. dvander@324: * dvander@324: * @param name Name of public function to use in forward. dvander@324: * @param et Execution type to be used. dvander@324: * @param num_params Number of parameter this function will have. dvander@324: * NOTE: For varargs, this should include the vararg parameter. dvander@324: * @param types Array of type information about each parameter. If NULL, types dvander@324: * are read off the vararg stream. dvander@324: * @param ... If types is NULL, num_params ParamTypes should be pushed. dvander@324: * @return A new IForward on success, NULL if type combination is impossible. dvander@324: */ dvander@324: virtual IForward *CreateForward(const char *name, dvander@324: ExecType et, dvander@324: unsigned int num_params, dvander@324: ParamType *types, dvander@324: ...) =0; dvander@324: dvander@324: /** dvander@324: * @brief Creates an unmanaged forward. This forward exists privately. dvander@324: * Unlike managed forwards, no functions are ever added by the Manager. dvander@324: * However, functions will be removed automatically if their parent plugin is unloaded. dvander@324: * dvander@324: * @param name Name of forward (unused except for lookup, can be NULL for anonymous). dvander@324: * @param et Execution type to be used. dvander@324: * @param num_params Number of parameter this function will have. dvander@324: * NOTE: For varargs, this should include the vararg parameter. dvander@324: * @param types Array of type information about each parameter. If NULL, types dvander@324: * are read off the vararg stream. dvander@324: * @param ... If types is NULL, num_params ParamTypes should be pushed. dvander@324: * @return A new IChangeableForward on success, NULL if type combination is impossible. dvander@324: */ dvander@324: virtual IChangeableForward *CreateForwardEx(const char *name, dvander@324: ExecType et, dvander@324: int num_params, dvander@324: ParamType *types, dvander@324: ...) =0; dvander@324: dvander@324: /** dvander@324: * @brief Finds a forward by name. Does not return anonymous forwards (named NULL or ""). dvander@324: * dvander@324: * @param name Name of forward. dvander@324: * @param ifchng Optionally store either NULL or an IChangeableForward pointer dvander@324: * depending on type of forward. dvander@324: * @return IForward pointer, or NULL if none found matching the name. dvander@324: */ dvander@324: virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0; dvander@324: dvander@324: /** dvander@324: * @brief Frees and destroys a forward object. dvander@324: * dvander@324: * @param forward An IForward created by CreateForward() or CreateForwardEx(). dvander@324: */ dvander@324: virtual void ReleaseForward(IForward *forward) =0; dvander@324: }; ds@432: } dvander@324: dvander@372: /* dvander@324: * In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing dvander@324: * a function ID and an AMX structure. The forward structure itself did very little but hold parameter types. dvander@324: * An execution call worked like this: dvander@324: * - executeForward() took in a function id and a list of parameters dvander@324: * - for each contained plugin: dvander@324: * - the list of parameters was preprocessed and pushed dvander@324: * - the call was made dvander@324: * - the list was freed and copybacks were made dvander@324: * - return dvander@324: * dvander@324: * The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is dvander@324: * varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting. You also dvander@324: * lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM. dvander@324: * dvander@324: * SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that dvander@324: * each plugin has a table of functions, and each function is an ICallable object. As well as being an ICallable, dvander@324: * each function is an IPluginFunction. An ICallable simply describes the process of adding parameters to a dvander@324: * function call. An IPluginFunction describes the process of actually calling a function and performing allocation, dvander@324: * copybacks, and deallocations. dvander@324: * dvander@324: * A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions. Thus, the same dvander@324: * API can be easily wrapped around a simple list, and it will look transparent to the user. dvander@324: * Advantages: dvander@324: * 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection. dvander@324: * 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime.. dvander@324: * 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked. dvander@324: * 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward. dvander@324: * This helps alleviate messes like Fakemeta. dvander@324: * 5) Parameter pushing is type-checked and allows for variable arguments. dvander@324: * dvander@324: * Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system dvander@324: * object oriented, rather than AMX Mod X, which hides the objects behind static functions. It is entirely a design dvander@324: * issue, rather than a usability one. The interesting part is when it gets to implementation, which has to cache dvander@324: * parameter pushing until execution. Without this, multiple function calls can be started across one plugin, which dvander@324: * will result in heap corruption given SourcePawn's implementation. dvander@324: * dvander@324: * Observe the new calling process: dvander@324: * - Each parameter is pushed into a local cache using the ICallable interface. dvander@324: * - For each function in the collection: dvander@324: * - Each parameter is decoded and -pushed into the function. dvander@324: * - The call is made. dvander@324: * - Return dvander@324: * dvander@324: * Astute readers will note the (minor) problems: dvander@324: * 1) More memory is used. Specifically, rather than N params of memory, you now have N params * M plugins. dvander@324: * This is because, again, parameters are cached both per-function and per-forward. dvander@324: * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual. dvander@324: * dvander@324: * HISTORICAL NOTES: dvander@324: * There used to be a # about copy backs. dvander@324: * Note that originally, the Forward implementation was a thin wrapper around IForwards. It did not cache pushes, dvander@324: * and instead immediately fired them to each internal plugin. This was to allow users to know that pointers would dvander@324: * be immediately resolved. Unfortunately, this became extremely burdensome on the API and exposed many problems, dvander@324: * the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same dvander@324: * plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order ds@432: * and become impossible to re-arrange without some global heap tracking mechanism. It also made iterative copy backs dvander@324: * for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy. dvander@324: * Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X dvander@324: * uses. It is both faster and works better. dvander@324: */ dvander@324: dvander@324: #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_