3 * ===============================================================
4 * SourceMod, Copyright (C) 2004-2007 AlliedModders LLC.
6 * ===============================================================
8 * This file is part of the SourceMod/SourcePawn SDK. This file may only be
9 * used or modified under the Terms and Conditions of its License Agreement,
10 * which is found in public/licenses/LICENSE.txt. As of this notice, derivative
11 * works must be licensed under the GNU General Public License (version 2 or
12 * greater). A copy of the GPL is included under public/licenses/GPL.txt.
14 * To view the latest information, see: http://www.sourcemod.net/license.php
19 #ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
20 #define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
24 * @brief Defines the interface for managing collections ("forwards") of plugin calls.
26 * The Forward System is responsible for managing automated collections of IPluginFunctions.
27 * It thus provides wrappers to calling many functions at once. There are two types of such
28 * wrappers: Managed and Unmanaged. Confusingly, these terms refer to whether the user manages
29 * the forwards, not Core. Managed forwards are completely managed by the user, and are custom
30 * editable collections. Unmanaged forwards are the opposite, and will only work on a single global
31 * function name in all plugins.
34 #include <IPluginSys.h>
35 #include <sp_vm_api.h>
37 using namespace SourcePawn;
39 #define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager"
40 #define SMINTERFACE_FORWARDMANAGER_VERSION 2
43 * There is some very important documentation at the bottom of this file.
44 * Readers interested in knowing more about the forward system, scrolling down is a must!
50 * @brief Defines the event hook result types plugins can return.
54 Pl_Continue = 0, /**< No result */
55 Pl_Handled = 3, /**< Result was handled, stop at the end */
56 Pl_Stop = 4, /**< Result was handled, stop now */
60 * @brief Defines how a forward iterates through plugin functions.
64 ET_Ignore = 0, /**< Ignore all return values, return 0 */
65 ET_Single = 1, /**< Only return the last exec, ignore all others */
66 ET_Event = 2, /**< Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */
67 ET_Hook = 3, /**< Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */
68 ET_Custom = 4, /**< Ignored or handled by an IForwardFilter */
74 * @brief Allows interception of how the Forward System executes functions.
80 * @brief Called when an error occurs executing a plugin.
82 * @param fwd IForward pointer.
83 * @param func IPluginFunction pointer to the failed function.
84 * @param err Error code.
85 * @return True to handle, false to pass to global error reporter.
87 virtual bool OnErrorReport(IForward *fwd,
88 IPluginFunction *func,
95 * @brief Called after each function return during execution.
96 * NOTE: Only used for ET_Custom.
98 * @param fwd IForward pointer.
99 * @param func IPluginFunction pointer to the executed function.
100 * @param retval Pointer to current return value (can be modified).
101 * @return ResultType denoting the next action to take.
103 virtual ResultType OnFunctionReturn(IForward *fwd,
104 IPluginFunction *func,
111 * @brief Called when execution begins.
113 virtual void OnExecuteBegin()
118 * @brief Called when execution ends.
120 * @param final_ret Final return value (modifiable).
121 * @param success Number of successful execs.
122 * @param failed Number of failed execs.
124 virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed)
130 * @brief Unmanaged Forward, abstracts calling multiple functions as "forwards," or collections of functions.
132 * Parameters should be pushed in forward order, unlike the virtual machine/IPluginContext order.
133 * Some functions are repeated in here because their documentation differs from their IPluginFunction equivalents.
134 * Missing are the Push functions, whose only doc change is that they throw SP_ERROR_PARAM on type mismatches.
136 class IForward : public ICallable
139 /** Virtual Destructor */
145 * @brief Returns the name of the forward.
147 * @return Forward name.
149 virtual const char *GetForwardName() =0;
152 * @brief Returns the number of functions in this forward.
154 * @return Number of functions in forward.
156 virtual unsigned int GetFunctionCount() =0;
159 * @brief Returns the method of multi-calling this forward has.
161 * @return ExecType of the forward.
163 virtual ExecType GetExecType() =0;
166 * @brief Executes the forward.
168 * @param result Optional pointer to store result in.
169 * @param filter Optional pointer to an IForwardFilter.
170 * @return Error code, if any.
172 virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0;
175 * @brief Pushes an array of cells onto the current call. Different rules than ICallable.
176 * NOTE: On Execute, the pointer passed will be modified according to the copyback rule.
178 * @param inarray Array to copy. Cannot be NULL, unlike ICallable's version.
179 * @param cells Number of cells to allocate and optionally read from the input array.
180 * @param flags Whether or not changes should be copied back to the input array.
181 * @return Error code, if any.
183 virtual int PushArray(cell_t *inarray, unsigned int cells, int flags=0) =0;
187 * @brief Managed Forward, same as IForward, except the collection can be modified.
189 class IChangeableForward : public IForward
193 * @brief Removes a function from the call list.
194 * NOTE: Only removes one instance.
196 * @param func Function to remove.
197 * @return Whether or not the function was removed.
199 virtual bool RemoveFunction(IPluginFunction *func) =0;
202 * @brief Removes all instances of a plugin from the call list.
204 * @param plugin Plugin to remove instances of.
205 * @return Number of functions removed therein.
207 virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
210 * @brief Adds a function to the call list.
211 * NOTE: Cannot be used during an incomplete call.
212 * NOTE: If used during a call, function is temporarily queued until calls are over.
213 * NOTE: Adding multiple copies of the same function is illegal.
215 * @param func Function to add.
216 * @return True on success, otherwise false.
218 virtual bool AddFunction(IPluginFunction *func) =0;
221 * @brief Adds a function to the call list.
222 * NOTE: Cannot be used during an incomplete call.
223 * NOTE: If used during a call, function is temporarily queued until calls are over.
225 * @param ctx Context to use as a look-up.
226 * @param index Function id to add.
227 * @return True on success, otherwise false.
229 virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
232 * @brief Removes a function from the call list.
233 * NOTE: Only removes one instance.
235 * @param ctx Context to use as a look-up.
236 * @param index Function id to add.
237 * @return Whether or not the function was removed.
239 virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0;
242 #define SP_PARAMTYPE_ANY 0
243 #define SP_PARAMFLAG_BYREF (1<<0)
244 #define SP_PARAMTYPE_CELL (1<<1)
245 #define SP_PARAMTYPE_FLOAT (2<<1)
246 #define SP_PARAMTYPE_STRING (3<<1)|SP_PARAMFLAG_BYREF
247 #define SP_PARAMTYPE_ARRAY (4<<1)|SP_PARAMFLAG_BYREF
248 #define SP_PARAMTYPE_VARARG (5<<1)
251 * @brief Describes the various ways to pass parameters to plugins.
255 Param_Any = SP_PARAMTYPE_ANY, /**< Any data type can be pushed */
256 Param_Cell = SP_PARAMTYPE_CELL, /**< Only basic cells can be pushed */
257 Param_Float = SP_PARAMTYPE_FLOAT, /**< Only floats can be pushed */
258 Param_String = SP_PARAMTYPE_STRING, /**< Only strings can be pushed */
259 Param_Array = SP_PARAMTYPE_ARRAY, /**< Only arrays can be pushed */
260 Param_VarArgs = SP_PARAMTYPE_VARARG, /**< Same as "..." in plugins, anything can be pushed, but it will always be byref */
261 Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF, /**< Only a cell by reference can be pushed */
262 Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF, /**< Only a float by reference can be pushed */
266 * @brief Provides functions for creating/destroying managed and unmanaged forwards.
268 class IForwardManager : public SMInterface
271 virtual const char *GetInterfaceName()
273 return SMINTERFACE_FORWARDMANAGER_NAME;
275 virtual unsigned int GetInterfaceVersion()
277 return SMINTERFACE_FORWARDMANAGER_VERSION;
279 virtual bool IsVersionCompatible(unsigned int version)
281 if (version < 2 || version > GetInterfaceVersion())
289 * @brief Creates a managed forward. This forward exists globally.
290 * The name used to create the forward is used as its public function in all target plugins.
291 * As new non-private plugins become loaded or unloaded, they will be automatically added
292 * or removed. This is ideal for global, static forwards that are never changed.
294 * @param name Name of public function to use in forward.
295 * @param et Execution type to be used.
296 * @param num_params Number of parameter this function will have.
297 * NOTE: For varargs, this should include the vararg parameter.
298 * @param types Array of type information about each parameter. If NULL, types
299 * are read off the vararg stream.
300 * @param ... If types is NULL, num_params ParamTypes should be pushed.
301 * @return A new IForward on success, NULL if type combination is impossible.
303 virtual IForward *CreateForward(const char *name,
305 unsigned int num_params,
306 const ParamType *types,
310 * @brief Creates an unmanaged forward. This forward exists privately.
311 * Unlike managed forwards, no functions are ever added by the Manager.
312 * However, functions will be removed automatically if their parent plugin is unloaded.
314 * @param name Name of forward (unused except for lookup, can be NULL for anonymous).
315 * @param et Execution type to be used.
316 * @param num_params Number of parameter this function will have.
317 * NOTE: For varargs, this should include the vararg parameter.
318 * @param types Array of type information about each parameter. If NULL, types
319 * are read off the vararg stream.
320 * @param ... If types is NULL, num_params ParamTypes should be pushed.
321 * @return A new IChangeableForward on success, NULL if type combination is impossible.
323 virtual IChangeableForward *CreateForwardEx(const char *name,
326 const ParamType *types,
330 * @brief Finds a forward by name. Does not return anonymous forwards (named NULL or "").
332 * @param name Name of forward.
333 * @param ifchng Optionally store either NULL or an IChangeableForward pointer
334 * depending on type of forward.
335 * @return IForward pointer, or NULL if none found matching the name.
337 virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0;
340 * @brief Frees and destroys a forward object.
342 * @param forward An IForward created by CreateForward() or CreateForwardEx().
344 virtual void ReleaseForward(IForward *forward) =0;
349 * In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing
350 * a function ID and an AMX structure. The forward structure itself did very little but hold parameter types.
351 * An execution call worked like this:
352 * - executeForward() took in a function id and a list of parameters
353 * - for each contained plugin:
354 * - the list of parameters was preprocessed and pushed
355 * - the call was made
356 * - the list was freed and copybacks were made
359 * The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is
360 * varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting. You also
361 * lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM.
363 * SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that
364 * each plugin has a table of functions, and each function is an ICallable object. As well as being an ICallable,
365 * each function is an IPluginFunction. An ICallable simply describes the process of adding parameters to a
366 * function call. An IPluginFunction describes the process of actually calling a function and performing allocation,
367 * copybacks, and deallocations.
369 * A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions. Thus, the same
370 * API can be easily wrapped around a simple list, and it will look transparent to the user.
372 * 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection.
373 * 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime..
374 * 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked.
375 * 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward.
376 * This helps alleviate messes like Fakemeta.
377 * 5) Parameter pushing is type-checked and allows for variable arguments.
379 * Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
380 * object oriented, rather than AMX Mod X, which hides the objects behind static functions. It is entirely a design
381 * issue, rather than a usability one. The interesting part is when it gets to implementation, which has to cache
382 * parameter pushing until execution. Without this, multiple function calls can be started across one plugin, which
383 * will result in heap corruption given SourcePawn's implementation.
385 * Observe the new calling process:
386 * - Each parameter is pushed into a local cache using the ICallable interface.
387 * - For each function in the collection:
388 * - Each parameter is decoded and -pushed into the function.
389 * - The call is made.
392 * Astute readers will note the (minor) problems:
393 * 1) More memory is used. Specifically, rather than N params of memory, you now have N params * M plugins.
394 * This is because, again, parameters are cached both per-function and per-forward.
395 * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
398 * There used to be a # about copy backs.
399 * Note that originally, the Forward implementation was a thin wrapper around IForwards. It did not cache pushes,
400 * and instead immediately fired them to each internal plugin. This was to allow users to know that pointers would
401 * be immediately resolved. Unfortunately, this became extremely burdensome on the API and exposed many problems,
402 * the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same
403 * plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
404 * and become impossible to re-arrange without some global heap tracking mechanism. It also made iterative copy backs
405 * for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
406 * Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X
407 * uses. It is both faster and works better.
410 #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_