ds@1952: /** ds@1952: * vim: set ts=4 : ds@1952: * ============================================================================= ds@1952: * SourceMod ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1952: * ============================================================================= ds@1952: * ds@1952: * This program is free software; you can redistribute it and/or modify it under ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the ds@1952: * Free Software Foundation. ds@1952: * ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1952: * details. ds@1952: * ds@1952: * You should have received a copy of the GNU General Public License along with ds@1952: * this program. If not, see . ds@1952: * ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1952: * this exception to all derivative works. AlliedModders LLC defines further ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1952: * or . ds@1952: * ds@1952: * Version: $Id$ ds@1952: */ ds@1952: ds@1952: #include "EventManager.h" ds@1952: #include "ForwardSys.h" ds@1952: #include "HandleSys.h" ds@1952: #include "PluginSys.h" ds@1952: #include "sm_stringutil.h" ds@1952: ds@1952: EventManager g_EventManager; ds@1952: ds@1952: SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool); ds@1952: ds@1952: const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell}; ds@1952: typedef List EventHookList; ds@1952: ds@1952: EventManager::EventManager() : m_EventType(0) ds@1952: { ds@1952: /* Create an event lookup trie */ ds@1952: m_EventHooks = sm_trie_create(); ds@1952: } ds@1952: ds@1952: EventManager::~EventManager() ds@1952: { ds@1952: sm_trie_destroy(m_EventHooks); ds@1952: ds@1952: /* Free memory used by EventInfo structs if any */ ds@1952: CStack::iterator iter; ds@1952: for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++) ds@1952: { ds@1952: delete (*iter); ds@1952: } ds@1952: ds@1952: m_FreeEvents.popall(); ds@1952: } ds@1952: ds@1952: void EventManager::OnSourceModAllInitialized() ds@1952: { ds@1952: /* Add a hook for IGameEventManager2::FireEvent() */ ds@1952: SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false); ds@1952: SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true); ds@1952: ds@1952: HandleAccess sec; ds@1952: ds@1952: /* Handle access security for 'GameEvent' handle type */ ds@1952: sec.access[HandleAccess_Read] = 0; ds@1952: sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; ds@1952: sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; ds@1952: ds@1952: /* Create the 'GameEvent' handle type */ ds@1952: m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL); ds@1952: } ds@1952: ds@1952: void EventManager::OnSourceModShutdown() ds@1952: { ds@1952: /* Remove hook for IGameEventManager2::FireEvent() */ ds@1952: SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false); ds@1952: SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true); ds@1952: ds@1952: /* Remove the 'GameEvent' handle type */ ds@1952: g_HandleSys.RemoveType(m_EventType, g_pCoreIdent); ds@1952: ds@1952: /* Remove ourselves as listener for events */ ds@1952: gameevents->RemoveListener(this); ds@1952: } ds@1952: ds@1952: void EventManager::OnHandleDestroy(HandleType_t type, void *object) ds@1952: { ds@1952: EventInfo *pInfo = static_cast(object); ds@1952: ds@1952: /* Should only free event when created by a plugin */ ds@1952: if (pInfo->pOwner) ds@1952: { ds@1952: /* Free IGameEvent */ ds@1952: gameevents->FreeEvent(pInfo->pEvent); ds@1952: ds@1952: /* Add EventInfo struct to free event stack */ ds@1952: m_FreeEvents.push(pInfo); ds@1952: } ds@1952: } ds@1952: ds@1952: void EventManager::OnPluginUnloaded(IPlugin *plugin) ds@1952: { ds@1952: EventHookList *pHookList; ds@1952: EventHookList::iterator iter; ds@1952: EventHook *pHook; ds@1952: ds@1952: // If plugin has an event hook list... ds@1952: if (plugin->GetProperty("EventHooks", reinterpret_cast(&pHookList), true)) ds@1952: { ds@1952: for (iter = pHookList->begin(); iter != pHookList->end(); iter++) ds@1952: { ds@1952: pHook = (*iter); ds@1952: ds@1952: if (--pHook->refCount == 0) ds@1952: { ds@1952: if (pHook->pPreHook) ds@1952: { ds@1952: g_Forwards.ReleaseForward(pHook->pPreHook); ds@1952: } ds@1952: ds@1952: if (pHook->pPostHook) ds@1952: { ds@1952: g_Forwards.ReleaseForward(pHook->pPostHook); ds@1952: } ds@1952: ds@1952: delete pHook; ds@1952: } ds@1952: } ds@1952: ds@1952: delete pHookList; ds@1952: } ds@1952: } ds@1952: ds@1952: /* IGameEventListener2::FireGameEvent */ ds@1952: void EventManager::FireGameEvent(IGameEvent *pEvent) ds@1952: { ds@1952: /* Not going to do anything here. ds@1952: Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */ ds@1952: } ds@1952: ds@1952: EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode) ds@1952: { ds@1952: EventHook *pHook; ds@1952: ds@1952: /* If we aren't listening to this event... */ ds@1952: if (!gameevents->FindListener(this, name)) ds@1952: { ds@1952: /* Then add ourselves */ ds@1952: if (!gameevents->AddListener(this, name, true)) ds@1952: { ds@1952: /* If event doesn't exist... */ ds@1952: return EventHookErr_InvalidEvent; ds@1952: } ds@1952: } ds@1952: ds@1952: /* If a hook structure does not exist... */ ds@1952: if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook)) ds@1952: { ds@1952: EventHookList *pHookList; ds@1952: IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@1952: ds@1952: /* Check plugin for an existing EventHook list */ ds@1952: if (!plugin->GetProperty("EventHooks", (void **)&pHookList)) ds@1952: { ds@1952: pHookList = new EventHookList(); ds@1952: plugin->SetProperty("EventHooks", pHookList); ds@1952: } ds@1952: ds@1952: /* Create new GameEventHook structure */ ds@1952: pHook = new EventHook(); ds@1952: ds@1952: if (mode == EventHookMode_Pre) ds@1952: { ds@1952: /* Create forward for a pre hook */ ds@1952: pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS); ds@1952: /* Add to forward list */ ds@1952: pHook->pPreHook->AddFunction(pFunction); ds@1952: } else { ds@1952: /* Create forward for a post hook */ ds@1952: pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); ds@1952: /* Should we copy data from a pre hook to the post hook? */ ds@1952: pHook->postCopy = (mode == EventHookMode_Post); ds@1952: /* Add to forward list */ ds@1952: pHook->pPostHook->AddFunction(pFunction); ds@1952: } ds@1952: ds@1952: /* Increase reference count */ ds@1952: pHook->refCount++; ds@1952: ds@1952: /* Add hook structure to hook lists */ ds@1952: pHookList->push_back(pHook); ds@1952: sm_trie_insert(m_EventHooks, name, pHook); ds@1952: ds@1952: return EventHookErr_Okay; ds@1952: } ds@1952: ds@1952: /* Hook structure already exists at this point */ ds@1952: ds@1952: if (mode == EventHookMode_Pre) ds@1952: { ds@1952: /* Create pre hook forward if necessary */ ds@1952: if (!pHook->pPreHook) ds@1952: { ds@1952: pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS); ds@1952: } ds@1952: ds@1952: /* Add plugin function to forward list */ ds@1952: pHook->pPreHook->AddFunction(pFunction); ds@1952: } else { ds@1952: /* Create post hook forward if necessary */ ds@1952: if (!pHook->pPostHook) ds@1952: { ds@1952: pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); ds@1952: } ds@1952: ds@1952: /* If postCopy is false, then we may want to set it to true */ ds@1952: if (!pHook->postCopy) ds@1952: { ds@1952: pHook->postCopy = (mode == EventHookMode_Post); ds@1952: } ds@1952: ds@1952: /* Add plugin function to forward list */ ds@1952: pHook->pPostHook->AddFunction(pFunction); ds@1952: } ds@1952: ds@1952: /* Increase reference count */ ds@1952: pHook->refCount++; ds@1952: ds@1952: return EventHookErr_Okay; ds@1952: } ds@1952: ds@1952: EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode) ds@1952: { ds@1952: EventHook *pHook; ds@1952: IChangeableForward **pEventForward; ds@1952: ds@1952: /* If hook does not exist at all */ ds@1952: if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook)) ds@1952: { ds@1952: return EventHookErr_NotActive; ds@1952: } ds@1952: ds@1952: /* One forward to rule them all */ ds@1952: if (mode == EventHookMode_Pre) ds@1952: { ds@1952: pEventForward = &pHook->pPreHook; ds@1952: } else { ds@1952: pEventForward = &pHook->pPostHook; ds@1952: } ds@1952: ds@1952: /* Remove function from forward's list */ ds@1952: if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction)) ds@1952: { ds@1952: return EventHookErr_InvalidCallback; ds@1952: } ds@1952: ds@1952: /* If forward's list contains 0 functions now, free it */ ds@1952: if ((*pEventForward)->GetFunctionCount() == 0) ds@1952: { ds@1952: g_Forwards.ReleaseForward(*pEventForward); ds@1952: *pEventForward = NULL; ds@1952: } ds@1952: ds@1952: /* Decrement reference count */ ds@1952: if (--pHook->refCount == 0) ds@1952: { ds@1952: /* If reference count is now 0, free hook structure */ ds@1952: ds@1952: EventHookList *pHookList; ds@1952: IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@1952: ds@1952: /* Get plugin's event hook list */ ds@1952: if (!plugin->GetProperty("EventHooks", (void**)&pHookList)) ds@1952: { ds@1952: return EventHookErr_NotActive; ds@1952: } ds@1952: ds@1952: /* Make sure the event was actually being hooked */ ds@1952: if (pHookList->find(pHook) == pHookList->end()) ds@1952: { ds@1952: return EventHookErr_NotActive; ds@1952: } ds@1952: ds@1952: /* Remove current structure from plugin's list */ ds@1952: pHookList->remove(pHook); ds@1952: ds@1952: /* Delete entry in trie */ ds@1952: sm_trie_delete(m_EventHooks, name); ds@1952: ds@1952: /* And finally free structure memory */ ds@1952: delete pHook; ds@1952: } ds@1952: ds@1952: return EventHookErr_Okay; ds@1952: } ds@1952: ds@1952: EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force) ds@1952: { ds@1952: EventInfo *pInfo; ds@1952: IGameEvent *pEvent = gameevents->CreateEvent(name, force); ds@1952: ds@1952: if (pEvent) ds@1952: { ds@1952: if (m_FreeEvents.empty()) ds@1952: { ds@1952: pInfo = new EventInfo(); ds@1952: } else { ds@1952: pInfo = m_FreeEvents.front(); ds@1952: m_FreeEvents.pop(); ds@1952: } ds@1952: ds@1952: ds@1952: pInfo->pEvent = pEvent; ds@1952: pInfo->pOwner = pContext->GetIdentity(); ds@1952: ds@1952: return pInfo; ds@1952: } ds@1952: ds@1952: return NULL; ds@1952: } ds@1952: ds@1952: void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast) ds@1952: { ds@1952: /* Actually fire event now */ ds@1952: gameevents->FireEvent(pInfo->pEvent, bDontBroadcast); ds@1952: ds@1952: /* IGameEvent is free at this point, so no one owns this */ ds@1952: pInfo->pOwner = NULL; ds@1952: ds@1952: /* Add EventInfo struct to free event stack */ ds@1952: m_FreeEvents.push(pInfo); ds@1952: } ds@1952: ds@1952: void EventManager::CancelCreatedEvent(EventInfo *pInfo) ds@1952: { ds@1952: /* Free event from IGameEventManager2 */ ds@1952: gameevents->FreeEvent(pInfo->pEvent); ds@1952: ds@1952: /* IGameEvent is free at this point, so no one owns this */ ds@1952: pInfo->pOwner = NULL; ds@1952: ds@1952: /* Add EventInfo struct to free event stack */ ds@1952: m_FreeEvents.push(pInfo); ds@1952: } ds@1952: ds@1952: /* IGameEventManager2::FireEvent hook */ ds@1952: bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast) ds@1952: { ds@1952: EventHook *pHook; ds@1952: IChangeableForward *pForward; ds@1952: const char *name; ds@1952: cell_t res = Pl_Continue; ds@2106: ds@2106: /* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */ ds@2106: if (!pEvent) ds@2106: { ds@2106: RETURN_META_VALUE(MRES_IGNORED, false); ds@2106: } ds@1952: ds@1952: /* Get the event name, we're going to need this for passing to post hooks */ ds@1952: name = pEvent->GetName(); ds@1952: ds@1952: m_EventNames.push(name); ds@1952: ds@1952: if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast(&pHook))) ds@1952: { ds@1952: pForward = pHook->pPreHook; ds@1952: ds@1952: if (pForward) ds@1952: { ds@1952: EventInfo info = { pEvent, NULL }; ds@1952: ds@1952: Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); ds@1952: pForward->PushCell(hndl); ds@1952: pForward->PushString(name); ds@1952: pForward->PushCell(bDontBroadcast); ds@1952: pForward->Execute(&res, NULL); ds@1952: ds@1952: HandleSecurity sec(NULL, g_pCoreIdent); ds@1952: g_HandleSys.FreeHandle(hndl, &sec); ds@1952: } ds@1952: ds@1952: if (pHook->postCopy) ds@1952: { ds@1952: pHook->pEventCopy = gameevents->DuplicateEvent(pEvent); ds@1952: } ds@1952: ds@1952: if (res) ds@1952: { ds@1952: gameevents->FreeEvent(pEvent); ds@1952: RETURN_META_VALUE(MRES_SUPERCEDE, false); ds@1952: } ds@1952: } ds@1952: ds@1952: RETURN_META_VALUE(MRES_IGNORED, true); ds@1952: } ds@1952: ds@1952: /* IGameEventManager2::FireEvent post hook */ ds@1952: bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast) ds@1952: { ds@1952: IGameEvent *pEventCopy = NULL; ds@1952: EventHook *pHook; ds@1952: IChangeableForward *pForward; ds@1952: const char *name; ds@1952: Handle_t hndl = 0; ds@1952: ds@2106: /* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */ ds@2106: if (!pEvent) ds@2106: { ds@2106: RETURN_META_VALUE(MRES_IGNORED, false); ds@2106: } ds@2106: ds@1952: name = m_EventNames.front(); ds@1952: ds@1952: if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast(&pHook))) ds@1952: { ds@1952: pForward = pHook->pPostHook; ds@1952: ds@1952: if (pForward) ds@1952: { ds@1952: EventInfo info = { pHook->pEventCopy, NULL }; ds@1952: ds@1952: if (pHook->postCopy) ds@1952: { ds@1952: hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); ds@1952: ds@1952: pForward->PushCell(hndl); ds@1952: } else { ds@1952: pForward->PushCell(BAD_HANDLE); ds@1952: } ds@1952: ds@1952: pForward->PushString(name); ds@1952: pForward->PushCell(bDontBroadcast); ds@1952: pForward->Execute(NULL); ds@1952: ds@1952: if (pHook->postCopy) ds@1952: { ds@1952: /* Free handle */ ds@1952: HandleSecurity sec(NULL, g_pCoreIdent); ds@1952: g_HandleSys.FreeHandle(hndl, &sec); ds@1952: ds@1952: /* Free event structure */ ds@1952: gameevents->FreeEvent(pEventCopy); ds@1952: pHook->pEventCopy = NULL; ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: m_EventNames.pop(); ds@1952: ds@1952: RETURN_META_VALUE(MRES_IGNORED, true); ds@1952: }