core/smn_timers.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1330 ab94316bb52b
child 1397 c105f054c72e
permissions -rw-r--r--
fixed amb834 - command callbacks could potentially occur on invalid clients in very rare cases
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HandleSys.h"
    33 #include "TimerSys.h"
    34 #include "Logger.h"
    35 
    36 #define TIMER_HNDL_CLOSE	(1<<9)
    37 
    38 HandleType_t g_TimerType;
    39 
    40 struct TimerInfo 
    41 {
    42 	ITimer *Timer;
    43 	IPluginFunction *Hook;
    44 	IPluginContext *pContext;
    45 	Handle_t TimerHandle;
    46 	int UserData;
    47 	int Flags;
    48 };
    49 
    50 class TimerNatives :
    51 	public SMGlobalClass,
    52 	public IHandleTypeDispatch,
    53 	public ITimedEvent
    54 {
    55 public:
    56 	~TimerNatives();
    57 public: //ITimedEvent
    58 	ResultType OnTimer(ITimer *pTimer, void *pData);
    59 	void OnTimerEnd(ITimer *pTimer, void *pData);
    60 public: //IHandleTypeDispatch
    61 	void OnHandleDestroy(HandleType_t type, void *object);
    62 public: //SMGlobalClass
    63 	void OnSourceModAllInitialized();
    64 	void OnSourceModShutdown();
    65 public:
    66 	TimerInfo *CreateTimerInfo();
    67 	void DeleteTimerInfo(TimerInfo *pInfo);
    68 private:
    69 	CStack<TimerInfo *> m_FreeTimers;
    70 };
    71 
    72 TimerNatives::~TimerNatives()
    73 {
    74 	CStack<TimerInfo *>::iterator iter;
    75 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
    76 	{
    77 		delete (*iter);
    78 	}
    79 	m_FreeTimers.popall();
    80 }
    81 
    82 void TimerNatives::OnSourceModAllInitialized()
    83 {
    84 	HandleAccess sec;
    85 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    86 
    87 	g_TimerType = g_HandleSys.CreateType("Timer", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    88 }
    89 
    90 void TimerNatives::OnSourceModShutdown()
    91 {
    92 	g_HandleSys.RemoveType(g_TimerType, g_pCoreIdent);
    93 	g_TimerType = 0;
    94 }
    95 
    96 void TimerNatives::OnHandleDestroy(HandleType_t type, void *object)
    97 {
    98 	TimerInfo *pTimer = reinterpret_cast<TimerInfo *>(object);
    99 
   100 	g_Timers.KillTimer(pTimer->Timer);
   101 }
   102 
   103 TimerInfo *TimerNatives::CreateTimerInfo()
   104 {
   105 	TimerInfo *pInfo;
   106 
   107 	if (m_FreeTimers.empty())
   108 	{
   109 		pInfo = new TimerInfo;
   110 	} else {
   111 		pInfo = m_FreeTimers.front();
   112 		m_FreeTimers.pop();
   113 	}
   114 
   115 	return pInfo;
   116 }
   117 
   118 void TimerNatives::DeleteTimerInfo(TimerInfo *pInfo)
   119 {
   120 	m_FreeTimers.push(pInfo);
   121 }
   122 
   123 ResultType TimerNatives::OnTimer(ITimer *pTimer, void *pData)
   124 {
   125 	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
   126 	IPluginFunction *pFunc = pInfo->Hook;
   127 	cell_t res = static_cast<ResultType>(Pl_Continue);
   128 
   129 	pFunc->PushCell(pInfo->TimerHandle);
   130 	pFunc->PushCell(pInfo->UserData);
   131 	pFunc->Execute(&res);
   132 
   133 	return static_cast<ResultType>(res);
   134 }
   135 
   136 void TimerNatives::OnTimerEnd(ITimer *pTimer, void *pData)
   137 {
   138 	HandleSecurity sec;
   139 	HandleError herr;
   140 	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
   141 	Handle_t usrhndl = static_cast<Handle_t>(pInfo->UserData);
   142 
   143 	sec.pOwner = pInfo->pContext->GetIdentity();
   144 	sec.pIdentity = g_pCoreIdent;
   145 
   146 	if (pInfo->Flags & TIMER_HNDL_CLOSE)
   147 	{
   148 		if ((herr=g_HandleSys.FreeHandle(usrhndl, &sec)) != HandleError_None)
   149 		{
   150 			g_Logger.LogError("Invalid data handle %x (error %d) passed during timer end", usrhndl, herr);
   151 		}
   152 	}
   153 
   154 	g_HandleSys.FreeHandle(pInfo->TimerHandle, &sec);
   155 	DeleteTimerInfo(pInfo);
   156 }
   157 
   158 /*******************************
   159 *                              *
   160 * TIMER NATIVE IMPLEMENTATIONS *
   161 *                              *
   162 ********************************/
   163 
   164 static TimerNatives s_TimerNatives;
   165 
   166 static cell_t smn_CreateTimer(IPluginContext *pCtx, const cell_t *params)
   167 {
   168 	IPluginFunction *pFunc;
   169 	TimerInfo *pInfo;
   170 	ITimer *pTimer;
   171 	Handle_t hndl;
   172 	int flags = params[4];
   173 
   174 	pFunc = pCtx->GetFunctionById(params[2]);
   175 	if (!pFunc)
   176 	{
   177 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   178 	}
   179 
   180 	pInfo = s_TimerNatives.CreateTimerInfo();
   181 	pTimer = g_Timers.CreateTimer(&s_TimerNatives, sp_ctof(params[1]), pInfo, flags);
   182 	hndl = g_HandleSys.CreateHandle(g_TimerType, pInfo, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   183 
   184 	pInfo->UserData = params[3];
   185 	pInfo->Flags = flags;
   186 	pInfo->TimerHandle = hndl;
   187 	pInfo->Hook = pFunc;
   188 	pInfo->Timer = pTimer;
   189 	pInfo->pContext = pCtx;
   190 
   191 	return hndl;
   192 }
   193 
   194 static cell_t smn_KillTimer(IPluginContext *pCtx, const cell_t *params)
   195 {
   196 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   197 	HandleError herr;
   198 	HandleSecurity sec;
   199 	TimerInfo *pInfo;
   200 
   201 	sec.pOwner = pCtx->GetIdentity();
   202 	sec.pIdentity = g_pCoreIdent;
   203 
   204 	if ((herr=g_HandleSys.ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
   205 		!= HandleError_None)
   206 	{
   207 		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
   208 	}
   209 
   210 	g_Timers.KillTimer(pInfo->Timer);
   211 
   212 	if (params[2] && !(pInfo->Flags & TIMER_HNDL_CLOSE))
   213 	{
   214 		sec.pOwner = pInfo->pContext->GetIdentity();
   215 		sec.pIdentity = g_pCoreIdent;
   216 
   217 		if ((herr=g_HandleSys.FreeHandle(static_cast<Handle_t>(pInfo->UserData), &sec)) != HandleError_None)
   218 		{
   219 			return pCtx->ThrowNativeError("Invalid data handle %x (error %d)", hndl, herr);
   220 		}
   221 	}
   222 
   223 	return 1;
   224 }
   225 
   226 static cell_t smn_TriggerTimer(IPluginContext *pCtx, const cell_t *params)
   227 {
   228 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   229 	HandleError herr;
   230 	HandleSecurity sec;
   231 	TimerInfo *pInfo;
   232 
   233 	sec.pOwner = NULL;
   234 	sec.pIdentity = g_pCoreIdent;
   235 
   236 	if ((herr=g_HandleSys.ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
   237 		!= HandleError_None)
   238 	{
   239 		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
   240 	}
   241 
   242 	g_Timers.FireTimerOnce(pInfo->Timer, params[2] ? true : false);
   243 
   244 	return 1;
   245 }
   246 
   247 static cell_t smn_GetTickedTime(IPluginContext *pContext, const cell_t *params)
   248 {
   249 	cell_t *simulating;
   250 	pContext->LocalToPhysAddr(params[1], &simulating);
   251 
   252 	*simulating = g_SimTicks.ticking ? 0 : 1;
   253 
   254 	float t = g_SimTicks.ticking ? gpGlobals->curtime : g_SimTicks.ticktime;
   255 	return sp_ftoc(t);
   256 }
   257 
   258 REGISTER_NATIVES(timernatives)
   259 {
   260 	{"CreateTimer",				smn_CreateTimer},
   261 	{"KillTimer",				smn_KillTimer},
   262 	{"TriggerTimer",			smn_TriggerTimer},
   263 	{"GetTickedTime",			smn_GetTickedTime},
   264 	{NULL,						NULL}
   265 };