core/logic/smn_timers.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed May 25 13:31:14 2011 -0400 (2011-05-25)
changeset 3285 065beec73902
parent 2989 e8b0605226ce
child 3792 d8a07e40bcb6
permissions -rw-r--r--
Backed out changeset: 65eb3df32b1c
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 "common_logic.h"
    33 #include <string.h>
    34 #include <IHandleSys.h>
    35 #include <ITimerSystem.h>
    36 #include <IPluginSys.h>
    37 #include <sh_stack.h>
    38 
    39 using namespace SourceHook;
    40 
    41 #define TIMER_DATA_HNDL_CLOSE	(1<<9)
    42 #define TIMER_HNDL_CLOSE		(1<<9)
    43 
    44 HandleType_t g_TimerType;
    45 
    46 struct TimerInfo 
    47 {
    48 	ITimer *Timer;
    49 	IPluginFunction *Hook;
    50 	IPluginContext *pContext;
    51 	Handle_t TimerHandle;
    52 	int UserData;
    53 	int Flags;
    54 };
    55 
    56 class TimerNatives :
    57 	public SMGlobalClass,
    58 	public IHandleTypeDispatch,
    59 	public ITimedEvent
    60 {
    61 public:
    62 	~TimerNatives();
    63 public: //ITimedEvent
    64 	ResultType OnTimer(ITimer *pTimer, void *pData);
    65 	void OnTimerEnd(ITimer *pTimer, void *pData);
    66 public: //IHandleTypeDispatch
    67 	void OnHandleDestroy(HandleType_t type, void *object);
    68 public: //SMGlobalClass
    69 	void OnSourceModAllInitialized();
    70 	void OnSourceModShutdown();
    71 public:
    72 	TimerInfo *CreateTimerInfo();
    73 	void DeleteTimerInfo(TimerInfo *pInfo);
    74 private:
    75 	CStack<TimerInfo *> m_FreeTimers;
    76 };
    77 
    78 TimerNatives::~TimerNatives()
    79 {
    80 	CStack<TimerInfo *>::iterator iter;
    81 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
    82 	{
    83 		delete (*iter);
    84 	}
    85 	m_FreeTimers.popall();
    86 }
    87 
    88 void TimerNatives::OnSourceModAllInitialized()
    89 {
    90 	HandleAccess sec;
    91 
    92 	handlesys->InitAccessDefaults(NULL, &sec);
    93 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
    94 
    95 	g_TimerType = handlesys->CreateType("Timer", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    96 }
    97 
    98 void TimerNatives::OnSourceModShutdown()
    99 {
   100 	handlesys->RemoveType(g_TimerType, g_pCoreIdent);
   101 	g_TimerType = 0;
   102 }
   103 
   104 void TimerNatives::OnHandleDestroy(HandleType_t type, void *object)
   105 {
   106 	TimerInfo *pTimer = reinterpret_cast<TimerInfo *>(object);
   107 
   108 	timersys->KillTimer(pTimer->Timer);
   109 }
   110 
   111 TimerInfo *TimerNatives::CreateTimerInfo()
   112 {
   113 	TimerInfo *pInfo;
   114 
   115 	if (m_FreeTimers.empty())
   116 	{
   117 		pInfo = new TimerInfo;
   118 	} else {
   119 		pInfo = m_FreeTimers.front();
   120 		m_FreeTimers.pop();
   121 	}
   122 
   123 	return pInfo;
   124 }
   125 
   126 void TimerNatives::DeleteTimerInfo(TimerInfo *pInfo)
   127 {
   128 	m_FreeTimers.push(pInfo);
   129 }
   130 
   131 ResultType TimerNatives::OnTimer(ITimer *pTimer, void *pData)
   132 {
   133 	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
   134 	IPluginFunction *pFunc = pInfo->Hook;
   135 	cell_t res = static_cast<ResultType>(Pl_Continue);
   136 
   137 	pFunc->PushCell(pInfo->TimerHandle);
   138 	pFunc->PushCell(pInfo->UserData);
   139 	pFunc->Execute(&res);
   140 
   141 	return static_cast<ResultType>(res);
   142 }
   143 
   144 void TimerNatives::OnTimerEnd(ITimer *pTimer, void *pData)
   145 {
   146 	HandleSecurity sec;
   147 	HandleError herr;
   148 	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
   149 	Handle_t usrhndl = static_cast<Handle_t>(pInfo->UserData);
   150 
   151 	sec.pOwner = pInfo->pContext->GetIdentity();
   152 	sec.pIdentity = g_pCoreIdent;
   153 
   154 	if (pInfo->Flags & TIMER_DATA_HNDL_CLOSE)
   155 	{
   156 		if ((herr=handlesys->FreeHandle(usrhndl, &sec)) != HandleError_None)
   157 		{
   158 			smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(), 
   159 							  	 SP_ERROR_NATIVE, 
   160 							  	 "Invalid data handle %x (error %d) passed during timer end with TIMER_DATA_HNDL_CLOSE",
   161 							  	 usrhndl, herr);
   162 		}
   163 	}
   164 
   165 	if (pInfo->TimerHandle != BAD_HANDLE)
   166 	{
   167 		if ((herr=handlesys->FreeHandle(pInfo->TimerHandle, &sec)) != HandleError_None)
   168 		{
   169 			smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(), 
   170 				SP_ERROR_NATIVE, 
   171 				"Invalid timer handle %x (error %d) during timer end, displayed function is timer callback, not the stack trace", 
   172 				pInfo->TimerHandle, herr);
   173 		}
   174 	}
   175 
   176 	DeleteTimerInfo(pInfo);
   177 }
   178 
   179 /*******************************
   180 *                              *
   181 * TIMER NATIVE IMPLEMENTATIONS *
   182 *                              *
   183 ********************************/
   184 
   185 static TimerNatives s_TimerNatives;
   186 
   187 static cell_t smn_CreateTimer(IPluginContext *pCtx, const cell_t *params)
   188 {
   189 	IPluginFunction *pFunc;
   190 	TimerInfo *pInfo;
   191 	ITimer *pTimer;
   192 	Handle_t hndl;
   193 	int flags = params[4];
   194 
   195 	pFunc = pCtx->GetFunctionById(params[2]);
   196 	if (!pFunc)
   197 	{
   198 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   199 	}
   200 
   201 	pInfo = s_TimerNatives.CreateTimerInfo();
   202 	pTimer = timersys->CreateTimer(&s_TimerNatives, sp_ctof(params[1]), pInfo, flags);
   203 
   204 	if (!pTimer)
   205 	{
   206 		s_TimerNatives.DeleteTimerInfo(pInfo);
   207 		return 0;
   208 	}
   209 
   210 	hndl = handlesys->CreateHandle(g_TimerType, pInfo, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   211 
   212 	/* If we can't get a handle, the timer isn't refcounted against the plugin and 
   213 	 * we need to bail out to prevent a crash.
   214 	 */
   215 	if (hndl == BAD_HANDLE)
   216 	{
   217 		/* Free this for completeness. */
   218 		if (flags & TIMER_DATA_HNDL_CLOSE)
   219 		{
   220 			HandleSecurity sec(pCtx->GetIdentity(), g_pCoreIdent);
   221 			handlesys->FreeHandle(params[3], &sec);
   222 		}
   223 		/* Zero everything so there's no conflicts */
   224 		memset(pInfo, 0, sizeof(TimerInfo));
   225 		timersys->KillTimer(pTimer);
   226 
   227 		return pCtx->ThrowNativeError("Could not create timer, no more handles");
   228 	}
   229 
   230 	pInfo->UserData = params[3];
   231 	pInfo->Flags = flags;
   232 	pInfo->TimerHandle = hndl;
   233 	pInfo->Hook = pFunc;
   234 	pInfo->Timer = pTimer;
   235 	pInfo->pContext = pCtx;
   236 
   237 	return hndl;
   238 }
   239 
   240 static cell_t smn_KillTimer(IPluginContext *pCtx, const cell_t *params)
   241 {
   242 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   243 	HandleError herr;
   244 	HandleSecurity sec;
   245 	TimerInfo *pInfo;
   246 
   247 	sec.pOwner = pCtx->GetIdentity();
   248 	sec.pIdentity = g_pCoreIdent;
   249 
   250 	if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
   251 		!= HandleError_None)
   252 	{
   253 		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
   254 	}
   255 
   256 	timersys->KillTimer(pInfo->Timer);
   257 
   258 	if (params[2] && !(pInfo->Flags & TIMER_DATA_HNDL_CLOSE))
   259 	{
   260 		sec.pOwner = pInfo->pContext->GetIdentity();
   261 		sec.pIdentity = g_pCoreIdent;
   262 
   263 		if ((herr=handlesys->FreeHandle(static_cast<Handle_t>(pInfo->UserData), &sec)) != HandleError_None)
   264 		{
   265 			return pCtx->ThrowNativeError("Invalid data handle %x (error %d) on timer kill with TIMER_DATA_HNDL_CLOSE", hndl, herr);
   266 		}
   267 	}
   268 
   269 	return 1;
   270 }
   271 
   272 static cell_t smn_TriggerTimer(IPluginContext *pCtx, const cell_t *params)
   273 {
   274 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   275 	HandleError herr;
   276 	HandleSecurity sec;
   277 	TimerInfo *pInfo;
   278 
   279 	sec.pOwner = pCtx->GetIdentity();
   280 	sec.pIdentity = g_pCoreIdent;
   281 
   282 	if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
   283 		!= HandleError_None)
   284 	{
   285 		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
   286 	}
   287 
   288 	timersys->FireTimerOnce(pInfo->Timer, params[2] ? true : false);
   289 
   290 	return 1;
   291 }
   292 
   293 static cell_t smn_GetTickedTime(IPluginContext *pContext, const cell_t *params)
   294 {
   295 	return sp_ftoc(*serverGlobals.universalTime);
   296 }
   297 
   298 static cell_t smn_GetMapTimeLeft(IPluginContext *pContext, const cell_t *params)
   299 {
   300 	float time_left;
   301 	if (!timersys->GetMapTimeLeft(&time_left))
   302 	{
   303 		return 0;
   304 	}
   305 
   306 	cell_t *addr;
   307 	pContext->LocalToPhysAddr(params[1], &addr);
   308 	*addr = (int)time_left;
   309 
   310 	return 1;
   311 }
   312 
   313 static cell_t smn_GetMapTimeLimit(IPluginContext *pContext, const cell_t *params)
   314 {
   315 	IMapTimer *pMapTimer = timersys->GetMapTimer();
   316 
   317 	if (!pMapTimer)
   318 	{
   319 		return 0;
   320 	}
   321 
   322 	cell_t *addr;
   323 	pContext->LocalToPhysAddr(params[1], &addr);
   324 
   325 	*addr = pMapTimer->GetMapTimeLimit();
   326 
   327 	return 1;
   328 }
   329 
   330 static cell_t smn_ExtendMapTimeLimit(IPluginContext *pContext, const cell_t *params)
   331 {
   332 	IMapTimer *pMapTimer = timersys->GetMapTimer();
   333 
   334 	if (!pMapTimer)
   335 	{
   336 		return 0;
   337 	}
   338 
   339 	pMapTimer->ExtendMapTimeLimit(params[1]);
   340 
   341 	return 1;
   342 }
   343 
   344 static cell_t smn_IsServerProcessing(IPluginContext *pContext, const cell_t *params)
   345 {
   346 	return (*serverGlobals.frametime > 0.0f) ? 1 : 0;
   347 }
   348 
   349 static cell_t smn_GetTickInterval(IPluginContext *pContext, const cell_t *params)
   350 {
   351 	return sp_ftoc(*serverGlobals.interval_per_tick);
   352 }
   353 
   354 REGISTER_NATIVES(timernatives)
   355 {
   356 	{"CreateTimer",				smn_CreateTimer},
   357 	{"KillTimer",				smn_KillTimer},
   358 	{"TriggerTimer",			smn_TriggerTimer},
   359 	{"GetTickedTime",			smn_GetTickedTime},
   360 	{"GetMapTimeLeft",			smn_GetMapTimeLeft},
   361 	{"GetMapTimeLimit",			smn_GetMapTimeLimit},
   362 	{"ExtendMapTimeLimit",		smn_ExtendMapTimeLimit},
   363 	{"IsServerProcessing",		smn_IsServerProcessing},
   364 	{"GetTickInterval",			smn_GetTickInterval},
   365 	{NULL,						NULL}
   366 };
   367