1.1 --- a/core/interfaces/IForwardSys.h Sat Nov 11 05:49:52 2006 +0000
1.2 +++ b/core/interfaces/IForwardSys.h Sat Nov 11 11:10:24 2006 +0000
1.3 @@ -7,6 +7,11 @@
1.4 #define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager"
1.5 #define SMINTERFACE_FORWARDMANAGER_VERSION 1
1.6
1.7 +/**
1.8 + * There is some very important documentation at the bottom of this file.
1.9 + * Readers interested in knowing more about the forward system, scrolling down is a must!
1.10 + */
1.11 +
1.12 namespace SourceMod
1.13 {
1.14 enum ResultType
1.15 @@ -25,12 +30,14 @@
1.16 };
1.17
1.18 /**
1.19 + * :TODO: finish this spec
1.20 * @brief Abstracts multiple function calling.
1.21 - *
1.22 * NOTE: Parameters should be pushed in forward order, unlike
1.23 * the virtual machine/IPluginContext order.
1.24 + * NOTE: Some functions are repeated in here because their documentation differs
1.25 + * from their IPluginFunction equivalents.
1.26 */
1.27 - class IForward : public IPluginFunction
1.28 + class IForward : public ICallable
1.29 {
1.30 public:
1.31 /**
1.32 @@ -41,61 +48,6 @@
1.33 virtual const char *GetForwardName() =0;
1.34
1.35 /**
1.36 - * @brief Pushes a cell onto the current call.
1.37 - *
1.38 - * @param cell Parameter value to push.
1.39 - * @return True if successful, false if type or count mismatch.
1.40 - */
1.41 - virtual bool PushCell(cell_t cell) =0;
1.42 -
1.43 - /**
1.44 - * @brief Pushes a float onto the current call.
1.45 - *
1.46 - * @param float Parameter value to push.
1.47 - * @return True if successful, false if type or count mismatch.
1.48 - */
1.49 - virtual bool PushFloat(float number) =0;
1.50 -
1.51 - /**
1.52 - * @brief Pushes an array of cells onto the current call, each cell individually.
1.53 - * NOTE: This is essentially a crippled version of PushArray().
1.54 - *
1.55 - * @param array Array of cells.
1.56 - * @param numcells Number of cells in array.
1.57 - * @param each Whether or not to push as an array or individual cells.
1.58 - * @return True if successful, false if too many cells were pushed.
1.59 - */
1.60 - virtual bool PushCells(cell_t array[], unsigned int numcells, bool each) =0;
1.61 -
1.62 - /**
1.63 - * @brief Pushes an array of cells onto the current call.
1.64 - *
1.65 - * @param array Array to copy, NULL if no initial array should be copied.
1.66 - * @param cells Number of cells to allocate and optionally read from the input array.
1.67 - * @param phys_addr Optional return address for physical array.
1.68 - * @param copyback Whether or not changes should be copied back to the input array.
1.69 - * @return True if successful, false otherwise.
1.70 - */
1.71 - virtual bool PushArray(cell_t *inarray, size_t cells, cell_t **phys_addr, bool copyback) =0;
1.72 -
1.73 - /**
1.74 - * @brief Pushes a string onto the current call.
1.75 - *
1.76 - * @param string String to push.
1.77 - * @return True if successful, false if type or count mismatch.
1.78 - */
1.79 - virtual bool PushString(const char *string) =0;
1.80 -
1.81 - /**
1.82 - * @brief Executes the forward, resets the pushed parameter list, and performs any copybacks.
1.83 - *
1.84 - * @param result Pointer to store return value in (dependent on forward type).
1.85 - * @param last_err Pointer to store number of successful executions in.
1.86 - * @return Error code, if any.
1.87 - */
1.88 - virtual int Execute(cell_t *result, unsigned int *num_functions) =0;
1.89 -
1.90 - /**
1.91 * @brief Returns the number of functions in this forward.
1.92 *
1.93 * @return Number of functions in forward.
1.94 @@ -105,9 +57,18 @@
1.95 /**
1.96 * @brief Returns the method of multi-calling this forward has.
1.97 *
1.98 - * @return ResultType of the forward.
1.99 + * @return ExecType of the forward.
1.100 */
1.101 - virtual ResultType GetResultType() =0;
1.102 + virtual ExecType GetExecType() =0;
1.103 +
1.104 + /**
1.105 + * @brief Executes the forward.
1.106 + *
1.107 + * @param result Pointer to store result in.
1.108 + * @param num_functions Optionally filled with the number of function sucessfully executed.
1.109 + * @return Error code, if any.
1.110 + */
1.111 + virtual int Execute(cell_t *result, unsigned int *num_functions) =0;
1.112 };
1.113
1.114
1.115 @@ -127,24 +88,37 @@
1.116 * @param plugin Plugin to remove instances of.
1.117 * @return Number of functions removed therein.
1.118 */
1.119 - virtual void RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
1.120 + virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
1.121
1.122 /**
1.123 * @brief Adds a function to the call list.
1.124 + * NOTE: Cannot be used during a call.
1.125 *
1.126 * @param func Function to add.
1.127 + * @return True on success, otherwise false.
1.128 */
1.129 - virtual void AddFunction(IPluginFunction *func) =0;
1.130 + virtual bool AddFunction(IPluginFunction *func) =0;
1.131 +
1.132 + /**
1.133 + * @brief Adds a function to the call list.
1.134 + * NOTE: Cannot be used during a call.
1.135 + *
1.136 + * @param ctx Context to use as a look-up.
1.137 + * @param funcid Function id to add.
1.138 + * @return True on success, otherwise false.
1.139 + */
1.140 + virtual bool AddFunction(sp_context_t *ctx, funcid_t index) =0;
1.141 };
1.142
1.143 enum ParamType
1.144 {
1.145 - Param_Any = 0,
1.146 - Param_Cell = 1,
1.147 - Param_Float = 2,
1.148 - Param_String = 3,
1.149 - Param_Array = 4,
1.150 - Param_VarArgs = 5,
1.151 + Param_Any = 0, //Any type will be accepted
1.152 + Param_Cell = 1, //Only a cell will be accepted
1.153 + Param_Float = 2, //Only a float value will be accepted
1.154 + Param_String = 3, //Only a string will be accepted
1.155 + Param_Array = 4, //Only a 1D array will be accepted
1.156 + Param_VarArgs = 5, //Anything will be accepted
1.157 + ParamTypes_Total = 6,
1.158 };
1.159
1.160 class IForwardManager : public SMInterface
1.161 @@ -174,7 +148,11 @@
1.162 * @param ... If types is NULL, num_params ParamTypes should be pushed.
1.163 * @return A new IForward on success, NULL if type combination is impossible.
1.164 */
1.165 - virtual IForward *CreateForward(const char *name, ExecType et, int num_params, ParamType *types, ...) =0;
1.166 + virtual IForward *CreateForward(const char *name,
1.167 + ExecType et,
1.168 + unsigned int num_params,
1.169 + ParamType *types,
1.170 + ...) =0;
1.171
1.172 /**
1.173 * @brief Creates an unmanaged forward. This forward exists privately.
1.174 @@ -208,4 +186,81 @@
1.175 };
1.176 };
1.177
1.178 +/**
1.179 + * In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing
1.180 + * a function ID and an AMX structure. The forward structure itself did very little but hold parameter types.
1.181 + * An execution call worked like this:
1.182 + * - executeForward() took in a function id and a list of parameters
1.183 + * - for each contained plugin:
1.184 + * - the list of parameters was preprocessed and pushed
1.185 + * - the call was made
1.186 + * - the list was freed and copybacks were made
1.187 + * - return
1.188 + *
1.189 + * The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is
1.190 + * varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting. You also
1.191 + * lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM.
1.192 + *
1.193 + * SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that
1.194 + * each plugin has a table of functions, and each function is an ICallable object. As well as being an ICallable,
1.195 + * each function is an IPluginFunction. An ICallable simply describes the process of adding parameters to a
1.196 + * function call. An IPluginFunction describes the process of actually calling a function and performing allocation,
1.197 + * copybacks, and deallocations.
1.198 + *
1.199 + * A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions. Thus, the same
1.200 + * API can be easily wrapped around a simple list, and it will look transparent to the user.
1.201 + * Advantages:
1.202 + * 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection.
1.203 + * 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime..
1.204 + * 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked.
1.205 + * 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward.
1.206 + * This helps alleviate messes like Fakemeta.
1.207 + * 5) Parameter pushing is type-checked and allows for variable arguments.
1.208 + *
1.209 + * Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
1.210 + * object oriented, rather than AMX Mod X, which hides the objects behind static functions. It is entirely a design
1.211 + * issue, rather than a usability one. The interesting part is when it gets to implementation, which is when the
1.212 + * problems for SourceMod arise. Specifically, the implementation is easier in GENERAL -- the tough part is the oddball
1.213 + * cases.
1.214 + *
1.215 + * Observe the calling process:
1.216 + * - Each parameter is pushed using the ICallable interface. For each parameter:
1.217 + * - For each function in the collection, the parameter is processed and pushed.
1.218 + * - For each function in the collection:
1.219 + * - The call is made.
1.220 + * - Copy backs are performed.
1.221 + * - Return
1.222 + *
1.223 + * Astute readers will note the problems - the parameters are processed individually for each push,
1.224 + * rather than for each call. This means:
1.225 + * 1) More memory is used. Specifically, rather than N params of memory, you now have N params * M plugins.
1.226 + * This is because, again, parameters are processed per function object, per-push, before the call.
1.227 + * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
1.228 + * 3) Copybacks won't work as expected.
1.229 + *
1.230 + * Number 3 is hard to see. For example, say a forward has two functions, and an array is pushed with copyback.
1.231 + * The array gets pushed and copied into each plugin. When the call is made, each plugin now has separate copies of
1.232 + * the array. When the copyback is performed, it gets mirrored to the originall address, but not to the next plugin!
1.233 +
1.234 + * Implementing this is "difficult." To be re-entrant, an IPluginFunction can't tell you anything
1.235 + * about its copybacks after it has returned, because its internal states were reset long ago.
1.236 + *
1.237 + * In order to implement this feature, IPluginFunction::Execute function now lets you pass a listener in.
1.238 + * This listener is notified each time an array parameter is about to be copied back. Specifically, the forward
1.239 + * uses this to detect when it needs to push a new parameter out to the next plugin. When the forward gets the
1.240 + * call back, it detects whether there is another plugin in the queue. If there is, it grabs the address it will
1.241 + * be using for the same arrays, and specifies it as the new copy-back point. If no plugins are left, it allows
1.242 + * the copyback to chain up to the original address.
1.243 + *
1.244 + * This wonderful little hack is somewhat reminiscent of how SourceHook parameter rewrite chains work. It seems
1.245 + * ugly at first, but it is actually the correct design pattern to apply to an otherwise awful problem.
1.246 + *
1.247 + * Note that there are other solutions to this that aren't based on a visitor-like pattern. For example, the Function
1.248 + * class could expose a "set new original address" function. Then after arrays are pushed, the arrays are re-browsed,
1.249 + * and function #1 gets function #2's original address, function #2 gets function #3's original address, et cetera.
1.250 + * This extra browse step is a bit less efficient though, since the "visitor" method implies only taking action when
1.251 + * necessary. Furthermore, it would require exposing such a "set address" function, which should fire a red flag
1.252 + * that the API is doing something it shouldn't (namely, exposing the direct setting of very internal properties).
1.253 + */
1.254 +
1.255 #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_