/** * 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; }