dvander@592: /** dvander@592: * vim: set ts=4 : dvander@592: * =============================================================== dvander@592: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. dvander@592: * =============================================================== dvander@592: * dvander@592: * This file is not open source and may not be copied without explicit dvander@592: * written permission of AlliedModders LLC. This file may not be redistributed dvander@592: * in whole or significant part. dvander@592: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php dvander@592: * dvander@592: * Version: $Id$ dvander@592: */ dvander@592: dvander@592: #include "UserMessages.h" dvander@592: dvander@592: CUserMessages 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@592: //:TODO: USE NEW MM IFACE TO SEARCH FOR MESSAGE NAMES ETC! faluco--> i'll do this when i have some spare time dvander@592: dvander@592: CUserMessages::CUserMessages() : 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@592: CUserMessages::~CUserMessages() dvander@592: { dvander@592: sm_trie_destroy(m_Names); dvander@592: } dvander@592: dvander@592: void CUserMessages::OnSourceModAllInitialized() dvander@592: { dvander@592: g_ShareSys.AddInterface(NULL, this); dvander@592: } dvander@592: dvander@592: void CUserMessages::OnSourceModAllShutdown() dvander@592: { dvander@592: if (m_HookCount) dvander@592: { dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); dvander@592: } dvander@592: m_HookCount = 0; dvander@592: } dvander@592: dvander@592: int CUserMessages::GetMessageIndex(const char *msg) dvander@592: { dvander@592: int msgid; dvander@592: if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast(&msgid))) dvander@592: { dvander@592: char buf[255]; dvander@592: int dummy; dvander@592: msgid = 0; dvander@592: dvander@592: while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy)) dvander@592: { dvander@592: if (strcmp(msg, buf) == 0) dvander@592: { dvander@592: sm_trie_insert(m_Names, msg, reinterpret_cast(msgid)); dvander@592: return msgid; dvander@592: } dvander@592: msgid++; dvander@592: } dvander@592: dvander@592: return INVALID_MESSAGE_ID; dvander@592: } dvander@592: dvander@592: return msgid; dvander@592: } dvander@592: dvander@592: bool CUserMessages::GetMessageName(int msgid, char *buffer, size_t maxlen) const dvander@592: { dvander@592: int dummy; dvander@592: dvander@592: return gamedll->GetUserMessageInfo(msgid, buffer, maxlen, dummy); dvander@592: } dvander@592: dvander@592: bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags) dvander@592: { dvander@592: bf_write *buffer; dvander@592: dvander@592: if (m_InExec) 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: dvander@592: m_CellRecFilter.SetRecipientPtr(players, playersNum); dvander@592: dvander@592: m_CurFlags = flags; dvander@592: if (m_CurFlags & USERMSG_INITMSG) dvander@592: { dvander@592: m_CellRecFilter.SetInitMessage(true); dvander@592: } dvander@592: if (m_CurFlags & USERMSG_RELIABLE) dvander@592: { dvander@592: m_CellRecFilter.SetReliable(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@592: bool CUserMessages::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; dvander@592: m_CellRecFilter.ResetFilter(); dvander@592: dvander@592: return true; dvander@592: } dvander@592: dvander@592: bool CUserMessages::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: dvander@592: if (!m_HookCount++) dvander@592: { dvander@592: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); dvander@592: SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); dvander@592: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); dvander@592: SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); dvander@592: } dvander@592: dvander@592: if (intercept) dvander@592: { dvander@592: m_msgIntercepts[msg_id].push_back(pListener); dvander@592: } else { dvander@592: m_msgHooks[msg_id].push_back(pListener); dvander@592: } dvander@592: dvander@592: return true; dvander@592: } dvander@592: dvander@592: bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept) dvander@592: { dvander@592: //:TODO: restrict user from unhooking stuff during a message hook to avoid iterator mess dvander@592: List *lst; dvander@592: IUserMessageListener *listener; dvander@592: MsgIter iter; 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: dvander@592: lst = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id]; dvander@592: for (iter=lst->begin(); iter!=lst->end(); iter++) dvander@592: { dvander@592: listener = (*iter); dvander@592: if (listener == pListener) dvander@592: { dvander@592: lst->erase(iter); dvander@592: deleted = true; dvander@592: break; dvander@592: } dvander@592: } dvander@592: dvander@592: if (deleted && !(--m_HookCount)) dvander@592: { dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); dvander@592: SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); dvander@592: } dvander@592: dvander@592: return (deleted) ? true : false; dvander@592: } dvander@592: dvander@592: bf_write *CUserMessages::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: dvander@592: if (is_intercept_empty && is_hook_empty) dvander@592: { dvander@592: m_InHook = false; dvander@592: RETURN_META_VALUE(MRES_IGNORED, NULL); dvander@592: } dvander@592: dvander@592: if ((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_InterceptBuffer.Reset(); dvander@592: m_InHook = true; dvander@592: dvander@592: if (!is_intercept_empty) dvander@592: { 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@592: bf_write *CUserMessages::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@592: void CUserMessages::OnMessageEnd_Post() dvander@592: { dvander@592: if (!m_InHook || (META_RESULT_STATUS == MRES_SUPERCEDE)) dvander@592: { dvander@592: RETURN_META(MRES_IGNORED); dvander@592: } dvander@592: dvander@592: List *lst; dvander@592: IUserMessageListener *listener; dvander@592: MsgIter iter; dvander@592: dvander@592: lst = &m_msgIntercepts[m_CurId]; dvander@592: for (iter=lst->begin(); iter!=lst->end(); iter++) dvander@592: { dvander@592: listener = (*iter); dvander@592: listener->OnUserMessageSent(m_CurId); dvander@592: } dvander@592: dvander@592: lst = &m_msgHooks[m_CurId]; dvander@592: for (iter=lst->begin(); iter!=lst->end(); iter++) dvander@592: { dvander@592: listener = (*iter); dvander@592: listener->OnUserMessageSent(m_CurId); dvander@592: } dvander@592: } dvander@592: dvander@592: void CUserMessages::OnMessageEnd_Pre() dvander@592: { dvander@592: if (!m_InHook) dvander@592: { dvander@592: RETURN_META(MRES_IGNORED); dvander@592: } dvander@592: dvander@592: List *lst; dvander@592: IUserMessageListener *listener; dvander@592: bf_write *engine_bfw; dvander@592: MsgIter iter; dvander@592: ResultType res; dvander@592: bool intercepted = false; dvander@592: bool handled = false; dvander@592: dvander@592: lst = &m_msgIntercepts[m_CurId]; dvander@592: for (iter=lst->begin(); iter!=lst->end(); iter++) dvander@592: { dvander@592: listener = (*iter); dvander@592: res = listener->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter); dvander@592: if (res == Pl_Stop) dvander@592: { dvander@592: goto supercede; dvander@592: } else if (res == Pl_Handled) { dvander@592: handled = true; dvander@592: } dvander@592: intercepted = true; dvander@592: } dvander@592: dvander@592: if (handled) dvander@592: { dvander@592: goto supercede; dvander@592: } dvander@592: dvander@592: if (intercepted) dvander@592: { 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: goto supercede; dvander@592: } dvander@592: dvander@592: lst = &m_msgHooks[m_CurId]; dvander@592: for (iter=lst->begin(); iter!=lst->end(); iter++) dvander@592: { dvander@592: listener = (*iter); dvander@592: listener->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter); dvander@592: } dvander@592: dvander@592: RETURN_META(MRES_IGNORED); dvander@592: supercede: dvander@592: RETURN_META(MRES_SUPERCEDE); dvander@592: }