core/EventManager.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 01 02:12:47 2007 +0000 (2007-08-01)
changeset 1231 17badaf66ce4
parent 1022 9e3223e5a423
child 1330 ab94316bb52b
permissions -rw-r--r--
updated more header files (mostly internal now)
ds@576
     1
/**
ds@653
     2
 * vim: set ts=4 :
dvander@1231
     3
 * ================================================================
dvander@1231
     4
 * SourceMod
dvander@1231
     5
 * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
dvander@1231
     6
 * ================================================================
ds@653
     7
 *
dvander@1231
     8
 * This program is free software; you can redistribute it and/or 
dvander@1231
     9
 * modify it under the terms of the GNU General Public License, 
dvander@1231
    10
 * version 3.0, as published by the Free Software Foundation.
dvander@1231
    11
 * 
dvander@1231
    12
 * This program is distributed in the hope that it will be useful,
dvander@1231
    13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
dvander@1231
    14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
dvander@1231
    15
 * GNU General Public License for more details.
dvander@1231
    16
 *
dvander@1231
    17
 * You should have received a copy of the GNU General Public License
dvander@1231
    18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
dvander@1231
    20
 * As a special exception, AlliedModders LLC gives you permission to 
dvander@1231
    21
 * link the code of this program (as well as its derivative works) to 
dvander@1231
    22
 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
dvander@1231
    23
 * Game MODs that run on software by the Valve Corporation.  You must 
dvander@1231
    24
 * obey the GNU General Public License in all respects for all other 
dvander@1231
    25
 * code used. Additionally, AlliedModders LLC grants this exception 
dvander@1231
    26
 * to all derivative works. AlliedModders LLC defines further 
dvander@1231
    27
 * exceptions, found in LICENSE.txt (as of this writing, version 
dvander@1231
    28
 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
ds@653
    29
 *
ds@653
    30
 * Version: $Id$
ds@653
    31
 */
ds@576
    32
ds@576
    33
#include "EventManager.h"
ds@576
    34
#include "ForwardSys.h"
ds@576
    35
#include "HandleSys.h"
ds@576
    36
#include "PluginSys.h"
ds@576
    37
#include "sm_stringutil.h"
ds@576
    38
ds@576
    39
EventManager g_EventManager;
ds@576
    40
ds@576
    41
SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool);
ds@576
    42
ds@582
    43
const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell};
ds@576
    44
typedef List<EventHook *> EventHookList;
ds@576
    45
ds@712
    46
EventManager::EventManager() : m_EventType(0)
ds@576
    47
{
ds@576
    48
	/* Create an event lookup trie */
ds@576
    49
	m_EventHooks = sm_trie_create();
ds@576
    50
}
ds@576
    51
ds@576
    52
EventManager::~EventManager()
ds@576
    53
{
ds@576
    54
	sm_trie_destroy(m_EventHooks);
ds@577
    55
ds@577
    56
	/* Free memory used by EventInfo structs if any */
ds@577
    57
	CStack<EventInfo *>::iterator iter;
ds@577
    58
	for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++)
ds@577
    59
	{
ds@577
    60
		delete (*iter);
ds@577
    61
	}
ds@577
    62
ds@577
    63
	m_FreeEvents.popall();
ds@576
    64
}
ds@576
    65
ds@576
    66
void EventManager::OnSourceModAllInitialized()
ds@576
    67
{
ds@576
    68
	/* Add a hook for IGameEventManager2::FireEvent() */
ds@576
    69
	SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
ds@576
    70
	SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
ds@576
    71
ds@576
    72
	HandleAccess sec;
ds@576
    73
ds@576
    74
	/* Handle access security for 'GameEvent' handle type */
ds@576
    75
	sec.access[HandleAccess_Read] = 0;
ds@576
    76
	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
ds@576
    77
	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
ds@576
    78
ds@576
    79
	/* Create the 'GameEvent' handle type */
ds@576
    80
	m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL);
ds@576
    81
}
ds@576
    82
ds@576
    83
