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 "UserMessages.h"
ds@1952: #include "sm_stringutil.h"
ds@1952:
ds@1952: UserMessages g_UserMsgs;
ds@1952:
ds@1952: SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
ds@1952: SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
ds@1952:
ds@1952: UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
ds@1952: {
ds@1952: m_Names = sm_trie_create();
ds@1952: m_HookCount = 0;
ds@1952: m_InExec = false;
ds@1952: m_InHook = false;
ds@1952: m_CurFlags = 0;
ds@1952: m_CurId = INVALID_MESSAGE_ID;
ds@1952: }
ds@1952:
ds@1952: UserMessages::~UserMessages()
ds@1952: {
ds@1952: sm_trie_destroy(m_Names);
ds@1952:
ds@1952: CStack::iterator iter;
ds@1952: for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
ds@1952: {
ds@1952: delete (*iter);
ds@1952: }
ds@1952: m_FreeListeners.popall();
ds@1952: }
ds@1952:
ds@1952: void UserMessages::OnSourceModStartup(bool late)
ds@1952: {
ds@1952: /* -1 means SourceMM was unable to get the user message list */
ds@1952: m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
ds@1952: }
ds@1952:
ds@1952: void UserMessages::OnSourceModAllInitialized()
ds@1952: {
ds@1952: g_ShareSys.AddInterface(NULL, this);
ds@1952: }
ds@1952:
ds@1952: void UserMessages::OnSourceModAllShutdown()
ds@1952: {
ds@1952: if (m_HookCount)
ds@1952: {
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
ds@1952: }
ds@1952: m_HookCount = 0;
ds@1952: }
ds@1952:
ds@1952: int UserMessages::GetMessageIndex(const char *msg)
ds@1952: {
ds@1952: int msgid;
ds@1952:
ds@1952: if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast(&msgid)))
ds@1952: {
ds@1952: if (m_FallbackSearch)
ds@1952: {
ds@1952: char msgbuf[64];
ds@1952: int size;
ds@1952: msgid = 0;
ds@1952:
ds@1952: while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
ds@1952: {
ds@1952: if (strcmp(msgbuf, msg) == 0)
ds@1952: {
ds@1952: sm_trie_insert(m_Names, msg, reinterpret_cast(msgid));
ds@1952: return msgid;
ds@1952: }
ds@1952: msgid++;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: msgid = g_SMAPI->FindUserMessage(msg);
ds@1952:
ds@1952: if (msgid != INVALID_MESSAGE_ID)
ds@1952: {
ds@1952: sm_trie_insert(m_Names, msg, reinterpret_cast(msgid));
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: return msgid;
ds@1952: }
ds@1952:
ds@1952: bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
ds@1952: {
ds@1952: if (m_FallbackSearch)
ds@1952: {
ds@1952: int size;
ds@1952: return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
ds@1952: }
ds@1952:
ds@1952: const char *msg = g_SMAPI->GetUserMessage(msgid);
ds@1952:
ds@1952: if (msg)
ds@1952: {
ds@1952: strncopy(buffer, msg, maxlength);
ds@1952: return true;
ds@1952: }
ds@1952:
ds@1952: return false;
ds@1952: }
ds@1952:
ds@1952: bf_write *UserMessages::StartMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags)
ds@1952: {
ds@1952: bf_write *buffer;
ds@1952:
ds@1952: if (m_InExec || m_InHook)
ds@1952: {
ds@1952: return NULL;
ds@1952: }
ds@1952: if (msg_id < 0 || msg_id >= 255)
ds@1952: {
ds@1952: return NULL;
ds@1952: }
ds@1952:
ds@1952: m_CellRecFilter.Initialize(players, playersNum);
ds@1952:
ds@1952: m_CurFlags = flags;
ds@1952: if (m_CurFlags & USERMSG_INITMSG)
ds@1952: {
ds@1952: m_CellRecFilter.SetToInit(true);
ds@1952: }
ds@1952: if (m_CurFlags & USERMSG_RELIABLE)
ds@1952: {
ds@1952: m_CellRecFilter.SetToReliable(true);
ds@1952: }
ds@1952:
ds@1952: m_InExec = true;
ds@1952:
ds@1952: if (m_CurFlags & USERMSG_BLOCKHOOKS)
ds@1952: {
ds@1952: buffer = ENGINE_CALL(UserMessageBegin)(static_cast(&m_CellRecFilter), msg_id);
ds@1952: } else {
ds@1952: buffer = engine->UserMessageBegin(static_cast(&m_CellRecFilter), msg_id);
ds@1952: }
ds@1952:
ds@1952: return buffer;
ds@1952: }
ds@1952:
ds@1952: bool UserMessages::EndMessage()
ds@1952: {
ds@1952: if (!m_InExec)
ds@1952: {
ds@1952: return false;
ds@1952: }
ds@1952:
ds@1952: if (m_CurFlags & USERMSG_BLOCKHOOKS)
ds@1952: {
ds@1952: ENGINE_CALL(MessageEnd)();
ds@1952: } else {
ds@1952: engine->MessageEnd();
ds@1952: }
ds@1952:
ds@1952: m_InExec = false;
ds@1952: m_CurFlags = 0;
ds@1952: m_CellRecFilter.Reset();
ds@1952:
ds@1952: return true;
ds@1952: }
ds@1952:
ds@1952: bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
ds@1952: {
ds@1952: if (msg_id < 0 || msg_id >= 255)
ds@1952: {
ds@1952: return false;
ds@1952: }
ds@1952:
ds@1952: ListenerInfo *pInfo;
ds@1952: if (m_FreeListeners.empty())
ds@1952: {
ds@1952: pInfo = new ListenerInfo;
ds@1952: } else {
ds@1952: pInfo = m_FreeListeners.front();
ds@1952: m_FreeListeners.pop();
ds@1952: }
ds@1952:
ds@1952: pInfo->Callback = pListener;
ds@1952: pInfo->IsHooked = false;
ds@1952: pInfo->KillMe = false;
ds@1952:
ds@1952: if (!m_HookCount++)
ds@1952: {
ds@1952: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
ds@1952: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
ds@1952: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
ds@1952: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
ds@1952: }
ds@1952:
ds@1952: if (intercept)
ds@1952: {
ds@1952: m_msgIntercepts[msg_id].push_back(pInfo);
ds@1952: } else {
ds@1952: m_msgHooks[msg_id].push_back(pInfo);
ds@1952: }
ds@1952:
ds@1952: return true;
ds@1952: }
ds@1952:
ds@1952: bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
ds@1952: {
ds@1952: MsgList *pList;
ds@1952: MsgIter iter;
ds@1952: ListenerInfo *pInfo;
ds@1952: bool deleted = false;
ds@1952:
ds@1952: if (msg_id < 0 || msg_id >= 255)
ds@1952: {
ds@1952: return false;
ds@1952: }
ds@1952:
ds@1952: pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
ds@1952: for (iter=pList->begin(); iter!=pList->end(); iter++)
ds@1952: {
ds@1952: pInfo = (*iter);
ds@1952: if (pInfo->Callback == pListener)
ds@1952: {
ds@1952: if (pInfo->IsHooked)
ds@1952: {
ds@1952: pInfo->KillMe = true;
ds@1952: return true;
ds@1952: }
ds@1952: pList->erase(iter);
ds@1952: deleted = true;
ds@1952: break;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: if (deleted)
ds@1952: {
ds@1952: _DecRefCounter();
ds@1952: }
ds@1952:
ds@1952: return deleted;
ds@1952: }
ds@1952:
ds@1952: void UserMessages::_DecRefCounter()
ds@1952: {
ds@1952: if (--m_HookCount == 0)
ds@1952: {
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
ds@1952: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
ds@1952: {
ds@1952: bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
ds@1952: bool is_hook_empty = m_msgHooks[msg_type].empty();
ds@1952:
ds@1952: if ((is_intercept_empty && is_hook_empty)
ds@1952: || (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
ds@1952: {
ds@1952: m_InHook = false;
ds@1952: RETURN_META_VALUE(MRES_IGNORED, NULL);
ds@1952: }
ds@1952:
ds@1952: m_CurId = msg_type;
ds@1952: m_CurRecFilter = filter;
ds@1952: m_InHook = true;
ds@1952: m_BlockEndPost = false;
ds@1952:
ds@1952: if (!is_intercept_empty)
ds@1952: {
ds@1952: m_InterceptBuffer.Reset();
ds@1952: RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
ds@1952: }
ds@1952:
ds@1952: RETURN_META_VALUE(MRES_IGNORED, NULL);
ds@1952: }
ds@1952:
ds@1952: bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
ds@1952: {
ds@1952: if (!m_InHook)
ds@1952: {
ds@1952: RETURN_META_VALUE(MRES_IGNORED, NULL);
ds@1952: }
ds@1952:
ds@1952: m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
ds@1952:
ds@1952: RETURN_META_VALUE(MRES_IGNORED, NULL);
ds@1952: }
ds@1952:
ds@1952: void UserMessages::OnMessageEnd_Post()
ds@1952: {
ds@1952: if (!m_InHook || m_BlockEndPost)
ds@1952: {
ds@1952: RETURN_META(MRES_IGNORED);
ds@1952: }
ds@1952:
ds@1952: MsgList *pList;
ds@1952: MsgIter iter;
ds@1952: ListenerInfo *pInfo;
ds@1952:
ds@1952: pList = &m_msgIntercepts[m_CurId];
ds@1952: for (iter=pList->begin(); iter!=pList->end(); )
ds@1952: {
ds@1952: pInfo = (*iter);
ds@1952: pInfo->IsHooked = true;
ds@1952: pInfo->Callback->OnUserMessageSent(m_CurId);
ds@1952:
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: iter = pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: continue;
ds@1952: }
ds@1952:
ds@1952: pInfo->IsHooked = false;
ds@1952: iter++;
ds@1952: }
ds@1952:
ds@1952: m_InHook = false;
ds@1952:
ds@1952: pList = &m_msgHooks[m_CurId];
ds@1952: for (iter=pList->begin(); iter!=pList->end(); )
ds@1952: {
ds@1952: pInfo = (*iter);
ds@1952: pInfo->IsHooked = true;
ds@1952: pInfo->Callback->OnUserMessageSent(m_CurId);
ds@1952:
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: iter = pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: continue;
ds@1952: }
ds@1952:
ds@1952: pInfo->IsHooked = false;
ds@1952: iter++;
ds@1952: }
ds@1952: }
ds@1952:
ds@1952: void UserMessages::OnMessageEnd_Pre()
ds@1952: {
ds@1952: if (!m_InHook)
ds@1952: {
ds@1952: RETURN_META(MRES_IGNORED);
ds@1952: }
ds@1952:
ds@1952: MsgList *pList;
ds@1952: MsgIter iter;
ds@1952: ListenerInfo *pInfo;
ds@1952:
ds@1952: ResultType res;
ds@1952: bool intercepted = false;
ds@1952: bool handled = false;
ds@1952:
ds@1952: pList = &m_msgIntercepts[m_CurId];
ds@1952: for (iter=pList->begin(); iter!=pList->end(); )
ds@1952: {
ds@1952: pInfo = (*iter);
ds@1952: pInfo->IsHooked = true;
ds@1952: res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
ds@1952:
ds@1952: intercepted = true;
ds@1952:
ds@1952: switch (res)
ds@1952: {
ds@1952: case Pl_Stop:
ds@1952: {
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: goto supercede;
ds@1952: }
ds@1952: pInfo->IsHooked = false;
ds@1952: goto supercede;
ds@1952: }
ds@1952: case Pl_Handled:
ds@1952: {
ds@1952: handled = true;
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: iter = pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: continue;
ds@1952: }
ds@1952: break;
ds@1952: }
ds@1952: default:
ds@1952: {
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: iter = pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: continue;
ds@1952: }
ds@1952: break;
ds@1952: }
ds@1952: }
ds@1952: pInfo->IsHooked = false;
ds@1952: iter++;
ds@1952: }
ds@1952:
ds@1952: if (handled)
ds@1952: {
ds@1952: goto supercede;
ds@1952: }
ds@1952:
ds@1952: if (intercepted)
ds@1952: {
ds@1952: bf_write *engine_bfw;
ds@1952:
ds@1952: engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
ds@1952: m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
ds@1952: engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
ds@1952: ENGINE_CALL(MessageEnd)();
ds@1952: }
ds@1952:
ds@1952: pList = &m_msgHooks[m_CurId];
ds@1952: for (iter=pList->begin(); iter!=pList->end(); )
ds@1952: {
ds@1952: pInfo = (*iter);
ds@1952: pInfo->IsHooked = true;
ds@1952: pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
ds@1952:
ds@1952: if (pInfo->KillMe)
ds@1952: {
ds@1952: iter = pList->erase(iter);
ds@1952: m_FreeListeners.push(pInfo);
ds@1952: _DecRefCounter();
ds@1952: continue;
ds@1952: }
ds@1952:
ds@1952: pInfo->IsHooked = false;
ds@1952: iter++;
ds@1952: }
ds@1952:
ds@1952: RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
ds@1952: supercede:
ds@1952: m_InHook = false;
ds@1952: m_BlockEndPost = true;
ds@1952: RETURN_META(MRES_SUPERCEDE);
ds@1952: }