sourcemod/core/interfaces/IForwardSys.h
David Anderson bad69571b6 rewrote forward API to use cached parameter pushing instead of immediate pushing
removed copy back cruft since it's no longer needed
removed PushCells() from API requirements, not needed
adjusted documentation and added TODO list to ForwardSys.cpp
various internal improvements

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40188
2006-11-12 09:51:39 +00:00

340 lines
14 KiB
C++

#ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
#define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
#include <IForwardSys.h>
#include <IPluginSys.h>
#include <IPluginFunction.h>
#define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager"
#define SMINTERFACE_FORWARDMANAGER_VERSION 1
/**
* 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!
*/
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 last 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 */
ET_Custom = 4, /* Ignored or handled by an IForwardFilter */
};
class IForward;
class IForwardFilter
{
public:
/**
* @brief Called when an error occurs executing a plugin.
*
* @param fwd IForward pointer.
* @param func IPluginFunction pointer to the failed function.
* @param err Error code.
* @return True to handle, false to pass to global error reporter.
*/
virtual bool OnErrorReport(IForward *fwd,
IPluginFunction *func,
int err)
{
return false;
}
/**
* @brief Called after each function return during execution.
* NOTE: Only used for ET_Custom.
*
* @param fwd IForward pointer.
* @param func IPluginFunction pointer to the executed function.
* @param retval Pointer to current return value (can be modified).
* @return ResultType denoting the next action to take.
*/
virtual ResultType OnFunctionReturn(IForward *fwd,
IPluginFunction *func,
cell_t *retval)
{
return Pl_Continue;
}
/**
* @brief Called when execution begins.
*/
virtual void OnExecuteBegin()
{
};
/**
* @brief Called when execution ends.
*
* @param final_ret Final return value (modifiable).
* @param success Number of successful execs.
* @param failed Number of failed execs.
*/
virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed)
{
}
};
/**
* @brief Abstracts multiple function calling.
*
* NOTE: Parameters should be pushed in forward order, unlike
* the virtual machine/IPluginContext order.
* NOTE: Some functions are repeated in here because their
* documentation differs from their IPluginFunction equivalents.
* Missing are the Push functions, whose only doc change is that
* they throw SP_ERROR_PARAM on type mismatches.
*/
class IForward : public ICallable
{
public:
/**
* @brief Returns the name of the forward.
*
* @return Forward name.
*/
virtual const char *GetForwardName() =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 ExecType of the forward.
*/
virtual ExecType GetExecType() =0;
/**
* @brief Executes the forward.
*
* @param result Pointer to store result in.
* @param filter Optional pointer to an IForwardFilter.
* @return Error code, if any.
*/
virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0;
/**
* @brief Pushes an array of cells onto the current call. Different rules than ICallable.
* NOTE: On Execute, the pointer passed will be modified according to the copyback rule.
*
* @param array Array to copy. Cannot be NULL, unlike ICallable's version.
* @param cells Number of cells to allocate and optionally read from the input array.
* @param phys_addr Unused. If a value is passed, it will be filled with NULL.
* @param flags Whether or not changes should be copied back to the input array.
* @return Error code, if any.
*/
virtual int PushArray(cell_t *inarray,
unsigned int cells,
cell_t **phys_addr,
int flags=0) =0;
};
class IChangeableForward : public IForward
{
public:
/**
* @brief Removes a function from the call list.
* NOTE: Only removes one instance.
*
* @param func Function to remove.
* @return Whether or not the function was removed.
*/
virtual bool 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 unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
/**
* @brief Adds a function to the call list.
* NOTE: Cannot be used during an incompleted call.
* NOTE: If used during a call, function is temporarily queued until calls are over.
* NOTE: Adding mulitple copies of the same function is illegal.
*
* @param func Function to add.
* @return True on success, otherwise false.
*/
virtual bool AddFunction(IPluginFunction *func) =0;
/**
* @brief Adds a function to the call list.
* NOTE: Cannot be used during an incompleted call.
* NOTE: If used during a call, function is temporarily queued until calls are over.
*
* @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;
};
#define SP_PARAMTYPE_ANY 0
#define SP_PARAMFLAG_BYREF (1<<0)
#define SP_PARAMTYPE_CELL (1<<1)
#define SP_PARAMTYPE_FLOAT (2<<1)
#define SP_PARAMTYPE_STRING (3<<1)|SP_PARAMFLAG_BYREF
#define SP_PARAMTYPE_ARRAY (4<<1)|SP_PARAMFLAG_BYREF
#define SP_PARAMTYPE_VARARG (5<<1)
enum ParamType
{
Param_Any = SP_PARAMTYPE_ANY,
Param_Cell = SP_PARAMTYPE_CELL,
Param_Float = SP_PARAMTYPE_FLOAT,
Param_String = SP_PARAMTYPE_STRING,
Param_Array = SP_PARAMTYPE_ARRAY,
Param_VarArgs = SP_PARAMTYPE_VARARG,
Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF,
Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF,
};
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,
unsigned 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;
};
};
/**
* 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 has to cache
* parameter pushing until execution. Without this, multiple function calls can be started across one plugin, which
* will result in heap corruption given SourcePawn's implementation.
*
* Observe the new calling process:
* - Each parameter is pushed into a local cache using the ICallable interface.
* - For each function in the collection:
* - Each parameter is decoded and -pushed into the function.
* - The call is made.
* - Return
*
* Astute readers will note the (minor) problems:
* 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 cached both per-function and per-forward.
* 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
*
* HISTORICAL NOTES:
* There used to be a # about copy backs.
* Note that originally, the Forward implementation was a thin wrapper around IForwards. It did not cache pushes,
* and instead immediately fired them to each internal plugin. This was to allow users to know that pointers would
* be immediately resolved. Unfortunately, this became extremely burdensome on the API and exposed many problems,
* the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same
* plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
* and become impossible to re-arrange without some global heap tracking mechanis. It also made iterative copy backs
* for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
* Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X
* uses. It is both faster and works better.
*/
#endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_