void EventManager::OnSourceModShutdown()
ds@576
    84
{
ds@576
    85
	/* Remove hook for IGameEventManager2::FireEvent() */
ds@576
    86
	SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
ds@576
    87
	SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
ds@576
    88
ds@576
    89
	/* Remove the 'GameEvent' handle type */
ds@576
    90
	g_HandleSys.RemoveType(m_EventType, g_pCoreIdent);
ds@576
    91
ds@576
    92
	/* Remove ourselves as listener for events */
ds@576
    93
	gameevents->RemoveListener(this);
ds@576
    94
}
ds@576
    95
ds@576
    96
void EventManager::OnHandleDestroy(HandleType_t type, void *object)
ds@576
    97
{
ds@577
    98
	EventInfo *pInfo = static_cast<EventInfo *>(object);
ds@576
    99
ds@577
   100
	/* Should only free event when created by a plugin */
ds@577
   101
	if (pInfo->pOwner)
ds@577
   102
	{
ds@577
   103
		/* Free IGameEvent */
ds@577
   104
		gameevents->FreeEvent(pInfo->pEvent);
ds@577
   105
ds@577
   106
		/* Add EventInfo struct to free event stack */
ds@577
   107
		m_FreeEvents.push(pInfo);
ds@576
   108
	}
ds@576
   109
}
ds@576
   110
ds@576
   111
void EventManager::OnPluginUnloaded(IPlugin *plugin)
ds@576
   112
{
ds@576
   113
	EventHookList *pHookList;
ds@582
   114
	EventHookList::iterator iter;
ds@576
   115
	EventHook *pHook;
ds@576
   116
ds@576
   117
	// If plugin has an event hook list...
ds@576
   118
	if (plugin->GetProperty("EventHooks", reinterpret_cast<void **>(&pHookList), true))
ds@576
   119
	{
ds@582
   120
		for (iter = pHookList->begin(); iter != pHookList->end(); iter++)
ds@576
   121
		{
ds@576
   122
			pHook = (*iter);
ds@576
   123
ds@576
   124
			if (--pHook->refCount == 0)
ds@576
   125
			{
ds@576
   126
				if (pHook->pPreHook)
ds@576
   127
				{
ds@576
   128
					g_Forwards.ReleaseForward(pHook->pPreHook);
ds@576
   129
				}
ds@576
   130
ds@576
   131
				if (pHook->pPostHook)
ds@576
   132
				{
ds@576
   133
					g_Forwards.ReleaseForward(pHook->pPostHook);
ds@576
   134
				}
ds@576
   135
ds@576
   136
				delete pHook;
ds@576
   137
			}
ds@576
   138
		}
ds@576
   139
ds@576
   140
		delete pHookList;
ds@576
   141
	}
ds@576
   142
}
ds@576
   143
ds@576
   144
/* IGameEventListener2::FireGameEvent */
ds@576
   145
void EventManager::FireGameEvent(IGameEvent *pEvent)
ds@576
   146
{
ds@576
   147
	/* Not going to do anything here.
ds@576
   148
	   Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */
ds@576
   149
}
ds@576
   150
ds@576
   151
EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
ds@576
   152
{
ds@576
   153
	EventHook *pHook;
ds@576
   154
ds@576
   155
	/* If we aren't listening to this event... */
ds@576
   156
	if (!gameevents->FindListener(this, name))
ds@576
   157
	{
ds@576
   158
		/* Then add ourselves */
ds@576
   159
		if (!gameevents->AddListener(this, name, true))
ds@576
   160
		{
ds@576
   161
			/* If event doesn't exist... */
ds@576
   162
			return EventHookErr_InvalidEvent;
ds@576
   163
		}
ds@576
   164
	}
ds@576
   165
ds@576
   166
	/* If a hook structure does not exist... */
ds@576
   167
	if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
ds@576
   168
	{
ds@576
   169
		EventHookList *pHookList;
ds@576
   170
		IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
ds@576
   171
ds@576
   172
		/* Check plugin for an existing EventHook list */
ds@576
   173
		if (!plugin->GetProperty("EventHooks", (void **)&pHookList))
ds@576
   174
		{
ds@576
   175
			pHookList = new EventHookList();
ds@576
   176
			plugin->SetProperty("EventHooks", pHookList);
ds@576
   177
		}
ds@576
   178
ds@576
   179
		/* Create new GameEventHook structure */
ds@576
   180
		pHook = new EventHook();
ds@576
   181
ds@576
   182
		if (mode == EventHookMode_Pre)
ds@576
   183
		{
ds@576
   184
			/* Create forward for a pre hook */
ds@890
   185
			pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS);
ds@576
   186
			/* Add to forward list */
ds@576
   187
			pHook->pPreHook->AddFunction(pFunction);
ds@576
   188
		} else {
ds@576
   189
			/* Create forward for a post hook */
ds@576
   190
			pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
ds@576
   191
			/* Should we copy data from a pre hook to the post hook? */
ds@576
   192
			pHook->postCopy = (mode == EventHookMode_Post);
ds@576
   193
			/* Add to forward list */
ds@576
   194
			pHook->pPostHook->AddFunction(pFunction);
ds@576
   195
		}
ds@576
   196
ds@576
   197
		/* Increase reference count */
ds@576
   198
		pHook->refCount++;
ds@576
   199
ds@576
   200
		/* Add hook structure to hook lists */
ds@576
   201
		pHookList->push_back(pHook);
ds@576
   202
		sm_trie_insert(m_EventHooks, name, pHook);
ds@576
   203
ds@576
   204
		return EventHookErr_Okay;
ds@576
   205
	}
ds@576
   206
ds@576
   207
	/* Hook structure already exists at this point */
ds@576
   208
ds@576
   209
	if (mode == EventHookMode_Pre)
ds@576
   210
	{
ds@576
   211
		/* Create pre hook forward if necessary */
ds@576
   212
		if (!pHook->pPreHook)
ds@576
   213
		{
ds@576
   214
			pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS);
ds@576
   215
		}
ds@576
   216
ds@576
   217
		/* Add plugin function to forward list */
ds@576
   218
		pHook->pPreHook->AddFunction(pFunction);
ds@576
   219
	} else {
ds@576
   220
		/* Create post hook forward if necessary */
ds@576
   221
		if (!pHook->pPostHook)
ds@576
   222
		{
ds@576
   223
			pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
ds@576
   224
		}
ds@576
   225
ds@576
   226
		/* If postCopy is false, then we may want to set it to true */
ds@576
   227
		if (!pHook->postCopy)
ds@576
   228
		{
ds@576
   229
			pHook->postCopy = (mode == EventHookMode_Post);
ds@576
   230
		}
ds@576
   231
ds@576
   232
		/* Add plugin function to forward list */
ds@576
   233
		pHook->pPostHook->AddFunction(pFunction);
ds@576
   234
	}
ds@576
   235
ds@576
   236
	/* Increase reference count */
ds@576
   237
	pHook->refCount++;
ds@576
   238
ds@576
   239
	return EventHookErr_Okay;
ds@576
   240
}
ds@576
   241
ds@576
   242
EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
ds@576
   243
{
ds@576
   244
	EventHook *pHook;
ds@576
   245
	IChangeableForward **pEventForward;
ds@576
   246
ds@576
   247
	/* If hook does not exist at all */
ds@576
   248
	if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
ds@576
   249
	{
ds@576
   250
		return EventHookErr_NotActive;
ds@576
   251
	}
ds@576
   252
ds@576
   253
	/* One forward to rule them all */
ds@576
   254
	if (mode == EventHookMode_Pre)
ds@576
   255
	{
ds@576
   256
		pEventForward = &pHook->pPreHook;
ds@576
   257
	} else {
ds@576
   258
		pEventForward = &pHook->pPostHook;
ds@576
   259
	}
ds@576
   260
ds@576
   261
	/* Remove function from forward's list */
ds@576
   262
	if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction))
ds@576
   263
	{
ds@576
   264
		return EventHookErr_InvalidCallback;
ds@576
   265
	}
ds@576
   266
ds@576
   267
	/* If forward's list contains 0 functions now, free it */
