core/logic/smn_timers.cpp
author David Anderson <dvander@alliedmods.net>
Thu May 13 01:47:12 2010 -0700 (2010-05-13)
changeset 2989 e8b0605226ce
parent 2639 core/smn_timers.cpp@021fbd7a8cf6
child 3792 d8a07e40bcb6
permissions -rw-r--r--
Move timer natives into logic binary (bug 4402, r=ds).
faluco@612
     1
/**
ds@653
     2
 * vim: set ts=4 :
ds@1330
     3
 * =============================================================================
dvander@1231
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1330
     6
 * =============================================================================
ds@653
     7
 *
ds@1330
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    10
 * Free Software Foundation.
dvander@1231
    11
 * 
ds@1330
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    15
 * details.
dvander@1231
    16
 *
ds@1330
    17
 * You should have received a copy of the GNU General Public License along with
ds@1330
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
ds@1330
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    27
 * or <http://www.sourcemod.net/license.php>.
ds@653
    28
 *
ds@653
    29
 * Version: $Id$
ds@653
    30
 */
faluco@612
    31
dvander@2989
    32
#include "common_logic.h"
dvander@2989
    33
#include <string.h>
dvander@2989
    34
#include <IHandleSys.h>
dvander@2989
    35
#include <ITimerSystem.h>
dvander@2989
    36
#include <IPluginSys.h>
dvander@2989
    37
#include <sh_stack.h>
dvander@2989
    38
dvander@2989
    39
using namespace SourceHook;
faluco@612
    40
fyrenmoo@2639
    41
#define TIMER_DATA_HNDL_CLOSE	(1<<9)
fyrenmoo@2639
    42
#define TIMER_HNDL_CLOSE		(1<<9)
faluco@612
    43
faluco@612
    44
HandleType_t g_TimerType;
faluco@612
    45
faluco@612
    46
struct TimerInfo 
faluco@612
    47
{
faluco@612
    48
	ITimer *Timer;
faluco@612
    49
	IPluginFunction *Hook;
faluco@612
    50
	IPluginContext *pContext;
faluco@612
    51
	Handle_t TimerHandle;
faluco@612
    52
	int UserData;
faluco@612
    53
	int Flags;
faluco@612
    54
};
faluco@612
    55
faluco@612
    56
class TimerNatives :
faluco@612
    57
	public SMGlobalClass,
faluco@612
    58
	public IHandleTypeDispatch,
faluco@612
    59
	public ITimedEvent
faluco@612
    60
{
faluco@641
    61
public:
faluco@641
    62
	~TimerNatives();
faluco@612
    63
public: //ITimedEvent
faluco@612
    64
	ResultType OnTimer(ITimer *pTimer, void *pData);
faluco@612
    65
	void OnTimerEnd(ITimer *pTimer, void *pData);
faluco@612
    66
public: //IHandleTypeDispatch
faluco@612
    67
	void OnHandleDestroy(HandleType_t type, void *object);
faluco@612
    68
public: //SMGlobalClass
faluco@612
    69
	void OnSourceModAllInitialized();
faluco@612
    70
	void OnSourceModShutdown();
faluco@612
    71
public:
faluco@612
    72
	TimerInfo *CreateTimerInfo();
faluco@612
    73
	void DeleteTimerInfo(TimerInfo *pInfo);
faluco@612
    74
private:
faluco@612
    75
	CStack<TimerInfo *> m_FreeTimers;
faluco@612
    76
};
faluco@612
    77
faluco@641
    78
TimerNatives::~TimerNatives()
faluco@641
    79
{
faluco@641
    80
	CStack<TimerInfo *>::iterator iter;
faluco@641
    81
	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
faluco@641
    82
	{
faluco@641
    83
		delete (*iter);
faluco@641
    84
	}
faluco@641
    85
	m_FreeTimers.popall();
faluco@641
    86
}
faluco@641
    87
faluco@612
    88
