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