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