From 5fa53bfc682035f472fd7f790b0d5c76033c8485 Mon Sep 17 00:00:00 2001 From: Scott Ehlert Date: Fri, 16 Mar 2007 06:54:24 +0000 Subject: [PATCH] Dear me, I should have committed this long ago... 1) Added natives to create and manipulate global and private forward 2) Added natives to call forwards and functions 3) Added an IChanageableForward::RemoveFunction overload for convenience or something 4) Added test suite plugins for functions and forwards 5) Some random touch-ups to some include files --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40627 --- core/msvc8/sourcemod_mm.vcproj | 4 + core/smn_functions.cpp | 590 ++++++++++++++++++++++++++++++ core/systems/ForwardSys.cpp | 12 + core/systems/ForwardSys.h | 1 + core/vm/sp_vm_function.cpp | 5 - plugins/include/clients.inc | 2 +- plugins/include/console.inc | 6 +- plugins/include/core.inc | 8 + plugins/include/files.inc | 4 +- plugins/include/functions.inc | 289 ++++++++++++++- plugins/include/helpers.inc | 27 ++ plugins/include/sourcemod.inc | 8 +- plugins/include/string.inc | 4 +- plugins/include/usermessages.inc | 6 +- plugins/testsuite/callfunctest.sp | 104 ++++++ plugins/testsuite/fwdtest1.sp | 169 +++++++++ plugins/testsuite/fwdtest2.sp | 43 +++ public/IForwardSys.h | 10 + 18 files changed, 1268 insertions(+), 24 deletions(-) create mode 100644 core/smn_functions.cpp create mode 100644 plugins/testsuite/callfunctest.sp create mode 100644 plugins/testsuite/fwdtest1.sp create mode 100644 plugins/testsuite/fwdtest2.sp diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 16c50aef..0e4575de 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -725,6 +725,10 @@ RelativePath="..\smn_float.cpp" > + + diff --git a/core/smn_functions.cpp b/core/smn_functions.cpp new file mode 100644 index 00000000..b0f9154e --- /dev/null +++ b/core/smn_functions.cpp @@ -0,0 +1,590 @@ +/** +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#include "sm_globals.h" +#include "PluginSys.h" +#include "ForwardSys.h" +#include "HandleSys.h" + +HandleType_t g_GlobalFwdType = 0; +HandleType_t g_PrivateFwdType = 0; + +static bool s_CallStarted = false; +static ICallable *s_pCallable = NULL; +static IPluginFunction *s_pFunction = NULL; +static IForward *s_pForward = NULL; + +class ForwardNativeHelpers : + public SMGlobalClass, + public IHandleTypeDispatch +{ +public: + void OnSourceModAllInitialized() + { + HandleAccess sec; + + /* Set GlobalFwd handle access security */ + g_HandleSys.InitAccessDefaults(NULL, &sec); + sec.access[HandleAccess_Read] = 0; + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; + + /* Create 'GlobalFwd' handle type */ + g_GlobalFwdType = g_HandleSys.CreateType("GlobalFwd", this, 0, NULL, &sec, g_pCoreIdent, NULL); + + /* Private forwards are cloneable */ + sec.access[HandleAccess_Clone] = 0; + + /* Create 'PrivateFwd' handle type */ + g_PrivateFwdType = g_HandleSys.CreateType("PrivateFwd", this, g_GlobalFwdType, NULL, &sec, g_pCoreIdent, NULL); + } + + void OnSourceModShutdown() + { + g_HandleSys.RemoveType(g_PrivateFwdType, g_pCoreIdent); + g_HandleSys.RemoveType(g_GlobalFwdType, g_pCoreIdent); + } + + void OnHandleDestroy(HandleType_t type, void *object) + { + IForward *pForward = static_cast(object); + + g_Forwards.ReleaseForward(pForward); + } +} g_ForwardNativeHelpers; + + +/* Turn a public index into a function ID */ +inline funcid_t PublicIndexToFuncId(uint32_t idx) +{ + return (idx << 1) | (1 << 0); +} + +/* Reset global function/forward call variables */ +inline void ResetCall() +{ + s_CallStarted = false; + s_pFunction = NULL; + s_pCallable = NULL; +} + +static cell_t sm_GetFunctionByName(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + char *name; + uint32_t idx; + IPlugin *pPlugin; + + if (hndl == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + pPlugin = g_PluginSys.PluginFromHandle(hndl, &err); + + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", hndl, err); + } + } + + pContext->LocalToString(params[2], &name); + + /* Get public function index */ + if (pPlugin->GetBaseContext()->FindPublicByName(name, &idx) == SP_ERROR_NOT_FOUND) + { + /* Return INVALID_FUNCTION if not found */ + return -1; + } + + /* Return function ID */ + return PublicIndexToFuncId(idx); +} + +static cell_t sm_CreateGlobalForward(IPluginContext *pContext, const cell_t *params) +{ + cell_t count = params[0]; + char *name; + ParamType forwardParams[SP_MAX_EXEC_PARAMS]; + + if (count - 2 > SP_MAX_EXEC_PARAMS) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, NULL); + } + + pContext->LocalToString(params[1], &name); + + cell_t *addr; + for (int i = 3; i <= count; i++) + { + pContext->LocalToPhysAddr(params[i], &addr); + forwardParams[i - 3] = static_cast(*addr); + } + + IForward *pForward = g_Forwards.CreateForward(name, static_cast(params[2]), count - 2, forwardParams); + + return g_HandleSys.CreateHandle(g_GlobalFwdType, pForward, pContext->GetIdentity(), g_pCoreIdent, NULL); +} + +static cell_t sm_CreateForward(IPluginContext *pContext, const cell_t *params) +{ + cell_t count = params[0]; + ParamType forwardParams[SP_MAX_EXEC_PARAMS]; + + if (count - 1 > SP_MAX_EXEC_PARAMS) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, NULL); + } + + cell_t *addr; + for (int i = 2; i <= count; i++) + { + pContext->LocalToPhysAddr(params[i], &addr); + forwardParams[i - 2] = static_cast(*addr); + } + + IChangeableForward *pForward = g_Forwards.CreateForwardEx(NULL, static_cast(params[1]), count - 1, forwardParams); + + return g_HandleSys.CreateHandle(g_PrivateFwdType, pForward, pContext->GetIdentity(), g_pCoreIdent, NULL); +} + +static cell_t sm_GetForwardFunctionCount(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + IForward *pForward; + + if ((err=g_HandleSys.ReadHandle(hndl, g_GlobalFwdType, NULL, (void **)&pForward)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid forward handle %x (error %d)", hndl, err); + } + + return pForward->GetFunctionCount(); +} + +static cell_t sm_AddToForward(IPluginContext *pContext, const cell_t *params) +{ + Handle_t fwdHandle = static_cast(params[1]); + Handle_t plHandle = static_cast(params[2]); + HandleError err; + IChangeableForward *pForward; + IPlugin *pPlugin; + + if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err); + } + + if (plHandle == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err); + + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err); + } + } + + IPluginFunction *pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[3]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[3]); + } + + return pForward->AddFunction(pFunction); +} + +static cell_t sm_RemoveFromForward(IPluginContext *pContext, const cell_t *params) +{ + Handle_t fwdHandle = static_cast(params[1]); + Handle_t plHandle = static_cast(params[2]); + HandleError err; + IChangeableForward *pForward; + IPlugin *pPlugin; + + if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err); + } + + if (plHandle == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err); + + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err); + } + } + + IPluginFunction *pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[3]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[3]); + } + + return pForward->RemoveFunction(pFunction); +} + +static cell_t sm_RemoveAllFromForward(IPluginContext *pContext, const cell_t *params) +{ + Handle_t fwdHandle = static_cast(params[1]); + Handle_t plHandle = static_cast(params[2]); + HandleError err; + IChangeableForward *pForward; + IPlugin *pPlugin; + + if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err); + } + + if (plHandle == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err); + + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err); + } + } + + return pForward->RemoveFunctionsOfPlugin(pPlugin); +} + +static cell_t sm_CallStartFunction(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl; + HandleError err; + IPlugin *pPlugin; + + if (s_CallStarted) + { + return pContext->ThrowNativeError("Cannot start a call while one is already in progress"); + } + + hndl = static_cast(params[1]); + + if (hndl == 0) + { + pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext()); + } else { + pPlugin = g_PluginSys.PluginFromHandle(hndl, &err); + + if (!pPlugin) + { + return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", hndl, err); + } + } + + s_pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[2]); + + if (!s_pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + s_pCallable = static_cast(s_pFunction); + + s_CallStarted = true; + + return 1; +} + +static cell_t sm_CallStartForward(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl; + HandleError err; + IForward *pForward; + + if (s_CallStarted) + { + return pContext->ThrowNativeError("Cannot start a call while one is already in progress"); + } + + hndl = static_cast(params[1]); + + if ((err=g_HandleSys.ReadHandle(hndl, g_GlobalFwdType, NULL, (void **)&pForward)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid forward handle %x (error %d)", hndl, err); + } + + s_pForward = pForward; + + s_pCallable = static_cast(pForward); + + s_CallStarted = true; + + return 1; +} + +static cell_t sm_CallPushCell(IPluginContext *pContext, const cell_t *params) +{ + int err; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + err = s_pCallable->PushCell(params[1]); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushCellRef(IPluginContext *pContext, const cell_t *params) +{ + int err; + cell_t *addr; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToPhysAddr(params[1], &addr); + + err = s_pCallable->PushCellByRef(addr); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushFloat(IPluginContext *pContext, const cell_t *params) +{ + int err; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + err = s_pCallable->PushFloat(sp_ctof(params[1])); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushFloatRef(IPluginContext *pContext, const cell_t *params) +{ + int err; + cell_t *addr; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToPhysAddr(params[1], &addr); + + err = s_pCallable->PushFloatByRef(reinterpret_cast(addr)); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushArray(IPluginContext *pContext, const cell_t *params) +{ + int err; + cell_t *addr; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToPhysAddr(params[1], &addr); + + err = s_pCallable->PushArray(addr, params[2], NULL); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushArrayEx(IPluginContext *pContext, const cell_t *params) +{ + int err; + cell_t *addr; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToPhysAddr(params[1], &addr); + + err = s_pCallable->PushArray(addr, params[2], params[3]); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushString(IPluginContext *pContext, const cell_t *params) +{ + int err; + char *value; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToString(params[1], &value); + + err = s_pCallable->PushString(value); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushStringEx(IPluginContext *pContext, const cell_t *params) +{ + int err; + char *value; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + pContext->LocalToString(params[1], &value); + + err = s_pCallable->PushStringEx(value, params[2], params[3], params[4]); + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallFinish(IPluginContext *pContext, const cell_t *params) +{ + int err; + cell_t *result; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot finish call when there is no call in progress"); + } + + pContext->LocalToPhysAddr(params[1], &result); + + if (s_pFunction) + { + IPluginFunction *pFunction = s_pFunction; + err = pFunction->Execute(result); + } else if (s_pForward) { + IForward *pForward = s_pForward; + err = pForward->Execute(result, NULL); + } + + ResetCall(); + + return err; +} + +static cell_t sm_CallCancel(IPluginContext *pContext, const cell_t *params) +{ + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot cancel call when there is no call in progress"); + } + + s_pCallable->Cancel(); + ResetCall(); + + return 1; +} + +REGISTER_NATIVES(functionNatives) +{ + {"GetFunctionByName", sm_GetFunctionByName}, + {"CreateGlobalForward", sm_CreateGlobalForward}, + {"CreateForward", sm_CreateForward}, + {"GetForwardFunctionCount", sm_GetForwardFunctionCount}, + {"AddToForward", sm_AddToForward}, + {"RemoveFromForward", sm_RemoveFromForward}, + {"RemoveAllFromForward", sm_RemoveAllFromForward}, + {"Call_StartFunction", sm_CallStartFunction}, + {"Call_StartForward", sm_CallStartForward}, + {"Call_PushCell", sm_CallPushCell}, + {"Call_PushCellRef", sm_CallPushCellRef}, + {"Call_PushFloat", sm_CallPushFloat}, + {"Call_PushFloatRef", sm_CallPushFloatRef}, + {"Call_PushArray", sm_CallPushArray}, + {"Call_PushArrayEx", sm_CallPushArrayEx}, + {"Call_PushString", sm_CallPushString}, + {"Call_PushStringEx", sm_CallPushStringEx}, + {"Call_Finish", sm_CallFinish}, + {"Call_Cancel", sm_CallCancel}, + {NULL, NULL}, +}; diff --git a/core/systems/ForwardSys.cpp b/core/systems/ForwardSys.cpp index ae18abdf..f0ef70c6 100644 --- a/core/systems/ForwardSys.cpp +++ b/core/systems/ForwardSys.cpp @@ -601,6 +601,18 @@ bool CForward::AddFunction(IPluginContext *pContext, funcid_t index) return AddFunction(pFunc); } +bool CForward::RemoveFunction(IPluginContext *pContext, funcid_t index) +{ + IPluginFunction *pFunc = pContext->GetFunctionById(index); + + if (!pFunc) + { + return false; + } + + return RemoveFunction(pFunc); +} + bool CForward::RemoveFunction(IPluginFunction *func) { bool found = false; diff --git a/core/systems/ForwardSys.h b/core/systems/ForwardSys.h index b1d8703a..77d798c6 100644 --- a/core/systems/ForwardSys.h +++ b/core/systems/ForwardSys.h @@ -64,6 +64,7 @@ public: //IChangeableForward virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin); virtual bool AddFunction(IPluginFunction *func); virtual bool AddFunction(IPluginContext *ctx, funcid_t index); + virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index); public: static CForward *CreateForward(const char *name, ExecType et, diff --git a/core/vm/sp_vm_function.cpp b/core/vm/sp_vm_function.cpp index 3bd995f6..58abee22 100644 --- a/core/vm/sp_vm_function.cpp +++ b/core/vm/sp_vm_function.cpp @@ -81,11 +81,6 @@ int CFunction::PushCell(cell_t cell) int CFunction::PushCellByRef(cell_t *cell, int flags) { - if (m_curparam >= SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - return PushArray(cell, 1, flags); } diff --git a/plugins/include/clients.inc b/plugins/include/clients.inc index 25780f25..c01150dd 100644 --- a/plugins/include/clients.inc +++ b/plugins/include/clients.inc @@ -272,4 +272,4 @@ native CreateFakeClient(const String:name[]); * @error Invalid client index, client not connected, * or client not a fake client. */ -native SetFakeClientConVar(client, const String:cvar[], const String:value[]); \ No newline at end of file +native SetFakeClientConVar(client, const String:cvar[], const String:value[]); diff --git a/plugins/include/console.inc b/plugins/include/console.inc index fd915290..dcebe2e7 100644 --- a/plugins/include/console.inc +++ b/plugins/include/console.inc @@ -91,7 +91,7 @@ native ServerExecute(); * @noreturn * @error Invalid client index, or client not connected. */ -native ClientCommand(client, const String:fmt[], {String,Float,Handle,_}:...); +native ClientCommand(client, const String:fmt[], {String,Float,Handle,Function,_}:...); /** * Sends a message to the server console. @@ -100,7 +100,7 @@ native ClientCommand(client, const String:fmt[], {String,Float,Handle,_}:...); * @param ... Variable number of format parameters. * @noreturn */ -native PrintToServer(const String:format[], {Handle,Float,String,_}:...); +native PrintToServer(const String:format[], {Handle,Float,String,Function,_}:...); /** * Sends a message to a client's console. @@ -111,7 +111,7 @@ native PrintToServer(const String:format[], {Handle,Float,String,_}:...); * @noreturn * @error If the client is not connected an error will be thrown. */ -native PrintToConsole(client, const String:format[], {Handle,Float,String,_}:...); +native PrintToConsole(client, const String:format[], {Handle,Float,String,Function,_}:...); /** * Called when a server-only command is invoked. diff --git a/plugins/include/core.inc b/plugins/include/core.inc index 49a53d49..9e2272d3 100644 --- a/plugins/include/core.inc +++ b/plugins/include/core.inc @@ -27,6 +27,14 @@ struct PlVers String:filevers[], }; +/** + * Function helper values + */ +enum Function +{ + INVALID_FUNCTION = -1, +}; + /** * Specifies what to do after a hook completes. */ diff --git a/plugins/include/files.inc b/plugins/include/files.inc index dc83b13e..9fdb5406 100644 --- a/plugins/include/files.inc +++ b/plugins/include/files.inc @@ -54,7 +54,7 @@ enum PathType * @param ... Format arguments. * @return Number of bytes written to buffer (not including null terminator). */ -native BuildPath(PathType:type, String:buffer[], maxlength, const String:fmt[], ...); +native BuildPath(PathType:type, String:buffer[], maxlength, const String:fmt[], {Handle,Float,String,Function,_}:...); /** * @brief Opens a directory/folder for contents enumeration. @@ -186,4 +186,4 @@ native bool:RemoveDir(const String:path[]); * @param ... Variable number of format parameters. * @return True on success, false otherwise. */ -native bool:WriteFileLine(Handle:hndl, const String:format[], {Handle,Float,String,_}:...); +native bool:WriteFileLine(Handle:hndl, const String:format[], {Handle,Float,String,Function,_}:...); diff --git a/plugins/include/functions.inc b/plugins/include/functions.inc index 2cea70a0..bc0bf255 100644 --- a/plugins/include/functions.inc +++ b/plugins/include/functions.inc @@ -13,6 +13,48 @@ * Version: $Id$ */ +#define SP_PARAMFLAG_BYREF (1<<0) /**< Internal use only. */ + +/** + * Describes the various ways to pass parameters to functions or forwards. + */ +enum ParamType +{ + Param_Any = 0, /**< Any data type can be pushed */ + Param_Cell = (1<<1), /**< Only basic cells can be pushed */ + Param_Float = (2<<1), /**< Only floats can be pushed */ + Param_String = (3<<1)|SP_PARAMFLAG_BYREF, /**< Only strings can be pushed */ + Param_Array = (4<<1)|SP_PARAMFLAG_BYREF, /**< Only arrays can be pushed */ + Param_VarArgs = (5<<1), /**< Same as "..." in plugins, anything can be pushed, but it will always be byref */ + Param_CellByRef = (1<<1)|SP_PARAMFLAG_BYREF, /**< Only a cell by reference can be pushed */ + Param_FloatByRef = (2<<1)|SP_PARAMFLAG_BYREF /**< Only a float by reference can be pushed */ +}; + +/** + * Defines how a forward iterates through plugin functions. + */ +enum ExecType +{ + ET_Ignore = 0, /**< Ignore all return values, return 0 */ + ET_Single = 1, /**< Only return the last exec, ignore all others */ + ET_Event = 2, /**< Acts as an event with the Actions defined in core.inc, no mid-Stops allowed, returns highest */ + ET_Hook = 3 /**< Acts as a hook with the Actions defined in core.inc, mid-Stops allowed, returns highest */ +}; + +/** + * @section Flags that are used with Call_PushArrayEx() and Call_PushStringEx() + */ +#define SM_PARAM_COPYBACK (1<<0) /**< Copy an array/reference back after call */ +#define SM_PARAM_STRING_UTF8 (1<<0) /**< String should be UTF-8 handled */ +#define SM_PARAM_STRING_COPY (1<<1) /**< String should be copied into the plugin */ + +/** + * @endsection + */ + +/** + * @section Error codes + */ #define SP_ERROR_NONE 0 /**< No error occurred */ #define SP_ERROR_FILE_FORMAT 1 /**< File format unrecognized */ #define SP_ERROR_DECOMPRESSOR 2 /**< A decompressor was not found */ @@ -40,6 +82,243 @@ #define SP_ERROR_NOT_RUNNABLE 24 /**< Function or plugin is not runnable */ #define SP_ERROR_ABORTED 25 /**< Function call was aborted */ +/** + * @endsection + */ + +/** + * Gets a function id from a function name. + * + * @param plugin Handle of the plugin that contains the function. + Pass INVALID_HANDLE to search in the calling plugin. + * @param name Name of the function. + * @return Function id or INVALID_FUNCTION if not found. + * @error Invalid or corrupt plugin handle. + */ +native Function:GetFunctionByName(Handle:plugin, const String:name[]); + +/** + * Creates a global forward. + * + * @note The name used to create the forward is used as its public function in all target plugins. + * @note This is ideal for global, static forwards that are never changed. + * + * @param name Name of public function to use in forward. + * @param type Execution type to be used. + * @param ... Variable number of parameter types (up to 32). + * @return Handle to new global forward. + * @error More than 32 paramater types passed. + */ +native Handle:CreateGlobalForward(const String:name[], ExecType:type, {ParamType}:...); + +/** + * Creates a private forward. + * + * @note No functions are automatically added. Use AddToForward() to do this. + * + * @param type Execution type to be used. + * @param ... Variable number of parameter types (up to 32). + * @return Handle to new private forward. + * @error More than 32 paramater types passed. + */ +native Handle:CreateForward(ExecType:type, {ParamType}:...); + +/** + * Returns the number of functions in a global or private forward's call list. + * + * @param fwd Handle to global or private forward. + * @return Number of functions in forward. + * @error Invalid or corrupt forward handle. + */ +native GetForwardFunctionCount(Handle:fwd); + +/** + * Adds a function to a private forward's call list. + * + * @note Cannot be used during an incompleted call. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to add to forward. + * @return True on success, false otherwise. + * @error Invalid or corrupt private forward handle, invalid or corrupt plugin handle, or invalid function. + */ +native bool:AddToForward(Handle:fwd, Handle:plugin, Function:func); + +/** + * Removes a function from a private forward's call list. + * + * @note Only removes one instance. + * @note Functions will be removed automatically if their parent plugin is unloaded. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to remove from forward. + * @return True on success, false otherwise. + * @error Invalid or corrupt private forward handle, invalid or corrupt plugin handle, or invalid function. + */ +native bool:RemoveFromForward(Handle:fwd, Handle:plugin, Function:func); + +/** + * Removes all instances of a plugin from a private forward's call list. + * + * @note Functions will be removed automatically if their parent plugin is unloaded. + * + * @param fwd Handle to private forward. + * @param plugin Handle of the plugin to remove instances of. + * Pass INVALID_HANDLE to specify the calling plugin. + * @return Number of functions removed from forward. + * @error Invalid or corrupt private forward handle or invalid or corrupt plugin handle. + */ +native RemoveAllFromForward(Handle:fwd, Handle:plugin); + +/** + * Starts a call to functions in a forward's call list. + * + * @note Cannot be used during an incompleted call. + * + * @param fwd Handle to global or private forward. + * @noreturn + * @error Invalid or corrupt forward handle or called before another call has completed. + */ +native Call_StartForward(Handle:fwd); + +/** + * Starts a call to a function. + * + * @note Cannot be used during an incompleted call. + * + * @param plugin Handle of the plugin that contains the function. + * Pass INVALID_HANDLE to specify the calling plugin. + * @param func Function to call. + * @noreturn + * @error Invalid or corrupt plugin handle, invalid function, or called before another call has completed. + */ +native Call_StartFunction(Handle:plugin, Function:func); + +/** + * Pushes a cell onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Cell value to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushCell({Handle,Function,_}:value); + +/** + * Pushes a cell by reference onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Cell reference to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushCellRef(&{Handle,Function,_}:value); + + +/** + * Pushes a float onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Floating point value to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushFloat(Float:value); + +/** + * Pushes a float by reference onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Floating point reference to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushFloatRef(&Float:value); + +/** + * Pushes an array onto the current call. + * + * @note Changes to array are not copied back to caller. Use PushArrayEx() to do this. + * @note Cannot be used before a call has been started. + * + * @param value Array to push. + * @param size Size of array. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushArray(const {Handle,Float,Function,_}:value[], size); + +/** + * Pushes an array onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value Array to push. + * @param size Size of array. + * @param cpflags Whether or not changes should be copied back to the input array. + * See SP_PARAM_* constants for details. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushArrayEx({Handle,Float,Function,_}:value[], size, cpflags); + +/** + * Pushes a string onto the current call. + * + * @note Changes to string are not copied back to caller. Use PushStringEx() to do this. + * @note Cannot be used before a call has been started. + * + * @param value String to push. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushString(const String:value[]); + +/** + * Pushes a string onto the current call. + * + * @note Cannot be used before a call has been started. + * + * @param value String to push. + * @param length Length of string buffer. + * @param szflags Flags determining how string should be handled. + * See SP_PARAM_STRING_* constants for details. + * @param cpflags Whether or not changes should be copied back to the input array. + * See SP_PARAM_* constants for details. + * @noreturn + * @error Called before a call has been started. + */ +native Call_PushStringEx(String:value[], length, szflags, cpflags); + +/** + * Completes a call to a function or forward's call list. + * + * @note Cannot be used before a call has been started. + * + * @param result Return value of function or forward's call list. + * @return SP_ERROR_NONE on success, any other integer on failure. + * @error Called before a call has been started. + */ +native Call_Finish(&result); + +/** + * Cancels a call to a function or forward's call list. + * + * @note Cannot be used before a call has been started. + * + * @noreturn + * @error Called before a call has been started. + */ +native Call_Cancel(); + /** * Defines a native function. * @@ -69,7 +348,7 @@ native CreateNative(const String:name[], NativeCall:func); * @param fmt Error message format. * @param ... Format arguments. */ -native ThrowNativeError(error, const String:fmt[], {Handle,Float,String,_}:...); +native ThrowNativeError(error, const String:fmt[], {Handle,Float,String,Function,_}:...); /** * Retrieves the string length from a native parameter string. This is useful @@ -78,7 +357,7 @@ native ThrowNativeError(error, const String:fmt[], {Handle,Float,String,_}:...); * * @param param Parameter number, starting from 1. * @param length Stores the length of the string. - * @return SP_ERROR_NONE on sucecss, any other integer on failure. + * @return SP_ERROR_NONE on success, any other integer on failure. * @error Invalid parameter number or calling from a non-native function. */ native GetNativeStringLength(param, &length); @@ -137,7 +416,7 @@ native GetNativeCellRef(param); * @noreturn * @error Invalid parameter number or calling from a non-native function. */ -native SetNativeCellRef(param, {Float,Handle,_}:value); +native SetNativeCellRef(param, {Float,Function,Handle,_}:value); /** * Gets an array from a native parameter (always by reference). @@ -148,7 +427,7 @@ native SetNativeCellRef(param, {Float,Handle,_}:value); * @return SP_ERROR_NONE on success, anything else on failure. * @error Invalid parameter number or calling from a non-native function. */ -native GetNativeArray(param, {Float,Handle,_}:local[], size); +native GetNativeArray(param, {Float,Function,Handle,_}:local[], size); /** * Copies a local array into a native parameter array (always by reference). @@ -159,7 +438,7 @@ native GetNativeArray(param, {Float,Handle,_}:local[], size); * @return SP_ERROR_NONE on success, anything else on failure. * @error Invalid parameter number or calling from a non-native function. */ -native SetNativeArray(param, const {Float,Handle,_}:local[], size); +native SetNativeArray(param, const {Float,Function,Handle,_}:local[], size); /** * Formats a string using parameters from a native. diff --git a/plugins/include/helpers.inc b/plugins/include/helpers.inc index 61fd8f82..e33e2f58 100644 --- a/plugins/include/helpers.inc +++ b/plugins/include/helpers.inc @@ -44,3 +44,30 @@ stock FormatUserLogText(client, String:buffer[], maxlength) Format(buffer, maxlength, "\"%s<%d><%s><>\"", name, userid, auth); } + +/** + * Returns plugin handle from plugin filename. + * + * @param filename Filename of the plugin to search for. + * @Returns Handle to plugin if found, INVALID_HANDLE otherwise. + */ +stock Handle:FindPluginByFile(const String:filename[]) +{ + decl String:buffer[256]; + + new Handle:iter = GetPluginIterator(); + new Handle:pl; + + while (MorePlugins(iter)) + { + pl = ReadPlugin(iter); + + GetPluginFilename(pl, buffer, sizeof(buffer)); + if (StrCompare(buffer, filename) == 0) + { + return pl; + } + } + + return INVALID_HANDLE; +} diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 41baa13b..782512a8 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -189,7 +189,7 @@ native bool:GetPluginInfo(Handle:plugin, PluginInfo:info, String:buffer[], maxle * @noreturn * @error Always! */ -native ThrowError(const String:fmt[], {Handle,Float,String,_}:...); +native ThrowError(const String:fmt[], {Handle,Float,String,Function,_}:...); /** * Logs a generic message to the HL2 logs. @@ -198,7 +198,7 @@ native ThrowError(const String:fmt[], {Handle,Float,String,_}:...); * @param ... Format arguments. * @noreturn */ -native LogToGame(const String:format[], {Handle,Float,String,_}:...); +native LogToGame(const String:format[], {Handle,Float,String,Function,_}:...); /** * Logs a plugin message to the SourceMod logs. @@ -207,7 +207,7 @@ native LogToGame(const String:format[], {Handle,Float,String,_}:...); * @param ... Format arguments. * @noreturn */ -native LogMessage(const String:format[], {Handle,Float,String,_}:...); +native LogMessage(const String:format[], {Handle,Float,String,Function,_}:...); /** * Logs a plugin error message to the SourceMod logs. @@ -216,7 +216,7 @@ native LogMessage(const String:format[], {Handle,Float,String,_}:...); * @param ... Format arguments. * @noreturn */ -native LogError(const String:format[], {Handle,Float,String,_}:...); +native LogError(const String:format[], {Handle,Float,String,Function,_}:...); /** * Gets the system time as a unix timestamp. diff --git a/plugins/include/string.inc b/plugins/include/string.inc index b8ed4180..9dee3579 100644 --- a/plugins/include/string.inc +++ b/plugins/include/string.inc @@ -93,7 +93,7 @@ native StrCopy(String:dest[], destLen, const String:source[]); * @param ... Variable number of format parameters. * @return Number of cells written. */ -native Format(String:buffer[], maxlength, const String:format[], {Handle,Float,String,_}:...); +native Format(String:buffer[], maxlength, const String:format[], {Handle,Float,String,Function,_}:...); /** * Formats a string according to the SourceMod format rules (see documentation). @@ -106,7 +106,7 @@ native Format(String:buffer[], maxlength, const String:format[], {Handle,Float,S * @param ... Variable number of format parameters. * @return Number of cells written. */ -native FormatEx(String:buffer[], maxlength, const String:format[], {Handle,Float,String,_}:...); +native FormatEx(String:buffer[], maxlength, const String:format[], {Handle,Float,String,Function,_}:...); /** * Formats a string according to the SourceMod format rules (see documentation). diff --git a/plugins/include/usermessages.inc b/plugins/include/usermessages.inc index fbb6c1ae..41909fda 100644 --- a/plugins/include/usermessages.inc +++ b/plugins/include/usermessages.inc @@ -57,11 +57,12 @@ native bool:GetUserMessageName(UserMsg:msg_id, String:msg[], maxlength); * @param msgname Message name to start. * @param clients Array containing player indexes to broadcast to. * @param numClients Number of players in the array. + * @param flags Optional flags to set. * @return A handle to a bf_write bit packing structure, or * INVALID_HANDLE on failure. * @error Invalid message name or unable to start a message. */ -native Handle:StartMessage(String:msgname[], clients[], numClients, flags); +native Handle:StartMessage(String:msgname[], clients[], numClients, flags=0); /** * Starts a usermessage (network message). @@ -71,11 +72,12 @@ native Handle:StartMessage(String:msgname[], clients[], numClients, flags); * @param msg Message index to start. * @param clients Array containing player indexes to broadcast to. * @param numClients Number of players in the array. + * @param flags Optional flags to set. * @return A handle to a bf_write bit packing structure, or * INVALID_HANDLE on failure. * @error Invalid message name or unable to start a message. */ -native Handle:StartMessageEx(UserMsg:msg, clients[], numClients, flags); +native Handle:StartMessageEx(UserMsg:msg, clients[], numClients, flags=0); /** * Ends a previously started user message (network message). diff --git a/plugins/testsuite/callfunctest.sp b/plugins/testsuite/callfunctest.sp new file mode 100644 index 00000000..93bc379b --- /dev/null +++ b/plugins/testsuite/callfunctest.sp @@ -0,0 +1,104 @@ +#include + +public Plugin:myinfo = +{ + name = "Function Call Testing Lab", + author = "AlliedModders LLC", + description = "Tests basic function calls", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + RegServerCmd("test_callfunc", Command_CallFunc); +} + +public OnCallFuncReceived(num, Float:fnum, String:str[], String:str2[], &val, &Float:fval, array[], array2[], size, hello2[1]) +{ + PrintToServer("Inside OnCallFuncReceived..."); + + PrintToServer("num = %d (expected: %d)", num, 5); + PrintToServer("fnum = %f (expected: %f)", fnum, 7.17); + PrintToServer("str[] = \"%s\" (expected: \"%s\")", str, "Gaben"); + PrintToServer("str2[] = \"%s\" (expected: \"%s\")", str2, ".taf si nebaG"); + + PrintToServer("val = %d (expected %d, setting to %d)", val, 62, 15); + val = 15; + + PrintToServer("fval = %f (expected: %f, setting to %f)", fval, 6.25, 1.5); + fval = 1.5; + + PrintToServer("Printing %d elements of array[] (expected: %d)", size, 6); + for (new i = 0; i < size; i++) + { + PrintToServer("array[%d] = %d (expected: %d)", i, array[i], i); + } + for (new i = 0; i < size; i++) + { + PrintToServer("array2[%d] = %d (expected: %d)", i, array[i], i); + } + + /* This shouldn't get copied back */ + StrCopy(str, strlen(str) + 1, "Yeti"); + /* This should get copied back */ + StrCopy(str2, strlen(str2) + 1, "Gaben is fat."); + + /* This should get copied back */ + array[0] = 5; + array[1] = 6; + /* This shouldn't get copied back */ + hello2[0] = 25; + + return 42; +} + +public Action:Command_CallFunc(args) +{ + new a = 62; + new Float:b = 6.25; + new const String:what[] = "Gaben"; + new String:truth[] = ".taf si nebaG"; + new hello[] = {0, 1, 2, 3, 4, 5}; + new hello2[] = {9}; + new pm = 6; + new err; + new ret; + + new Function:func = GetFunctionByName(INVALID_HANDLE, "OnCallFuncReceived"); + + if (func == INVALID_FUNCTION) + { + PrintToServer("Failed to get the function id of OnCallFuncReceived"); + return Plugin_Handled; + } + + PrintToServer("Calling OnCallFuncReceived..."); + + Call_StartFunction(INVALID_HANDLE, func); + Call_PushCell(5); + Call_PushFloat(7.17); + Call_PushString(what); + Call_PushStringEx(truth, sizeof(truth), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + Call_PushCellRef(a); + Call_PushFloatRef(b); + Call_PushArrayEx(hello, pm, SM_PARAM_COPYBACK); + Call_PushArray(hello, pm); + Call_PushCell(pm); + Call_PushArray(hello2, 1); + err = Call_Finish(ret); + + PrintToServer("Call to OnCallFuncReceived has finished!"); + PrintToServer("Error code = %d (expected: %d)", err, 0); + PrintToServer("Return value = %d (expected: %d)", ret, 42); + + PrintToServer("a = %d (expected: %d)", a, 15); + PrintToServer("b = %f (expected: %f)", b, 1.5); + PrintToServer("what = \"%s\" (expected: \"%s\")", what, "Gaben"); + PrintToServer("truth = \"%s\" (expected: \"%s\")", truth, "Gaben is fat."); + PrintToServer("hello[0] = %d (expected: %d)", hello[0], 5); + PrintToServer("hello[1] = %d (expected: %d)", hello[1], 6); + PrintToServer("hello2[0] = %d (expected: %d)", hello2[0], 9); + + return Plugin_Handled; +} diff --git a/plugins/testsuite/fwdtest1.sp b/plugins/testsuite/fwdtest1.sp new file mode 100644 index 00000000..29dc4ce0 --- /dev/null +++ b/plugins/testsuite/fwdtest1.sp @@ -0,0 +1,169 @@ +#include + +public Plugin:myinfo = +{ + name = "Forward Testing Lab #1", + author = "AlliedModders LLC", + description = "Tests suite #1 for forwards created by plugins", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +new Handle:g_GlobalFwd = INVALID_HANDLE; +new Handle:g_PrivateFwd = INVALID_HANDLE; + +public OnPluginStart() +{ + PrintToServer("OnPluginStart: %d", OnPluginStart); + + RegServerCmd("test_create_gforward", Command_CreateGlobalForward); + RegServerCmd("test_create_pforward", Command_CreatePrivateForward); + RegServerCmd("test_exec_gforward", Command_ExecGlobalForward); + RegServerCmd("test_exec_pforward", Command_ExecPrivateForward); +} + +public OnPluginEnd() +{ + CloseHandle(g_GlobalFwd); + CloseHandle(g_PrivateFwd); +} + +public Action:Command_CreateGlobalForward(args) +{ + if (g_GlobalFwd != INVALID_HANDLE) + { + CloseHandle(g_GlobalFwd); + } + + g_GlobalFwd = CreateGlobalForward("OnGlobalForward", ET_Ignore, Param_Any, Param_Cell, Param_Float, Param_String, Param_Array, Param_CellByRef, Param_FloatByRef); + + if (g_GlobalFwd == INVALID_HANDLE) + { + PrintToServer("Failed to create global forward!"); + } + + return Plugin_Handled; +} + +public Action:Command_CreatePrivateForward(args) +{ + new Handle:pl; + new Function:func; + + if (g_PrivateFwd != INVALID_HANDLE) + { + CloseHandle(g_PrivateFwd); + } + + g_PrivateFwd = CreateForward(ET_Hook, Param_Cell, Param_String, Param_VarArgs); + + if (g_PrivateFwd == INVALID_HANDLE) + { + PrintToServer("Failed to create private forward!") + } + + pl = FindPluginByFile("fwdtest2.smx"); + + if (!pl) + { + PrintToServer("Could not find fwdtest2.smx!"); + return Plugin_Handled; + } + + func = GetFunctionByName(pl, "OnPrivateForward"); + + /* This shouldn't happen, but oh well */ + if (func == INVALID_FUNCTION) + { + PrintToServer("Could not find \"OnPrivateForward\" in fwdtest2.smx!"); + return Plugin_Handled; + } + + if (!AddToForward(g_PrivateFwd, pl, func) || !AddToForward(g_PrivateFwd, GetMyHandle(), ZohMyGod)) + { + PrintToServer("Failed to add functions to private forward!"); + return Plugin_Handled; + } + + return Plugin_Handled; +} + +public Action:Command_ExecGlobalForward(args) +{ + new a = 99; + new Float:b = 4.215; + new err, ret; + + if (g_GlobalFwd == INVALID_HANDLE) + { + PrintToServer("Failed to execute global forward. Create it first."); + return Plugin_Handled; + } + + PrintToServer("Beginning call to %d functions in global forward \"OnGlobalForward\"", GetForwardFunctionCount(g_GlobalFwd)); + + Call_StartForward(g_GlobalFwd); + Call_PushCell(OnPluginStart); + Call_PushCell(7); + Call_PushFloat(-8.5); + Call_PushString("Anata wa doko desu ka?"); + Call_PushArray({0.0, 1.1, 2.2}, 3); + Call_PushCellRef(a); + Call_PushFloatRef(b); + err = Call_Finish(ret); + + PrintToServer("Call to global forward \"OnGlobalForward\" completed"); + PrintToServer("Error code = %d (expected: %d)", err, 0); + PrintToServer("Return value = %d (expected: %d)", ret, Plugin_Continue); + + PrintToServer("a = %d (expected: %d)", a, 777); + PrintToServer("b = %f (expected: %f)", b, -0.782); + + return Plugin_Handled; +} + +public Action:Command_ExecPrivateForward(args) +{ + new err, ret; + + if (g_PrivateFwd == INVALID_HANDLE) + { + PrintToServer("Failed to execute private forward. Create it first."); + return Plugin_Handled; + } + + PrintToServer("Beginning call to %d functions in private forward", GetForwardFunctionCount(g_PrivateFwd)); + + Call_StartForward(g_PrivateFwd); + Call_PushCell(24); + Call_PushString("I am a format string: %d %d %d %d %d %d"); + Call_PushCell(0); + Call_PushCell(1); + Call_PushCell(2); + Call_PushCell(3); + Call_PushCell(4); + Call_PushCell(5); + err = Call_Finish(ret); + + PrintToServer("Call to private forward completed"); + PrintToServer("Error code = %d (expected: %d)", err, 0); + PrintToServer("Return value = %d (expected: %d)", ret, Plugin_Handled); + + return Plugin_Handled; +} + +public Action:ZohMyGod(num, const String:format[], ...) +{ + decl String:buffer[128]; + + PrintToServer("Inside private forward #1"); + + PrintToServer("num = %d (expected: %d)", num, 24); + + VFormat(buffer, sizeof(buffer), format, 3); + PrintToServer("buffer = \"%s\" (expected: \"%s\")", buffer, "I am a format string: 0 1 2 3 4 5"); + + PrintToServer("End private forward #1"); + + return Plugin_Continue; +} diff --git a/plugins/testsuite/fwdtest2.sp b/plugins/testsuite/fwdtest2.sp new file mode 100644 index 00000000..a979eb68 --- /dev/null +++ b/plugins/testsuite/fwdtest2.sp @@ -0,0 +1,43 @@ +#include + +public Plugin:myinfo = +{ + name = "Forward Testing Lab #2", + author = "AlliedModders LLC", + description = "Tests suite #2 for forwards created by plugins", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public Action:OnPrivateForward(num, const String:format[], ...) +{ + decl String:buffer[128]; + + PrintToServer("Inside private forward #2"); + + PrintToServer("num = %d (expected: %d)", num, 24); + + VFormat(buffer, sizeof(buffer), format, 3); + PrintToServer("buffer = \"%s\" (expected: \"%s\")", buffer, "I am a format string: 0 1 2 3 4 5"); + + PrintToServer("End private forward #2"); + + return Plugin_Handled; +} + +public OnGlobalForward(Function:a, b, Float:c, const String:d[], Float:e[3], &f, &Float:g) +{ + PrintToServer("Inside global forward \"OnGlobalForward\""); + + PrintToServer("a = %d (expected: %d)", a, 11); + PrintToServer("b = %d (expected: %d)", b, 7); + PrintToServer("c = %f (expected: %f)", c, -8.5); + PrintToServer("d = \"%s\" (expected: \"%s\")", d, "Anata wa doko desu ka?"); + PrintToServer("e = %f %f %f (expected: %f %f %f)", e[0], e[1], e[2], 0.0, 1.1, 2.2); + + PrintToServer("f = %d (expected %d, setting to %d)", f, 99, 777); + f = 777; + + PrintToServer("g = %f (expected %f, setting to %f)", g, 4.215, -0.782); + g = -0.782; +} diff --git a/public/IForwardSys.h b/public/IForwardSys.h index da188d27..478670aa 100644 --- a/public/IForwardSys.h +++ b/public/IForwardSys.h @@ -227,6 +227,16 @@ namespace SourceMod * @return True on success, otherwise false. */ virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0; + + /** + * @brief Removes a function from the call list. + * NOTE: Only removes one instance. + * + * @param ctx Context to use as a look-up. + * @param index Function id to add. + * @return Whether or not the function was removed. + */ + virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0; }; #define SP_PARAMTYPE_ANY 0