public/IForwardSys.h
author Scott Ehlert <ds@alliedmods.net>
Mon Feb 05 08:58:03 2007 +0000 (2007-02-05)
changeset 432 77112e9620fa
parent 430 ed37e9767ed4
child 447 f072596618c9
permissions -rw-r--r--
Worst commit ever, but I have removed some unnecessary things
dvander@372
     1
/**
dvander@375
     2
 * vim: set ts=4 :
dvander@372
     3
 * ===============================================================
dvander@376
     4
 * SourceMod, Copyright (C) 2004-2007 AlliedModders LLC. 
dvander@376
     5
 * All rights reserved.
dvander@372
     6
 * ===============================================================
dvander@372
     7
 *
dvander@379
     8
 *  This file is part of the SourceMod/SourcePawn SDK.  This file may only be 
dvander@379
     9
 * used or modified under the Terms and Conditions of its License Agreement, 
dvander@379
    10
 * which is found in public/licenses/LICENSE.txt.  As of this notice, derivative 
dvander@379
    11
 * works must be licensed under the GNU General Public License (version 2 or 
dvander@379
    12
 * greater).  A copy of the GPL is included under public/licenses/GPL.txt.
dvander@379
    13
 * 
dvander@379
    14
 * To view the latest information, see: http://www.sourcemod.net/license.php
dvander@372
    15
 *
dvander@372
    16
 * Version: $Id$
dvander@372
    17
 */
dvander@372
    18
dvander@324
    19
#ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
dvander@324
    20
#define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
dvander@324
    21
dvander@372
    22
/**
dvander@372
    23
 * @file IForwardSys.h
dvander@372
    24
 * @brief Defines the interface for managing collections ("forwards") of plugin calls.
dvander@372
    25
 *
dvander@372
    26
 *   The Forward System is responsible for managing automated collections of IPluginFunctions.  
dvander@372
    27
 * It thus provides wrappers to calling many functions at once.  There are two types of such 
dvander@372
    28
 * wrappers: Managed and Unmanaged.  Confusingly, these terms refer to whether the user manages
dvander@372
    29
 * the forwards, not Core.  Managed forwards are completely managed by the user, and are custom
dvander@372
    30
 * editable collections.  Unmanaged forwards are the opposite, and will only work on a single global
dvander@372
    31
 * function name in all plugins.
dvander@372
    32
 */
dvander@372
    33
dvander@324
    34
#include <IPluginSys.h>
dvander@327
    35
#include <sp_vm_api.h>
dvander@327
    36
dvander@327
    37
using namespace SourcePawn;
dvander@324
    38
dvander@324
    39
#define SMINTERFACE_FORWARDMANAGER_NAME		"IForwardManager"
dvander@324
    40
#define SMINTERFACE_FORWARDMANAGER_VERSION	1
dvander@324
    41
dvander@372
    42
/*
dvander@324
    43
 * There is some very important documentation at the bottom of this file.
dvander@324
    44
 * Readers interested in knowing more about the forward system, scrolling down is a must!
dvander@324
    45
 */
dvander@324
    46
dvander@324
    47