void TimerNatives::OnSourceModAllInitialized()
faluco@612
    89
{
faluco@612
    90
	HandleAccess sec;
faluco@1397
    91
dvander@2989
    92
	handlesys->InitAccessDefaults(NULL, &sec);
faluco@1397
    93
	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
faluco@612
    94
dvander@2989
    95
	g_TimerType = handlesys->CreateType("Timer", this, 0, NULL, &sec, g_pCoreIdent, NULL);
faluco@612
    96
}
faluco@612
    97
faluco@612
    98
void TimerNatives::OnSourceModShutdown()
faluco@612
    99
{
dvander@2989
   100
	handlesys->RemoveType(g_TimerType, g_pCoreIdent);
faluco@612
   101
	g_TimerType = 0;
faluco@612
   102
}
faluco@612
   103
faluco@612
   104
void TimerNatives::OnHandleDestroy(HandleType_t type, void *object)
faluco@612
   105
{
faluco@612
   106
	TimerInfo *pTimer = reinterpret_cast<TimerInfo *>(object);
faluco@612
   107
dvander@2989
   108
	timersys->KillTimer(pTimer->Timer);
faluco@612
   109
}
faluco@612
   110
faluco@612
   111
TimerInfo *TimerNatives::CreateTimerInfo()
faluco@612
   112
{
faluco@612
   113
	TimerInfo *pInfo;
faluco@612
   114
faluco@612
   115
	if (m_FreeTimers.empty())
faluco@612
   116
	{
faluco@612
   117
		pInfo = new TimerInfo;
faluco@612
   118
	} else {
faluco@612
   119
		pInfo = m_FreeTimers.front();
faluco@612
   120
		m_FreeTimers.pop();
faluco@612
   121
	}
faluco@612
   122
faluco@612
   123
	return pInfo;
faluco@612
   124
}
faluco@612
   125
faluco@612
   126
void TimerNatives::DeleteTimerInfo(TimerInfo *pInfo)
faluco@612
   127
{
faluco@612
   128
	m_FreeTimers.push(pInfo);
faluco@612
   129
}
faluco@612
   130
faluco@612
   131
ResultType TimerNatives::OnTimer(ITimer *pTimer, void *pData)
faluco@612
   132
{
faluco@612
   133
	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
faluco@612
   134
	IPluginFunction *pFunc = pInfo->Hook;
faluco@612
   135
	cell_t res = static_cast<ResultType>(Pl_Continue);
faluco@612
   136
faluco@612
   137
	pFunc->PushCell(pInfo->TimerHandle);
faluco@612
   138
	pFunc->PushCell(pInfo->UserData);
faluco@612
   139
	pFunc->Execute(&res);
faluco@612
   140
faluco@612
   141
	return static_cast<ResultType>(res);
faluco@612
   142
}
faluco@612
   143
faluco@612
   144
void TimerNatives::OnTimerEnd(ITimer *pTimer, void *pData)
faluco@612
   145
{
faluco@612
   146
	HandleSecurity sec;
faluco@612
   147
	HandleError herr;
faluco@612
   148
	TimerInfo *pInfo = reinterpret_cast<TimerInfo *>(pData);
faluco@612
   149
	Handle_t usrhndl = static_cast<Handle_t>(pInfo->UserData);
faluco@612
   150
faluco@612
   151
	sec.pOwner = pInfo->pContext->GetIdentity();
faluco@612
   152
	sec.pIdentity = g_pCoreIdent;
faluco@612
   153
fyrenmoo@2639
   154
	if (pInfo->Flags & TIMER_DATA_HNDL_CLOSE)
faluco@612
   155
	{
dvander@2989
   156
		if ((herr=handlesys->FreeHandle(usrhndl, &sec)) != HandleError_None)
faluco@612
   157
		{
dvander@2989
   158
			smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(), 
dvander@2989
   159
							  	 SP_ERROR_NATIVE, 
dvander@2989
   160
							  	 "Invalid data handle %x (error %d) passed during timer end with TIMER_DATA_HNDL_CLOSE",
dvander@2989
   161
							  	 usrhndl, herr);
faluco@612
   162
		}
faluco@612
   163
	}
