3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
32 #include "common_logic.h"
34 #include <IHandleSys.h>
35 #include <ITimerSystem.h>
36 #include <IPluginSys.h>
39 using namespace SourceHook;
41 #define TIMER_DATA_HNDL_CLOSE (1<<9)
42 #define TIMER_HNDL_CLOSE (1<<9)
44 HandleType_t g_TimerType;
49 IPluginFunction *Hook;
50 IPluginContext *pContext;
58 public IHandleTypeDispatch,
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();
72 TimerInfo *CreateTimerInfo();
73 void DeleteTimerInfo(TimerInfo *pInfo);
75 CStack<TimerInfo *> m_FreeTimers;
78 TimerNatives::~TimerNatives()
80 CStack<TimerInfo *>::iterator iter;
81 for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
85 m_FreeTimers.popall();
88 void TimerNatives::OnSourceModAllInitialized()
92 handlesys->InitAccessDefaults(NULL, &sec);
93 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
95 g_TimerType = handlesys->CreateType("Timer", this, 0, NULL, &sec, g_pCoreIdent, NULL);
98 void TimerNatives::OnSourceModShutdown()
100 handlesys->RemoveType(g_TimerType, g_pCoreIdent);
104 void TimerNatives::OnHandleDestroy(HandleType_t type, void *object)
106 TimerInfo *pTimer = reinterpret_cast<TimerInfo *>(object);
108 timersys->KillTimer(pTimer->Timer);
111 TimerInfo *TimerNatives::CreateTimerInfo()
115 if (m_FreeTimers.empty())
117 pInfo = new TimerInfo;
119 pInfo = m_FreeTimers.front();
126 void TimerNatives::DeleteTimerInfo(TimerInfo *pInfo)
128 m_FreeTimers.push(pInfo);
131 ResultType TimerNatives::OnTimer(ITimer *pTimer, void *pData)
133 TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
134 IPluginFunction *pFunc = pInfo->Hook;
135 cell_t res = static_cast<ResultType>(Pl_Continue);
137 pFunc->PushCell(pInfo->TimerHandle);
138 pFunc->PushCell(pInfo->UserData);
139 pFunc->Execute(&res);
141 return static_cast<ResultType>(res);
144 void TimerNatives::OnTimerEnd(ITimer *pTimer, void *pData)
148 TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
149 Handle_t usrhndl = static_cast<Handle_t>(pInfo->UserData);
151 sec.pOwner = pInfo->pContext->GetIdentity();
152 sec.pIdentity = g_pCoreIdent;
154 if (pInfo->Flags & TIMER_DATA_HNDL_CLOSE)
156 if ((herr=handlesys->FreeHandle(usrhndl, &sec)) != HandleError_None)
158 smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(),
160 "Invalid data handle %x (error %d) passed during timer end with TIMER_DATA_HNDL_CLOSE",
165 if (pInfo->TimerHandle != BAD_HANDLE)
167 if ((herr=handlesys->FreeHandle(pInfo->TimerHandle, &sec)) != HandleError_None)
169 smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(),
171 "Invalid timer handle %x (error %d) during timer end, displayed function is timer callback, not the stack trace",
172 pInfo->TimerHandle, herr);
176 DeleteTimerInfo(pInfo);
179 /*******************************
181 * TIMER NATIVE IMPLEMENTATIONS *
183 ********************************/
185 static TimerNatives s_TimerNatives;
187 static cell_t smn_CreateTimer(IPluginContext *pCtx, const cell_t *params)
189 IPluginFunction *pFunc;
193 int flags = params[4];
195 pFunc = pCtx->GetFunctionById(params[2]);
198 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
201 pInfo = s_TimerNatives.CreateTimerInfo();
202 pTimer = timersys->CreateTimer(&s_TimerNatives, sp_ctof(params[1]), pInfo, flags);
206 s_TimerNatives.DeleteTimerInfo(pInfo);
210 hndl = handlesys->CreateHandle(g_TimerType, pInfo, pCtx->GetIdentity(), g_pCoreIdent, NULL);
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.
215 if (hndl == BAD_HANDLE)
217 /* Free this for completeness. */
218 if (flags & TIMER_DATA_HNDL_CLOSE)
220 HandleSecurity sec(pCtx->GetIdentity(), g_pCoreIdent);
221 handlesys->FreeHandle(params[3], &sec);
223 /* Zero everything so there's no conflicts */
224 memset(pInfo, 0, sizeof(TimerInfo));
225 timersys->KillTimer(pTimer);
227 return pCtx->ThrowNativeError("Could not create timer, no more handles");
230 pInfo->UserData = params[3];
231 pInfo->Flags = flags;
232 pInfo->TimerHandle = hndl;
234 pInfo->Timer = pTimer;
235 pInfo->pContext = pCtx;
240 static cell_t smn_KillTimer(IPluginContext *pCtx, const cell_t *params)
242 Handle_t hndl = static_cast<Handle_t>(params[1]);
247 sec.pOwner = pCtx->GetIdentity();
248 sec.pIdentity = g_pCoreIdent;
250 if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
253 return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
256 timersys->KillTimer(pInfo->Timer);
258 if (params[2] && !(pInfo->Flags & TIMER_DATA_HNDL_CLOSE))
260 sec.pOwner = pInfo->pContext->GetIdentity();
261 sec.pIdentity = g_pCoreIdent;
263 if ((herr=handlesys->FreeHandle(static_cast<Handle_t>(pInfo->UserData), &sec)) != HandleError_None)
265 return pCtx->ThrowNativeError("Invalid data handle %x (error %d) on timer kill with TIMER_DATA_HNDL_CLOSE", hndl, herr);
272 static cell_t smn_TriggerTimer(IPluginContext *pCtx, const cell_t *params)
274 Handle_t hndl = static_cast<Handle_t>(params[1]);
279 sec.pOwner = pCtx->GetIdentity();
280 sec.pIdentity = g_pCoreIdent;
282 if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
285 return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
288 timersys->FireTimerOnce(pInfo->Timer, params[2] ? true : false);
293 static cell_t smn_GetTickedTime(IPluginContext *pContext, const cell_t *params)
295 return sp_ftoc(*serverGlobals.universalTime);
298 static cell_t smn_GetMapTimeLeft(IPluginContext *pContext, const cell_t *params)
301 if (!timersys->GetMapTimeLeft(&time_left))
307 pContext->LocalToPhysAddr(params[1], &addr);
308 *addr = (int)time_left;
313 static cell_t smn_GetMapTimeLimit(IPluginContext *pContext, const cell_t *params)
315 IMapTimer *pMapTimer = timersys->GetMapTimer();
323 pContext->LocalToPhysAddr(params[1], &addr);
325 *addr = pMapTimer->GetMapTimeLimit();
330 static cell_t smn_ExtendMapTimeLimit(IPluginContext *pContext, const cell_t *params)
332 IMapTimer *pMapTimer = timersys->GetMapTimer();
339 pMapTimer->ExtendMapTimeLimit(params[1]);
344 static cell_t smn_IsServerProcessing(IPluginContext *pContext, const cell_t *params)
346 return (*serverGlobals.frametime > 0.0f) ? 1 : 0;
349 static cell_t smn_GetTickInterval(IPluginContext *pContext, const cell_t *params)
351 return sp_ftoc(*serverGlobals.interval_per_tick);
354 REGISTER_NATIVES(timernatives)
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},