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