core/EventManager.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4353 b642023bec0c
permissions -rw-r--r--
Update CS:GO team score gamedata.
     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 = 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 		/* And finally free structure memory */
   318 		delete pHook;
   319 	}
   320 
   321 	return EventHookErr_Okay;
   322 }
   323 
   324 EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force)
   325 {
   326 	EventInfo *pInfo;
   327 	IGameEvent *pEvent = gameevents->CreateEvent(name, force);
   328 
   329 	if (pEvent)
   330 	{
   331 		if (m_FreeEvents.empty())
   332 		{
   333 			pInfo = new EventInfo();
   334 		} else {
   335 			pInfo = m_FreeEvents.front();
   336 			m_FreeEvents.pop();
   337 		}
   338 
   339 
   340 		pInfo->pEvent = pEvent;
   341 		pInfo->pOwner = pContext->GetIdentity();
   342 		pInfo->bDontBroadcast = false;
   343 
   344 		return pInfo;
   345 	}
   346 
   347 	return NULL;
   348 }
   349 
   350 void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
   351 {
   352 	/* Actually fire event now */
   353 	gameevents->FireEvent(pInfo->pEvent, bDontBroadcast);
   354 
   355 	/* IGameEvent is free at this point, so no one owns this */
   356 	pInfo->pOwner = NULL;
   357 
   358 	/* Add EventInfo struct to free event stack */
   359 	m_FreeEvents.push(pInfo);
   360 }
   361 
   362 void EventManager::CancelCreatedEvent(EventInfo *pInfo)
   363 {
   364 	/* Free event from IGameEventManager2 */
   365 	gameevents->FreeEvent(pInfo->pEvent);
   366 
   367 	/* IGameEvent is free at this point, so no one owns this */
   368 	pInfo->pOwner = NULL;
   369 
   370 	/* Add EventInfo struct to free event stack */
   371 	m_FreeEvents.push(pInfo);
   372 }
   373 
   374 /* IGameEventManager2::FireEvent hook */
   375 bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
   376 {
   377 	EventHook *pHook;
   378 	IChangeableForward *pForward;
   379 	const char *name;
   380 	cell_t res = Pl_Continue;
   381 	bool broadcast = bDontBroadcast;
   382 
   383 	/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
   384 	if (!pEvent)
   385 	{
   386 		RETURN_META_VALUE(MRES_IGNORED, false);
   387 	}
   388 
   389 	name = pEvent->GetName();
   390 	
   391 	if (m_EventHooks.retrieve(name, &pHook))
   392 	{
   393 		/* Push the event onto the event stack.  The reference count is increased to make sure 
   394 		 * the structure is not garbage collected in between now and the post hook.
   395 		 */
   396 		pHook->refCount++;
   397 		m_EventStack.push(pHook);
   398 
   399 		pForward = pHook->pPreHook;
   400 
   401 		if (pForward)
   402 		{
   403 			EventInfo info(pEvent, NULL);
   404 			HandleSecurity sec(NULL, g_pCoreIdent);
   405 			Handle_t hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
   406 
   407 			info.bDontBroadcast = bDontBroadcast;
   408 
   409 			EventForwardFilter filter(&info);
   410 
   411 			pForward->PushCell(hndl);
   412 			pForward->PushString(name);
   413 			pForward->PushCell(bDontBroadcast);
   414 			pForward->Execute(&res, &filter);
   415 
   416 			broadcast = info.bDontBroadcast;
   417 
   418 			handlesys->FreeHandle(hndl, &sec);
   419 		}
   420 
   421 		if (pHook->postCopy)
   422 		{
   423 			m_EventCopies.push(gameevents->DuplicateEvent(pEvent));
   424 		}
   425 
   426 		if (res)
   427 		{
   428 			gameevents->FreeEvent(pEvent);
   429 			RETURN_META_VALUE(MRES_SUPERCEDE, false);
   430 		}
   431 	}
   432 	else
   433 	{
   434 		m_EventStack.push(NULL);
   435 	}
   436 
   437 	if (broadcast != bDontBroadcast)
   438 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, true, &IGameEventManager2::FireEvent, (pEvent, broadcast));
   439 
   440 	RETURN_META_VALUE(MRES_IGNORED, true);
   441 }
   442 
   443 /* IGameEventManager2::FireEvent post hook */
   444 bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
   445 {
   446 	EventHook *pHook;
   447 	EventInfo info;
   448 	IChangeableForward *pForward;
   449 	Handle_t hndl = 0;
   450 
   451 	/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
   452 	if (!pEvent)
   453 	{
   454 		RETURN_META_VALUE(MRES_IGNORED, false);
   455 	}
   456 
   457 	pHook = m_EventStack.front();
   458 
   459 	if (pHook != NULL)
   460 	{
   461 		pForward = pHook->pPostHook;
   462 
   463 		if (pForward)
   464 		{
   465 			if (pHook->postCopy)
   466 			{
   467 				info.bDontBroadcast = bDontBroadcast;
   468 				info.pEvent = m_EventCopies.front();
   469 				info.pOwner = NULL;
   470 				hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
   471 
   472 				pForward->PushCell(hndl);
   473 			} else {
   474 				pForward->PushCell(BAD_HANDLE);
   475 			}
   476 
   477 			pForward->PushString(pHook->name.chars());
   478 			pForward->PushCell(bDontBroadcast);
   479 			pForward->Execute(NULL);
   480 
   481 			if (pHook->postCopy)
   482 			{
   483 				/* Free handle */
   484 				HandleSecurity sec(NULL, g_pCoreIdent);
   485 				handlesys->FreeHandle(hndl, &sec);
   486 
   487 				/* Free event structure */
   488 				gameevents->FreeEvent(info.pEvent);
   489 				m_EventCopies.pop();
   490 			}
   491 		}
   492 
   493 		/* Decrement reference count, check if a delayed delete is needed */
   494 		if (--pHook->refCount == 0)
   495 		{
   496 			assert(pHook->pPostHook == NULL);
   497 			assert(pHook->pPreHook == NULL);
   498 			m_EventHooks.remove(pHook->name.chars());
   499 			delete pHook;
   500 		}
   501 	}
   502 
   503 	m_EventStack.pop();
   504 
   505 	RETURN_META_VALUE(MRES_IGNORED, true);
   506 }