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