namespace SourceMod
dvander@324
    48
{
dvander@372
    49
	/**
dvander@372
    50
	 * @brief Defines the event hook result types plugins can return.
dvander@372
    51
	 */
dvander@324
    52
	enum ResultType
dvander@324
    53
	{
dvander@372
    54
		Pl_Continue = 0,	/**< No result */
dvander@372
    55
		Pl_Handled = 1,		/**< Result was handled, stop at the end */
dvander@372
    56
		Pl_Stop = 2,		/**< Result was handled, stop now */
dvander@324
    57
	};
dvander@324
    58
dvander@372
    59
	/**
dvander@372
    60
	 * @brief Defines how a forward iterates through plugin functions.
dvander@372
    61
	 */
dvander@324
    62
	enum ExecType
dvander@324
    63
	{
dvander@372
    64
		ET_Ignore = 0,		/**< Ignore all return values, return 0 */
dvander@372
    65
		ET_Single = 1,		/**< Only return the last exec, ignore all others */
dvander@372
    66
		ET_Event = 2,		/**< Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */
dvander@372
    67
		ET_Hook = 3,		/**< Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */
dvander@372
    68
		ET_Custom = 4,		/**< Ignored or handled by an IForwardFilter */
dvander@324
    69
	};
dvander@324
    70
dvander@324
    71
	class IForward;
dvander@324
    72
dvander@372
    73
	/**
dvander@372
    74
	 * @brief Allows interception of how the Forward System executes functions.
dvander@372
    75
	 */
dvander@324
    76
	class IForwardFilter
dvander@324
    77
	{
dvander@324
    78
	public:
dvander@324
    79
		/**
dvander@324
    80
		 * @brief Called when an error occurs executing a plugin.
dvander@324
    81
		 *
dvander@324
    82
		 * @param fwd		IForward pointer.
dvander@324
    83
		 * @param func		IPluginFunction pointer to the failed function.
dvander@324
    84
		 * @param err		Error code.
dvander@324
    85
		 * @return			True to handle, false to pass to global error reporter.
dvander@324
    86
		 */
dvander@324
    87
		virtual bool OnErrorReport(IForward *fwd,
dvander@324
    88
									IPluginFunction *func,
dvander@324
    89
									int err)
dvander@324
    90
		{
dvander@324
    91
			return false;
dvander@324
    92
		}
dvander@324
    93
dvander@324
    94
		/** 
dvander@324
    95
		 * @brief Called after each function return during execution.
dvander@324
    96
		 * NOTE: Only used for ET_Custom.
dvander@324
    97
		 *
dvander@324
    98
		 * @param fwd		IForward pointer.
dvander@324
    99
		 * @param func		IPluginFunction pointer to the executed function.
dvander@324
   100
		 * @param retval	Pointer to current return value (can be modified).
dvander@324
   101
		 * @return			ResultType denoting the next action to take.
dvander@324
   102
		 */
dvander@324
   103
		virtual ResultType OnFunctionReturn(IForward *fwd,
dvander@324
   104
									  IPluginFunction *func,
dvander@324
   105
									  cell_t *retval)
dvander@324
   106
		{
dvander@324
   107
			return Pl_Continue;
dvander@324
   108
		}
dvander@324
   109
dvander@324
   110
		/** 
dvander@324
   111
		 * @brief Called when execution begins.
dvander@324
   112
		 */
dvander@324
   113
		virtual void OnExecuteBegin()
dvander@324
   114
		{
dvander@324
   115
		};
dvander@324
   116
dvander@324
   117
		/**
dvander@324
   118
		 * @brief Called when execution ends.
dvander@324
   119
		 * 
dvander@324
   120
		 * @param final_ret	Final return value (modifiable).
dvander@324
   121
		 * @param success	Number of successful execs.
dvander@324
   122
		 * @param failed	Number of failed execs.
dvander@324
   123
		 */
dvander@324
   124
		virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed)
dvander@324
   125
		{
dvander@324
   126
		}
dvander@324
   127
	};
dvander@324
   128
dvander@324
   129
	/**
dvander@372
   130
	 * @brief Unmanaged Forward, abstracts calling multiple functions as "forwards," or collections of functions.
dvander@324
   131
	 * 
dvander@372
   132
	 *  Parameters should be pushed in forward order, unlike the virtual machine/IPluginContext order.
dvander@372
   133
	 * Some functions are repeated in here because their documentation differs from their IPluginFunction equivalents.
dvander@372
   134
	 * Missing are the Push functions, whose only doc change is that they throw SP_ERROR_PARAM on type mismatches.
dvander@324
   135
	 */
dvander@324
   136
	class IForward : public ICallable
