From 6bef3c2c5a5f6ee14a39b932e2c06839febba1bb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 12 Nov 2006 01:06:17 +0000 Subject: [PATCH] Initial import of forward system --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40185 --- core/interfaces/IForwardSys.h | 122 +++++- core/interfaces/IPluginFunction.h | 24 +- core/msvc8/sourcemod_mm.vcproj | 4 + core/sourcemod.cpp | 15 +- core/systems/CFunction.cpp | 13 +- core/systems/ForwardSys.cpp | 667 ++++++++++++++++++++++++++++++ core/systems/ForwardSys.h | 88 ++++ 7 files changed, 906 insertions(+), 27 deletions(-) create mode 100644 core/systems/ForwardSys.cpp create mode 100644 core/systems/ForwardSys.h diff --git a/core/interfaces/IForwardSys.h b/core/interfaces/IForwardSys.h index a6e17dd3..49e2d16a 100644 --- a/core/interfaces/IForwardSys.h +++ b/core/interfaces/IForwardSys.h @@ -3,6 +3,7 @@ #include #include +#include #define SMINTERFACE_FORWARDMANAGER_NAME "IForwardManager" #define SMINTERFACE_FORWARDMANAGER_VERSION 1 @@ -24,18 +25,76 @@ namespace SourceMod enum ExecType { ET_Ignore = 0, /* Ignore all return values, return 0 */ - ET_Single = 1, /* Only return the first exec, ignore all others */ + ET_Single = 1, /* Only return the last exec, ignore all others */ ET_Event = 2, /* Acts as an event with the ResultTypes above, no mid-Stops allowed, returns highest */ ET_Hook = 3, /* Acts as a hook with the ResultTypes above, mid-Stops allowed, returns highest */ + ET_Custom = 4, /* Ignored or handled by an IForwardFilter */ + }; + + class IForward; + + class IForwardFilter + { + public: + /** + * @brief Called when an error occurs executing a plugin. + * + * @param fwd IForward pointer. + * @param func IPluginFunction pointer to the failed function. + * @param err Error code. + * @return True to handle, false to pass to global error reporter. + */ + virtual bool OnErrorReport(IForward *fwd, + IPluginFunction *func, + int err) + { + return false; + } + + /** + * @brief Called after each function return during execution. + * NOTE: Only used for ET_Custom. + * + * @param fwd IForward pointer. + * @param func IPluginFunction pointer to the executed function. + * @param retval Pointer to current return value (can be modified). + * @return ResultType denoting the next action to take. + */ + virtual ResultType OnFunctionReturn(IForward *fwd, + IPluginFunction *func, + cell_t *retval) + { + return Pl_Continue; + } + + /** + * @brief Called when execution begins. + */ + virtual void OnExecuteBegin() + { + }; + + /** + * @brief Called when execution ends. + * + * @param final_ret Final return value (modifiable). + * @param success Number of successful execs. + * @param failed Number of failed execs. + */ + virtual void OnExecuteEnd(cell_t *final_ret, unsigned int success, unsigned int failed) + { + } }; /** - * :TODO: finish this spec * @brief Abstracts multiple function calling. + * * NOTE: Parameters should be pushed in forward order, unlike - * the virtual machine/IPluginContext order. - * NOTE: Some functions are repeated in here because their documentation differs - * from their IPluginFunction equivalents. + * the virtual machine/IPluginContext order. + * NOTE: Some functions are repeated in here because their + * documentation differs from their IPluginFunction equivalents. + * Missing are the Push functions, whose only doc change is that + * they throw SP_ERROR_PARAM on type mismatches. */ class IForward : public ICallable { @@ -65,10 +124,25 @@ namespace SourceMod * @brief Executes the forward. * * @param result Pointer to store result in. - * @param num_functions Optionally filled with the number of function sucessfully executed. + * @param filter Optional pointer to an IForwardFilter. * @return Error code, if any. */ - virtual int Execute(cell_t *result, unsigned int *num_functions) =0; + virtual int Execute(cell_t *result, IForwardFilter *filter=NULL) =0; + + /** + * @brief Pushes an array of cells onto the current call. Different rules than ICallable. + * NOTE: On Execute, the pointer passed will be modified according to the copyback rule. + * + * @param array Array to copy. Cannot be NULL, unlike ICallable's version. + * @param cells Number of cells to allocate and optionally read from the input array. + * @param phys_addr Unused. If a value is passed, it will be filled with NULL. + * @param flags Whether or not changes should be copied back to the input array. + * @return Error code, if any. + */ + virtual int PushArray(cell_t *inarray, + unsigned int cells, + cell_t **phys_addr, + int flags=SMFUNC_COPYBACK_NONE) =0; }; @@ -77,10 +151,12 @@ namespace SourceMod public: /** * @brief Removes a function from the call list. + * NOTE: Only removes one instance. * * @param func Function to remove. + * @return Whether or not the function was removed. */ - virtual void RemoveFunction(IPluginFunction *func) =0; + virtual bool RemoveFunction(IPluginFunction *func) =0; /** * @brief Removes all instances of a plugin from the call list. @@ -92,7 +168,9 @@ namespace SourceMod /** * @brief Adds a function to the call list. - * NOTE: Cannot be used during a call. + * NOTE: Cannot be used during an incompleted call. + * NOTE: If used during a call, function is temporarily queued until calls are over. + * NOTE: Adding mulitple copies of the same function is illegal. * * @param func Function to add. * @return True on success, otherwise false. @@ -101,7 +179,8 @@ namespace SourceMod /** * @brief Adds a function to the call list. - * NOTE: Cannot be used during a call. + * NOTE: Cannot be used during an incompleted call. + * NOTE: If used during a call, function is temporarily queued until calls are over. * * @param ctx Context to use as a look-up. * @param funcid Function id to add. @@ -110,15 +189,24 @@ namespace SourceMod virtual bool AddFunction(sp_context_t *ctx, funcid_t index) =0; }; + #define SP_PARAMTYPE_ANY 0 + #define SP_PARAMFLAG_BYREF (1<<0) + #define SP_PARAMTYPE_CELL (1<<1) + #define SP_PARAMTYPE_FLOAT (2<<1) + #define SP_PARAMTYPE_STRING (3<<1)|SP_PARAMFLAG_BYREF + #define SP_PARAMTYPE_ARRAY (4<<1)|SP_PARAMFLAG_BYREF + #define SP_PARAMTYPE_VARARG (5<<1) + enum ParamType { - Param_Any = 0, //Any type will be accepted - Param_Cell = 1, //Only a cell will be accepted - Param_Float = 2, //Only a float value will be accepted - Param_String = 3, //Only a string will be accepted - Param_Array = 4, //Only a 1D array will be accepted - Param_VarArgs = 5, //Anything will be accepted - ParamTypes_Total = 6, + Param_Any = SP_PARAMTYPE_ANY, + Param_Cell = SP_PARAMTYPE_CELL, + Param_Float = SP_PARAMTYPE_FLOAT, + Param_String = SP_PARAMTYPE_STRING, + Param_Array = SP_PARAMTYPE_ARRAY, + Param_VarArgs = SP_PARAMTYPE_VARARG, + Param_CellByRef = SP_PARAMTYPE_CELL|SP_PARAMFLAG_BYREF, + Param_FloatByRef = SP_PARAMTYPE_FLOAT|SP_PARAMFLAG_BYREF, }; class IForwardManager : public SMInterface diff --git a/core/interfaces/IPluginFunction.h b/core/interfaces/IPluginFunction.h index 14e34058..c77e9552 100644 --- a/core/interfaces/IPluginFunction.h +++ b/core/interfaces/IPluginFunction.h @@ -69,7 +69,7 @@ namespace SourceMod * * @param array Array to copy, NULL if no initial array should be copied. * @param cells Number of cells to allocate and optionally read from the input array. - * @param phys_addr Optional return address for physical array (if array was non-NULL, will be array). + * @param phys_addr Optional return address for physical array, if one was made. * @param flags Whether or not changes should be copied back to the input array. * @return Error code, if any. */ @@ -171,6 +171,28 @@ namespace SourceMod * @return Address, or NULL if invalid parameter specified. */ virtual cell_t *GetAddressOfPushedParam(unsigned int param) =0; + +#if 0 + /** + * @brief Clones the function object. This can be used to maintain a private copy. + * The copy retains no push states and must be freeed with ReleaseCopy(). + */ + virtual IPluginFunction *Copy() =0; + + /** + * @brief If this object is a clone, this function must be called to destroy it. + */ + virtual void ReleaseCopy() =0; + + /** + * @brief Returns original function pointer, if any. + * Note: IsCopyOf() will return the original non-copy, even if used + * from a copy. + * + * @return The original object that this was sourced from. + */ + virtual IPluginFunction *IsCopyOf() =0; +#endif }; }; diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 8eb18041..536548da 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -259,6 +259,10 @@ RelativePath="..\systems\CFunction.cpp" > + + diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index a10a690b..2d910762 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -5,6 +5,7 @@ #include "vm/sp_vm_engine.h" #include #include "PluginSys.h" +#include "ForwardSys.h" SourcePawnEngine g_SourcePawn; SourceModBase g_SourceMod; @@ -111,17 +112,21 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) return false; } -#if 0 g_SMAPI->PathFormat(file, sizeof(file), "%s/addons/sourcemod/plugins/test.smx", g_BaseDir.c_str()); IPlugin *pPlugin = g_PluginMngr.LoadPlugin(file, false, PluginType_Global, error, err_max); IPluginFunction *func = pPlugin->GetFunctionByName("Test"); + IPluginFunction *func2 = pPlugin->GetFunctionByName("Test2"); cell_t result = 2; cell_t val = 6; - func->PushCell(1); - func->PushCellByRef(&val, SMFUNC_COPYBACK_ONCE); - func->Execute(&result, NULL); + ParamType types[] = {Param_Cell, Param_CellByRef}; + va_list ap = va_start(ap, late); + CForward *fwd = CForward::CreateForward(NULL, ET_Custom, 2, types, ap); + fwd->AddFunction(func); + fwd->AddFunction(func2); + fwd->PushCell(1); + fwd->PushCellByRef(&val, SMFUNC_COPYBACK_ALWAYS); + fwd->Execute(&result, NULL); g_PluginMngr.UnloadPlugin(pPlugin); -#endif return true; } diff --git a/core/systems/CFunction.cpp b/core/systems/CFunction.cpp index ee187a51..18d01909 100644 --- a/core/systems/CFunction.cpp +++ b/core/systems/CFunction.cpp @@ -106,7 +106,7 @@ int CFunction::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr return SetError(err); } - info->flags = inarray ? copyback : SMFUNC_COPYBACK_NONE; + info->flags = (inarray || (copyback & SMFUNC_COPYBACK_ALWAYS)) ? copyback : SMFUNC_COPYBACK_NONE; info->marked = true; info->size = cells; m_params[m_curparam] = info->local_addr; @@ -246,9 +246,14 @@ int CFunction::Execute(cell_t *result, IFunctionCopybackReader *reader) } if (temp_info[numparams].orig_addr) { - memcpy(temp_info[numparams].orig_addr, - temp_info[numparams].phys_addr, - temp_info[numparams].size * sizeof(cell_t)); + if (temp_info[numparams].size == 1) + { + *temp_info[numparams].orig_addr = *temp_info[numparams].phys_addr; + } else { + memcpy(temp_info[numparams].orig_addr, + temp_info[numparams].phys_addr, + temp_info[numparams].size * sizeof(cell_t)); + } } } _skipcopy: diff --git a/core/systems/ForwardSys.cpp b/core/systems/ForwardSys.cpp new file mode 100644 index 00000000..428bf31c --- /dev/null +++ b/core/systems/ForwardSys.cpp @@ -0,0 +1,667 @@ +#include +#include "ForwardSys.h" +#include "PluginSys.h" + +CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, ParamType *types, va_list ap) +{ + ParamType _types[SP_MAX_EXEC_PARAMS]; + + if (num_params > SP_MAX_EXEC_PARAMS) + { + return NULL; + } + + if (types == NULL && num_params) + { + for (unsigned int i=0; im_curparam = 0; + pForward->m_ExecType = et; + snprintf(pForward->m_name, FORWARDS_NAME_MAX, "%s", name ? name : ""); + + for (unsigned int i=0; im_types[i] = types[i]; + } + + if (num_params && types[num_params-1] == Param_VarArgs) + { + pForward->m_varargs = true; + } else { + pForward->m_varargs = false; + } + + pForward->m_numparams = num_params; + pForward->m_errstate = SP_ERROR_NONE; + pForward->m_CopyBacks.numrecopy = 0; + + return pForward; +} + +bool CForward::OnCopybackArray(unsigned int param, + unsigned int cells, + cell_t *source_addr, + cell_t *orig_addr, + int flags) +{ + /* Check if the stack is empty, this should be an assertion */ + if (m_NextStack.empty()) + { + /* This should never happen! */ + assert(!m_NextStack.empty()); + return true; + } + + /* Check if we even want to copy to the next plugin */ + if (!(flags & SMFUNC_COPYBACK_ALWAYS)) + { + return true; + } + + /* Keep track of the copy back and save the info */ + NextCallInfo &info = m_NextStack.front(); + info.recopy[info.numrecopy++] = param; + info.orig_addrs[param] = orig_addr; + info.sizes[param] = cells; + + /* We don't want to override the copy. */ + return true; +} + +int CForward::Execute(cell_t *result, IForwardFilter *filter) +{ + if (m_errstate) + { + int err = m_errstate; + Cancel(); + return err; + } + + /* Reset marker */ + m_curparam = 0; + + if (filter) + { + filter->OnExecuteBegin(); + } + + FuncIter iter = m_functions.begin(); + IPluginFunction *func; + cell_t cur_result = 0; + cell_t high_result = 0; + int err; + unsigned int failed=0, success=0; + unsigned int save_numcopy = 0; + + /** + * Save copyback into to start the chain, + * then reset it for re-entrancy + */ + m_NextStack.push(m_CopyBacks); + NextCallInfo &info = m_NextStack.front(); + m_CopyBacks.numrecopy = 0; + + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + + /** + * Check if we need to copy a new array back into the plugin. + */ + if (info.numrecopy) + { + /* The last plugin has a chained copyback, we must redirect it here. */ + unsigned int param; + cell_t *orig_addr, *targ_addr; + for (unsigned int i=0; iGetAddressOfPushedParam(param); + orig_addr = info.orig_addrs[param]; + /* Only do the copy for valid targets */ + if (targ_addr && orig_addr) + { + if (info.sizes[param] == 1) + { + *targ_addr = *orig_addr; + } else { + memcpy(targ_addr, orig_addr, info.sizes[param] * sizeof(cell_t)); + } + } + /* If this failed, the plugin will most likely be failing as well. */ + } + save_numcopy = info.numrecopy; + info.numrecopy = 0; + } + + /* Call the function and deal with the return value. + * :TODO: only pass reader if we know we have an array in the list + */ + if ((err=func->Execute(&cur_result, this)) != SP_ERROR_NONE) + { + bool handled = false; + if (filter) + { + handled = filter->OnErrorReport(this, func, err); + } + if (!handled) + { + /* :TODO: invoke global error reporting here */ + } + /* If we failed, we're not quite done. The copy chain has been broken. + * We have to restore it so past changes will continue to get mirrored. + */ + info.numrecopy = save_numcopy; + failed++; + } else { + success++; + switch (m_ExecType) + { + case ET_Event: + { + if (cur_result > high_result) + { + high_result = cur_result; + } + break; + } + case ET_Hook: + { + if (cur_result > high_result) + { + high_result = cur_result; + if ((ResultType)high_result == Pl_Stop) + { + iter = m_functions.end(); + break; + } + } + break; + } + case ET_Custom: + { + if (filter) + { + if (filter->OnFunctionReturn(this, func, &cur_result) == Pl_Stop) + { + iter = m_functions.end(); + } + } + break; + } + } + } + } + + m_NextStack.pop(); + + if (m_ExecType == ET_Event || m_ExecType == ET_Hook) + { + cur_result = high_result; + } else if (m_ExecType == ET_Ignore) { + cur_result = 0; + } + + *result = cur_result; + + DumpAdditionQueue(); + + if (filter) + { + filter->OnExecuteEnd(&cur_result, success, failed); + } + + return SP_ERROR_NONE; +} + +int CForward::PushCell(cell_t cell) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_Cell && m_types[m_curparam] != Param_Any) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushCell(cell); + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushFloat(float number) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_Float && m_types[m_curparam] != Param_Any) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushFloat(number); + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushCellByRef(cell_t *cell, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_CellByRef && m_types[m_curparam] != Param_Any) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + if (flags & SMFUNC_COPYBACK_ALWAYS) + { + _Int_PushArray(cell, 1, flags); + } else { + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushCellByRef(cell, flags); + } + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushFloatByRef(float *num, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_FloatByRef && m_types[m_curparam] != Param_Any) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + if (flags & SMFUNC_COPYBACK_ALWAYS) + { + _Int_PushArray((cell_t *)num, 1, flags); + } else { + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushFloatByRef(num, flags); + } + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushCells(cell_t array[], unsigned int numcells, bool each) +{ + if (each) + { + /* Type check each cell if we need to! */ + if (m_curparam + numcells >= m_numparams && !m_varargs) + { + return SetError(SP_ERROR_PARAMS_MAX); + } else { + for (unsigned int i=m_curparam; i SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushCells(array, numcells, each); + } + + m_curparam += each ? numcells : 1; + + return SP_ERROR_NONE; +} + +void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags) +{ + FuncIter iter; + IPluginFunction *func; + if (flags & SMFUNC_COPYBACK_ALWAYS) + { + /* As a special optimization, we create blank default arrays because they will be + * copied over anyway! + */ + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushArray(NULL, cells, NULL, flags); + } + m_CopyBacks.recopy[m_CopyBacks.numrecopy++] = m_curparam; + m_CopyBacks.orig_addrs[m_curparam] = inarray; + m_CopyBacks.sizes[m_curparam] = cells; + } else { + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushArray(inarray, cells, NULL, flags); + } + } +} + +int CForward::PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr, int flags) +{ + /* We don't allow this here */ + if (!inarray) + { + return SetError(SP_ERROR_PARAM); + } + + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_Array) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + if (phys_addr) + { + *phys_addr = NULL; + } + + _Int_PushArray(inarray, cells, flags); + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushString(const char *string) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_String) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushString(string); + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushStringByRef(char *string, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] != Param_Any || m_types[m_curparam] == Param_String) + { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->PushStringByRef(string, flags); + } + + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::Cancel() +{ + if (!m_curparam) + { + return; + } + + FuncIter iter; + IPluginFunction *func; + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + func->Cancel(); + } + + m_CopyBacks.numrecopy = 0; + + DumpAdditionQueue(); + + m_errstate = SP_ERROR_NONE; +} + +bool CForward::AddFunction(sp_context_t *ctx, funcid_t index) +{ + IPlugin *pPlugin = g_PluginMngr.FindPluginByContext(ctx); + if (!pPlugin) + { + return false; + } + + IPluginFunction *pFunc = pPlugin->GetFunctionById(index); + if (!pFunc) + { + return false; + } + + return AddFunction(pFunc); +} + +bool CForward::RemoveFunction(IPluginFunction *func) +{ + bool found = false; + FuncIter iter; + IPluginFunction *pNext = NULL; + + for (iter=m_functions.begin(); iter!=m_functions.end();) + { + if ((*iter) == func) + { + found = true; + /* If this iterator is being used, swap in a new one !*/ + iter = m_functions.erase(iter); + } else { + iter++; + } + } + + /* Just in case */ + m_AddQueue.remove(func); + + /* Cancel a call, if any */ + if (found && (!m_NextStack.empty() || m_curparam)) + { + func->Cancel(); + } + + return found; +} + +unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin) +{ + FuncIter iter; + IPluginFunction *func; + unsigned int removed = 0; + for (iter=m_functions.begin(); iter!=m_functions.end();) + { + func = (*iter); + if (func->GetParentPlugin() == plugin) + { + iter = m_functions.erase(iter); + removed++; + } else { + iter++; + } + } + + for (iter=m_AddQueue.begin(); iter!=m_AddQueue.end();) + { + func = (*iter); + if (func->GetParentPlugin() == plugin) + { + /* Don't count these toward the total */ + iter = m_functions.erase(iter); + } else { + iter++; + } + } + + return removed; +} + +void CForward::DumpAdditionQueue() +{ + FuncIter iter = m_AddQueue.begin(); + while (iter != m_AddQueue.end()) + { + m_functions.push_back((*iter)); + //:TODO: eventually we will tell the plugin we're using it + iter = m_AddQueue.erase(iter); + } +} + +bool CForward::AddFunction(IPluginFunction *func) +{ + if (m_curparam) + { + return false; + } + + if (!m_NextStack.empty()) + { + m_AddQueue.push_back(func); + } else { + //:TODO: eventually we will tell the plugin we're using it + m_functions.push_back(func); + } + + return true; +} + +const char *CForward::GetForwardName() +{ + return m_name; +} + +unsigned int CForward::GetFunctionCount() +{ + return m_functions.size(); +} + +ExecType CForward::GetExecType() +{ + return m_ExecType; +} diff --git a/core/systems/ForwardSys.h b/core/systems/ForwardSys.h new file mode 100644 index 00000000..17b55a61 --- /dev/null +++ b/core/systems/ForwardSys.h @@ -0,0 +1,88 @@ +#ifndef _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ +#define _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ + +#include +#include +#include "sm_globals.h" +#include +#include + +using namespace SourceHook; + +typedef List::iterator FuncIter; + +//:TODO: a global name max define for sourcepawn, should mirror compiler's sNAMEMAX +#define FORWARDS_NAME_MAX 64 + +struct NextCallInfo +{ + unsigned int recopy[SP_MAX_EXEC_PARAMS]; + unsigned int sizes[SP_MAX_EXEC_PARAMS]; + cell_t *orig_addrs[SP_MAX_EXEC_PARAMS]; + unsigned int numrecopy; +}; + +class CForward : public IChangeableForward, IFunctionCopybackReader +{ +public: //ICallable + virtual int PushCell(cell_t cell); + virtual int PushCellByRef(cell_t *cell, int flags); + virtual int PushFloat(float number); + virtual int PushFloatByRef(float *number, int flags); + virtual int PushCells(cell_t array[], unsigned int numcells, bool each); + virtual int PushArray(cell_t *inarray, unsigned int cells, cell_t **phys_addr, int flags); + virtual int PushString(const char *string); + virtual int PushStringByRef(char *string, int flags); + virtual void Cancel(); +public: //IForward + virtual const char *GetForwardName(); + virtual unsigned int GetFunctionCount(); + virtual ExecType GetExecType(); + virtual int Execute(cell_t *result, IForwardFilter *filter); +public: //IChangeableForward + virtual bool RemoveFunction(IPluginFunction *func); + virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin); + virtual bool AddFunction(IPluginFunction *func); + virtual bool AddFunction(sp_context_t *ctx, funcid_t index); +public: //IFunctionCopybackReader + virtual bool OnCopybackArray(unsigned int param, + unsigned int cells, + cell_t *source_addr, + cell_t *orig_addr, + int flags); +public: + static CForward *CreateForward(const char *name, + ExecType et, + unsigned int num_params, + ParamType *types, + va_list ap); +private: + void DumpAdditionQueue(); + void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags); + inline int SetError(int err) + { + m_errstate = err; + return err; + } +protected: + /* :TODO: I want a caching list type here. + * Destroying these things and using new/delete for their members feels bad. + */ + List m_functions; + + /* Type and name information */ + ParamType m_types[SP_MAX_EXEC_PARAMS]; + char m_name[FORWARDS_NAME_MAX+1]; + unsigned int m_numparams; + bool m_varargs; + ExecType m_ExecType; + + /* State information */ + unsigned int m_curparam; + int m_errstate; + CStack m_NextStack; + List m_AddQueue; + NextCallInfo m_CopyBacks; +}; + +#endif //_INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_