diff -r e28a9247061f -r 9ac720a36ce4 core/ForwardSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ForwardSys.cpp Wed Nov 19 02:16:45 2008 -0600 @@ -0,0 +1,751 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include "ForwardSys.h" +#include "PluginSys.h" +#include "ShareSys.h" +#include "DebugReporter.h" + +CForwardManager g_Forwards; + +/** + * Genesis turns to its source, reduction occurs stepwise although the essence is all one. + * End of line. FTL system check. + * + * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST + * VARARG FUNCTIONS: + * - Pushing no varargs + */ + +// :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails +// so later evaluation of this result may cause problems on higher levels of abstraction. DOCUMENT OR FIX ALL FORWARDS! + +CForwardManager::~CForwardManager() +{ + CStack::iterator iter; + for (iter=m_FreeForwards.begin(); iter!=m_FreeForwards.end(); iter++) + { + delete (*iter); + } + m_FreeForwards.popall(); +} + +void CForwardManager::OnSourceModAllInitialized() +{ + g_PluginSys.AddPluginsListener(this); + g_ShareSys.AddInterface(NULL, this); +} + +IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...) +{ + CForward *fwd; + va_list ap; + va_start(ap, types); + + fwd = CForward::CreateForward(name, et, num_params, types, ap); + + va_end(ap); + + if (fwd) + { + g_PluginSys.AddFunctionsToForward(name, fwd); + + m_managed.push_back(fwd); + } + + return fwd; +} + +IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...) +{ + CForward *fwd; + va_list ap; + va_start(ap, types); + + fwd = CForward::CreateForward(name, et, num_params, types, ap); + + va_end(ap); + + if (fwd) + { + m_unmanaged.push_back(fwd); + } + + return fwd; +} + +void CForwardManager::OnPluginLoaded(IPlugin *plugin) +{ + /* Attach any globally managed forwards */ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName()); + if (pFunc) + { + fwd->AddFunction(pFunc); + } + } +} + +void CForwardManager::OnPluginUnloaded(IPlugin *plugin) +{ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + fwd->RemoveFunctionsOfPlugin(plugin); + } + + for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) + { + fwd = (*iter); + fwd->RemoveFunctionsOfPlugin(plugin); + } +} + +IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng) +{ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + if (strcmp(fwd->GetForwardName(), name) == 0) + { + if (ifchng) + { + *ifchng = NULL; + } + return fwd; + } + } + + for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) + { + fwd = (*iter); + if (strcmp(fwd->GetForwardName(), name) == 0) + { + if (ifchng) + { + *ifchng = fwd; + } + return fwd; + } + } + + if (ifchng) + { + *ifchng = NULL; + } + + return NULL; +} + +void CForwardManager::ReleaseForward(IForward *forward) +{ + ForwardFree(static_cast(forward)); +} + +void CForwardManager::ForwardFree(CForward *fwd) +{ + if (fwd == NULL) + { + return; + } + + m_FreeForwards.push(fwd); + m_managed.remove(fwd); +} + +CForward *CForwardManager::ForwardMake() +{ + CForward *fwd; + if (m_FreeForwards.empty()) + { + fwd = new CForward; + } else { + fwd = m_FreeForwards.front(); + m_FreeForwards.pop(); + } + return fwd; +} + +void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused) +{ + /* No longer used */ +} + +/************************************* + * ACTUAL FORWARD API IMPLEMENTATION * + *************************************/ + +CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const 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 = num_params--; + } else { + pForward->m_varargs = false; + } + + pForward->m_numparams = num_params; + pForward->m_errstate = SP_ERROR_NONE; + + pForward->m_functions.clear(); + + return pForward; +} + +int CForward::Execute(cell_t *result, IForwardFilter *filter) +{ + if (m_errstate) + { + int err = m_errstate; + Cancel(); + return err; + } + + FuncIter iter = m_functions.begin(); + IPluginFunction *func; + cell_t cur_result = 0; + cell_t high_result = 0; + cell_t low_result = 0; + int err; + unsigned int failed=0, success=0; + unsigned int num_params = m_curparam; + FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS]; + FwdParamInfo *param; + ParamType type; + + /* Save local, reset */ + memcpy(temp_info, m_params, sizeof(m_params)); + m_curparam = 0; + + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + + for (unsigned int i=0; i= m_numparams || m_types[i] == Param_Any) + { + type = param->pushedas; + } + else + { + type = m_types[i]; + } + + 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) + { + 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 + { + err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); + assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); + } + } + else + { + /* If we're not byref or not vararg, our job is a bit easier. */ + assert(type == Param_Cell || type == Param_Float); + err = func->PushCell(param->val); + } + + if (err != SP_ERROR_NONE) + { + g_DbgReporter.GenerateError(func->GetParentContext(), + func->GetFunctionID(), + err, + "Failed to push parameter while executing forward"); + continue; + } + } + + /* Call the function and deal with the return value. */ + if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE) + { + 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) + { + goto done; + } + } + break; + } + case ET_LowEvent: + { + /* Check if the current result is the lowest so far (or if it's the first result) */ + if (cur_result < low_result || success == 1) + { + low_result = cur_result; + } + break; + } + default: + { + break; + } + } + } + } + +done: + + if (success) + { + switch (m_ExecType) + { + case ET_Ignore: + { + cur_result = 0; + break; + } + case ET_Event: + case ET_Hook: + { + cur_result = high_result; + break; + } + case ET_LowEvent: + { + cur_result = low_result; + break; + } + default: + { + break; + } + } + + if (result) + { + *result = cur_result; + } + } + + return SP_ERROR_NONE; +} + +int CForward::PushCell(cell_t cell) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Cell; + } else if (m_types[m_curparam] != Param_Cell) { + 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_Cell; + } + + m_params[m_curparam++].val = cell; + + return SP_ERROR_NONE; +} + +int CForward::PushFloat(float number) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Float; + } else if (m_types[m_curparam] != Param_Float) { + 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_Float; + } + + m_params[m_curparam++].val = *(cell_t *)&number; + + return SP_ERROR_NONE; +} + +int CForward::PushCellByRef(cell_t *cell, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_CellByRef; + } else if (m_types[m_curparam] != Param_CellByRef) { + 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_CellByRef; + } + + _Int_PushArray(cell, 1, 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_Any) + { + m_params[m_curparam].pushedas = Param_FloatByRef; + } else if (m_types[m_curparam] != Param_FloatByRef) { + 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_FloatByRef; + } + + _Int_PushArray((cell_t *)num, 1, flags); + m_curparam++; + + return SP_ERROR_NONE; +} + +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; +} + +int CForward::PushArray(cell_t *inarray, unsigned int cells, 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_params[m_curparam].pushedas = Param_Array; + } else if (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); + } + m_params[m_curparam].pushedas = Param_Array; + } + + _Int_PushArray(inarray, cells, flags); + + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags) +{ + m_params[m_curparam].byref.cells = cells; /* Notice this contains the char count not cell count */ + 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; +} + +int CForward::PushString(const char *string) +{ + 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_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_String; + } + + _Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0); + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags) +{ + 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_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_String; + } + + _Int_PushString((cell_t *)buffer, length, sz_flags, cp_flags); + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::Cancel() +{ + if (!m_curparam) + { + return; + } + + m_curparam = 0; + m_errstate = SP_ERROR_NONE; +} + +bool CForward::AddFunction(IPluginContext *pContext, funcid_t index) +{ + IPluginFunction *pFunc = pContext->GetFunctionById(index); + + if (!pFunc) + { + return false; + } + + 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; + FuncIter iter; + List *lst; + + if (func->IsRunnable()) + { + lst = &m_functions; + } else { + lst = &m_paused; + } + + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + if ((*iter) == func) + { + found = true; + lst->erase(iter); + break; + } + } + + /* Cancel a call, if any */ + if (found || m_curparam) + { + func->Cancel(); + } + + return found; +} + +unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin) +{ + FuncIter iter; + IPluginFunction *func; + unsigned int removed = 0; + IPluginContext *pContext = plugin->GetBaseContext(); + for (iter=m_functions.begin(); iter!=m_functions.end();) + { + func = (*iter); + if (func->GetParentContext() == pContext) + { + iter = m_functions.erase(iter); + removed++; + } else { + iter++; + } + } + + return removed; +} + +bool CForward::AddFunction(IPluginFunction *func) +{ + if (m_curparam) + { + return false; + } + + if (func->IsRunnable()) + { + m_functions.push_back(func); + } else { + m_paused.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; +}