core/EventManager.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu Feb 13 12:12:36 2014 -0500 (2014-02-13)
changeset 4251 9290c598d3cb
parent 4139 de7d1cd00703
child 4353 b642023bec0c
permissions -rw-r--r--
Update CSSDM gdc patch bytes (NPOTB)
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2010 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 "sm_stringutil.h"
    34 #include "logic_bridge.h"
    35 
    36 EventManager g_EventManager;
    37 
    38 SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool);
    39 
    40 const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell};
    41 typedef List<EventHook *> EventHookList;
    42 
    43 class EventForwardFilter : public IForwardFilter
    44 {
    45 	EventInfo *pEventInfo;
    46 public:
    47 	EventForwardFilter(EventInfo *pEventInfo) : pEventInfo(pEventInfo)
    48 	{
    49 	}
    50 
    51 	void Preprocess(IPluginFunction *fun, FwdParamInfo *params)
    52 	{
    53 		params[2].val = pEventInfo->bDontBroadcast ? 1 : 0;
    54 	}
    55 };
    56 
    57 EventManager::EventManager() : m_EventType(0)
    58 {
    59 }
    60 
    61 EventManager::~EventManager()
    62 {
    63 	/* Free memory used by EventInfo structs if any */
    64 	CStack<EventInfo *>::iterator iter;
    65 	for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++)
    66 	{
    67 		delete (*iter);
    68 	}
    69 
    70 	m_FreeEvents.popall();
    71 }
    72 
    73 void EventManager::OnSourceModAllInitialized()
    74 {
    75 	/* Add a hook for IGameEventManager2::FireEvent() */
    76 	SH_ADD_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent), false);
    77 	SH_ADD_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent_Post), true);
    78 
    79 	HandleAccess sec;
    80 
    81 	/* Handle access security for 'GameEvent' handle type */
    82 	sec.access[HandleAccess_Read] = 0;
    83 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
    84 	sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
    85 
    86 	/* Create the 'GameEvent' handle type */
    87 	m_EventType = handlesys->CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    88 }
    89 
    90 void EventManager::OnSourceModShutdown()
    91 {
    92 	/* Remove hook for IGameEventManager2::FireEvent() */
    93 	SH_REMOVE_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent), false);
    94 	SH_REMOVE_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent_Post), true);
    95 
    96 	/* Remove the 'GameEvent' handle type */
    97 	handlesys->RemoveType(m_EventType, g_pCoreIdent);
    98 
    99 	/* Remove ourselves as listener for events */
   100 	gameevents->RemoveListener(this);
   101 }
   102 
   103 void EventManager::OnHandleDestroy(HandleType_t type, void *object)
   104 {
   105 	EventInfo *pInfo = static_cast<EventInfo *>(object);
   106 
   107 	/* Should only free event when created by a plugin */
   108 	if (pInfo->pOwner)
   109 	{
   110 		/* Free IGameEvent */
   111 		gameevents->FreeEvent(pInfo->pEvent);
   112 
   113 		/* Add EventInfo struct to free event stack */
   114 		m_FreeEvents.push(pInfo);
   115 	}
   116 }
   117 
   118 void EventManager::OnPluginUnloaded(IPlugin *plugin)
   119 {
   120 	EventHookList *pHookList;
   121 	EventHookList::iterator iter;
   122 	EventHook *pHook;
   123 
   124 	// If plugin has an event hook list...
   125 	if (plugin->GetProperty("EventHooks", reinterpret_cast<void **>(&pHookList), true))
   126 	{
   127 		for (iter = pHookList->begin(); iter != pHookList->end(); iter++)
   128 		{
   129 			pHook = (*iter);
   130 
   131 			if (--pHook->refCount == 0)
   132 			{
   133 				if (pHook->pPreHook)
   134 				{
   135 					forwardsys->ReleaseForward(pHook->pPreHook);
   136 				}
   137 
   138 				if (pHook->pPostHook)
   139 				{
   140 					forwardsys->ReleaseForward(pHook->pPostHook);
   141 				}
   142 
   143 				delete pHook;
   144 			}
   145 		}
   146 
   147 		delete pHookList;
   148 	}
   149 }
   150 
   151 /* IGameEventListener2::FireGameEvent */
   152 void EventManager::FireGameEvent(IGameEvent *pEvent)
   153 {
   154 	/* Not going to do anything here.
   155 	   Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */
   156 }
   157 
   158 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   159 int EventManager::GetEventDebugID()
   160 {
   161 	return EVENT_DEBUG_ID_INIT;
   162 }
   163 #endif
   164 
   165 EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
   166 {
   167 	EventHook *pHook;
   168 
   169 	/* If we aren't listening to this event... */
   170 	if (!gameevents->FindListener(this, name))
   171 	{
   172 		/* Then add ourselves */
   173 		if (!gameevents->AddListener(this, name, true))
   174 		{
   175 			/* If event doesn't exist... */
   176 			return EventHookErr_InvalidEvent;
   177 		}
   178 	}
   179 
   180 	/* If a hook structure does not exist... */
   181 	if (!m_EventHooks.retrieve(name, &pHook))
   182 	{
   183 		EventHookList *pHookList;
   184 		IPlugin *plugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext());
   185 
   186 		/* Check plugin for an existing EventHook list */
   187 		if (!plugin->GetProperty("EventHooks", (void **)&pHookList))
   188 		{
   189 			pHookList = new EventHookList();
   190 			plugin->SetProperty("EventHooks", pHookList);
   191 		}
   192 
   193 		/* Create new GameEventHook structure */
   194 		pHook = new EventHook();
   195 
   196 		if (mode == EventHookMode_Pre)
   197 		{
   198 			/* Create forward for a pre hook */
   199 			pHook->pPreHook = forwardsys->CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS);
   200 			/* Add to forward list */
   201 			pHook->pPreHook->AddFunction(pFunction);
   202 		} else {
   203 			/* Create forward for a post hook */
   204 			pHook->pPostHook = forwardsys->CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
   205 			/* Should we copy data from a pre hook to the post hook? */
   206 			pHook->postCopy = (mode == EventHookMode_Post);
   207 			/* Add to forward list */
   208 			pHook->pPostHook->AddFunction(pFunction);
   209 		}
   210 
   211 		/* Cache the name for post hooks */
   212 		pHook->name = sm_strdup(name);
   213 
   214 		/* Increase reference count */
   215 		pHook->refCount++;
   216 
   217 		/* Add hook structure to hook lists */
   218 		pHookList->push_back(pHook);
   219 		m_EventHooks.insert(name, pHook);
   220 
   221 		return EventHookErr_Okay;
   222 	}
   223 
   224 	/* Hook structure already exists at this point */
   225 
   226 	if (mode == EventHookMode_Pre)
   227 	{
   228 		/* Create pre hook forward if necessary */
   229 		if (!pHook->pPreHook)
   230 		{
   231 			pHook->pPreHook = forwardsys->CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS);
   232 		}
   233 
   234 		/* Add plugin function to forward list */
   235 		pHook->pPreHook->AddFunction(pFunction);
   236 	} else {
   237 		/* Create post hook forward if necessary */
   238 		if (!pHook->pPostHook)
   239 		{
   240 			pHook->pPostHook = forwardsys->CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
   241 		}
   242 
   243 		/* If postCopy is false, then we may want to set it to true */
   244 		if (!pHook->postCopy)
   245 		{
   246 			pHook->postCopy = (mode == EventHookMode_Post);
   247 		}
   248 
   249 		/* Add plugin function to forward list */
   250 		pHook->pPostHook->AddFunction(pFunction);
   251 	}
   252 
   253 	/* Increase reference count */
   254 	pHook->refCount++;
   255 
   256 	return EventHookErr_Okay;
   257 }
   258 
   259 EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
   260 {
   261 	EventHook *pHook;
   262 	IChangeableForward **pEventForward;
   263 
   264 	/* If hook does not exist at all */
   265 	if (!m_EventHooks.retrieve(name, &pHook))
   266 	{
   267 		return EventHookErr_NotActive;
   268 	}
   269 
   270 	/* One forward to rule them all */
   271 	if (mode == EventHookMode_Pre)
   272 	{
   273 		pEventForward = &pHook->pPreHook;
   274 	} else {
   275 		pEventForward = &pHook->pPostHook;
   276 	}
   277 
   278 	/* Remove function from forward's list */
   279 	if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction))
   280 	{
   281 		return EventHookErr_InvalidCallback;
   282 	}
   283 
   284 	/* If forward's list contains 0 functions now, free it */
   285 	if ((*pEventForward)->GetFunctionCount() == 0)
   286 	{
   287 		forwardsys->ReleaseForward(*pEventForward);
   288 		*pEventForward = NULL;
   289 	}
   290 
   291 	/* Decrement reference count */
   292 	if (--pHook->refCount == 0)
   293 	{
   294 		/* If reference count is now 0, free hook structure */
   295 
   296 		EventHookList *pHookList;
   297 		IPlugin *plugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext());
   298 
   299 		/* Get plugin's event hook list */
   300 		if (!plugin->GetProperty("EventHooks", (void**)&pHookList))
   301 		{
   302 			return EventHookErr_NotActive;
   303 		}
   304 
   305 		/* Make sure the event was actually being hooked */
   306 		if (pHookList->find(pHook) == pHookList->end())
   307 		{
   308 			return EventHookErr_NotActive;
   309 		}
   310 
   311 		/* Remove current structure from plugin's list */
   312 		pHookList->remove(pHook);
   313 
   314 		/* Delete entry in trie */
   315 		m_EventHooks.remove(name);
   316 
   317 		/* Free the cached name */
   318 		delete pHook->name;
   319 
   320 		/* And finally free structure memory */
   321 		delete pHook;
   322 	}
   323 
   324 	return EventHookErr_Okay;
   325 }
   326 
   327 EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force)
   328 {
   329 	EventInfo *pInfo;
   330 	IGameEvent *pEvent = gameevents->CreateEvent(name, force);
   331 
   332 	if (pEvent)
   333 	{
   334 		if (m_FreeEvents.empty())
   335 		{
   336 			pInfo = new EventInfo();
   337 		} else {
   338 			pInfo = m_FreeEvents.front();
   339 			m_FreeEvents.pop();
   340 		}
   341 
   342 
   343 		pInfo->pEvent = pEvent;
   344 		pInfo->pOwner = pContext->GetIdentity();
   345 		pInfo->bDontBroadcast = false;
   346 
   347 		return pInfo;
   348 	}
   349 
   350 	return NULL;
   351 }
   352 
   353 void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
   354 {
   355 	/* Actually fire event now */
   356 	gameevents->FireEvent(pInfo->pEvent, bDontBroadcast);
   357 
   358 	/* IGameEvent is free at this point, so no one owns this */
   359 	pInfo->pOwner = NULL;
   360 
   361 	/* Add EventInfo struct to free event stack */
   362 	m_FreeEvents.push(pInfo);
   363 }
   364 
   365 void EventManager::CancelCreatedEvent(EventInfo *pInfo)
   366 {
   367 	/* Free event from IGameEventManager2 */
   368 	gameevents->FreeEvent(pInfo->pEvent);
   369 
   370 	/* IGameEvent is free at this point, so no one owns this */
   371 	pInfo->pOwner = NULL;
   372 
   373 	/* Add EventInfo struct to free event stack */
   374 	m_FreeEvents.push(pInfo);
   375 }
   376 
   377 /* IGameEventManager2::FireEvent hook */
   378 bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
   379 {
   380 	EventHook *pHook;
   381 	IChangeableForward *pForward;
   382 	const char *name;
   383 	cell_t res = Pl_Continue;
   384 	bool broadcast = bDontBroadcast;
   385 
   386 	/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
   387 	if (!pEvent)
   388 	{
   389 		RETURN_META_VALUE(MRES_IGNORED, false);
   390 	}
   391 
   392 	name = pEvent->GetName();
   393 	
   394 	if (m_EventHooks.retrieve(name, &pHook))
   395 	{
   396 		/* Push the event onto the event stack.  The reference count is increased to make sure 
   397 		 * the structure is not garbage collected in between now and the post hook.
   398 		 */
   399 		pHook->refCount++;
   400 		m_EventStack.push(pHook);
   401 
   402 		pForward = pHook->pPreHook;
   403 
   404 		if (pForward)
   405 		{
   406 			EventInfo info(pEvent, NULL);
   407 			HandleSecurity sec(NULL, g_pCoreIdent);
   408 			Handle_t hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
   409 
   410 			info.bDontBroadcast = bDontBroadcast;
   411 
   412 			EventForwardFilter filter(&info);
   413 
   414 			pForward->PushCell(hndl);
   415 			pForward->PushString(name);
   416 			pForward->PushCell(bDontBroadcast);
   417 			pForward->Execute(&res, &filter);
   418 
   419 			broadcast = info.bDontBroadcast;
   420 
   421 			handlesys->FreeHandle(hndl, &sec);
   422 		}
   423 
   424 		if (pHook->postCopy)
   425 		{
   426 			m_EventCopies.push(gameevents->DuplicateEvent(pEvent));
   427 		}
   428 
   429 		if (res)
   430 		{
   431 			gameevents->FreeEvent(pEvent);
   432 			RETURN_META_VALUE(MRES_SUPERCEDE, false);
   433 		}
   434 	}
   435 	else
   436 	{
   437 		m_EventStack.push(NULL);
   438 	}
   439 
   440 	if (broadcast != bDontBroadcast)
   441 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, true, &IGameEventManager2::FireEvent, (pEvent, broadcast));
   442 
   443 	RETURN_META_VALUE(MRES_IGNORED, true);
   444 }
   445 
   446 /* IGameEventManager2::FireEvent post hook */
   447 bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
   448 {
   449 	EventHook *pHook;
   450 	EventInfo info;
   451 	IChangeableForward *pForward;
   452 	Handle_t hndl = 0;
   453 
   454 	/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
   455 	if (!pEvent)
   456 	{
   457 		RETURN_META_VALUE(MRES_IGNORED, false);
   458 	}
   459 
   460 	pHook = m_EventStack.front();
   461 
   462 	if (pHook != NULL)
   463 	{
   464 		pForward = pHook->pPostHook;
   465 
   466 		if (pForward)
   467 		{
   468 			if (pHook->postCopy)
   469 			{
   470 				info.bDontBroadcast = bDontBroadcast;
   471 				info.pEvent = m_EventCopies.front();
   472 				info.pOwner = NULL;
   473 				hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
   474 
   475 				pForward->PushCell(hndl);
   476 			} else {
   477 				pForward->PushCell(BAD_HANDLE);
   478 			}
   479 
   480 			pForward->PushString(pHook->name);
   481 			pForward->PushCell(bDontBroadcast);
   482 			pForward->Execute(NULL);
   483 
   484 			if (pHook->postCopy)
   485 			{
   486 				/* Free handle */
   487 				HandleSecurity sec(NULL, g_pCoreIdent);
   488 				handlesys->FreeHandle(hndl, &sec);
   489 
   490 				/* Free event structure */
   491 				gameevents->FreeEvent(info.pEvent);
   492 				m_EventCopies.pop();
   493 			}
   494 		}
   495 
   496 		/* Decrement reference count, check if a delayed delete is needed */
   497 		if (--pHook->refCount == 0)
   498 		{
   499 			assert(pHook->pPostHook == NULL);
   500 			assert(pHook->pPreHook == NULL);
   501 			m_EventHooks.remove(pHook->name);
   502 			delete pHook->name;
   503 			delete pHook;
   504 		}
   505 	}
   506 
   507 	m_EventStack.pop();
   508 
   509 	RETURN_META_VALUE(MRES_IGNORED, true);
   510 }