diff --git a/core/logic/ForwardSys.cpp b/core/logic/ForwardSys.cpp index f7932621..88b00998 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 420335c5..6808fae4 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 fc4f763b..ef66c493 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 c4afff93..b9bd2f9c 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 3ea98739..9040d549 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; }; /**