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