ds@576: /** ds@653: * vim: set ts=4 : ds@653: * =============================================================== ds@653: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. ds@653: * =============================================================== ds@653: * ds@653: * This file is not open source and may not be copied without explicit ds@653: * written permission of AlliedModders LLC. This file may not be redistributed ds@653: * in whole or significant part. ds@653: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php ds@653: * ds@653: * Version: $Id$ ds@653: */ ds@576: ds@576: #include "EventManager.h" ds@576: #include "ForwardSys.h" ds@576: #include "HandleSys.h" ds@576: #include "PluginSys.h" ds@576: #include "sm_stringutil.h" ds@576: ds@576: EventManager g_EventManager; ds@576: ds@576: SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool); ds@576: ds@582: const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell}; ds@576: typedef List EventHookList; ds@576: ds@712: EventManager::EventManager() : m_EventType(0) ds@576: { ds@576: /* Create an event lookup trie */ ds@576: m_EventHooks = sm_trie_create(); ds@576: } ds@576: ds@576: EventManager::~EventManager() ds@576: { ds@576: sm_trie_destroy(m_EventHooks); ds@577: ds@577: /* Free memory used by EventInfo structs if any */ ds@577: CStack::iterator iter; ds@577: for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++) ds@577: { ds@577: delete (*iter); ds@577: } ds@577: ds@577: m_FreeEvents.popall(); ds@576: } ds@576: ds@576: void EventManager::OnSourceModAllInitialized() ds@576: { ds@576: /* Add a hook for IGameEventManager2::FireEvent() */ ds@576: SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false); ds@576: SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true); ds@576: ds@576: HandleAccess sec; ds@576: ds@576: /* Handle access security for 'GameEvent' handle type */ ds@576: sec.access[HandleAccess_Read] = 0; ds@576: sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; ds@576: sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; ds@576: ds@576: /* Create the 'GameEvent' handle type */ ds@576: m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL); ds@576: } ds@576: ds@576: void EventManager::OnSourceModShutdown() ds@576: { ds@576: /* Remove hook for IGameEventManager2::FireEvent() */ ds@576: SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false); ds@576: SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true); ds@576: ds@576: /* Remove the 'GameEvent' handle type */ ds@576: g_HandleSys.RemoveType(m_EventType, g_pCoreIdent); ds@576: ds@576: /* Remove ourselves as listener for events */ ds@576: gameevents->RemoveListener(this); ds@576: } ds@576: ds@576: void EventManager::OnHandleDestroy(HandleType_t type, void *object) ds@576: { ds@577: EventInfo *pInfo = static_cast(object); ds@576: ds@577: /* Should only free event when created by a plugin */ ds@577: if (pInfo->pOwner) ds@577: { ds@577: /* Free IGameEvent */ ds@577: gameevents->FreeEvent(pInfo->pEvent); ds@577: ds@577: /* Add EventInfo struct to free event stack */ ds@577: m_FreeEvents.push(pInfo); ds@576: } ds@576: } ds@576: ds@576: void EventManager::OnPluginUnloaded(IPlugin *plugin) ds@576: { ds@576: EventHookList *pHookList; ds@582: EventHookList::iterator iter; ds@576: EventHook *pHook; ds@576: ds@576: // If plugin has an event hook list... ds@576: if (plugin->GetProperty("EventHooks", reinterpret_cast(&pHookList), true)) ds@576: { ds@582: for (iter = pHookList->begin(); iter != pHookList->end(); iter++) ds@576: { ds@576: pHook = (*iter); ds@576: ds@576: if (--pHook->refCount == 0) ds@576: { ds@576: if (pHook->pPreHook) ds@576: { ds@576: g_Forwards.ReleaseForward(pHook->pPreHook); ds@576: } ds@576: ds@576: if (pHook->pPostHook) ds@576: { ds@576: g_Forwards.ReleaseForward(pHook->pPostHook); ds@576: } ds@576: ds@576: delete pHook; ds@576: } ds@576: } ds@576: ds@576: delete pHookList; ds@576: } ds@576: } ds@576: ds@576: /* IGameEventListener2::FireGameEvent */ ds@576: void EventManager::FireGameEvent(IGameEvent *pEvent) ds@576: { ds@576: /* Not going to do anything here. ds@576: Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */ ds@576: } ds@576: ds@576: EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode) ds@576: { ds@576: EventHook *pHook; ds@576: ds@576: /* If we aren't listening to this event... */ ds@576: if (!gameevents->FindListener(this, name)) ds@576: { ds@576: /* Then add ourselves */ ds@576: if (!gameevents->AddListener(this, name, true)) ds@576: { ds@576: /* If event doesn't exist... */ ds@576: return EventHookErr_InvalidEvent; ds@576: } ds@576: } ds@576: ds@576: /* If a hook structure does not exist... */ ds@576: if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook)) ds@576: { ds@576: EventHookList *pHookList; ds@576: IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@576: ds@576: /* Check plugin for an existing EventHook list */ ds@576: if (!plugin->GetProperty("EventHooks", (void **)&pHookList)) ds@576: { ds@576: pHookList = new EventHookList(); ds@576: plugin->SetProperty("EventHooks", pHookList); ds@576: } ds@576: ds@576: /* Create new GameEventHook structure */ ds@576: pHook = new EventHook(); ds@576: ds@576: if (mode == EventHookMode_Pre) ds@576: { ds@576: /* Create forward for a pre hook */ ds@576: pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS); ds@576: /* Add to forward list */ ds@576: pHook->pPreHook->AddFunction(pFunction); ds@576: } else { ds@576: /* Create forward for a post hook */ ds@576: pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); ds@576: /* Should we copy data from a pre hook to the post hook? */ ds@576: pHook->postCopy = (mode == EventHookMode_Post); ds@576: /* Add to forward list */ ds@576: pHook->pPostHook->AddFunction(pFunction); ds@576: } ds@576: ds@576: /* Increase reference count */ ds@576: pHook->refCount++; ds@576: ds@576: /* Add hook structure to hook lists */ ds@576: pHookList->push_back(pHook); ds@576: sm_trie_insert(m_EventHooks, name, pHook); ds@576: ds@576: return EventHookErr_Okay; ds@576: } ds@576: ds@576: /* Hook structure already exists at this point */ ds@576: ds@576: if (mode == EventHookMode_Pre) ds@576: { ds@576: /* Create pre hook forward if necessary */ ds@576: if (!pHook->pPreHook) ds@576: { ds@576: pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS); ds@576: } ds@576: ds@576: /* Add plugin function to forward list */ ds@576: pHook->pPreHook->AddFunction(pFunction); ds@576: } else { ds@576: /* Create post hook forward if necessary */ ds@576: if (!pHook->pPostHook) ds@576: { ds@576: pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); ds@576: } ds@576: ds@576: /* If postCopy is false, then we may want to set it to true */ ds@576: if (!pHook->postCopy) ds@576: { ds@576: pHook->postCopy = (mode == EventHookMode_Post); ds@576: } ds@576: ds@576: /* Add plugin function to forward list */ ds@576: pHook->pPostHook->AddFunction(pFunction); ds@576: } ds@576: ds@576: /* Increase reference count */ ds@576: pHook->refCount++; ds@576: ds@576: return EventHookErr_Okay; ds@576: } ds@576: ds@576: EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode) ds@576: { ds@576: EventHook *pHook; ds@576: IChangeableForward **pEventForward; ds@576: ds@576: /* If hook does not exist at all */ ds@576: if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook)) ds@576: { ds@576: return EventHookErr_NotActive; ds@576: } ds@576: ds@576: /* One forward to rule them all */ ds@576: if (mode == EventHookMode_Pre) ds@576: { ds@576: pEventForward = &pHook->pPreHook; ds@576: } else { ds@576: pEventForward = &pHook->pPostHook; ds@576: } ds@576: ds@576: /* Remove function from forward's list */ ds@576: if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction)) ds@576: { ds@576: return EventHookErr_InvalidCallback; ds@576: } ds@576: ds@576: /* If forward's list contains 0 functions now, free it */ ds@576: if ((*pEventForward)->GetFunctionCount() == 0) ds@576: { ds@576: g_Forwards.ReleaseForward(*pEventForward); ds@576: *pEventForward = NULL; ds@576: } ds@576: ds@576: /* Decrement reference count */ ds@576: if (--pHook->refCount == 0) ds@576: { ds@576: /* If reference count is now 0, free hook structure */ ds@576: ds@576: EventHookList *pHookList; ds@576: IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@576: ds@576: /* Get plugin's event hook list */ ds@576: plugin->GetProperty("EventHooks", (void**)&pHookList); ds@576: ds@576: /* Remove current structure from plugin's list */ ds@576: pHookList->remove(pHook); ds@576: ds@576: /* Delete entry in trie */ ds@576: sm_trie_delete(m_EventHooks, name); ds@576: ds@576: /* And finally free structure memory */ ds@576: delete pHook; ds@576: } ds@576: ds@576: return EventHookErr_Okay; ds@576: } ds@576: ds@713: EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force) ds@577: { ds@577: EventInfo *pInfo; ds@713: IGameEvent *pEvent = gameevents->CreateEvent(name, force); ds@577: ds@577: if (pEvent) ds@577: { ds@577: if (m_FreeEvents.empty()) ds@577: { ds@577: pInfo = new EventInfo(); ds@577: } else { ds@577: pInfo = m_FreeEvents.front(); ds@577: m_FreeEvents.pop(); ds@577: } ds@577: ds@577: ds@577: pInfo->pEvent = pEvent; ds@577: pInfo->pOwner = pContext->GetIdentity(); ds@577: ds@577: return pInfo; ds@577: } ds@577: ds@577: return NULL; ds@577: } ds@577: ds@712: void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast) ds@577: { ds@577: /* Actually fire event now */ ds@577: gameevents->FireEvent(pInfo->pEvent, bDontBroadcast); ds@577: ds@577: /* IGameEvent is free at this point, so no one owns this */ ds@577: pInfo->pOwner = NULL; ds@577: ds@577: /* Add EventInfo struct to free event stack */ ds@577: m_FreeEvents.push(pInfo); ds@577: } ds@577: ds@687: void EventManager::CancelCreatedEvent(EventInfo *pInfo) ds@687: { ds@687: /* Free event from IGameEventManager2 */ ds@687: gameevents->FreeEvent(pInfo->pEvent); ds@687: ds@687: /* IGameEvent is free at this point, so no one owns this */ ds@687: pInfo->pOwner = NULL; ds@687: ds@687: /* Add EventInfo struct to free event stack */ ds@687: m_FreeEvents.push(pInfo); ds@687: } ds@687: ds@576: /* IGameEventManager2::FireEvent hook */ ds@576: bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast) ds@576: { ds@576: EventHook *pHook; ds@576: IChangeableForward *pForward; ds@684: const char *name; ds@576: cell_t res = Pl_Continue; ds@576: ds@576: /* Get the event name, we're going to need this for passing to post hooks */ ds@684: name = pEvent->GetName(); ds@576: ds@684: m_EventNames.push(name); ds@684: ds@684: if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast(&pHook))) ds@576: { ds@576: pForward = pHook->pPreHook; ds@576: ds@576: if (pForward) ds@576: { ds@792: EventInfo info = { pEvent, NULL }; ds@576: ds@576: Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); ds@576: pForward->PushCell(hndl); ds@684: pForward->PushString(name); ds@576: pForward->PushCell(bDontBroadcast); ds@576: pForward->Execute(&res, NULL); ds@576: dvander@857: HandleSecurity sec(NULL, g_pCoreIdent); ds@576: g_HandleSys.FreeHandle(hndl, &sec); ds@576: } ds@576: ds@576: if (pHook->postCopy) ds@576: { ds@684: pHook->pEventCopy = gameevents->DuplicateEvent(pEvent); ds@576: } ds@576: ds@576: if (res) ds@576: { ds@576: gameevents->FreeEvent(pEvent); ds@576: RETURN_META_VALUE(MRES_SUPERCEDE, false); ds@576: } ds@576: } ds@576: ds@576: RETURN_META_VALUE(MRES_IGNORED, true); ds@576: } ds@576: ds@576: /* IGameEventManager2::FireEvent post hook */ ds@576: bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast) ds@576: { ds@690: IGameEvent *pEventCopy = NULL; ds@576: EventHook *pHook; ds@576: IChangeableForward *pForward; ds@684: const char *name; dvander@603: Handle_t hndl = 0; ds@576: ds@684: name = m_EventNames.front(); ds@684: ds@684: if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast(&pHook))) ds@576: { ds@576: pForward = pHook->pPostHook; ds@576: ds@576: if (pForward) ds@576: { ds@792: EventInfo info = { pHook->pEventCopy, NULL }; ds@792: ds@576: if (pHook->postCopy) ds@576: { ds@576: hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); ds@684: ds@576: pForward->PushCell(hndl); ds@576: } else { ds@576: pForward->PushCell(BAD_HANDLE); ds@576: } ds@576: ds@684: pForward->PushString(name); ds@576: pForward->PushCell(bDontBroadcast); ds@576: pForward->Execute(NULL); ds@576: ds@576: if (pHook->postCopy) ds@576: { ds@576: /* Free handle */ dvander@857: HandleSecurity sec(NULL, g_pCoreIdent); ds@576: g_HandleSys.FreeHandle(hndl, &sec); ds@576: ds@576: /* Free event structure */ ds@684: gameevents->FreeEvent(pEventCopy); ds@684: pHook->pEventCopy = NULL; ds@576: } ds@576: } ds@576: } ds@576: ds@684: m_EventNames.pop(); ds@684: ds@576: RETURN_META_VALUE(MRES_IGNORED, true); ds@576: }