ds@576
   268
	if ((*pEventForward)->GetFunctionCount() == 0)
ds@576
   269
	{
ds@576
   270
		g_Forwards.ReleaseForward(*pEventForward);
ds@576
   271
		*pEventForward = NULL;
ds@576
   272
	}
ds@576
   273
ds@576
   274
	/* Decrement reference count */
ds@576
   275
	if (--pHook->refCount == 0)
ds@576
   276
	{
ds@576
   277
		/* If reference count is now 0, free hook structure */
ds@576
   278
ds@576
   279
		EventHookList *pHookList;
ds@576
   280
		IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
ds@576
   281
ds@576
   282
		/* Get plugin's event hook list */
dvander@1022
   283
		if (!plugin->GetProperty("EventHooks", (void**)&pHookList))
dvander@1022
   284
		{
dvander@1022
   285
			return EventHookErr_NotActive;
dvander@1022
   286
		}
dvander@1022
   287
dvander@1022
   288
		/* Make sure the event was actually being hooked */
dvander@1022
   289
		if (pHookList->find(pHook) == pHookList->end())
dvander@1022
   290
		{
dvander@1022
   291
			return EventHookErr_NotActive;
dvander@1022
   292
		}
ds@576
   293
ds@576
   294
		/* Remove current structure from plugin's list */
ds@576
   295
		pHookList->remove(pHook);
ds@576
   296
ds@576
   297
		/* Delete entry in trie */
ds@576
   298
		sm_trie_delete(m_EventHooks, name);
ds@576
   299
ds@576
   300
		/* And finally free structure memory */
ds@576
   301
		delete pHook;
ds@576
   302
	}
ds@576
   303
ds@576
   304
	return EventHookErr_Okay;
ds@576
   305
}
ds@576
   306
ds@713
   307
EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force)
ds@577
   308
{
ds@577
   309
	EventInfo *pInfo;
ds@713
   310
	IGameEvent *pEvent = gameevents->CreateEvent(name, force);
ds@577
   311
ds@577
   312
	if (pEvent)
ds@577
   313
	{
ds@577
   314
		if (m_FreeEvents.empty())
ds@577
   315
		{
ds@577
   316
			pInfo = new EventInfo();
ds@577
   317
		} else {
ds@577
   318
			pInfo = m_FreeEvents.front();
ds@577
   319
			m_FreeEvents.pop();
ds@577
   320
		}
ds@577
   321
ds@577
   322
ds@577
   323
		pInfo->pEvent = pEvent;
ds@577
   324
		pInfo->pOwner = pContext->GetIdentity();
ds@577
   325
ds@577
   326
		return pInfo;
ds@577
   327
	}
ds@577
   328
ds@577
   329
	return NULL;
ds@577
   330
}
ds@577
   331
ds@712
   332
void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
ds@577
   333
{
ds@577
   334
	/* Actually fire event now */
ds@577
   335
	gameevents->FireEvent(pInfo->pEvent, bDontBroadcast);
ds@577
   336
ds@577
   337
	/* IGameEvent is free at this point, so no one owns this */
ds@577
   338
	pInfo->pOwner = NULL;
ds@577
   339
ds@577
   340
	/* Add EventInfo struct to free event stack */
ds@577
   341
	m_FreeEvents.push(pInfo);
ds@577
   342
}
ds@577
   343
ds@687
   344
void EventManager::CancelCreatedEvent(EventInfo *pInfo)
ds@687
   345
{
ds@687
   346
	/* Free event from IGameEventManager2 */
ds@687
   347
	gameevents->FreeEvent(pInfo->pEvent);
ds@687
   348
ds@687
   349
	/* IGameEvent is free at this point, so no one owns this */
ds@687
   350
	pInfo->pOwner = NULL;
ds@687
   351
ds@687
   352
	/* Add EventInfo struct to free event stack */
ds@687
   353
	m_FreeEvents.push(pInfo);
ds@687
   354
}
ds@687
   355
ds@576
   356
/* IGameEventManager2::FireEvent hook */
ds@576
   357
bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
ds@576
   358
{
ds@576
   359
	EventHook *pHook;
ds@576
   360
	IChangeableForward *pForward;
ds@684
   361
	const char *name;
ds@576
   362
	cell_t res = Pl_Continue;
ds@576
   363
	
ds@576
   364
	/* Get the event name, we're going to need this for passing to post hooks */
ds@684
   365
	name = pEvent->GetName();
ds@576
   366
ds@684
   367
	m_EventNames.push(name);
ds@684
   368
ds@684
   369
	if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
ds@576
   370
	{
ds@576
   371
		pForward = pHook->pPreHook;
ds@576
   372
ds@576
   373
		if (pForward)
ds@576
   374
		{
ds@792
   375
			EventInfo info = { pEvent, NULL };
ds@576
   376
ds@576
   377
			Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
ds@576
   378
			pForward->PushCell(hndl);
ds@684
   379
			pForward->PushString(name);
ds@576
   380
			pForward->PushCell(bDontBroadcast);
ds@576
   381
			pForward->Execute(&res, NULL);
ds@576
   382
dvander@857
   383
			HandleSecurity sec(NULL, g_pCoreIdent);
ds@576
   384
			g_HandleSys.FreeHandle(hndl, &sec);
ds@576
   385
		}
ds@576
   386
ds@576
   387
		if (pHook->postCopy)
ds@576
   388
		{
ds@684
   389
			pHook->pEventCopy = gameevents->DuplicateEvent(pEvent);
ds@576
   390
		}
ds@576
   391
ds@576
   392
		if (res)
ds@576
   393
		{
ds@576
   394
			gameevents->FreeEvent(pEvent);
ds@576
   395
			RETURN_META_VALUE(MRES_SUPERCEDE, false);
ds@576
   396
		}
ds@576
   397
	}
ds@576
   398
ds@576
   399
	RETURN_META_VALUE(MRES_IGNORED, true);
ds@576
   400
}
ds@576
   401
ds@576
   402
/* IGameEventManager2::FireEvent post hook */
ds@576
   403
bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
ds@576
   404
{
ds@690
   405
	IGameEvent *pEventCopy = NULL;
ds@576
   406
	EventHook *pHook;
ds@576
   407
	IChangeableForward *pForward;
ds@684
   408
	const char *name;
dvander@603
   409
	Handle_t hndl = 0;
ds@576
   410
ds@684
   411
	name = m_EventNames.front();
ds@684
   412
ds@684
   413
	if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
ds@576
   414
	{
ds@576
   415
		pForward = pHook->pPostHook;
ds@576
   416
ds@576
   417
		if (pForward)
ds@576
   418
		{
ds@792
   419
			EventInfo info = { pHook->pEventCopy, NULL };
ds@792
   420
ds@576
   421
			if (pHook->postCopy)
ds@576
   422
			{
ds@576
   423
				hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
ds@684
   424
ds@576
   425
				pForward->PushCell(hndl);
ds@576
   426
			} else {
ds@576
   427
				pForward->PushCell(BAD_HANDLE);
ds@576
   428
			}
ds@576
   429
ds@684
   430
			pForward->PushString(name);
ds@576
   431
			pForward->PushCell(bDontBroadcast);
ds@576
   432
			pForward->Execute(NULL);
ds@576
   433
ds@576
   434
			if (pHook->postCopy)
ds@576
   435
			{
ds@576
   436
				/* Free handle */
dvander@857
   437
				HandleSecurity sec(NULL, g_pCoreIdent);
ds@576
   438
				g_HandleSys.FreeHandle(hndl, &sec);
ds@576
   439
ds@576
   440
				/* Free event structure */
ds@684
   441
				gameevents->FreeEvent(pEventCopy);
ds@684
   442
				pHook->pEventCopy = NULL;
ds@576
   443
			}
ds@576
   444
		}
ds@576
   445
	}
ds@576
   446
ds@684
   447
	m_EventNames.pop();
ds@684
   448
ds@576
   449
	RETURN_META_VALUE(MRES_IGNORED, true);
ds@576
   450
}