dvander@592: /**
ds@653: * vim: set ts=4 :
ds@1330: * =============================================================================
dvander@1231: * SourceMod
ds@3099: * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
ds@1330: * =============================================================================
ds@653: *
ds@1330: * This program is free software; you can redistribute it and/or modify it under
ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330: * Free Software Foundation.
dvander@1231: *
ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
ds@1330: * details.
dvander@1231: *
ds@1330: * You should have received a copy of the GNU General Public License along with
ds@1330: * this program. If not, see .
dvander@1231: *
ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in
ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants
ds@1330: * this exception to all derivative works. AlliedModders LLC defines further
ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330: * or .
ds@653: *
ds@653: * Version: $Id$
ds@653: */
dvander@592:
dvander@592: #include "UserMessages.h"
ds@765: #include "sm_stringutil.h"
dvander@592:
dvander@616: UserMessages g_UserMsgs;
dvander@592:
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: SH_DECL_HOOK3(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int, const char *);
ds@2400: #else
dvander@592: SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
ds@2400: #endif
dvander@592: SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
dvander@592:
dvander@616: UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
dvander@592: {
dvander@592: m_Names = sm_trie_create();
dvander@592: m_HookCount = 0;
dvander@592: m_InExec = false;
dvander@592: m_InHook = false;
dvander@592: m_CurFlags = 0;
dvander@592: m_CurId = INVALID_MESSAGE_ID;
dvander@592: }
dvander@592:
dvander@616: UserMessages::~UserMessages()
dvander@592: {
dvander@592: sm_trie_destroy(m_Names);
faluco@641:
faluco@641: CStack::iterator iter;
faluco@641: for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
faluco@641: {
faluco@641: delete (*iter);
faluco@641: }
faluco@641: m_FreeListeners.popall();
dvander@592: }
dvander@592:
ds@765: void UserMessages::OnSourceModStartup(bool late)
ds@765: {
ds@765: /* -1 means SourceMM was unable to get the user message list */
ds@765: m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
ds@765: }
ds@765:
dvander@616: void UserMessages::OnSourceModAllInitialized()
dvander@592: {
dvander@592: g_ShareSys.AddInterface(NULL, this);
dvander@592: }
dvander@592:
dvander@616: void UserMessages::OnSourceModAllShutdown()
dvander@592: {
dvander@592: if (m_HookCount)
dvander@592: {
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592: }
dvander@592: m_HookCount = 0;
dvander@592: }
dvander@592:
dvander@616: int UserMessages::GetMessageIndex(const char *msg)
dvander@592: {
dvander@592: int msgid;
ds@698:
dvander@592: if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast(&msgid)))
dvander@592: {
ds@765: if (m_FallbackSearch)
ds@765: {
ds@765: char msgbuf[64];
ds@765: int size;
ds@765: msgid = 0;
ds@765:
ds@765: while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
ds@765: {
ds@765: if (strcmp(msgbuf, msg) == 0)
ds@765: {
ds@765: sm_trie_insert(m_Names, msg, reinterpret_cast(msgid));
ds@765: return msgid;
ds@765: }
ds@765: msgid++;
ds@765: }
ds@765: }
ds@765:
ds@698: msgid = g_SMAPI->FindUserMessage(msg);
dvander@592:
ds@698: if (msgid != INVALID_MESSAGE_ID)
dvander@592: {
ds@698: sm_trie_insert(m_Names, msg, reinterpret_cast(msgid));
dvander@592: }
dvander@592: }
dvander@592:
dvander@592: return msgid;
dvander@592: }
dvander@592:
ds@765: bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
ds@765: {
ds@765: if (m_FallbackSearch)
ds@765: {
ds@765: int size;
ds@765: return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
ds@765: }
ds@766:
ds@765: const char *msg = g_SMAPI->GetUserMessage(msgid);
ds@765:
ds@765: if (msg)
ds@765: {
ds@765: strncopy(buffer, msg, maxlength);
ds@765: return true;
ds@765: }
ds@765:
ds@765: return false;
ds@765: }
ds@765:
dvander@1690: bf_write *UserMessages::StartMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags)
dvander@592: {
dvander@592: bf_write *buffer;
faluco@593:
faluco@593: if (m_InExec || m_InHook)
dvander@592: {
dvander@592: return NULL;
dvander@592: }
dvander@592: if (msg_id < 0 || msg_id >= 255)
dvander@592: {
dvander@592: return NULL;
dvander@592: }
dvander@592:
faluco@593: m_CellRecFilter.Initialize(players, playersNum);
dvander@592:
dvander@592: m_CurFlags = flags;
dvander@592: if (m_CurFlags & USERMSG_INITMSG)
dvander@592: {
faluco@593: m_CellRecFilter.SetToInit(true);
dvander@592: }
dvander@592: if (m_CurFlags & USERMSG_RELIABLE)
dvander@592: {
faluco@593: m_CellRecFilter.SetToReliable(true);
dvander@592: }
dvander@592:
dvander@592: m_InExec = true;
dvander@592:
faluco@1250: if (m_CurFlags & USERMSG_BLOCKHOOKS)
dvander@592: {
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: buffer = ENGINE_CALL(UserMessageBegin)(static_cast(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
ds@2400: #else
faluco@1250: buffer = ENGINE_CALL(UserMessageBegin)(static_cast(&m_CellRecFilter), msg_id);
ds@2400: #endif
faluco@1250: } else {
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: buffer = engine->UserMessageBegin(static_cast(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
ds@2400: #else
dvander@592: buffer = engine->UserMessageBegin(static_cast(&m_CellRecFilter), msg_id);
ds@2400: #endif
dvander@592: }
dvander@592:
dvander@592: return buffer;
dvander@592: }
dvander@592:
dvander@616: bool UserMessages::EndMessage()
dvander@592: {
dvander@592: if (!m_InExec)
dvander@592: {
dvander@592: return false;
dvander@592: }
dvander@592:
faluco@1250: if (m_CurFlags & USERMSG_BLOCKHOOKS)
dvander@592: {
faluco@1250: ENGINE_CALL(MessageEnd)();
faluco@1250: } else {
dvander@592: engine->MessageEnd();
dvander@592: }
dvander@592:
dvander@592: m_InExec = false;
dvander@592: m_CurFlags = 0;
faluco@593: m_CellRecFilter.Reset();
dvander@592:
dvander@592: return true;
dvander@592: }
dvander@592:
dvander@2636: bool UserMessages::HookUserMessage2(int msg_id,
dvander@2636: IUserMessageListener *pListener,
dvander@2636: bool intercept)
dvander@2636: {
dvander@2636: return InternalHook(msg_id, pListener, intercept, true);
dvander@2636: }
dvander@2636:
dvander@2636: bool UserMessages::UnhookUserMessage2(int msg_id,
dvander@2636: IUserMessageListener *pListener,
dvander@2636: bool intercept)
dvander@2636: {
dvander@2636: return InternalUnhook(msg_id, pListener, intercept, true);
dvander@2636: }
dvander@2636:
dvander@616: bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
dvander@592: {
dvander@2636: return InternalHook(msg_id, pListener, intercept, false);
dvander@2636: }
dvander@2636:
dvander@2636: bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
dvander@2636: {
dvander@2636: return InternalUnhook(msg_id, pListener, intercept, false);
dvander@2636: }
dvander@2636:
dvander@2636: bool UserMessages::InternalHook(int msg_id, IUserMessageListener *pListener, bool intercept, bool isNew)
dvander@2636: {
dvander@592: if (msg_id < 0 || msg_id >= 255)
dvander@592: {
dvander@592: return false;
dvander@592: }
dvander@592:
faluco@593: ListenerInfo *pInfo;
faluco@593: if (m_FreeListeners.empty())
faluco@593: {
faluco@593: pInfo = new ListenerInfo;
faluco@593: } else {
faluco@593: pInfo = m_FreeListeners.front();
faluco@593: m_FreeListeners.pop();
faluco@593: }
faluco@593:
faluco@593: pInfo->Callback = pListener;
faluco@593: pInfo->IsHooked = false;
faluco@593: pInfo->KillMe = false;
dvander@2636: pInfo->IsNew = isNew;
faluco@593:
dvander@592: if (!m_HookCount++)
dvander@592: {
dvander@616: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592: }
dvander@592:
dvander@592: if (intercept)
dvander@592: {
faluco@593: m_msgIntercepts[msg_id].push_back(pInfo);
dvander@592: } else {
faluco@593: m_msgHooks[msg_id].push_back(pInfo);
dvander@592: }
dvander@592:
dvander@592: return true;
dvander@592: }
dvander@592:
dvander@2636: bool UserMessages::InternalUnhook(int msg_id, IUserMessageListener *pListener, bool intercept, bool isNew)
dvander@2636: {
faluco@593: MsgList *pList;
dvander@592: MsgIter iter;
faluco@593: ListenerInfo *pInfo;
dvander@592: bool deleted = false;
dvander@592:
dvander@592: if (msg_id < 0 || msg_id >= 255)
dvander@592: {
dvander@592: return false;
dvander@592: }
dvander@592:
faluco@593: pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
faluco@593: for (iter=pList->begin(); iter!=pList->end(); iter++)
dvander@592: {
faluco@593: pInfo = (*iter);
dvander@2636: if (pInfo->Callback == pListener && pInfo->IsNew == isNew)
dvander@592: {
faluco@593: if (pInfo->IsHooked)
faluco@593: {
faluco@593: pInfo->KillMe = true;
faluco@593: return true;
faluco@593: }
faluco@593: pList->erase(iter);
dvander@592: deleted = true;
dvander@592: break;
dvander@592: }
dvander@592: }
dvander@592:
faluco@593: if (deleted)
faluco@593: {
faluco@593: _DecRefCounter();
faluco@593: }
faluco@593:
faluco@593: return deleted;
faluco@593: }
faluco@593:
dvander@616: void UserMessages::_DecRefCounter()
faluco@593: {
faluco@593: if (--m_HookCount == 0)
dvander@592: {
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
dvander@616: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
dvander@592: }
dvander@592: }
dvander@592:
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
ds@2400: #else
dvander@616: bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
ds@2400: #endif
dvander@592: {
dvander@592: bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
dvander@592: bool is_hook_empty = m_msgHooks[msg_type].empty();
dvander@592:
faluco@593: if ((is_intercept_empty && is_hook_empty)
faluco@1250: || (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
dvander@592: {
dvander@592: m_InHook = false;
dvander@592: RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592: }
dvander@592:
dvander@592: m_CurId = msg_type;
dvander@592: m_CurRecFilter = filter;
dvander@592: m_InHook = true;
faluco@597: m_BlockEndPost = false;
dvander@592:
dvander@592: if (!is_intercept_empty)
dvander@592: {
faluco@593: m_InterceptBuffer.Reset();
dvander@592: RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
dvander@592: }
dvander@592:
dvander@592: RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592: }
dvander@592:
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
ds@2400: #else
dvander@616: bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
ds@2400: #endif
dvander@592: {
dvander@592: if (!m_InHook)
dvander@592: {
dvander@592: RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592: }
dvander@592:
dvander@592: m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
dvander@592:
dvander@592: RETURN_META_VALUE(MRES_IGNORED, NULL);
dvander@592: }
dvander@592:
dvander@616: void UserMessages::OnMessageEnd_Post()
dvander@592: {
dvander@2636: if (!m_InHook)
dvander@592: {
dvander@592: RETURN_META(MRES_IGNORED);
dvander@592: }
dvander@592:
faluco@593: MsgList *pList;
dvander@592: MsgIter iter;
faluco@593: ListenerInfo *pInfo;
faluco@593:
pred@2577: m_InHook = false;
pred@2577:
faluco@593: pList = &m_msgIntercepts[m_CurId];
faluco@593: for (iter=pList->begin(); iter!=pList->end(); )
faluco@593: {
faluco@593: pInfo = (*iter);
dvander@2636: if (m_BlockEndPost && !pInfo->IsNew)
dvander@2636: {
dvander@2636: continue;
dvander@2636: }
faluco@593: pInfo->IsHooked = true;
faluco@593: pInfo->Callback->OnUserMessageSent(m_CurId);
dvander@2636: if (pInfo->IsNew)
dvander@2636: {
dvander@2636: pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
dvander@2636: }
dvander@592:
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: iter = pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: continue;
faluco@593: }
faluco@593:
faluco@593: pInfo->IsHooked = false;
faluco@593: iter++;
dvander@592: }
dvander@592:
faluco@593: pList = &m_msgHooks[m_CurId];
faluco@594: for (iter=pList->begin(); iter!=pList->end(); )
dvander@592: {
faluco@593: pInfo = (*iter);
dvander@2636: if (m_BlockEndPost && !pInfo->IsNew)
dvander@2636: {
dvander@2636: continue;
dvander@2636: }
faluco@594: pInfo->IsHooked = true;
faluco@593: pInfo->Callback->OnUserMessageSent(m_CurId);
dvander@2636: if (pInfo->IsNew)
dvander@2636: {
dvander@2636: pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
dvander@2636: }
faluco@593:
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: iter = pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: continue;
faluco@593: }
faluco@593:
faluco@593: pInfo->IsHooked = false;
faluco@593: iter++;
dvander@592: }
dvander@592: }
dvander@592:
dvander@616: void UserMessages::OnMessageEnd_Pre()
dvander@592: {
dvander@592: if (!m_InHook)
dvander@592: {
dvander@592: RETURN_META(MRES_IGNORED);
dvander@592: }
dvander@592:
faluco@593: MsgList *pList;
dvander@592: MsgIter iter;
faluco@593: ListenerInfo *pInfo;
faluco@593:
dvander@592: ResultType res;
dvander@592: bool intercepted = false;
dvander@592: bool handled = false;
dvander@592:
faluco@593: pList = &m_msgIntercepts[m_CurId];
faluco@593: for (iter=pList->begin(); iter!=pList->end(); )
dvander@592: {
faluco@593: pInfo = (*iter);
faluco@593: pInfo->IsHooked = true;
faluco@593: res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
faluco@593:
faluco@597: intercepted = true;
faluco@597:
faluco@593: switch (res)
dvander@592: {
faluco@593: case Pl_Stop:
faluco@593: {
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: goto supercede;
faluco@593: }
faluco@593: pInfo->IsHooked = false;
faluco@593: goto supercede;
faluco@593: }
faluco@593: case Pl_Handled:
faluco@593: {
faluco@593: handled = true;
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: iter = pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: continue;
faluco@593: }
faluco@593: break;
faluco@593: }
faluco@593: default:
faluco@593: {
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: iter = pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: continue;
faluco@593: }
faluco@597: break;
faluco@593: }
dvander@592: }
faluco@593: pInfo->IsHooked = false;
faluco@593: iter++;
dvander@592: }
dvander@592:
dvander@2734: if (!handled && intercepted)
dvander@592: {
faluco@593: bf_write *engine_bfw;
faluco@593:
ds@3099: #if SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId, g_SMAPI->GetUserMessage(m_CurId));
ds@2400: #else
dvander@592: engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
ds@2400: #endif
dvander@592: m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
dvander@592: engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
dvander@592: ENGINE_CALL(MessageEnd)();
dvander@592: }
dvander@592:
faluco@593: pList = &m_msgHooks[m_CurId];
faluco@593: for (iter=pList->begin(); iter!=pList->end(); )
dvander@592: {
faluco@593: pInfo = (*iter);
faluco@593: pInfo->IsHooked = true;
faluco@593: pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
faluco@593:
faluco@593: if (pInfo->KillMe)
faluco@593: {
faluco@593: iter = pList->erase(iter);
faluco@593: m_FreeListeners.push(pInfo);
faluco@593: _DecRefCounter();
faluco@593: continue;
faluco@593: }
faluco@593:
faluco@594: pInfo->IsHooked = false;
faluco@593: iter++;
dvander@592: }
dvander@592:
faluco@597: RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
dvander@592: supercede:
faluco@597: m_BlockEndPost = true;
dvander@592: RETURN_META(MRES_SUPERCEDE);
dvander@592: }