dvander@324
   137
	{
dvander@324
   138
	public:
dvander@372
   139
		/** Virtual Destructor */
dvander@324
   140
		virtual ~IForward()
dvander@324
   141
		{
dvander@324
   142
		}
dvander@324
   143
	public:
dvander@324
   144
		/**
dvander@324
   145
		 * @brief Returns the name of the forward.
dvander@324
   146
		 *
dvander@324
   147
		 * @return			Forward name.
dvander@324
   148
		 */
faluco@430
   149
		virtual const char *GetForwardName() const =0;
dvander@324
   150
dvander@324
   151
		/**
dvander@324
   152
		 * @brief Returns the number of functions in this forward.
dvander@324
   153
		 *
dvander@324
   154
		 * @return			Number of functions in forward.
dvander@324
   155
		 */
faluco@430
   156
		virtual unsigned int GetFunctionCount() const =0;
dvander@324
   157
dvander@324
   158
		/**
dvander@324
   159
		 * @brief Returns the method of multi-calling this forward has.
dvander@324
   160
		 *
dvander@324
   161
		 * @return			ExecType of the forward.
dvander@324
   162
		 */
faluco@430
   163
		virtual ExecType GetExecType() const =0;
dvander@324
   164
dvander@324
   165
		/**
dvander@324
   166
		 * @brief Executes the forward.
dvander@324
   167
		 *
dvander@324
   168
		 * @param result		Pointer to store result in.
dvander@324
   169
		 * @param filter		Optional pointer to an IForwardFilter.
dvander@324
   170
		 * @return				Error code, if any.
dvander@324
   171
		 */
dvander@324
   172
		virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0;
dvander@324
   173
dvander@324
   174
		/**
dvander@324
   175
		 * @brief Pushes an array of cells onto the current call.  Different rules than ICallable.
dvander@324
   176
		 * NOTE: On Execute, the pointer passed will be modified according to the copyback rule.
dvander@324
   177
		 *
dvander@324
   178
		 * @param inarray	Array to copy.  Cannot be NULL, unlike ICallable's version.
dvander@324
   179
		 * @param cells		Number of cells to allocate and optionally read from the input array.
dvander@324
   180
		 * @param phys_addr	Unused.  If a value is passed, it will be filled with NULL.
dvander@324
   181
		 * @param flags		Whether or not changes should be copied back to the input array.
dvander@324
   182
		 * @return			Error code, if any.
dvander@324
   183
		 */
dvander@324
   184
		virtual int PushArray(cell_t *inarray, 
dvander@324
   185
								unsigned int cells, 
dvander@324
   186
								cell_t **phys_addr, 
dvander@324
   187
								int flags=0) =0;
dvander@324
   188
	};
dvander@324
   189
dvander@372
   190
	/**
dvander@372
   191
	 * @brief Managed Forward, same as IForward, except the collection can be modified.
dvander@372
   192
	 */
dvander@324
   193
	class IChangeableForward : public IForward
