core/EventManager.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1947 e73a1e51833b
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

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