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