faluco@612
   164
dvander@2379
   165
	if (pInfo->TimerHandle != BAD_HANDLE)
faluco@1674
   166
	{
dvander@2989
   167
		if ((herr=handlesys->FreeHandle(pInfo->TimerHandle, &sec)) != HandleError_None)
dvander@2379
   168
		{
dvander@2989
   169
			smcore.GenerateError(pInfo->pContext, pInfo->Hook->GetFunctionID(), 
dvander@2379
   170
				SP_ERROR_NATIVE, 
dvander@2379
   171
				"Invalid timer handle %x (error %d) during timer end, displayed function is timer callback, not the stack trace", 
dvander@2989
   172
				pInfo->TimerHandle, herr);
dvander@2379
   173
		}
faluco@1674
   174
	}
dvander@2379
   175
faluco@613
   176
	DeleteTimerInfo(pInfo);
faluco@612
   177
}
faluco@612
   178
faluco@612
   179
/*******************************
faluco@612
   180
*                              *
faluco@612
   181
* TIMER NATIVE IMPLEMENTATIONS *
faluco@612
   182
*                              *
faluco@612
   183
********************************/
faluco@612
   184
faluco@612
   185
static TimerNatives s_TimerNatives;
faluco@612
   186
faluco@612
   187
static cell_t smn_CreateTimer(IPluginContext *pCtx, const cell_t *params)
faluco@612
   188
{
faluco@612
   189
	IPluginFunction *pFunc;
faluco@612
   190
	TimerInfo *pInfo;
faluco@612
   191
	ITimer *pTimer;
faluco@612
   192
	Handle_t hndl;
faluco@612
   193
	int flags = params[4];
faluco@612
   194
faluco@612
   195
	pFunc = pCtx->GetFunctionById(params[2]);
faluco@612
   196
	if (!pFunc)
faluco@612
   197
	{
faluco@612
   198
		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
faluco@612
   199
	}
faluco@612
   200
faluco@612
   201
	pInfo = s_TimerNatives.CreateTimerInfo();
dvander@2989
   202
	pTimer = timersys->CreateTimer(&s_TimerNatives, sp_ctof(params[1]), pInfo, flags);
dvander@1409
   203
dvander@1409
   204
	if (!pTimer)
dvander@1409
   205
	{
dvander@1409
   206
		s_TimerNatives.DeleteTimerInfo(pInfo);
dvander@1409
   207
		return 0;
dvander@1409
   208
	}
dvander@1409
   209
dvander@2989
   210
	hndl = handlesys->CreateHandle(g_TimerType, pInfo, pCtx->GetIdentity(), g_pCoreIdent, NULL);
faluco@612
   211
dvander@2379
   212
	/* If we can't get a handle, the timer isn't refcounted against the plugin and 
dvander@2379
   213
	 * we need to bail out to prevent a crash.
dvander@2379
   214
	 */
dvander@2379
   215
	if (hndl == BAD_HANDLE)
dvander@2379
   216
	{
dvander@2379
   217
		/* Free this for completeness. */
fyrenmoo@2639
   218
		if (flags & TIMER_DATA_HNDL_CLOSE)
dvander@2379
   219
		{
dvander@2379
   220
			HandleSecurity sec(pCtx->GetIdentity(), g_pCoreIdent);
dvander@2989
   221
			handlesys->FreeHandle(params[3], &sec);
dvander@2379
   222
		}
dvander@2379
   223
		/* Zero everything so there's no conflicts */
dvander@2379
   224
		memset(pInfo, 0, sizeof(TimerInfo));
dvander@2989
   225
		timersys->KillTimer(pTimer);
dvander@2379
   226
dvander@2379
   227
		return pCtx->ThrowNativeError("Could not create timer, no more handles");
dvander@2379
   228
	}
dvander@2379
   229
faluco@612
   230
	pInfo->UserData = params[3];
faluco@612
   231
	pInfo->Flags = flags;
faluco@612
   232
	pInfo->TimerHandle = hndl;
faluco@612
   233
	pInfo->Hook = pFunc;
faluco@612
   234
	pInfo->Timer = pTimer;
faluco@612
   235
	pInfo->pContext = pCtx;
faluco@612
   236
faluco@612
   237
	return hndl;
faluco@612
   238
}
faluco@612
   239