dvander@324
   194
	{
dvander@324
   195
	public:
dvander@324
   196
		/** 
dvander@324
   197
		 * @brief Removes a function from the call list.
dvander@324
   198
		 * NOTE: Only removes one instance.
dvander@324
   199
		 *
dvander@324
   200
		 * @param func		Function to remove.
dvander@324
   201
		 * @return			Whether or not the function was removed.
dvander@324
   202
		 */
dvander@324
   203
		virtual bool RemoveFunction(IPluginFunction *func) =0;
dvander@324
   204
dvander@324
   205
		/** 
dvander@324
   206
		 * @brief Removes all instances of a plugin from the call list.
dvander@324
   207
		 *
dvander@324
   208
		 * @param plugin	Plugin to remove instances of.
dvander@324
   209
		 * @return			Number of functions removed therein.
dvander@324
   210
		 */
dvander@324
   211
		virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
dvander@324
   212
dvander@324
   213
		/**
dvander@324
   214
		 * @brief Adds a function to the call list.
dvander@324
   215
		 * NOTE: Cannot be used during an incompleted call.
dvander@324
   216
		 * NOTE: If used during a call, function is temporarily queued until calls are over.
dvander@324
   217
		 * NOTE: Adding mulitple copies of the same function is illegal.
dvander@324
   218
		 *
dvander@324
   219
		 * @param func		Function to add.
dvander@324
   220
		 * @return			True on success, otherwise false.
dvander@324
   221
		 */
dvander@324
   222
		virtual bool AddFunction(IPluginFunction *func) =0;
dvander@324
   223
dvander@324
   224
		/**
dvander@324
   225
		 * @brief Adds a function to the call list.
dvander@324
   226
		 * NOTE: Cannot be used during an incompleted call.
dvander@324
   227
		 * NOTE: If used during a call, function is temporarily queued until calls are over.
dvander@324
   228
		 *
dvander@324
   229
		 * @param ctx		Context to use as a look-up.
dvander@372
   230
		 * @param index		Function id to add.
dvander@324
   231
		 * @return			True on success, otherwise false.
dvander@324
   232
		 */
dvander@327
   233
		virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
dvander@324
   234
	};
dvander@324
   235
dvander@324
   236
	#define SP_PARAMTYPE_ANY	0
dvander@324
   237
	#define SP_PARAMFLAG_BYREF	(1<<0)
dvander@324
   238
	#define SP_PARAMTYPE_CELL	(1<<1)
dvander@324
   239
	#define SP_PARAMTYPE_FLOAT	(2<<1)
dvander@324
   240
	#define SP_PARAMTYPE_STRING	(3<<1)|SP_PARAMFLAG_BYREF
dvander@324
   241
	#define SP_PARAMTYPE_ARRAY	(4<<1)|SP_PARAMFLAG_BYREF
dvander@324
   242
	#define SP_PARAMTYPE_VARARG	(5<<1)
dvander@324
   243
dvander@372
   244
	/**
dvander@372
   245
	 * @brief Describes the various ways to pass parameters to plugins.
dvander@372
   246
	 */
dvander@324
   247
	enum ParamType
dvander@324
   248
	{
dvander@372
   249
		Param_Any = SP_PARAMTYPE_ANY,				/**< Any data type can be pushed */
dvander@372
   250
		Param_Cell = SP_PARAMTYPE_CELL,				/**< Only basic cells can be pushed */
dvander@372
   251
		Param_Float = SP_PARAMTYPE_FLOAT,			/**< Only floats can be pushed */
dvander@372
   252
		Param_String = SP_PARAMTYPE_STRING,			/**< Only strings can be pushed */
dvander@372
   253
		Param_Array = SP_PARAMTYPE_ARRAY,			/**< Only arrays can be pushed */
dvander@372
   254
		Param_VarArgs = SP_PARAMTYPE_VARARG,		/**< Same as "..." in plugins, anything can be pushed, but it will always be byref */
dvander@372
   255
		Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF,		/**< Only a cell by reference can be pushed */
dvander@372
   256
		Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF,	/**< Only a float by reference can be pushed */
dvander@324
   257
	};
dvander@324
   258
	
dvander@372
   259
	/**
dvander@372
   260
	 * @brief Provides functions for creating/destroying managed and unmanaged forwards.
dvander@372
   261
	 */
dvander@324
   262
	class IForwardManager : public SMInterface
