1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/EventManager.cpp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,449 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "EventManager.h"
1.36 +#include "ForwardSys.h"
1.37 +#include "HandleSys.h"
1.38 +#include "PluginSys.h"
1.39 +#include "sm_stringutil.h"
1.40 +
1.41 +EventManager g_EventManager;
1.42 +
1.43 +SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool);
1.44 +
1.45 +const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell};
1.46 +typedef List<EventHook *> EventHookList;
1.47 +
1.48 +EventManager::EventManager() : m_EventType(0)
1.49 +{
1.50 + /* Create an event lookup trie */
1.51 + m_EventHooks = sm_trie_create();
1.52 +}
1.53 +
1.54 +EventManager::~EventManager()
1.55 +{
1.56 + sm_trie_destroy(m_EventHooks);
1.57 +
1.58 + /* Free memory used by EventInfo structs if any */
1.59 + CStack<EventInfo *>::iterator iter;
1.60 + for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++)
1.61 + {
1.62 + delete (*iter);
1.63 + }
1.64 +
1.65 + m_FreeEvents.popall();
1.66 +}
1.67 +
1.68 +void EventManager::OnSourceModAllInitialized()
1.69 +{
1.70 + /* Add a hook for IGameEventManager2::FireEvent() */
1.71 + SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
1.72 + SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
1.73 +
1.74 + HandleAccess sec;
1.75 +
1.76 + /* Handle access security for 'GameEvent' handle type */
1.77 + sec.access[HandleAccess_Read] = 0;
1.78 + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.79 + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
1.80 +
1.81 + /* Create the 'GameEvent' handle type */
1.82 + m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL);
1.83 +}
1.84 +
1.85 +void EventManager::OnSourceModShutdown()
1.86 +{
1.87 + /* Remove hook for IGameEventManager2::FireEvent() */
1.88 + SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
1.89 + SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
1.90 +
1.91 + /* Remove the 'GameEvent' handle type */
1.92 + g_HandleSys.RemoveType(m_EventType, g_pCoreIdent);
1.93 +
1.94 + /* Remove ourselves as listener for events */
1.95 + gameevents->RemoveListener(this);
1.96 +}
1.97 +
1.98 +void EventManager::OnHandleDestroy(HandleType_t type, void *object)
1.99 +{
1.100 + EventInfo *pInfo = static_cast<EventInfo *>(object);
1.101 +
1.102 + /* Should only free event when created by a plugin */
1.103 + if (pInfo->pOwner)
1.104 + {
1.105 + /* Free IGameEvent */
1.106 + gameevents->FreeEvent(pInfo->pEvent);
1.107 +
1.108 + /* Add EventInfo struct to free event stack */
1.109 + m_FreeEvents.push(pInfo);
1.110 + }
1.111 +}
1.112 +
1.113 +void EventManager::OnPluginUnloaded(IPlugin *plugin)
1.114 +{
1.115 + EventHookList *pHookList;
1.116 + EventHookList::iterator iter;
1.117 + EventHook *pHook;
1.118 +
1.119 + // If plugin has an event hook list...
1.120 + if (plugin->GetProperty("EventHooks", reinterpret_cast<void **>(&pHookList), true))
1.121 + {
1.122 + for (iter = pHookList->begin(); iter != pHookList->end(); iter++)
1.123 + {
1.124 + pHook = (*iter);
1.125 +
1.126 + if (--pHook->refCount == 0)
1.127 + {
1.128 + if (pHook->pPreHook)
1.129 + {
1.130 + g_Forwards.ReleaseForward(pHook->pPreHook);
1.131 + }
1.132 +
1.133 + if (pHook->pPostHook)
1.134 + {
1.135 + g_Forwards.ReleaseForward(pHook->pPostHook);
1.136 + }
1.137 +
1.138 + delete pHook;
1.139 + }
1.140 + }
1.141 +
1.142 + delete pHookList;
1.143 + }
1.144 +}
1.145 +
1.146 +/* IGameEventListener2::FireGameEvent */
1.147 +void EventManager::FireGameEvent(IGameEvent *pEvent)
1.148 +{
1.149 + /* Not going to do anything here.
1.150 + Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */
1.151 +}
1.152 +
1.153 +EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
1.154 +{
1.155 + EventHook *pHook;
1.156 +
1.157 + /* If we aren't listening to this event... */
1.158 + if (!gameevents->FindListener(this, name))
1.159 + {
1.160 + /* Then add ourselves */
1.161 + if (!gameevents->AddListener(this, name, true))
1.162 + {
1.163 + /* If event doesn't exist... */
1.164 + return EventHookErr_InvalidEvent;
1.165 + }
1.166 + }
1.167 +
1.168 + /* If a hook structure does not exist... */
1.169 + if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
1.170 + {
1.171 + EventHookList *pHookList;
1.172 + IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.173 +
1.174 + /* Check plugin for an existing EventHook list */
1.175 + if (!plugin->GetProperty("EventHooks", (void **)&pHookList))
1.176 + {
1.177 + pHookList = new EventHookList();
1.178 + plugin->SetProperty("EventHooks", pHookList);
1.179 + }
1.180 +
1.181 + /* Create new GameEventHook structure */
1.182 + pHook = new EventHook();
1.183 +
1.184 + if (mode == EventHookMode_Pre)
1.185 + {
1.186 + /* Create forward for a pre hook */
1.187 + pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS);
1.188 + /* Add to forward list */
1.189 + pHook->pPreHook->AddFunction(pFunction);
1.190 + } else {
1.191 + /* Create forward for a post hook */
1.192 + pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
1.193 + /* Should we copy data from a pre hook to the post hook? */
1.194 + pHook->postCopy = (mode == EventHookMode_Post);
1.195 + /* Add to forward list */
1.196 + pHook->pPostHook->AddFunction(pFunction);
1.197 + }
1.198 +
1.199 + /* Increase reference count */
1.200 + pHook->refCount++;
1.201 +
1.202 + /* Add hook structure to hook lists */
1.203 + pHookList->push_back(pHook);
1.204 + sm_trie_insert(m_EventHooks, name, pHook);
1.205 +
1.206 + return EventHookErr_Okay;
1.207 + }
1.208 +
1.209 + /* Hook structure already exists at this point */
1.210 +
1.211 + if (mode == EventHookMode_Pre)
1.212 + {
1.213 + /* Create pre hook forward if necessary */
1.214 + if (!pHook->pPreHook)
1.215 + {
1.216 + pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS);
1.217 + }
1.218 +
1.219 + /* Add plugin function to forward list */
1.220 + pHook->pPreHook->AddFunction(pFunction);
1.221 + } else {
1.222 + /* Create post hook forward if necessary */
1.223 + if (!pHook->pPostHook)
1.224 + {
1.225 + pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
1.226 + }
1.227 +
1.228 + /* If postCopy is false, then we may want to set it to true */
1.229 + if (!pHook->postCopy)
1.230 + {
1.231 + pHook->postCopy = (mode == EventHookMode_Post);
1.232 + }
1.233 +
1.234 + /* Add plugin function to forward list */
1.235 + pHook->pPostHook->AddFunction(pFunction);
1.236 + }
1.237 +
1.238 + /* Increase reference count */
1.239 + pHook->refCount++;
1.240 +
1.241 + return EventHookErr_Okay;
1.242 +}
1.243 +
1.244 +EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
1.245 +{
1.246 + EventHook *pHook;
1.247 + IChangeableForward **pEventForward;
1.248 +
1.249 + /* If hook does not exist at all */
1.250 + if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
1.251 + {
1.252 + return EventHookErr_NotActive;
1.253 + }
1.254 +
1.255 + /* One forward to rule them all */
1.256 + if (mode == EventHookMode_Pre)
1.257 + {
1.258 + pEventForward = &pHook->pPreHook;
1.259 + } else {
1.260 + pEventForward = &pHook->pPostHook;
1.261 + }
1.262 +
1.263 + /* Remove function from forward's list */
1.264 + if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction))
1.265 + {
1.266 + return EventHookErr_InvalidCallback;
1.267 + }
1.268 +
1.269 + /* If forward's list contains 0 functions now, free it */
1.270 + if ((*pEventForward)->GetFunctionCount() == 0)
1.271 + {
1.272 + g_Forwards.ReleaseForward(*pEventForward);
1.273 + *pEventForward = NULL;
1.274 + }
1.275 +
1.276 + /* Decrement reference count */
1.277 + if (--pHook->refCount == 0)
1.278 + {
1.279 + /* If reference count is now 0, free hook structure */
1.280 +
1.281 + EventHookList *pHookList;
1.282 + IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.283 +
1.284 + /* Get plugin's event hook list */
1.285 + if (!plugin->GetProperty("EventHooks", (void**)&pHookList))
1.286 + {
1.287 + return EventHookErr_NotActive;
1.288 + }
1.289 +
1.290 + /* Make sure the event was actually being hooked */
1.291 + if (pHookList->find(pHook) == pHookList->end())
1.292 + {
1.293 + return EventHookErr_NotActive;
1.294 + }
1.295 +
1.296 + /* Remove current structure from plugin's list */
1.297 + pHookList->remove(pHook);
1.298 +
1.299 + /* Delete entry in trie */
1.300 + sm_trie_delete(m_EventHooks, name);
1.301 +
1.302 + /* And finally free structure memory */
1.303 + delete pHook;
1.304 + }
1.305 +
1.306 + return EventHookErr_Okay;
1.307 +}
1.308 +
1.309 +EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force)
1.310 +{
1.311 + EventInfo *pInfo;
1.312 + IGameEvent *pEvent = gameevents->CreateEvent(name, force);
1.313 +
1.314 + if (pEvent)
1.315 + {
1.316 + if (m_FreeEvents.empty())
1.317 + {
1.318 + pInfo = new EventInfo();
1.319 + } else {
1.320 + pInfo = m_FreeEvents.front();
1.321 + m_FreeEvents.pop();
1.322 + }
1.323 +
1.324 +
1.325 + pInfo->pEvent = pEvent;
1.326 + pInfo->pOwner = pContext->GetIdentity();
1.327 +
1.328 + return pInfo;
1.329 + }
1.330 +
1.331 + return NULL;
1.332 +}
1.333 +
1.334 +void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
1.335 +{
1.336 + /* Actually fire event now */
1.337 + gameevents->FireEvent(pInfo->pEvent, bDontBroadcast);
1.338 +
1.339 + /* IGameEvent is free at this point, so no one owns this */
1.340 + pInfo->pOwner = NULL;
1.341 +
1.342 + /* Add EventInfo struct to free event stack */
1.343 + m_FreeEvents.push(pInfo);
1.344 +}
1.345 +
1.346 +void EventManager::CancelCreatedEvent(EventInfo *pInfo)
1.347 +{
1.348 + /* Free event from IGameEventManager2 */
1.349 + gameevents->FreeEvent(pInfo->pEvent);
1.350 +
1.351 + /* IGameEvent is free at this point, so no one owns this */
1.352 + pInfo->pOwner = NULL;
1.353 +
1.354 + /* Add EventInfo struct to free event stack */
1.355 + m_FreeEvents.push(pInfo);
1.356 +}
1.357 +
1.358 +/* IGameEventManager2::FireEvent hook */
1.359 +bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
1.360 +{
1.361 + EventHook *pHook;
1.362 + IChangeableForward *pForward;
1.363 + const char *name;
1.364 + cell_t res = Pl_Continue;
1.365 +
1.366 + /* Get the event name, we're going to need this for passing to post hooks */
1.367 + name = pEvent->GetName();
1.368 +
1.369 + m_EventNames.push(name);
1.370 +
1.371 + if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
1.372 + {
1.373 + pForward = pHook->pPreHook;
1.374 +
1.375 + if (pForward)
1.376 + {
1.377 + EventInfo info = { pEvent, NULL };
1.378 +
1.379 + Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
1.380 + pForward->PushCell(hndl);
1.381 + pForward->PushString(name);
1.382 + pForward->PushCell(bDontBroadcast);
1.383 + pForward->Execute(&res, NULL);
1.384 +
1.385 + HandleSecurity sec(NULL, g_pCoreIdent);
1.386 + g_HandleSys.FreeHandle(hndl, &sec);
1.387 + }
1.388 +
1.389 + if (pHook->postCopy)
1.390 + {
1.391 + pHook->pEventCopy = gameevents->DuplicateEvent(pEvent);
1.392 + }
1.393 +
1.394 + if (res)
1.395 + {
1.396 + gameevents->FreeEvent(pEvent);
1.397 + RETURN_META_VALUE(MRES_SUPERCEDE, false);
1.398 + }
1.399 + }
1.400 +
1.401 + RETURN_META_VALUE(MRES_IGNORED, true);
1.402 +}
1.403 +
1.404 +/* IGameEventManager2::FireEvent post hook */
1.405 +bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
1.406 +{
1.407 + IGameEvent *pEventCopy = NULL;
1.408 + EventHook *pHook;
1.409 + IChangeableForward *pForward;
1.410 + const char *name;
1.411 + Handle_t hndl = 0;
1.412 +
1.413 + name = m_EventNames.front();
1.414 +
1.415 + if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
1.416 + {
1.417 + pForward = pHook->pPostHook;
1.418 +
1.419 + if (pForward)
1.420 + {
1.421 + EventInfo info = { pHook->pEventCopy, NULL };
1.422 +
1.423 + if (pHook->postCopy)
1.424 + {
1.425 + hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
1.426 +
1.427 + pForward->PushCell(hndl);
1.428 + } else {
1.429 + pForward->PushCell(BAD_HANDLE);
1.430 + }
1.431 +
1.432 + pForward->PushString(name);
1.433 + pForward->PushCell(bDontBroadcast);
1.434 + pForward->Execute(NULL);
1.435 +
1.436 + if (pHook->postCopy)
1.437 + {
1.438 + /* Free handle */
1.439 + HandleSecurity sec(NULL, g_pCoreIdent);
1.440 + g_HandleSys.FreeHandle(hndl, &sec);
1.441 +
1.442 + /* Free event structure */
1.443 + gameevents->FreeEvent(pEventCopy);
1.444 + pHook->pEventCopy = NULL;
1.445 + }
1.446 + }
1.447 + }
1.448 +
1.449 + m_EventNames.pop();
1.450 +
1.451 + RETURN_META_VALUE(MRES_IGNORED, true);
1.452 +}