From c1888c2d05447acf153966682a232f6371ca48e0 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Fri, 3 Mar 2017 11:45:39 -0700 Subject: [PATCH 1/4] Add IsNullVector and IsNullString natives Let plugins check if a string or vector passed to a function is their NULL_VECTOR or NULL_STRING. --- core/logic/smn_core.cpp | 23 +++++++++++++++++++++++ plugins/include/core.inc | 16 ++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index 8ba74ad76..d12e0fca6 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -759,6 +759,27 @@ static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params) return 0; } +static cell_t IsNullVector(IPluginContext *pContext, const cell_t *params) +{ + cell_t *pNullVec = pContext->GetNullRef(SP_NULL_VECTOR); + if (!pNullVec) + return 0; + + cell_t *addr; + pContext->LocalToPhysAddr(params[1], &addr); + + return addr == pNullVec; +} + +static cell_t IsNullString(IPluginContext *pContext, const cell_t *params) +{ + char *str; + if (pContext->LocalToStringNULL(params[1], &str) != SP_ERROR_NONE) + return 0; + + return str == nullptr; +} + REGISTER_NATIVES(coreNatives) { {"ThrowError", ThrowError}, @@ -787,5 +808,7 @@ REGISTER_NATIVES(coreNatives) {"RequireFeature", RequireFeature}, {"LoadFromAddress", LoadFromAddress}, {"StoreToAddress", StoreToAddress}, + {"IsNullVector", IsNullVector}, + {"IsNullString", IsNullString}, {NULL, NULL}, }; diff --git a/plugins/include/core.inc b/plugins/include/core.inc index 315f79dea..47f05e1ca 100644 --- a/plugins/include/core.inc +++ b/plugins/include/core.inc @@ -143,6 +143,22 @@ struct SharedPlugin public float NULL_VECTOR[3]; /**< Pass this into certain functions to act as a C++ NULL */ public const char NULL_STRING[1]; /**< pass this into certain functions to act as a C++ NULL */ +/** + * Check if the given vector is the NULL_VECTOR. + * + * @param vec The vector to test. + * @return True if NULL_VECTOR, false otherwise. + */ +native bool IsNullVector(const float vec[3]); + +/** + * Check if the given string is the NULL_STRING. + * + * @param str The string to test. + * @return True if NULL_STRING, false otherwise. + */ +native bool IsNullString(const char[] str); + /** * Horrible compatibility shim. */ From b67da15b5b3e773b9e59fb29db1c45e520f736b2 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Fri, 3 Mar 2017 11:47:03 -0700 Subject: [PATCH 2/4] Add IsNativeParamNullVector and IsNativeParamNullString natives Lets plugins check if some other plugin passed NULL_VECTOR or NULL_STRING to a native in the native callback. --- core/logic/smn_fakenatives.cpp | 54 ++++++++++++++++++++++++++++++++++ plugins/include/functions.inc | 16 ++++++++++ 2 files changed, 70 insertions(+) diff --git a/core/logic/smn_fakenatives.cpp b/core/logic/smn_fakenatives.cpp index c4c7cf8a6..b446d6235 100644 --- a/core/logic/smn_fakenatives.cpp +++ b/core/logic/smn_fakenatives.cpp @@ -424,6 +424,58 @@ static cell_t FormatNativeString(IPluginContext *pContext, const cell_t *params) return SP_ERROR_NONE; } +static cell_t IsNativeParamNullVector(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + cell_t *addr; + if ((err = s_curcaller->LocalToPhysAddr(s_curparams[param], &addr)) != SP_ERROR_NONE) + { + return err; + } + + cell_t *pNullVec = s_curcaller->GetNullRef(SP_NULL_VECTOR); + if (!pNullVec) + { + return 0; + } + + return addr == pNullVec ? 1 : 0; +} + +static cell_t IsNativeParamNullString(IPluginContext *pContext, const cell_t *params) +{ + if (!s_curnative || (s_curnative->ctx != pContext)) + { + return pContext->ThrowNativeError("Not called from inside a native function"); + } + + cell_t param = params[1]; + if (param < 1 || param > s_curparams[0]) + { + return pContext->ThrowNativeErrorEx(SP_ERROR_PARAM, "Invalid parameter number: %d", param); + } + + int err; + char *str; + if ((err = s_curcaller->LocalToStringNULL(s_curparams[param], &str)) != SP_ERROR_NONE) + { + return err; + } + + return str == nullptr ? 1 : 0; +} + //tee hee REGISTER_NATIVES(nativeNatives) { @@ -439,5 +491,7 @@ REGISTER_NATIVES(nativeNatives) {"SetNativeArray", SetNativeArray}, {"SetNativeCellRef", SetNativeCellRef}, {"SetNativeString", SetNativeString}, + {"IsNativeParamNullVector", IsNativeParamNullVector}, + {"IsNativeParamNullString", IsNativeParamNullString}, {NULL, NULL}, }; diff --git a/plugins/include/functions.inc b/plugins/include/functions.inc index 1c4796184..c4afff936 100644 --- a/plugins/include/functions.inc +++ b/plugins/include/functions.inc @@ -465,6 +465,22 @@ native int GetNativeArray(int param, any[] local, int size); */ native int SetNativeArray(int param, const any[] local, int size); +/** + * Check if the native parameter is the NULL_VECTOR. + * + * @param param Parameter number, starting from 1. + * @return True if NULL_VECTOR, false otherwise. + */ +native bool IsNativeParamNullVector(int param); + +/** + * Check if the native parameter is the NULL_STRING. + * + * @param param Parameter number, starting from 1. + * @return True if NULL_STRING, false otherwise. + */ +native bool IsNativeParamNullString(int param); + /** * Formats a string using parameters from a native. * From de42dfc783c79bec8b29f0a2b329c0ee1d34fe2e Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Thu, 4 May 2017 22:56:31 -0600 Subject: [PATCH 3/4] Add Call_PushNullVector and Call_PushNullString Be able to push NULL_[VECTOR|STRING] to a forward or direct function call. The callee can check the parameter using the IsNullVector/IsNullString natives. --- core/logic/ForwardSys.cpp | 92 ++++++++++++++++++++++++++++++++++- core/logic/ForwardSys.h | 2 + core/logic/smn_functions.cpp | 84 ++++++++++++++++++++++++++++++++ plugins/include/functions.inc | 20 ++++++++ public/IForwardSys.h | 19 +++++++- 5 files changed, 214 insertions(+), 3 deletions(-) diff --git a/core/logic/ForwardSys.cpp b/core/logic/ForwardSys.cpp index f79326212..88b009982 100644 --- a/core/logic/ForwardSys.cpp +++ b/core/logic/ForwardSys.cpp @@ -273,7 +273,26 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter) */ if (type == Param_String) { - err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); + // If NULL_STRING was pushed, push the reference to the pubvar of the callee instead. + if (param->isnull) + { + IPluginRuntime *runtime = func->GetParentRuntime(); + uint32_t null_string_idx; + err = runtime->FindPubvarByName("NULL_STRING", &null_string_idx); + + if (!err) + { + cell_t null_string; + err = runtime->GetPubvarAddrs(null_string_idx, &null_string, nullptr); + + if (!err) + err = func->PushCell(null_string); + } + } + else + { + err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); + } } else if (type == Param_Float || type == Param_Cell) { @@ -281,7 +300,26 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter) } else { - err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); + // If NULL_VECTOR was pushed, push the reference to the pubvar of the callee instead. + if (param->isnull && type == Param_Array) + { + IPluginRuntime *runtime = func->GetParentRuntime(); + uint32_t null_vector_idx; + err = runtime->FindPubvarByName("NULL_VECTOR", &null_vector_idx); + + if (!err) + { + cell_t null_vector; + err = runtime->GetPubvarAddrs(null_vector_idx, &null_vector, nullptr); + + if (!err) + err = func->PushCell(null_vector); + } + } + else + { + err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); + } assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); } } @@ -400,6 +438,7 @@ int CForward::PushCell(cell_t cell) m_params[m_curparam].pushedas = Param_Cell; } + m_params[m_curparam].isnull = false; m_params[m_curparam++].val = cell; return SP_ERROR_NONE; @@ -423,6 +462,7 @@ int CForward::PushFloat(float number) m_params[m_curparam].pushedas = Param_Float; } + m_params[m_curparam].isnull = false; m_params[m_curparam++].val = *(cell_t *)&number; return SP_ERROR_NONE; @@ -481,6 +521,7 @@ void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags) m_params[m_curparam].byref.cells = cells; m_params[m_curparam].byref.flags = flags; m_params[m_curparam].byref.orig_addr = inarray; + m_params[m_curparam].isnull = false; } int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags) @@ -520,6 +561,7 @@ void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags m_params[m_curparam].byref.flags = cp_flags; m_params[m_curparam].byref.orig_addr = inarray; m_params[m_curparam].byref.sz_flags = sz_flags; + m_params[m_curparam].isnull = false; } int CForward::PushString(const char *string) @@ -570,6 +612,52 @@ int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_fla return SP_ERROR_NONE; } +int CForward::PushNullString() +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_String; + } else if (m_types[m_curparam] != Param_String) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_String; + } + + m_params[m_curparam++].isnull = true; + + return SP_ERROR_NONE; +} + +int CForward::PushNullVector() +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Array; + } else if (m_types[m_curparam] != Param_Array) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_Array; + } + + m_params[m_curparam++].isnull = true; + + return SP_ERROR_NONE; +} + void CForward::Cancel() { if (!m_curparam) diff --git a/core/logic/ForwardSys.h b/core/logic/ForwardSys.h index 420335c5f..6808fae4e 100644 --- a/core/logic/ForwardSys.h +++ b/core/logic/ForwardSys.h @@ -55,6 +55,8 @@ public: //IForward virtual unsigned int GetFunctionCount(); virtual ExecType GetExecType(); virtual int Execute(cell_t *result, IForwardFilter *filter); + virtual int PushNullString(); + virtual int PushNullVector(); public: //IChangeableForward virtual bool RemoveFunction(IPluginFunction *func); virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin); diff --git a/core/logic/smn_functions.cpp b/core/logic/smn_functions.cpp index fc4f763b2..ef66c4933 100644 --- a/core/logic/smn_functions.cpp +++ b/core/logic/smn_functions.cpp @@ -552,6 +552,88 @@ static cell_t sm_CallPushStringEx(IPluginContext *pContext, const cell_t *params return 1; } +static cell_t sm_CallPushNullVector(IPluginContext *pContext, const cell_t *params) +{ + int err = SP_ERROR_NOT_FOUND; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + if (s_pFunction) + { + // Find the NULL_VECTOR pubvar in the target plugin and push the local address. + IPluginRuntime *runtime = s_pFunction->GetParentRuntime(); + uint32_t null_vector_idx; + err = runtime->FindPubvarByName("NULL_VECTOR", &null_vector_idx); + if (err) + { + return pContext->ThrowNativeErrorEx(err, "Target plugin has no NULL_VECTOR."); + } + + cell_t null_vector; + err = runtime->GetPubvarAddrs(null_vector_idx, &null_vector, nullptr); + + if (!err) + err = s_pCallable->PushCell(null_vector); + } + else if (s_pForward) + { + err = s_pForward->PushNullVector(); + } + + if (err) + { + s_pCallable->Cancel(); + ResetCall(); + return pContext->ThrowNativeErrorEx(err, NULL); + } + + return 1; +} + +static cell_t sm_CallPushNullString(IPluginContext *pContext, const cell_t *params) +{ + int err = SP_ERROR_NOT_FOUND; + + if (!s_CallStarted) + { + return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress"); + } + + if (s_pFunction) + { + // Find the NULL_STRING pubvar in the target plugin and push the local address. + IPluginRuntime *runtime = s_pFunction->GetParentRuntime(); + uint32_t null_string_idx; + err = runtime->FindPubvarByName("NULL_STRING", &null_string_idx); + if (err) + { + return pContext->ThrowNativeErrorEx(err, "Target plugin has no NULL_STRING."); + } + + cell_t null_string; + err = runtime->GetPubvarAddrs(null_string_idx, &null_string, nullptr); + + if (!err) + err = s_pCallable->PushCell(null_string); + } + else if (s_pForward) + { + err = s_pForward->PushNullString(); + } + + 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 = SP_ERROR_NOT_RUNNABLE; @@ -668,6 +750,8 @@ REGISTER_NATIVES(functionNatives) {"Call_PushArrayEx", sm_CallPushArrayEx}, {"Call_PushString", sm_CallPushString}, {"Call_PushStringEx", sm_CallPushStringEx}, + {"Call_PushNullVector", sm_CallPushNullVector}, + {"Call_PushNullString", sm_CallPushNullString}, {"Call_Finish", sm_CallFinish}, {"Call_Cancel", sm_CallCancel}, {"RequestFrame", sm_AddFrameAction}, diff --git a/plugins/include/functions.inc b/plugins/include/functions.inc index c4afff936..b9bd2f9c7 100644 --- a/plugins/include/functions.inc +++ b/plugins/include/functions.inc @@ -290,6 +290,16 @@ native void Call_PushArray(const any[] value, int size); */ native void Call_PushArrayEx(any[] value, int size, int cpflags); +/** + * Pushes the NULL_VECTOR onto the current call. + * @see IsNullVector + * + * @note Cannot be used before a call has been started. + * + * @error Called before a call has been started. + */ +native void Call_PushNullVector(); + /** * Pushes a string onto the current call. * @@ -317,6 +327,16 @@ native void Call_PushString(const char[] value); */ native void Call_PushStringEx(char[] value, int length, int szflags, int cpflags); +/** + * Pushes the NULL_STRING onto the current call. + * @see IsNullString + * + * @note Cannot be used before a call has been started. + * + * @error Called before a call has been started. + */ +native void Call_PushNullString(); + /** * Completes a call to a function or forward's call list. * diff --git a/public/IForwardSys.h b/public/IForwardSys.h index 3ea987390..9040d5494 100644 --- a/public/IForwardSys.h +++ b/public/IForwardSys.h @@ -50,7 +50,7 @@ using namespace SourcePawn; #define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager" -#define SMINTERFACE_FORWARDMANAGER_VERSION 3 +#define SMINTERFACE_FORWARDMANAGER_VERSION 4 /* * There is some very important documentation at the bottom of this file. @@ -118,6 +118,7 @@ namespace SourceMod cell_t val; ByrefInfo byref; ParamType pushedas; + bool isnull; }; class IForwardFilter @@ -183,6 +184,22 @@ namespace SourceMod * @return Error code, if any. */ virtual int PushArray(cell_t *inarray, unsigned int cells, int flags=0) =0; + + /** + * @brief Pushes the NULL_STRING onto the current call. This will always push the + * correct reference to each function in the forward. + * + * @return Error code, if any. + */ + virtual int PushNullString() =0; + + /** + * @brief Pushes the NULL_VECTOR onto the current call. This will always push the + * correct reference to each function in the forward. + * + * @return Error code, if any. + */ + virtual int PushNullVector() =0; }; /** From 60320cd845b8ee6e301758fe246970d2ba752ee7 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Sat, 22 Jul 2017 23:08:31 +0200 Subject: [PATCH 4/4] Cleanup CForward::Execute using a helper function Flatten out the nesting a bit. --- core/logic/ForwardSys.cpp | 108 +++++++++++++++++++------------------- core/logic/ForwardSys.h | 1 + 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/core/logic/ForwardSys.cpp b/core/logic/ForwardSys.cpp index 88b009982..f599cb56c 100644 --- a/core/logic/ForwardSys.cpp +++ b/core/logic/ForwardSys.cpp @@ -269,59 +269,9 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter) if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF)) { /* If we're byref or we're vararg, we always push everything by ref. - * Even if they're byval, we must push them byref. - */ - if (type == Param_String) - { - // If NULL_STRING was pushed, push the reference to the pubvar of the callee instead. - if (param->isnull) - { - IPluginRuntime *runtime = func->GetParentRuntime(); - uint32_t null_string_idx; - err = runtime->FindPubvarByName("NULL_STRING", &null_string_idx); - - if (!err) - { - cell_t null_string; - err = runtime->GetPubvarAddrs(null_string_idx, &null_string, nullptr); - - if (!err) - err = func->PushCell(null_string); - } - } - else - { - err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); - } - } - else if (type == Param_Float || type == Param_Cell) - { - err = func->PushCellByRef(¶m->val); - } - else - { - // If NULL_VECTOR was pushed, push the reference to the pubvar of the callee instead. - if (param->isnull && type == Param_Array) - { - IPluginRuntime *runtime = func->GetParentRuntime(); - uint32_t null_vector_idx; - err = runtime->FindPubvarByName("NULL_VECTOR", &null_vector_idx); - - if (!err) - { - cell_t null_vector; - err = runtime->GetPubvarAddrs(null_vector_idx, &null_vector, nullptr); - - if (!err) - err = func->PushCell(null_vector); - } - } - else - { - err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); - } - assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); - } + * Even if they're byval, we must push them byref. + */ + err = _ExecutePushRef(func, type, param); } else { @@ -420,6 +370,58 @@ done: return SP_ERROR_NONE; } +int CForward::_ExecutePushRef(IPluginFunction *func, ParamType type, FwdParamInfo *param) +{ + /* If we're byref or we're vararg, we always push everything by ref. + * Even if they're byval, we must push them byref. + */ + int err; + IPluginRuntime *runtime = func->GetParentRuntime(); + switch (type) + { + case Param_String: + // Normal string was pushed. + if (!param->isnull) + return func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); + + // If NULL_STRING was pushed, push the reference to the pubvar of the callee instead. + uint32_t null_string_idx; + err = runtime->FindPubvarByName("NULL_STRING", &null_string_idx); + if (err) + return err; + + cell_t null_string; + err = runtime->GetPubvarAddrs(null_string_idx, &null_string, nullptr); + if (err) + return err; + + return func->PushCell(null_string); + + case Param_Float: + case Param_Cell: + return func->PushCellByRef(¶m->val); + + default: + assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); + // No NULL_VECTOR was pushed. + if (type != Param_Array || !param->isnull) + return func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); + + // If NULL_VECTOR was pushed, push the reference to the pubvar of the callee instead. + uint32_t null_vector_idx; + err = runtime->FindPubvarByName("NULL_VECTOR", &null_vector_idx); + if (err) + return err; + + cell_t null_vector; + err = runtime->GetPubvarAddrs(null_vector_idx, &null_vector, nullptr); + if (err) + return err; + + return func->PushCell(null_vector); + } +} + int CForward::PushCell(cell_t cell) { if (m_curparam < m_numparams) diff --git a/core/logic/ForwardSys.h b/core/logic/ForwardSys.h index 6808fae4e..b5037b2dd 100644 --- a/core/logic/ForwardSys.h +++ b/core/logic/ForwardSys.h @@ -74,6 +74,7 @@ private: CForward(ExecType et, const char *name, const ParamType *types, unsigned num_params); + int _ExecutePushRef(IPluginFunction *func, ParamType type, FwdParamInfo *param); void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags); void _Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags); inline int SetError(int err)