dvander@324
   263
	{
dvander@324
   264
	public:
dvander@324
   265
		virtual const char *GetInterfaceName()
dvander@324
   266
		{
dvander@324
   267
			return SMINTERFACE_FORWARDMANAGER_NAME;
dvander@324
   268
		}
dvander@324
   269
		virtual unsigned int GetInterfaceVersion()
dvander@324
   270
		{
dvander@324
   271
			return SMINTERFACE_FORWARDMANAGER_VERSION;
dvander@324
   272
		}
dvander@324
   273
	public:
dvander@324
   274
		/**
dvander@324
   275
		 * @brief Creates a managed forward.  This forward exists globally.
dvander@324
   276
		 *  The name used to create the forward is used as its public function in all target plugins.
dvander@324
   277
		 *  As new non-private plugins become loaded or unloaded, they will be automatically added
dvander@324
   278
		 * or removed.  This is ideal for global, static forwards that are never changed.
dvander@324
   279
		 *
dvander@324
   280
		 * @param name			Name of public function to use in forward.
dvander@324
   281
		 * @param et			Execution type to be used.
dvander@324
   282
		 * @param num_params	Number of parameter this function will have.
dvander@324
   283
		 *						NOTE: For varargs, this should include the vararg parameter.
dvander@324
   284
		 * @param types			Array of type information about each parameter.  If NULL, types
dvander@324
   285
		 *						are read off the vararg stream.
dvander@324
   286
		 * @param ...			If types is NULL, num_params ParamTypes should be pushed.
dvander@324
   287
		 * @return				A new IForward on success, NULL if type combination is impossible.
dvander@324
   288
		 */
dvander@324
   289
		virtual IForward *CreateForward(const char *name, 
dvander@324
   290
										ExecType et, 
dvander@324
   291
										unsigned int num_params, 
dvander@324
   292
										ParamType *types, 
dvander@324
   293
										...) =0;
dvander@324
   294
dvander@324
   295
		/**
dvander@324
   296
		 * @brief Creates an unmanaged forward.  This forward exists privately.
dvander@324
   297
		 * Unlike managed forwards, no functions are ever added by the Manager.
dvander@324
   298
		 * However, functions will be removed automatically if their parent plugin is unloaded.
dvander@324
   299
		 *
dvander@324
   300
		 * @param name			Name of forward (unused except for lookup, can be NULL for anonymous).
dvander@324
   301
		 * @param et			Execution type to be used.
dvander@324
   302
		 * @param num_params	Number of parameter this function will have.
dvander@324
   303
		 *						NOTE: For varargs, this should include the vararg parameter.
dvander@324
   304
		 * @param types			Array of type information about each parameter.  If NULL, types
dvander@324
   305
		 *						are read off the vararg stream.
dvander@324
   306
		 * @param ...			If types is NULL, num_params ParamTypes should be pushed.
dvander@324
   307
		 * @return				A new IChangeableForward on success, NULL if type combination is impossible.
dvander@324
   308
		 */
dvander@324
   309
		virtual IChangeableForward *CreateForwardEx(const char *name, 
dvander@324
   310
													ExecType et, 
dvander@324
   311
													int num_params, 
dvander@324
   312
													ParamType *types, 
dvander@324
   313
													...) =0;
dvander@324
   314
dvander@324
   315
		/**
dvander@324
   316
		 * @brief Finds a forward by name.  Does not return anonymous forwards (named NULL or "").
dvander@324
   317
		 *
dvander@324
   318
		 * @param name			Name of forward.
dvander@324
   319
		 * @param ifchng		Optionally store either NULL or an IChangeableForward pointer
dvander@324
   320
		 *                      depending on type of forward.
dvander@324
   321
		 * @return				IForward pointer, or NULL if none found matching the name.
dvander@324
   322
		 */
dvander@324
   323
		virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0;
dvander@324
   324
dvander@324
   325
		/**
dvander@324
   326
		 * @brief Frees and destroys a forward object.
dvander@324
   327
		 *
dvander@324
   328
		 * @param forward		An IForward created by CreateForward() or CreateForwardEx().
dvander@324
   329
		 */
dvander@324
   330
		virtual void ReleaseForward(IForward *forward) =0;
dvander@324
   331
	};
ds@432
   332
}
dvander@324
   333
dvander@372
   334
