core/interfaces/IForwardSys.h
changeset 177 d29ade52d953
parent 174 5611e4b3fa42
child 180 fe406c5f2aeb
     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_