2006-11-11 02:19:46 +01:00
|
|
|
#ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
|
|
|
|
#define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
|
|
|
|
|
|
|
|
#include <IForwardSys.h>
|
|
|
|
#include <IPluginSys.h>
|
|
|
|
|
|
|
|
#define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager"
|
|
|
|
#define SMINTERFACE_FORWARDMANAGER_VERSION 1
|
|
|
|
|
2006-11-11 12:10:24 +01:00
|
|
|
/**
|
|
|
|
* There is some very important documentation at the bottom of this file.
|
|
|
|
* Readers interested in knowing more about the forward system, scrolling down is a must!
|
|
|
|
*/
|
|
|
|
|
2006-11-11 02:19:46 +01:00
|
|
|
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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2006-11-11 12:10:24 +01:00
|
|
|
* :TODO: finish this spec
|
2006-11-11 02:19:46 +01:00
|
|
|
* @brief Abstracts multiple function calling.
|
|
|
|
* NOTE: Parameters should be pushed in forward order, unlike
|
|
|
|
* the virtual machine/IPluginContext order.
|
2006-11-11 12:10:24 +01:00
|
|
|
* NOTE: Some functions are repeated in here because their documentation differs
|
|
|
|
* from their IPluginFunction equivalents.
|
2006-11-11 02:19:46 +01:00
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
class IForward : public ICallable
|
2006-11-11 02:19:46 +01:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Returns the name of the forward.
|
|
|
|
*
|
|
|
|
* @return Forward name.
|
|
|
|
*/
|
|
|
|
virtual const char *GetForwardName() =0;
|
|
|
|
|
|
|
|
/**
|
2006-11-11 12:10:24 +01:00
|
|
|
* @brief Returns the number of functions in this forward.
|
2006-11-11 02:19:46 +01:00
|
|
|
*
|
2006-11-11 12:10:24 +01:00
|
|
|
* @return Number of functions in forward.
|
2006-11-11 02:19:46 +01:00
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
virtual unsigned int GetFunctionCount() =0;
|
2006-11-11 02:19:46 +01:00
|
|
|
|
|
|
|
/**
|
2006-11-11 12:10:24 +01:00
|
|
|
* @brief Returns the method of multi-calling this forward has.
|
2006-11-11 02:19:46 +01:00
|
|
|
*
|
2006-11-11 12:10:24 +01:00
|
|
|
* @return ExecType of the forward.
|
2006-11-11 02:19:46 +01:00
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
virtual ExecType GetExecType() =0;
|
2006-11-11 02:19:46 +01:00
|
|
|
|
|
|
|
/**
|
2006-11-11 12:10:24 +01:00
|
|
|
* @brief Executes the forward.
|
2006-11-11 02:19:46 +01:00
|
|
|
*
|
2006-11-11 12:10:24 +01:00
|
|
|
* @param result Pointer to store result in.
|
|
|
|
* @param num_functions Optionally filled with the number of function sucessfully executed.
|
2006-11-11 02:19:46 +01:00
|
|
|
* @return Error code, if any.
|
|
|
|
*/
|
|
|
|
virtual int Execute(cell_t *result, unsigned int *num_functions) =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.
|
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
|
2006-11-11 02:19:46 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Adds a function to the call list.
|
2006-11-11 12:10:24 +01:00
|
|
|
* NOTE: Cannot be used during a call.
|
2006-11-11 02:19:46 +01:00
|
|
|
*
|
|
|
|
* @param func Function to add.
|
2006-11-11 12:10:24 +01:00
|
|
|
* @return True on success, otherwise false.
|
2006-11-11 02:19:46 +01:00
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
virtual bool AddFunction(IPluginFunction *func) =0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Adds a function to the call list.
|
|
|
|
* NOTE: Cannot be used during a call.
|
|
|
|
*
|
|
|
|
* @param ctx Context to use as a look-up.
|
|
|
|
* @param funcid Function id to add.
|
|
|
|
* @return True on success, otherwise false.
|
|
|
|
*/
|
|
|
|
virtual bool AddFunction(sp_context_t *ctx, funcid_t index) =0;
|
2006-11-11 02:19:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
enum ParamType
|
|
|
|
{
|
2006-11-11 12:10:24 +01:00
|
|
|
Param_Any = 0, //Any type will be accepted
|
|
|
|
Param_Cell = 1, //Only a cell will be accepted
|
|
|
|
Param_Float = 2, //Only a float value will be accepted
|
|
|
|
Param_String = 3, //Only a string will be accepted
|
|
|
|
Param_Array = 4, //Only a 1D array will be accepted
|
|
|
|
Param_VarArgs = 5, //Anything will be accepted
|
|
|
|
ParamTypes_Total = 6,
|
2006-11-11 02:19:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2006-11-11 12:10:24 +01:00
|
|
|
virtual IForward *CreateForward(const char *name,
|
|
|
|
ExecType et,
|
|
|
|
unsigned int num_params,
|
|
|
|
ParamType *types,
|
|
|
|
...) =0;
|
2006-11-11 02:19:46 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2006-11-11 12:10:24 +01:00
|
|
|
/**
|
|
|
|
* In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing
|
|
|
|
* a function ID and an AMX structure. The forward structure itself did very little but hold parameter types.
|
|
|
|
* An execution call worked like this:
|
|
|
|
* - executeForward() took in a function id and a list of parameters
|
|
|
|
* - for each contained plugin:
|
|
|
|
* - the list of parameters was preprocessed and pushed
|
|
|
|
* - the call was made
|
|
|
|
* - the list was freed and copybacks were made
|
|
|
|
* - return
|
|
|
|
*
|
|
|
|
* The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is
|
|
|
|
* varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting. You also
|
|
|
|
* lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM.
|
|
|
|
*
|
|
|
|
* SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that
|
|
|
|
* each plugin has a table of functions, and each function is an ICallable object. As well as being an ICallable,
|
|
|
|
* each function is an IPluginFunction. An ICallable simply describes the process of adding parameters to a
|
|
|
|
* function call. An IPluginFunction describes the process of actually calling a function and performing allocation,
|
|
|
|
* copybacks, and deallocations.
|
|
|
|
*
|
|
|
|
* A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions. Thus, the same
|
|
|
|
* API can be easily wrapped around a simple list, and it will look transparent to the user.
|
|
|
|
* Advantages:
|
|
|
|
* 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection.
|
|
|
|
* 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime..
|
|
|
|
* 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked.
|
|
|
|
* 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward.
|
|
|
|
* This helps alleviate messes like Fakemeta.
|
|
|
|
* 5) Parameter pushing is type-checked and allows for variable arguments.
|
|
|
|
*
|
|
|
|
* Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
|
|
|
|
* object oriented, rather than AMX Mod X, which hides the objects behind static functions. It is entirely a design
|
|
|
|
* issue, rather than a usability one. The interesting part is when it gets to implementation, which is when the
|
|
|
|
* problems for SourceMod arise. Specifically, the implementation is easier in GENERAL -- the tough part is the oddball
|
|
|
|
* cases.
|
|
|
|
*
|
|
|
|
* Observe the calling process:
|
|
|
|
* - Each parameter is pushed using the ICallable interface. For each parameter:
|
|
|
|
* - For each function in the collection, the parameter is processed and pushed.
|
|
|
|
* - For each function in the collection:
|
|
|
|
* - The call is made.
|
|
|
|
* - Copy backs are performed.
|
|
|
|
* - Return
|
|
|
|
*
|
|
|
|
* Astute readers will note the problems - the parameters are processed individually for each push,
|
|
|
|
* rather than for each call. This means:
|
|
|
|
* 1) More memory is used. Specifically, rather than N params of memory, you now have N params * M plugins.
|
|
|
|
* This is because, again, parameters are processed per function object, per-push, before the call.
|
|
|
|
* 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
|
|
|
|
* 3) Copybacks won't work as expected.
|
|
|
|
*
|
|
|
|
* Number 3 is hard to see. For example, say a forward has two functions, and an array is pushed with copyback.
|
|
|
|
* The array gets pushed and copied into each plugin. When the call is made, each plugin now has separate copies of
|
|
|
|
* the array. When the copyback is performed, it gets mirrored to the originall address, but not to the next plugin!
|
|
|
|
|
|
|
|
* Implementing this is "difficult." To be re-entrant, an IPluginFunction can't tell you anything
|
|
|
|
* about its copybacks after it has returned, because its internal states were reset long ago.
|
|
|
|
*
|
|
|
|
* In order to implement this feature, IPluginFunction::Execute function now lets you pass a listener in.
|
|
|
|
* This listener is notified each time an array parameter is about to be copied back. Specifically, the forward
|
|
|
|
* uses this to detect when it needs to push a new parameter out to the next plugin. When the forward gets the
|
|
|
|
* call back, it detects whether there is another plugin in the queue. If there is, it grabs the address it will
|
|
|
|
* be using for the same arrays, and specifies it as the new copy-back point. If no plugins are left, it allows
|
|
|
|
* the copyback to chain up to the original address.
|
|
|
|
*
|
|
|
|
* This wonderful little hack is somewhat reminiscent of how SourceHook parameter rewrite chains work. It seems
|
|
|
|
* ugly at first, but it is actually the correct design pattern to apply to an otherwise awful problem.
|
|
|
|
*
|
|
|
|
* Note that there are other solutions to this that aren't based on a visitor-like pattern. For example, the Function
|
|
|
|
* class could expose a "set new original address" function. Then after arrays are pushed, the arrays are re-browsed,
|
|
|
|
* and function #1 gets function #2's original address, function #2 gets function #3's original address, et cetera.
|
|
|
|
* This extra browse step is a bit less efficient though, since the "visitor" method implies only taking action when
|
|
|
|
* necessary. Furthermore, it would require exposing such a "set address" function, which should fire a red flag
|
|
|
|
* that the API is doing something it shouldn't (namely, exposing the direct setting of very internal properties).
|
|
|
|
*/
|
|
|
|
|
2006-11-11 02:19:46 +01:00
|
|
|
#endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
|