/*
dvander@324
   335
 *  In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing
dvander@324
   336
 * a function ID and an AMX structure.  The forward structure itself did very little but hold parameter types.
dvander@324
   337
 * An execution call worked like this:
dvander@324
   338
 *  - executeForward() took in a function id and a list of parameters
dvander@324
   339
 *  - for each contained plugin:
dvander@324
   340
 *   - the list of parameters was preprocessed and pushed
dvander@324
   341
 *   - the call was made
dvander@324
   342
 *   - the list was freed and copybacks were made
dvander@324
   343
 *  - return
dvander@324
   344
 * 
dvander@324
   345
 *  The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is 
dvander@324
   346
 * varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting.  You also 
dvander@324
   347
 * lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM.
dvander@324
   348
 *
dvander@324
   349
 *  SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that 
dvander@324
   350
 * each plugin has a table of functions, and each function is an ICallable object.  As well as being an ICallable, 
dvander@324
   351
 * each function is an IPluginFunction.  An ICallable simply describes the process of adding parameters to a 
dvander@324
   352
 * function call.  An IPluginFunction describes the process of actually calling a function and performing allocation, 
dvander@324
   353
 * copybacks, and deallocations.
dvander@324
   354
 *
dvander@324
   355
 * A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions.  Thus, the same 
dvander@324
   356
 * API can be easily wrapped around a simple list, and it will look transparent to the user.
dvander@324
   357
 * Advantages:
dvander@324
   358
 * 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection.
dvander@324
   359
 * 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime..
dvander@324
   360
 * 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked.
dvander@324
   361
 * 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward.
dvander@324
   362
 *    This helps alleviate messes like Fakemeta.
dvander@324
   363
 * 5) Parameter pushing is type-checked and allows for variable arguments.
dvander@324
   364
 *
dvander@324
   365
 *  Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
dvander@324
   366
 * object oriented, rather than AMX Mod X, which hides the objects behind static functions.  It is entirely a design
dvander@324
   367
 * issue, rather than a usability one.  The interesting part is when it gets to implementation, which has to cache
dvander@324
   368
 * parameter pushing until execution.  Without this, multiple function calls can be started across one plugin, which
dvander@324
   369
 * will result in heap corruption given SourcePawn's implementation.
dvander@324
   370
 *
dvander@324
   371
 * Observe the new calling process:
dvander@324
   372
 * - Each parameter is pushed into a local cache using the ICallable interface.
dvander@324
   373
 * - For each function in the collection:
dvander@324
   374
 *  - Each parameter is decoded and -pushed into the function.
dvander@324
   375
 *  - The call is made.
dvander@324
   376
 * - Return
dvander@324
   377
 *
dvander@324
   378
 *  Astute readers will note the (minor) problems:
dvander@324
   379
 * 1) More memory is used.  Specifically, rather than N params of memory, you now have N params * M plugins.
dvander@324
   380
 *    This is because, again, parameters are cached both per-function and per-forward.
dvander@324
   381
 * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
dvander@324
   382
 *
dvander@324
   383
 * HISTORICAL NOTES:
dvander@324
   384
 *  There used to be a # about copy backs.  
dvander@324
   385
 *  Note that originally, the Forward implementation was a thin wrapper around IForwards.  It did not cache pushes,
dvander@324
   386
 * and instead immediately fired them to each internal plugin.  This was to allow users to know that pointers would 
dvander@324
   387
 * be immediately resolved.  Unfortunately, this became extremely burdensome on the API and exposed many problems,
dvander@324
   388
 * the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same 
dvander@324
   389
 * plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
ds@432
   390
 * and become impossible to re-arrange without some global heap tracking mechanism.  It also made iterative copy backs 
dvander@324
   391
 * for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
dvander@324
   392
 *  Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X 
dvander@324
   393
 * uses.  It is both faster and works better.
dvander@324
   394
 */
dvander@324
   395
dvander@324
   396
#endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_