faluco@612
   240
static cell_t smn_KillTimer(IPluginContext *pCtx, const cell_t *params)
faluco@612
   241
{
faluco@612
   242
	Handle_t hndl = static_cast<Handle_t>(params[1]);
faluco@612
   243
	HandleError herr;
faluco@612
   244
	HandleSecurity sec;
faluco@612
   245
	TimerInfo *pInfo;
faluco@612
   246
dvander@1008
   247
	sec.pOwner = pCtx->GetIdentity();
faluco@612
   248
	sec.pIdentity = g_pCoreIdent;
faluco@612
   249
dvander@2989
   250
	if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
faluco@612
   251
		!= HandleError_None)
faluco@612
   252
	{
faluco@612
   253
		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
faluco@612
   254
	}
faluco@612
   255
dvander@2989
   256
	timersys->KillTimer(pInfo->Timer);
faluco@612
   257
fyrenmoo@2639
   258
	if (params[2] && !(pInfo->Flags & TIMER_DATA_HNDL_CLOSE))
faluco@612
   259
	{
faluco@612
   260
		sec.pOwner = pInfo->pContext->GetIdentity();
faluco@612
   261
		sec.pIdentity = g_pCoreIdent;
faluco@612
   262
dvander@2989
   263
		if ((herr=handlesys->FreeHandle(static_cast<Handle_t>(pInfo->UserData), &sec)) != HandleError_None)
faluco@612
   264
		{
fyrenmoo@2639
   265
			return pCtx->ThrowNativeError("Invalid data handle %x (error %d) on timer kill with TIMER_DATA_HNDL_CLOSE", hndl, herr);
faluco@612
   266
		}
faluco@612
   267
	}
faluco@612
   268
faluco@612
   269
	return 1;
faluco@612
   270
}
faluco@612
   271
faluco@612
   272
static cell_t smn_TriggerTimer(IPluginContext *pCtx, const cell_t *params)
faluco@612
   273
{
faluco@612
   274
	Handle_t hndl = static_cast<Handle_t>(params[1]);
faluco@612
   275
	HandleError herr;
faluco@612
   276
	HandleSecurity sec;
faluco@612
   277
	TimerInfo *pInfo;
faluco@612
   278
faluco@1397
   279
	sec.pOwner = pCtx->GetIdentity();
faluco@612
   280
	sec.pIdentity = g_pCoreIdent;
faluco@612
   281
dvander@2989
   282
	if ((herr=handlesys->ReadHandle(hndl, g_TimerType, &sec, (void **)&pInfo))
faluco@612
   283
		!= HandleError_None)
faluco@612
   284
	{
faluco@612
   285
		return pCtx->ThrowNativeError("Invalid timer handle %x (error %d)", hndl, herr);
faluco@612
   286
	}
faluco@612
   287
dvander@2989
   288
	timersys->FireTimerOnce(pInfo->Timer, params[2] ? true : false);
faluco@612
   289
faluco@612
   290
	return 1;
faluco@612
   291
}
faluco@612
   292
dvander@822
   293
static cell_t smn_GetTickedTime(IPluginContext *pContext, const cell_t *params)
dvander@822
   294
{
dvander@2989
   295
	return sp_ftoc(*serverGlobals.universalTime);
dvander@822
   296
}
dvander@822
   297
