dvander@2168: /** dvander@2168: * vim: set ts=4 : dvander@2168: * ============================================================================= dvander@2168: * SourceMod dvander@2168: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. dvander@2168: * ============================================================================= dvander@2168: * dvander@2168: * This program is free software; you can redistribute it and/or modify it under dvander@2168: * the terms of the GNU General Public License, version 3.0, as published by the dvander@2168: * Free Software Foundation. dvander@2168: * dvander@2168: * This program is distributed in the hope that it will be useful, but WITHOUT dvander@2168: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@2168: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more dvander@2168: * details. dvander@2168: * dvander@2168: * You should have received a copy of the GNU General Public License along with dvander@2168: * this program. If not, see . dvander@2168: * dvander@2168: * As a special exception, AlliedModders LLC gives you permission to link the dvander@2168: * code of this program (as well as its derivative works) to "Half-Life 2," the dvander@2168: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software dvander@2168: * by the Valve Corporation. You must obey the GNU General Public License in dvander@2168: * all respects for all other code used. Additionally, AlliedModders LLC grants dvander@2168: * this exception to all derivative works. AlliedModders LLC defines further dvander@2168: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), dvander@2168: * or . dvander@2168: * dvander@2168: * Version: $Id$ dvander@2168: */ dvander@2168: dvander@2168: #include "HandleSys.h" dvander@2168: #include "TimerSys.h" dvander@2168: #include "PluginSys.h" dvander@2168: #include "Logger.h" dvander@2168: #include "DebugReporter.h" dvander@2168: dvander@2168: #define TIMER_HNDL_CLOSE (1<<9) dvander@2168: dvander@2168: HandleType_t g_TimerType; dvander@2168: dvander@2168: struct TimerInfo dvander@2168: { dvander@2168: ITimer *Timer; dvander@2168: IPluginFunction *Hook; dvander@2168: IPluginContext *pContext; dvander@2168: Handle_t TimerHandle; dvander@2168: int UserData; dvander@2168: int Flags; dvander@2168: }; dvander@2168: dvander@2168: class TimerNatives : dvander@2168: public SMGlobalClass, dvander@2168: public IHandleTypeDispatch, dvander@2168: public ITimedEvent dvander@2168: { dvander@2168: public: dvander@2168: ~TimerNatives(); dvander@2168: public: //ITimedEvent dvander@2168: ResultType OnTimer(ITimer *pTimer, void *pData); dvander@2168: void OnTimerEnd(ITimer *pTimer, void *pData); dvander@2168: public: //IHandleTypeDispatch dvander@2168: void OnHandleDestroy(HandleType_t type, void *object); dvander@2168: public: //SMGlobalClass dvander@2168: void OnSourceModAllInitialized(); dvander@2168: void OnSourceModShutdown(); dvander@2168: public: dvander@2168: TimerInfo *CreateTimerInfo(); dvander@2168: void DeleteTimerInfo(TimerInfo *pInfo); dvander@2168: private: dvander@2168: CStack m_FreeTimers; dvander@2168: }; dvander@2168: dvander@2168: TimerNatives::~TimerNatives() dvander@2168: { dvander@2168: CStack::iterator iter; dvander@2168: for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++) dvander@2168: { dvander@2168: delete (*iter); dvander@2168: } dvander@2168: m_FreeTimers.popall(); dvander@2168: } dvander@2168: dvander@2168: void TimerNatives::OnSourceModAllInitialized() dvander@2168: { dvander@2168: HandleAccess sec; dvander@2168: dvander@2168: g_HandleSys.InitAccessDefaults(NULL, &sec); dvander@2168: sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY; dvander@2168: dvander@2168: g_TimerType = g_HandleSys.CreateType("Timer", this, 0, NULL, &sec, g_pCoreIdent, NULL); dvander@2168: } dvander@2168: dvander@2168: void TimerNatives::OnSourceModShutdown() dvander@2168: { dvander@2168: g_HandleSys.RemoveType(g_TimerType, g_pCoreIdent); dvander@2168: g_TimerType = 0; dvander@2168: } dvander@2168: dvander@2168: void TimerNatives::OnHandleDestroy(HandleType_t type, void *object) dvander@2168: { dvander@2168: TimerInfo *pTimer = reinterpret_cast(object); dvander@2168: dvander@2168: g_Timers.KillTimer(pTimer->Timer); dvander@2168: } dvander@2168: dvander@2168: TimerInfo *TimerNatives::CreateTimerInfo() dvander@2168: { dvander@2168: TimerInfo *pInfo; dvander@2168: dvander@2168: if (m_FreeTimers.empty()) dvander@2168: { dvander@2168: pInfo = new TimerInfo; dvander@2168: } else { dvander@2168: pInfo = m_FreeTimers.front(); dvander@2168: m_FreeTimers.pop(); dvander@2168: } dvander@2168: dvander@2168: return pInfo; dvander@2168: } dvander@2168: dvander@2168: void TimerNatives::DeleteTimerInfo(TimerInfo *pInfo) dvander@2168: { dvander@2168: m_FreeTimers.push(pInfo); dvander@2168: } dvander@2168: dvander@2168: ResultType TimerNatives::OnTimer(ITimer *pTimer, void *pData) dvander@2168: { dvander@2168: TimerInfo *pInfo = reinterpret_cast(pData); dvander@2168: IPluginFunction *pFunc = pInfo->Hook; dvander@2168: cell_t res = static_cast(Pl_Continue); dvander@2168: dvander@2168: pFunc->PushCell(pInfo->TimerHandle); dvander@2168: pFunc->PushCell(pInfo->UserData); dvander@2168: pFunc->Execute(&res); dvander@2168: dvander@2168: return static_cast(res); dvander@2168: } dvander@2168: dvander@2168: void TimerNatives::OnTimerEnd(ITimer *pTimer, void *pData) dvander@2168: { dvander@2168: HandleSecurity sec; dvander@2168: HandleError herr; dvander@2168: TimerInfo *pInfo = reinterpret_cast(pData); dvander@2168: Handle_t usrhndl = static_cast(pInfo->UserData); dvander@2168: dvander@2168: sec.pOwner = pInfo->pContext->GetIdentity(); dvander@2168: sec.pIdentity = g_pCoreIdent; dvander@2168: dvander@2168: if (pInfo->Flags & TIMER_HNDL_CLOSE) dvander@2168: { dvander@2168: if ((herr=g_HandleSys.FreeHandle(usrhndl, &sec)) != HandleError_None) dvander@2168: { dvander@2168: g_DbgReporter.GenerateError(pInfo->pContext, dvander@2168: pInfo->Hook->GetFunctionID(), dvander@2168: SP_ERROR_NATIVE, dvander@2168: "Invalid data handle %x (error %d) passed during timer end", dvander@2168: usrhndl, dvander@2168: herr); dvander@2168: } dvander@2168: } dvander@2168: dvander@2168: if ((herr=g_HandleSys.FreeHandle(pInfo->TimerHandle, &sec)) != HandleError_None) dvander@2168: { dvander@2168: g_DbgReporter.GenerateError(pInfo->pContext, dvander@2168: pInfo->Hook->GetFunctionID(), dvander@2168: SP_ERROR_NATIVE, dvander@2168: "Invalid timer handle %x (error %d) during timer end, displayed function is timer callback, not the stack trace", dvander@2168: pInfo->TimerHandle, dvander@2168: herr); dvander@2168: return; dvander@2168: } dvander@2168: DeleteTimerInfo(pInfo); dvander@2168: } dvander@2168: dvander@2168: /******************************* dvander@2168: * * dvander@2168: * TIMER NATIVE IMPLEMENTATIONS * dvander@2168: * * dvander@2168: ********************************/ dvander@2168: dvander@2168: static TimerNatives s_TimerNatives; dvander@2168: dvander@2168: static cell_t smn_CreateTimer(IPluginContext *pCtx, const cell_t *params) dvander@2168: { dvander@2168: IPluginFunction *pFunc; dvander@2168: TimerInfo *pInfo; dvander@2168: ITimer *pTimer; dvander@2168: Handle_t hndl; dvander@2168: int flags = params[4]; dvander@2168: dvander@2168: pFunc = pCtx->GetFunctionById(params[2]); dvander@2168: if (!pFunc) dvander@2168: { dvander@2168: return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]); dvander@2168: } dvander@2168: dvander@2168: pInfo = s_TimerNatives.CreateTimerInfo(); dvander@2168: pTimer = g_Timers.CreateTimer(&s_TimerNatives, sp_ctof(params[1]), pInfo, flags); dvander@2168: dvander@2168: if (!pTimer) dvander@2168: { dvander@2168: s_TimerNatives.DeleteTimerInfo(pInfo); dvander@2168: return 0; dvander@2168: } dvander@2168: dvander@2168: hndl = g_HandleSys.CreateHandle(g_TimerType, pInfo, pCtx->GetIdentity(), g_pCoreIdent, NULL); dvander@2168: dvander@2168: pInfo->UserData = params[3]; dvander@2168: pInfo->Flags = flags; dvander@2168: pInfo->TimerHandle = hndl; dvander@2168: pInfo->Hook = pFunc; dvander@2168: pInfo->Timer = pTimer; dvander@2168: pInfo->pContext = pCtx; dvander@2168: dvander@2168: return hndl; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_KillTimer(IPluginContext *pCtx, const cell_t *params) dvander@2168: { dvander@2168: Handle_t hndl = static_cast(params[1]); dvander@2168: HandleError herr; dvander@2168: HandleSecurity sec; dvander@2168: TimerInfo *pInfo; dvander@2168: dvander@2168: sec.pOwner = pCtx->GetIdentity(); dvander@2168: sec.pIdentity = g_pCoreIdent; dvander@2168: dvander@2168: if ((herr=g_HandleSys.ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo)) dvander@2168: != HandleError_None) dvander@2168: { dvander@2168: return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr); dvander@2168: } dvander@2168: dvander@2168: g_Timers.KillTimer(pInfo->Timer); dvander@2168: dvander@2168: if (params[2] && !(pInfo->Flags & TIMER_HNDL_CLOSE)) dvander@2168: { dvander@2168: sec.pOwner = pInfo->pContext->GetIdentity(); dvander@2168: sec.pIdentity = g_pCoreIdent; dvander@2168: dvander@2168: if ((herr=g_HandleSys.FreeHandle(static_cast(pInfo->UserData), &sec)) != HandleError_None) dvander@2168: { dvander@2168: return pCtx->ThrowNativeError("Invalid data handle %x (error %d)", hndl, herr); dvander@2168: } dvander@2168: } dvander@2168: dvander@2168: return 1; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_TriggerTimer(IPluginContext *pCtx, const cell_t *params) dvander@2168: { dvander@2168: Handle_t hndl = static_cast(params[1]); dvander@2168: HandleError herr; dvander@2168: HandleSecurity sec; dvander@2168: TimerInfo *pInfo; dvander@2168: dvander@2168: sec.pOwner = pCtx->GetIdentity(); dvander@2168: sec.pIdentity = g_pCoreIdent; dvander@2168: dvander@2168: if ((herr=g_HandleSys.ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo)) dvander@2168: != HandleError_None) dvander@2168: { dvander@2168: return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr); dvander@2168: } dvander@2168: dvander@2168: g_Timers.FireTimerOnce(pInfo->Timer, params[2] ? true : false); dvander@2168: dvander@2168: return 1; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_GetTickedTime(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: return sp_ftoc(*g_pUniversalTime); dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_GetMapTimeLeft(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: float time_left; dvander@2168: if (!g_Timers.GetMapTimeLeft(&time_left)) dvander@2168: { dvander@2168: return 0; dvander@2168: } dvander@2168: dvander@2168: cell_t *addr; dvander@2168: pContext->LocalToPhysAddr(params[1], &addr); dvander@2168: *addr = (int)time_left; dvander@2168: dvander@2168: return 1; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_GetMapTimeLimit(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: IMapTimer *pMapTimer = g_Timers.GetMapTimer(); dvander@2168: dvander@2168: if (!pMapTimer) dvander@2168: { dvander@2168: return 0; dvander@2168: } dvander@2168: dvander@2168: cell_t *addr; dvander@2168: pContext->LocalToPhysAddr(params[1], &addr); dvander@2168: dvander@2168: *addr = pMapTimer->GetMapTimeLimit(); dvander@2168: dvander@2168: return 1; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_ExtendMapTimeLimit(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: IMapTimer *pMapTimer = g_Timers.GetMapTimer(); dvander@2168: dvander@2168: if (!pMapTimer) dvander@2168: { dvander@2168: return 0; dvander@2168: } dvander@2168: dvander@2168: pMapTimer->ExtendMapTimeLimit(params[1]); dvander@2168: dvander@2168: return 1; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_IsServerProcessing(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: return (gpGlobals->frametime > 0.0f) ? 1 : 0; dvander@2168: } dvander@2168: dvander@2168: static cell_t smn_GetTickInterval(IPluginContext *pContext, const cell_t *params) dvander@2168: { dvander@2168: return sp_ftoc(gpGlobals->interval_per_tick); dvander@2168: } dvander@2168: dvander@2168: REGISTER_NATIVES(timernatives) dvander@2168: { dvander@2168: {"CreateTimer", smn_CreateTimer}, dvander@2168: {"KillTimer", smn_KillTimer}, dvander@2168: {"TriggerTimer", smn_TriggerTimer}, dvander@2168: {"GetTickedTime", smn_GetTickedTime}, dvander@2168: {"GetMapTimeLeft", smn_GetMapTimeLeft}, dvander@2168: {"GetMapTimeLimit", smn_GetMapTimeLimit}, dvander@2168: {"ExtendMapTimeLimit", smn_ExtendMapTimeLimit}, dvander@2168: {"IsServerProcessing", smn_IsServerProcessing}, dvander@2168: {"GetTickInterval", smn_GetTickInterval}, dvander@2168: {NULL, NULL} dvander@2168: }; dvander@2168: