dvander@592: /** ds@653: * vim: set ts=4 : ds@653: * =============================================================== ds@653: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. ds@653: * =============================================================== ds@653: * ds@653: * This file is not open source and may not be copied without explicit ds@653: * written permission of AlliedModders LLC. This file may not be redistributed ds@653: * in whole or significant part. ds@653: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php 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: dvander@592: SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int); 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@616: bf_write *UserMessages::StartMessage(int msg_id, 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: dvander@592: if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) dvander@592: { dvander@592: buffer = engine->UserMessageBegin(static_cast(&m_CellRecFilter), msg_id); dvander@592: } else { dvander@592: buffer = ENGINE_CALL(UserMessageBegin)(static_cast(&m_CellRecFilter), msg_id); 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: dvander@592: if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) dvander@592: { dvander@592: engine->MessageEnd(); dvander@592: } else { dvander@592: ENGINE_CALL(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@616: bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept) dvander@592: { 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; 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@616: bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept) faluco@593: { 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); faluco@593: if (pInfo->Callback == pListener) 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: dvander@616: bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type) 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@593: || (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL))) 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: dvander@616: bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type) 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: { faluco@597: if (!m_InHook || m_BlockEndPost) 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: faluco@593: pList = &m_msgIntercepts[m_CurId]; faluco@593: for (iter=pList->begin(); iter!=pList->end(); ) faluco@593: { faluco@593: pInfo = (*iter); faluco@593: pInfo->IsHooked = true; faluco@593: pInfo->Callback->OnUserMessageSent(m_CurId); 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: dvander@1132: m_InHook = false; dvander@1132: faluco@593: pList = &m_msgHooks[m_CurId]; faluco@594: for (iter=pList->begin(); iter!=pList->end(); ) dvander@592: { faluco@593: pInfo = (*iter); faluco@594: pInfo->IsHooked = true; faluco@593: pInfo->Callback->OnUserMessageSent(m_CurId); 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@592: if (handled) dvander@592: { dvander@592: goto supercede; dvander@592: } dvander@592: dvander@592: if (intercepted) dvander@592: { faluco@593: bf_write *engine_bfw; faluco@593: dvander@592: engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId); 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@593: m_InHook = false; faluco@597: m_BlockEndPost = true; dvander@592: RETURN_META(MRES_SUPERCEDE); dvander@592: }