public/IForwardSys.h

Go to the documentation of this file.
00001 /**
00002  * vim: set ts=4 :
00003  * =============================================================================
00004  * SourceMod
00005  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
00006  * =============================================================================
00007  *
00008  * This program is free software; you can redistribute it and/or modify it under
00009  * the terms of the GNU General Public License, version 3.0, as published by the
00010  * Free Software Foundation.
00011  * 
00012  * This program is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
00015  * details.
00016  *
00017  * You should have received a copy of the GNU General Public License along with
00018  * this program.  If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  * As a special exception, AlliedModders LLC gives you permission to link the
00021  * code of this program (as well as its derivative works) to "Half-Life 2," the
00022  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
00023  * by the Valve Corporation.  You must obey the GNU General Public License in
00024  * all respects for all other code used.  Additionally, AlliedModders LLC grants
00025  * this exception to all derivative works.  AlliedModders LLC defines further
00026  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
00027  * or <http://www.sourcemod.net/license.php>.
00028  *
00029  * Version: $Id: IForwardSys.h 2388 2008-07-08 21:17:05Z damagedsoul $
00030  */
00031 
00032 #ifndef _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
00033 #define _INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_
00034 
00035 /**
00036  * @file IForwardSys.h
00037  * @brief Defines the interface for managing collections ("forwards") of plugin calls.
00038  *
00039  *   The Forward System is responsible for managing automated collections of IPluginFunctions.  
00040  * It thus provides wrappers to calling many functions at once.  There are two types of such 
00041  * wrappers: Managed and Unmanaged.  Confusingly, these terms refer to whether the user manages
00042  * the forwards, not Core.  Managed forwards are completely managed by the user, and are custom
00043  * editable collections.  Unmanaged forwards are the opposite, and will only work on a single global
00044  * function name in all plugins.
00045  */
00046 
00047 #include <IPluginSys.h>
00048 #include <sp_vm_api.h>
00049 
00050 using namespace SourcePawn;
00051 
00052 #define SMINTERFACE_FORWARDMANAGER_NAME           "IForwardManager"
00053 #define SMINTERFACE_FORWARDMANAGER_VERSION        3
00054 
00055 /*
00056  * There is some very important documentation at the bottom of this file.
00057  * Readers interested in knowing more about the forward system, scrolling down is a must!
00058  */
00059 
00060 namespace SourceMod
00061 {
00062           /**
00063            * @brief Defines the event hook result types plugins can return.
00064            */
00065           enum ResultType
00066           {
00067                     Pl_Continue = 0,    /**< No result */
00068                     Pl_Changed = 1,               /**< Inputs or outputs have been overridden with new values */
00069                     Pl_Handled = 3,               /**< Result was handled, stop at the end */
00070                     Pl_Stop = 4,                  /**< Result was handled, stop now */
00071           };
00072 
00073           /**
00074            * @brief Defines how a forward iterates through plugin functions.
00075            */
00076           enum ExecType
00077           {
00078                     ET_Ignore = 0,                /**< Ignore all return values, return 0 */
00079                     ET_Single = 1,                /**< Only return the last exec, ignore all others */
00080                     ET_Event = 2,                 /**< Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */
00081                     ET_Hook = 3,                  /**< Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */
00082                     ET_LowEvent = 4,    /**< Same as ET_Event except that it returns the lowest value */
00083           };
00084 
00085           class IForward;
00086           class IForwardFilter;
00087 
00088           /**
00089            * @brief Unmanaged Forward, abstracts calling multiple functions as "forwards," or collections of functions.
00090            * 
00091            *  Parameters should be pushed in forward order, unlike the virtual machine/IPluginContext order.
00092            * Some functions are repeated in here because their documentation differs from their IPluginFunction equivalents.
00093            * Missing are the Push functions, whose only doc change is that they throw SP_ERROR_PARAM on type mismatches.
00094            */
00095           class IForward : public ICallable
00096           {
00097           public:
00098                     /** Virtual Destructor */
00099                     virtual ~IForward()
00100                     {
00101                     }
00102           public:
00103                     /**
00104                      * @brief Returns the name of the forward.
00105                      *
00106                      * @return                              Forward name.
00107                      */
00108                     virtual const char *GetForwardName() =0;
00109 
00110                     /**
00111                      * @brief Returns the number of functions in this forward.
00112                      *
00113                      * @return                              Number of functions in forward.
00114                      */
00115                     virtual unsigned int GetFunctionCount() =0;
00116 
00117                     /**
00118                      * @brief Returns the method of multi-calling this forward has.
00119                      *
00120                      * @return                              ExecType of the forward.
00121                      */
00122                     virtual ExecType GetExecType() =0;
00123 
00124                     /**
00125                      * @brief Executes the forward.
00126                      *
00127                      * @param result              Optional pointer to store result in.
00128                      * @param filter              Do not use.
00129                      * @return                                        Error code, if any.
00130                      */
00131                     virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0;
00132 
00133                     /**
00134                      * @brief Pushes an array of cells onto the current call.  Different rules than ICallable.
00135                      * NOTE: On Execute, the pointer passed will be modified according to the copyback rule.
00136                      *
00137                      * @param inarray   Array to copy.  Cannot be NULL, unlike ICallable's version.
00138                      * @param cells               Number of cells to allocate and optionally read from the input array.
00139                      * @param flags               Whether or not changes should be copied back to the input array.
00140                      * @return                              Error code, if any.
00141                      */
00142                     virtual int PushArray(cell_t *inarray, unsigned int cells, int flags=0) =0;
00143           };
00144 
00145           /**
00146            * @brief Managed Forward, same as IForward, except the collection can be modified.
00147            */
00148           class IChangeableForward : public IForward
00149           {
00150           public:
00151                     /** 
00152                      * @brief Removes a function from the call list.
00153                      * NOTE: Only removes one instance.
00154                      *
00155                      * @param func                Function to remove.
00156                      * @return                              Whether or not the function was removed.
00157                      */
00158                     virtual bool RemoveFunction(IPluginFunction *func) =0;
00159 
00160                     /** 
00161                      * @brief Removes all instances of a plugin from the call list.
00162                      *
00163                      * @param plugin    Plugin to remove instances of.
00164                      * @return                              Number of functions removed therein.
00165                      */
00166                     virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin) =0;
00167 
00168                     /**
00169                      * @brief Adds a function to the call list.
00170                      * NOTE: Cannot be used during an incomplete call.
00171                      * NOTE: If used during a call, function is temporarily queued until calls are over.
00172                      * NOTE: Adding multiple copies of the same function is illegal.
00173                      *
00174                      * @param func                Function to add.
00175                      * @return                              True on success, otherwise false.
00176                      */
00177                     virtual bool AddFunction(IPluginFunction *func) =0;
00178 
00179                     /**
00180                      * @brief Adds a function to the call list.
00181                      * NOTE: Cannot be used during an incomplete call.
00182                      * NOTE: If used during a call, function is temporarily queued until calls are over.
00183                      *
00184                      * @param ctx                 Context to use as a look-up.
00185                      * @param index               Function id to add.
00186                      * @return                              True on success, otherwise false.
00187                      */
00188                     virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
00189 
00190                     /** 
00191                      * @brief Removes a function from the call list.
00192                      * NOTE: Only removes one instance.
00193                      *
00194                      * @param ctx                 Context to use as a look-up.
00195                      * @param index               Function id to add.
00196                      * @return                              Whether or not the function was removed.
00197                      */
00198                     virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0;
00199           };
00200 
00201           #define SP_PARAMTYPE_ANY      0
00202           #define SP_PARAMFLAG_BYREF    (1<<0)
00203           #define SP_PARAMTYPE_CELL     (1<<1)
00204           #define SP_PARAMTYPE_FLOAT    (2<<1)
00205           #define SP_PARAMTYPE_STRING   (3<<1)|SP_PARAMFLAG_BYREF
00206           #define SP_PARAMTYPE_ARRAY    (4<<1)|SP_PARAMFLAG_BYREF
00207           #define SP_PARAMTYPE_VARARG   (5<<1)
00208 
00209           /**
00210            * @brief Describes the various ways to pass parameters to plugins.
00211            */
00212           enum ParamType
00213           {
00214                     Param_Any = SP_PARAMTYPE_ANY,                               /**< Any data type can be pushed */
00215                     Param_Cell = SP_PARAMTYPE_CELL,                                       /**< Only basic cells can be pushed */
00216                     Param_Float = SP_PARAMTYPE_FLOAT,                           /**< Only floats can be pushed */
00217                     Param_String = SP_PARAMTYPE_STRING,                         /**< Only strings can be pushed */
00218                     Param_Array = SP_PARAMTYPE_ARRAY,                           /**< Only arrays can be pushed */
00219                     Param_VarArgs = SP_PARAMTYPE_VARARG,              /**< Same as "..." in plugins, anything can be pushed, but it will always be byref */
00220                     Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF,               /**< Only a cell by reference can be pushed */
00221                     Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF,   /**< Only a float by reference can be pushed */
00222           };
00223           
00224           /**
00225            * @brief Provides functions for creating/destroying managed and unmanaged forwards.
00226            */
00227           class IForwardManager : public SMInterface
00228           {
00229           public:
00230                     virtual const char *GetInterfaceName()
00231                     {
00232                               return SMINTERFACE_FORWARDMANAGER_NAME;
00233                     }
00234                     virtual unsigned int GetInterfaceVersion()
00235                     {
00236                               return SMINTERFACE_FORWARDMANAGER_VERSION;
00237                     }
00238                     virtual bool IsVersionCompatible(unsigned int version)
00239                     {
00240                               if (version < 2 || version > GetInterfaceVersion())
00241                               {
00242                                         return false;
00243                               }
00244                               return true;
00245                     }
00246           public:
00247                     /**
00248                      * @brief Creates a managed forward.  This forward exists globally.
00249                      *  The name used to create the forward is used as its public function in all target plugins.
00250                      *  As new non-private plugins become loaded or unloaded, they will be automatically added
00251                      * or removed.  This is ideal for global, static forwards that are never changed.
00252                      *
00253                      * @param name                          Name of public function to use in forward.
00254                      * @param et                            Execution type to be used.
00255                      * @param num_params          Number of parameter this function will have.
00256                      *                                                          NOTE: For varargs, this should include the vararg parameter.
00257                      * @param types                         Array of type information about each parameter.  If NULL, types
00258                      *                                                          are read off the vararg stream.
00259                      * @param ...                           If types is NULL, num_params ParamTypes should be pushed.
00260                      * @return                                        A new IForward on success, NULL if type combination is impossible.
00261                      */
00262                     virtual IForward *CreateForward(const char *name, 
00263                                                                                                     ExecType et, 
00264                                                                                                     unsigned int num_params, 
00265                                                                                                     const ParamType *types, 
00266                                                                                                     ...) =0;
00267 
00268                     /**
00269                      * @brief Creates an unmanaged forward.  This forward exists privately.
00270                      * Unlike managed forwards, no functions are ever added by the Manager.
00271                      * However, functions will be removed automatically if their parent plugin is unloaded.
00272                      *
00273                      * @param name                          Name of forward (unused except for lookup, can be NULL for anonymous).
00274                      * @param et                            Execution type to be used.
00275                      * @param num_params          Number of parameter this function will have.
00276                      *                                                          NOTE: For varargs, this should include the vararg parameter.
00277                      * @param types                         Array of type information about each parameter.  If NULL, types
00278                      *                                                          are read off the vararg stream.
00279                      * @param ...                           If types is NULL, num_params ParamTypes should be pushed.
00280                      * @return                                        A new IChangeableForward on success, NULL if type combination is impossible.
00281                      */
00282                     virtual IChangeableForward *CreateForwardEx(const char *name, 
00283                                                                                                                                   ExecType et, 
00284                                                                                                                                   int num_params, 
00285                                                                                                                                   const ParamType *types, 
00286                                                                                                                                   ...) =0;
00287 
00288                     /**
00289                      * @brief Finds a forward by name.  Does not return anonymous forwards (named NULL or "").
00290                      *
00291                      * @param name                          Name of forward.
00292                      * @param ifchng              Optionally store either NULL or an IChangeableForward pointer
00293                      *                      depending on type of forward.
00294                      * @return                                        IForward pointer, or NULL if none found matching the name.
00295                      */
00296                     virtual IForward *FindForward(const char *name, IChangeableForward **ifchng) =0;
00297 
00298                     /**
00299                      * @brief Frees and destroys a forward object.
00300                      *
00301                      * @param forward             An IForward created by CreateForward() or CreateForwardEx().
00302                      */
00303                     virtual void ReleaseForward(IForward *forward) =0;
00304           };
00305 }
00306 
00307 /*
00308  *  In the AMX Mod X model of forwarding, each forward contained a list of pairs, each pair containing
00309  * a function ID and an AMX structure.  The forward structure itself did very little but hold parameter types.
00310  * An execution call worked like this:
00311  *  - executeForward() took in a function id and a list of parameters
00312  *  - for each contained plugin:
00313  *   - the list of parameters was preprocessed and pushed
00314  *   - the call was made
00315  *   - the list was freed and copybacks were made
00316  *  - return
00317  * 
00318  *  The advantages to this is that the system is very easy to implement, and it's fast. The disadvantage is 
00319  * varargs tend to be very unforgiving and inflexible, and thus weird problems arose with casting.  You also 
00320  * lose flexibility, type checking, and the ability to reasonably use variable arguments lists in the VM.
00321  *
00322  *  SourceMod replaces this forward system with a far more advanced, but a bit bulkier one. The idea is that 
00323  * each plugin has a table of functions, and each function is an ICallable object.  As well as being an ICallable, 
00324  * each function is an IPluginFunction.  An ICallable simply describes the process of adding parameters to a 
00325  * function call.  An IPluginFunction describes the process of actually calling a function and performing allocation, 
00326  * copybacks, and deallocations.
00327  *
00328  * A very powerful forward system emerges: a Forward is just a collection of IPluginFunctions.  Thus, the same 
00329  * API can be easily wrapped around a simple list, and it will look transparent to the user.
00330  * Advantages:
00331  * 1) "SP Forwards" from AMX Mod X are simply IPluginFunctions without a collection.
00332  * 2) Forwards are function based, rather than plugin based, and are thus far more flexible at runtime..
00333  * 3) [2] Individual functions can be paused and more than one function from the same plugin can be hooked.
00334  * 4) [2] One hook type that used to map to many SP Forwards can now be centralized as one Forward.
00335  *    This helps alleviate messes like Fakemeta.
00336  * 5) Parameter pushing is type-checked and allows for variable arguments.
00337  *
00338  *  Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
00339  * object oriented, rather than AMX Mod X, which hides the objects behind static functions.  It is entirely a design
00340  * issue, rather than a usability one.  The interesting part is when it gets to implementation, which has to cache
00341  * parameter pushing until execution.  Without this, multiple function calls can be started across one plugin, which
00342  * will result in heap corruption given SourcePawn's implementation.
00343  *
00344  * Observe the new calling process:
00345  * - Each parameter is pushed into a local cache using the ICallable interface.
00346  * - For each function in the collection:
00347  *  - Each parameter is decoded and -pushed into the function.
00348  *  - The call is made.
00349  * - Return
00350  *
00351  *  Astute readers will note the (minor) problems:
00352  * 1) More memory is used.  Specifically, rather than N params of memory, you now have N params * M plugins.
00353  *    This is because, again, parameters are cached both per-function and per-forward.
00354  * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
00355  *
00356  * HISTORICAL NOTES:
00357  *  There used to be a # about copy backs.  
00358  *  Note that originally, the Forward implementation was a thin wrapper around IForwards.  It did not cache pushes,
00359  * and instead immediately fired them to each internal plugin.  This was to allow users to know that pointers would 
00360  * be immediately resolved.  Unfortunately, this became extremely burdensome on the API and exposed many problems,
00361  * the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same 
00362  * plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
00363  * and become impossible to re-arrange without some global heap tracking mechanism.  It also made iterative copy backs 
00364  * for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
00365  *  Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X 
00366  * uses.  It is both faster and works better.
00367  */
00368 
00369 #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_

Generated on Fri Nov 21 04:10:05 2008 for SourceMod SDK by  doxygen 1.5.1