dvander@1415
   298
static cell_t smn_GetMapTimeLeft(IPluginContext *pContext, const cell_t *params)
dvander@1415
   299
{
dvander@1459
   300
	float time_left;
dvander@2989
   301
	if (!timersys->GetMapTimeLeft(&time_left))
dvander@1415
   302
	{
dvander@1462
   303
		return 0;
dvander@1415
   304
	}
dvander@1415
   305
dvander@1462
   306
	cell_t *addr;
dvander@1462
   307
	pContext->LocalToPhysAddr(params[1], &addr);
dvander@1462
   308
	*addr = (int)time_left;
dvander@1415
   309
dvander@1462
   310
	return 1;
dvander@1415
   311
}
dvander@1415
   312
dvander@1415
   313
static cell_t smn_GetMapTimeLimit(IPluginContext *pContext, const cell_t *params)
dvander@1415
   314
{
dvander@2989
   315
	IMapTimer *pMapTimer = timersys->GetMapTimer();
dvander@1415
   316
dvander@1415
   317
	if (!pMapTimer)
dvander@1415
   318
	{
dvander@1415
   319
		return 0;
dvander@1415
   320
	}
dvander@1415
   321
dvander@1415
   322
	cell_t *addr;
dvander@1415
   323
	pContext->LocalToPhysAddr(params[1], &addr);
dvander@1415
   324
dvander@1415
   325
	*addr = pMapTimer->GetMapTimeLimit();
dvander@1415
   326
dvander@1415
   327
	return 1;
dvander@1415
   328
}
dvander@1415
   329
dvander@1415
   330
static cell_t smn_ExtendMapTimeLimit(IPluginContext *pContext, const cell_t *params)
dvander@1415
   331
{
dvander@2989
   332
	IMapTimer *pMapTimer = timersys->GetMapTimer();
dvander@1415
   333
dvander@1415
   334
	if (!pMapTimer)
dvander@1415
   335
	{
dvander@1415
   336
		return 0;
dvander@1415
   337
	}
dvander@1415
   338
dvander@1415
   339
	pMapTimer->ExtendMapTimeLimit(params[1]);
dvander@1415
   340
dvander@1415
   341
	return 1;
dvander@1415
   342
}
dvander@1415
   343
dvander@1461
   344
static cell_t smn_IsServerProcessing(IPluginContext *pContext, const cell_t *params)
dvander@1459
   345
{
dvander@2989
   346
	return (*serverGlobals.frametime > 0.0f) ? 1 : 0;
dvander@1459
   347
}
dvander@1459
   348
dvander@2063
   349
static cell_t smn_GetTickInterval(IPluginContext *pContext, const cell_t *params)
dvander@2063
   350
{
dvander@2989
   351
	return sp_ftoc(*serverGlobals.interval_per_tick);
dvander@2063
   352
}
dvander@2063
   353
faluco@612
   354
REGISTER_NATIVES(timernatives)
faluco@612
   355
{
faluco@612
   356
	{"CreateTimer",				smn_CreateTimer},
faluco@612
   357
	{"KillTimer",				smn_KillTimer},
faluco@612
   358
	{"TriggerTimer",			smn_TriggerTimer},
dvander@822
   359
	{"GetTickedTime",			smn_GetTickedTime},
dvander@1415
   360
	{"GetMapTimeLeft",			smn_GetMapTimeLeft},
dvander@1415
   361
	{"GetMapTimeLimit",			smn_GetMapTimeLimit},
dvander@1415
   362
	{"ExtendMapTimeLimit",		smn_ExtendMapTimeLimit},
dvander@1461
   363
	{"IsServerProcessing",		smn_IsServerProcessing},
dvander@2063
   364
	{"GetTickInterval",			smn_GetTickInterval},
faluco@612
   365
	{NULL,						NULL}
faluco@612
   366
};
dvander@2063
   367