public/IForwardSys.h
author David Anderson <dvander@alliedmods.net>
Wed Jul 25 01:18:11 2007 +0000 (2007-07-25)
changeset 1163 0bb03cb6277b
parent 729 110c14c99545
child 1226 478550a4d5ec
permissions -rw-r--r--
- added MenuAction_DisplayItem callback for plugins
- fixed various bugs in MenuAction_Display
- added an "access" parameter for IMenuManager. better to do this now than later, even though it's not used yet
- hard bumped menu api to change the OnMenuDisplayItem callback. sorry!
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod, Copyright (C) 2004-2007 AlliedModders LLC. 
     5  * All rights reserved.
     6  * ===============================================================
     7  *
     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.
    13  * 
    14  * To view the latest information, see: http://www.sourcemod.net/license.php
    15  *
    16  * Version: $Id$
    17  */
    18 
    19 #ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
    20 #define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
    21 
    22 /**
    23  * @file IForwardSys.h
    24  * @brief Defines the interface for managing collections ("forwards") of plugin calls.
    25  *
    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.
    32  */
    33 
    34 #include <IPluginSys.h>
    35 #include <sp_vm_api.h>
    36 
    37 using namespace SourcePawn;
    38 
    39 #define SMINTERFACE_FORWARDMANAGER_NAME		"IForwardManager"
    40 #define SMINTERFACE_FORWARDMANAGER_VERSION	2
    41 
    42 /*
    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!
    45  */
    46 
    47 namespace SourceMod
    48 {
    49 	/**
    50 	 * @brief Defines the event hook result types plugins can return.
    51 	 */
    52 	enum ResultType
    53 	{
    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 */
    57 	};
    58 
    59 	/**
    60 	 * @brief Defines how a forward iterates through plugin functions.
    61 	 */
    62 	enum ExecType
    63 	{
    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 */
    69 	};
    70 
    71 	class IForward;
    72 
    73 	/**
    74 	 * @brief Allows interception of how the Forward System executes functions.
    75 	 */
    76 	class IForwardFilter
    77 	{
    78 	public:
    79 		/**
    80 		 * @brief Called when an error occurs executing a plugin.
    81 		 *
    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.
    86 		 */
    87 		virtual bool OnErrorReport(IForward *fwd,
    88 									IPluginFunction *func,
    89 									int err)
    90 		{
    91 			return false;
    92 		}
    93 
    94 		/** 
    95 		 * @brief Called after each function return during execution.
    96 		 * NOTE: Only used for ET_Custom.
    97 		 *
    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.
   102 		 */
   103 		virtual ResultType OnFunctionReturn(IForward *fwd,
   104 									  IPluginFunction *func,
   105 									  cell_t *retval)
   106 		{
   107 			return Pl_Continue;
   108 		}
   109 
   110 		/** 
   111 		 * @brief Called when execution begins.
   112 		 */
   113 		virtual void OnExecuteBegin()
   114 		{
   115 		};
   116 
   117 		/**
   118 		 * @brief Called when execution ends.
   119 		 * 
   120 		 * @param final_ret	Final return value (modifiable).
   121 		 * @param success	Number of successful execs.
   122 		 * @param failed	Number of failed execs.
   123 		 */
   124 		virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed)
   125 		{
   126 		}
   127 	};
   128 
   129 	/**
   130 	 * @brief Unmanaged Forward, abstracts calling multiple functions as "forwards," or collections of functions.
   131 	 * 
   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.
   135 	 */
   136 	class IForward : public ICallable
   137 	{
   138 	public:
   139 		/** Virtual Destructor */
   140 		virtual ~IForward()
   141 		{
   142 		}
   143 	public:
   144 		/**
   145 		 * @brief Returns the name of the forward.
   146 		 *
   147 		 * @return			Forward name.
   148 		 */
   149 		virtual const char *GetForwardName() =0;
   150 
   151 		/**
   152 		 * @brief Returns the number of functions in this forward.
   153 		 *
   154 		 * @return			Number of functions in forward.
   155 		 */
   156 		virtual unsigned int GetFunctionCount() =0;
   157 
   158 		/**
   159 		 * @brief Returns the method of multi-calling this forward has.
   160 		 *
   161 		 * @return			ExecType of the forward.
   162 		 */
   163 		virtual ExecType GetExecType() =0;
   164 
   165 		/**
   166 		 * @brief Executes the forward.
   167 		 *
   168 		 * @param result		Optional pointer to store result in.
   169 		 * @param filter		Optional pointer to an IForwardFilter.
   170 		 * @return				Error code, if any.
   171 		 */
   172 		virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0;
   173 
   174 		/**
   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.
   177 		 *
   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.
   182 		 */
   183 		virtual int PushArray(cell_t *inarray, unsigned int cells, int flags=0) =0;
   184 	};
   185 
   186 	/**
   187 	 * @brief Managed Forward, same as IForward, except the collection can be modified.
   188 	 */
   189 	class IChangeableForward : public IForward
   190 	{
   191 	public:
   192 		/** 
   193 		 * @brief Removes a function from the call list.
   194 		 * NOTE: Only removes one instance.
   195 		 *
   196 		 * @param func		Function to remove.
   197 		 * @return			Whether or not the function was removed.
   198 		 */
   199 		virtual bool RemoveFunction(IPluginFunction *func) =0;
   200 
   201 		/** 
   202 		 * @brief Removes all instances of a plugin from the call list.
   203 		 *
   204 		 * @param plugin	Plugin to remove instances of.
   205 		 * @return			Number of functions removed therein.
   206 		 */
   207 		virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
   208 
   209 		/**
   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.
   214 		 *
   215 		 * @param func		Function to add.
   216 		 * @return			True on success, otherwise false.
   217 		 */
   218 		virtual bool AddFunction(IPluginFunction *func) =0;
   219 
   220 		/**
   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.
   224 		 *
   225 		 * @param ctx		Context to use as a look-up.
   226 		 * @param index		Function id to add.
   227 		 * @return			True on success, otherwise false.
   228 		 */
   229 		virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
   230 
   231 		/** 
   232 		 * @brief Removes a function from the call list.
   233 		 * NOTE: Only removes one instance.
   234 		 *
   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.
   238 		 */
   239 		virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0;
   240 	};
   241 
   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)
   249 
   250 	/**
   251 	 * @brief Describes the various ways to pass parameters to plugins.
   252 	 */
   253 	enum ParamType
   254 	{
   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 */
   263 	};
   264 	
   265 	/**
   266 	 * @brief Provides functions for creating/destroying managed and unmanaged forwards.
   267 	 */
   268 	class IForwardManager : public SMInterface
   269 	{
   270 	public:
   271 		virtual const char *GetInterfaceName()
   272 		{
   273 			return SMINTERFACE_FORWARDMANAGER_NAME;
   274 		}
   275 		virtual unsigned int GetInterfaceVersion()
   276 		{
   277 			return SMINTERFACE_FORWARDMANAGER_VERSION;
   278 		}
   279 		virtual bool IsVersionCompatible(unsigned int version)
   280 		{
   281 			if (version < 2 || version > GetInterfaceVersion())
   282 			{
   283 				return false;
   284 			}
   285 			return true;
   286 		}
   287 	public:
   288 		/**
   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.
   293 		 *
   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.
   302 		 */
   303 		virtual IForward *CreateForward(const char *name, 
   304 										ExecType et, 
   305 										unsigned int num_params, 
   306 										const ParamType *types, 
   307 										...) =0;
   308 
   309 		/**
   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.
   313 		 *
   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.
   322 		 */
   323 		virtual IChangeableForward *CreateForwardEx(const char *name, 
   324 													ExecType et, 
   325 													int num_params, 
   326 													const ParamType *types, 
   327 													...) =0;
   328 
   329 		/**
   330 		 * @brief Finds a forward by name.  Does not return anonymous forwards (named NULL or "").
   331 		 *
   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.
   336 		 */
   337 		virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0;
   338 
   339 		/**
   340 		 * @brief Frees and destroys a forward object.
   341 		 *
   342 		 * @param forward		An IForward created by CreateForward() or CreateForwardEx().
   343 		 */
   344 		virtual void ReleaseForward(IForward *forward) =0;
   345 	};
   346 }
   347 
   348 /*
   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
   357  *  - return
   358  * 
   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.
   362  *
   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.
   368  *
   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.
   371  * Advantages:
   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.
   378  *
   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.
   384  *
   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.
   390  * - Return
   391  *
   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.
   396  *
   397  * HISTORICAL NOTES:
   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.
   408  */
   409 
   410 #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_