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: }