diff --git a/core/CMsgIdPool.h b/core/CMsgIdPool.h deleted file mode 100644 index de84ce4d..00000000 --- a/core/CMsgIdPool.h +++ /dev/null @@ -1,63 +0,0 @@ -/** -* vim: set ts=4 : -* =============================================================== -* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. -* =============================================================== -* -* This file is not open source and may not be copied without explicit -* written permission of AlliedModders LLC. This file may not be redistributed -* in whole or significant part. -* For information, see LICENSE.txt or http://www.sourcemod.net/license.php -* -* Version: $Id$ -*/ - -#ifndef _INCLUDE_SOURCEMOD_CMSGIDPOOL_H_ -#define _INCLUDE_SOURCEMOD_CMSGIDPOOL_H_ - -#include "sm_trie.h" -#include "sourcemm_api.h" - -#define INVALID_MESSAGE_ID -1 - -class CMessageIdPool -{ -public: - CMessageIdPool() - { - m_Names = sm_trie_create(); - } - ~CMessageIdPool() - { - sm_trie_destroy(m_Names); - } -public: - int GetMessageId(const char *name) - { - int msgid; - if (!sm_trie_retrieve(m_Names, name, reinterpret_cast(&msgid))) - { - char buf[255]; - int dummy; - msgid = 0; - - while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy)) - { - if (strcmp(name, buf) == 0) - { - sm_trie_insert(m_Names, name, reinterpret_cast(msgid)); - return msgid; - } - msgid++; - } - - return INVALID_MESSAGE_ID; - } - - return msgid; - } -private: - Trie *m_Names; -}; - -#endif //_INCLUDE_SOURCEMOD_CMSGIDPOOL_H_ diff --git a/core/CMsgListenerWrapper.h b/core/CMsgListenerWrapper.h new file mode 100644 index 00000000..254d24b9 --- /dev/null +++ b/core/CMsgListenerWrapper.h @@ -0,0 +1,144 @@ +/** +* vim: set ts=4 : +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#ifndef _INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_ +#define _INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_ + +extern int g_MsgPlayers[256]; + +class MsgListenerWrapper : public IUserMessageListener +{ +public: + void InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept); + bool IsInterceptHook() const; + int GetMessageId() const; + IPluginFunction *GetHookedFunction() const; + IPluginFunction *GetNotifyFunction() const; +public: //IUserMessageListener + void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter); + ResultType InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter); + void OnUserMessageSent(int msg_id); +private: + size_t PreparePlArray(int *pl_array, IRecipientFilter *pFilter); +private: + IPluginFunction *m_Hook; + IPluginFunction *m_Intercept; + IPluginFunction *m_Notify; + bool m_IsInterceptHook; + int m_MsgId; +}; + +inline size_t MsgListenerWrapper::PreparePlArray(int *pl_array, IRecipientFilter *pFilter) +{ + size_t size = static_cast(pFilter->GetRecipientCount()); + + for (size_t i=0; iGetRecipientIndex(i); + } + + return size; +} + +inline bool MsgListenerWrapper::IsInterceptHook() const +{ + return m_IsInterceptHook; +} + +inline int MsgListenerWrapper::GetMessageId() const +{ + return m_MsgId; +} + +inline IPluginFunction *MsgListenerWrapper::GetHookedFunction() const +{ + if (m_Hook) + { + return m_Hook; + } else { + return m_Intercept; + } +} + +inline IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const +{ + return m_Notify; +} + +inline void MsgListenerWrapper::InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept) +{ + if (intercept) + { + m_Intercept = hook; + m_Hook = NULL; + } else { + m_Hook = hook; + m_Intercept = NULL; + } + + if (notify) + { + m_Notify = notify; + } else { + m_Notify = NULL; + } + + m_MsgId = msgid; + m_IsInterceptHook = intercept; +} + +inline void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) +{ + cell_t res; + size_t size = PreparePlArray(g_MsgPlayers, pFilter); + + m_Hook->PushCell(msg_id); + m_Hook->PushCell(0); //:TODO: push handle! + m_Hook->PushArray(g_MsgPlayers, size, NULL); + m_Hook->PushCell(size); + m_Hook->PushCell(pFilter->IsReliable()); + m_Hook->PushCell(pFilter->IsInitMessage()); + m_Hook->Execute(&res); +} + +inline ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) +{ + cell_t res = static_cast(Pl_Continue); + size_t size = PreparePlArray(g_MsgPlayers, pFilter); + + m_Intercept->PushCell(msg_id); + m_Intercept->PushCell(0); //:TODO: push handle! + m_Intercept->PushArray(g_MsgPlayers, size, NULL); + m_Intercept->PushCell(size); + m_Intercept->PushCell(pFilter->IsReliable()); + m_Intercept->PushCell(pFilter->IsInitMessage()); + m_Intercept->Execute(&res); + + return static_cast(res); +} + +inline void MsgListenerWrapper::OnUserMessageSent(int msg_id) +{ + cell_t res; + + if (!m_Notify) + { + return; + } + + m_Notify->PushCell(msg_id); + m_Notify->Execute(&res); +} + +#endif //_INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_ diff --git a/core/CUserMessages.cpp b/core/CUserMessages.cpp new file mode 100644 index 00000000..3048faa3 --- /dev/null +++ b/core/CUserMessages.cpp @@ -0,0 +1,314 @@ +/** +* vim: set ts=4 : +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#include "CUserMessages.h" + +CUserMessages g_UserMsgs; + +SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int); +SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0); + +CUserMessages::CUserMessages() : 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; +} + +CUserMessages::~CUserMessages() +{ + sm_trie_destroy(m_Names); +} + +void CUserMessages::OnSourceModAllShutdown() +{ + if (m_HookCount) + { + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); + } + m_HookCount = 0; +} + +int CUserMessages::GetMessageIndex(const char *msg) +{ + int msgid; + if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast(&msgid))) + { + char buf[255]; + int dummy; + msgid = 0; + + while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy)) + { + if (strcmp(msg, buf) == 0) + { + sm_trie_insert(m_Names, msg, reinterpret_cast(msgid)); + return msgid; + } + msgid++; + } + + return INVALID_MESSAGE_ID; + } + + return msgid; +} + +bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags) +{ + bf_write *buffer; + + if (m_InExec) + { + return NULL; + } + if (msg_id < 0 || msg_id >= 255) + { + return NULL; + } + + m_CellRecFilter.SetRecipientPtr(players, playersNum); + + m_CurFlags = flags; + if (m_CurFlags & USERMSG_INITMSG) + { + m_CellRecFilter.SetInitMessage(true); + } + if (m_CurFlags & USERMSG_RELIABLE) + { + m_CellRecFilter.SetReliable(true); + } + + m_InExec = true; + + if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) + { + buffer = engine->UserMessageBegin(static_cast(&m_CellRecFilter), msg_id); + } else { + buffer = ENGINE_CALL(UserMessageBegin)(static_cast(&m_CellRecFilter), msg_id); + } + + return buffer; +} + +bool CUserMessages::EndMessage() +{ + if (!m_InExec) + { + return false; + } + + if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) + { + engine->MessageEnd(); + } else { + ENGINE_CALL(MessageEnd)(); + } + + m_InExec = false; + m_CurFlags = 0; + m_CellRecFilter.ResetFilter(); + + return true; +} + +bool CUserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept) +{ + if (msg_id < 0 || msg_id >= 255) + { + return false; + } + + if (!m_HookCount++) + { + SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); + SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); + SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); + SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); + } + + if (intercept) + { + m_msgIntercepts[msg_id].push_back(pListener); + } else { + m_msgHooks[msg_id].push_back(pListener); + } + + return true; +} + +bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept) +{ + //:TODO: restrict user from unhooking stuff during a message hook to avoid iterator mess + List *lst; + IUserMessageListener *listener; + MsgIter iter; + bool deleted = false; + + if (msg_id < 0 || msg_id >= 255) + { + return false; + } + + lst = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id]; + for (iter=lst->begin(); iter!=lst->end(); iter++) + { + listener = (*iter); + if (listener == pListener) + { + lst->erase(iter); + deleted = true; + break; + } + } + + if (deleted && !(--m_HookCount)) + { + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false); + SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true); + } + + return (deleted) ? true : false; +} + +bf_write *CUserMessages::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_InHook = false; + RETURN_META_VALUE(MRES_IGNORED, NULL); + } + + if ((m_InExec) && !(m_CurFlags & USERMSG_PASSTHRU_ALL)) + { + m_InHook = false; + RETURN_META_VALUE(MRES_IGNORED, NULL); + } + + m_CurId = msg_type; + m_CurRecFilter = filter; + m_InterceptBuffer.Reset(); + m_InHook = true; + + if (!is_intercept_empty) + { + RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer); + } + + RETURN_META_VALUE(MRES_IGNORED, NULL); +} + +bf_write *CUserMessages::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 CUserMessages::OnMessageEnd_Post() +{ + if (!m_InHook || (META_RESULT_STATUS == MRES_SUPERCEDE)) + { + RETURN_META(MRES_IGNORED); + } + + List *lst; + IUserMessageListener *listener; + MsgIter iter; + + lst = &m_msgIntercepts[m_CurId]; + for (iter=lst->begin(); iter!=lst->end(); iter++) + { + listener = (*iter); + listener->OnUserMessageSent(m_CurId); + } + + lst = &m_msgHooks[m_CurId]; + for (iter=lst->begin(); iter!=lst->end(); iter++) + { + listener = (*iter); + listener->OnUserMessageSent(m_CurId); + } +} + +void CUserMessages::OnMessageEnd_Pre() +{ + if (!m_InHook) + { + RETURN_META(MRES_IGNORED); + } + + List *lst; + IUserMessageListener *listener; + bf_write *engine_bfw; + MsgIter iter; + ResultType res; + bool intercepted = false; + bool handled = false; + + lst = &m_msgIntercepts[m_CurId]; + for (iter=lst->begin(); iter!=lst->end(); iter++) + { + listener = (*iter); + res = listener->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter); + if (res == Pl_Stop) + { + goto supercede; + } else if (res == Pl_Handled) { + handled = true; + } + intercepted = true; + } + + if (handled) + { + goto supercede; + } + + if (intercepted) + { + 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)(); + goto supercede; + } + + lst = &m_msgHooks[m_CurId]; + for (iter=lst->begin(); iter!=lst->end(); iter++) + { + listener = (*iter); + listener->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter); + } + + RETURN_META(MRES_IGNORED); +supercede: + RETURN_META(MRES_SUPERCEDE); +} diff --git a/core/CUserMessages.h b/core/CUserMessages.h new file mode 100644 index 00000000..c995e886 --- /dev/null +++ b/core/CUserMessages.h @@ -0,0 +1,72 @@ +/** +* vim: set ts=4 : +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#ifndef _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_ +#define _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_ + +#include "sm_globals.h" +#include +#include +#include "sourcemm_api.h" +#include "sm_trie.h" +#include "CellRecipientFilter.h" + +using namespace SourceHook; +using namespace SourceMod; + +#define INVALID_MESSAGE_ID -1 + +typedef List::iterator MsgIter; + +class CUserMessages : + public IUserMessages, + public SMGlobalClass +{ +public: + CUserMessages(); + ~CUserMessages(); +public: //SMGlobalClass + void OnSourceModAllShutdown(); +public: //IUserMessages + int GetMessageIndex(const char *msg); + bool HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false); + bool UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false); + bf_write *StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags); + bool EndMessage(); +public: + bf_write *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type); + bf_write *OnStartMessage_Post(IRecipientFilter *filter, int msg_type); + void OnMessageEnd_Pre(); + void OnMessageEnd_Post(); +private: + List m_msgHooks[255]; + List m_msgIntercepts[255]; + unsigned char m_pBase[2500]; + IRecipientFilter *m_CurRecFilter; + bf_write m_InterceptBuffer; + bf_write *m_OrigBuffer; + bf_read m_ReadBuffer; + size_t m_HookCount; + bool m_InHook; + + Trie *m_Names; + CellRecipientFilter m_CellRecFilter; + bool m_InExec; + int m_CurFlags; + int m_CurId; +}; + +extern CUserMessages g_UserMsgs; + +#endif //_INCLUDE_SOURCEMOD_CUSERMESSAGES_H_ diff --git a/core/CellRecipientFilter.h b/core/CellRecipientFilter.h index 1a082088..243855c2 100644 --- a/core/CellRecipientFilter.h +++ b/core/CellRecipientFilter.h @@ -21,7 +21,7 @@ class CellRecipientFilter : public IRecipientFilter { public: - CellRecipientFilter() : m_Reliable(false), m_InitMessage(false), m_Size(0), m_CellRecipients(NULL) {} + CellRecipientFilter() : m_Reliable(false), m_InitMessage(false), m_Size(0) {} ~CellRecipientFilter() {} public: //IRecipientFilter bool IsReliable() const; @@ -34,10 +34,10 @@ public: void SetInitMessage(bool isinitmsg); void ResetFilter(); private: + cell_t m_CellRecipients[255]; bool m_Reliable; bool m_InitMessage; size_t m_Size; - cell_t *m_CellRecipients; }; inline void CellRecipientFilter::ResetFilter() @@ -45,7 +45,6 @@ inline void CellRecipientFilter::ResetFilter() m_Reliable = false; m_InitMessage = false; m_Size = 0; - m_CellRecipients = NULL; } inline bool CellRecipientFilter::IsReliable() const @@ -84,7 +83,7 @@ inline void CellRecipientFilter::SetReliable(bool isreliable) inline void CellRecipientFilter::SetRecipientPtr(cell_t *ptr, size_t count) { - m_CellRecipients = ptr; + memcpy(m_CellRecipients, ptr, count * sizeof(cell_t)); m_Size = count; } diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 11888d8b..d8fce4ff 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -1,7 +1,7 @@ + + @@ -294,7 +298,7 @@ > + + @@ -409,6 +417,10 @@ RelativePath="..\..\public\ITextParsers.h" > + + m_FreeListeners; }; +void UsrMessageNatives::OnSourceModAllInitialized() +{ + g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL); + g_PluginSys.AddPluginsListener(this); +} + +void UsrMessageNatives::OnSourceModShutdown() +{ + g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent); + g_PluginSys.RemovePluginsListener(this); + g_WrBitBufType = 0; +} +void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object) +{ +} + +void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin) +{ + List *wrapper_list; + + if (plugin->GetProperty("MsgListeners", reinterpret_cast(&wrapper_list), true)) + { + List::iterator iter; + MsgListenerWrapper *listener; + + for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++) + { + listener = (*iter); + if (g_UserMsgs.UnhookUserMessage(listener->GetMessageId(), listener, listener->IsInterceptHook())) + { + m_FreeListeners.push(listener); + } + } + + delete wrapper_list; + } +} + +MsgListenerWrapper *UsrMessageNatives::GetNewListener(IPluginContext *pCtx) +{ + MsgListenerWrapper *listener_wrapper; + + if (m_FreeListeners.empty()) + { + listener_wrapper = new MsgListenerWrapper; + } else { + listener_wrapper = m_FreeListeners.front(); + m_FreeListeners.pop(); + } + + List *wrapper_list; + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); + + if (!pl->GetProperty("MsgListeners", reinterpret_cast(&wrapper_list))) + { + wrapper_list = new List; + pl->SetProperty("MsgListeners", wrapper_list); + } + + wrapper_list->push_back(listener_wrapper); + + return listener_wrapper; +} + +MsgListenerWrapper *UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept) +{ + MsgListenerWrapper *listener; + List *wrapper_list; + List::iterator iter; + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); + bool found = false; + + if (!pl->GetProperty("MsgListeners", reinterpret_cast(&wrapper_list))) + { + return NULL; + } + + for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++) + { + listener = (*iter); + if ((msgid == listener->GetMessageId()) + && (intercept == listener->IsInterceptHook()) + && (pHook == listener->GetHookedFunction())) + { + found = true; + break; + } + } + + return (found) ? listener : NULL; +} +bool UsrMessageNatives::RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept) +{ + List *wrapper_list; + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); + + if (!pl->GetProperty("MsgListeners", reinterpret_cast(&wrapper_list))) + { + return false; + } + + wrapper_list->remove(listener); + m_FreeListeners.push(listener); + + return true; +} + +/************************************** +* * +* USER MESSAGE NATIVE IMPLEMENTATIONS * +* * +***************************************/ + +static UsrMessageNatives s_UsrMessageNatives; + static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params) { char *msgname; @@ -57,7 +165,7 @@ static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params) return 0; } - return g_MessageIds.GetMessageId(msgname); + return g_UserMsgs.GetMessageIndex(msgname); } static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params) @@ -78,30 +186,14 @@ static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params) return 0; } - if ((msgid=g_MessageIds.GetMessageId(msgname)) == INVALID_MESSAGE_ID) + if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID) { return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname); } pCtx->LocalToPhysAddr(params[2], &cl_array); - g_CurMsgFlags = params[4]; - g_MsgRecFilter.SetRecipientPtr(cl_array, params[3]); - if (g_CurMsgFlags & USERMSG_INITMSG) - { - g_MsgRecFilter.SetInitMessage(true); - } - if (g_CurMsgFlags & USERMSG_RELIABLE) - { - g_MsgRecFilter.SetReliable(true); - } - - if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) //:TODO: change this when we can hook messages - { - pBitBuf = engine->UserMessageBegin(static_cast(&g_MsgRecFilter), msgid); - } else { - pBitBuf = ENGINE_CALL(UserMessageBegin)(static_cast(&g_MsgRecFilter), msgid); - } + pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]); g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL); g_IsMsgInExec = true; @@ -120,30 +212,14 @@ static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params) return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress"); } - if (msgid < 0 || msgid > 255) + if (msgid < 0 || msgid >= 255) { return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); } pCtx->LocalToPhysAddr(params[2], &cl_array); - g_CurMsgFlags = params[4]; - g_MsgRecFilter.SetRecipientPtr(cl_array, params[3]); - if (g_CurMsgFlags & USERMSG_INITMSG) - { - g_MsgRecFilter.SetInitMessage(true); - } - if (g_CurMsgFlags & USERMSG_RELIABLE) - { - g_MsgRecFilter.SetReliable(true); - } - - if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) //:TODO: change this when we can hook messages - { - pBitBuf = engine->UserMessageBegin(static_cast(&g_MsgRecFilter), msgid); - } else { - pBitBuf = ENGINE_CALL(UserMessageBegin)(static_cast(&g_MsgRecFilter), msgid); - } + pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]); g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL); g_IsMsgInExec = true; @@ -160,25 +236,78 @@ static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params) return pCtx->ThrowNativeError("Unable to end message, no message is in progress"); } - if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) - { - engine->MessageEnd(); - } else { - ENGINE_CALL(MessageEnd)(); - } + g_UserMsgs.EndMessage(); sec.pOwner = pCtx->GetIdentity(); sec.pIdentity = g_pCoreIdent; g_HandleSys.FreeHandle(g_CurMsgHandle, &sec); g_IsMsgInExec = false; - g_CurMsgFlags = 0; - g_MsgRecFilter.ResetFilter(); return 1; } -static UsrMessageNatives s_UsrMessageNatives; +static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params) +{ + IPluginFunction *pHook, *pNotify; + MsgListenerWrapper *listener; + bool intercept; + int msgid = params[1]; + + if (msgid < 0 || msgid >= 255) + { + return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); + } + + pHook = pCtx->GetFunctionById(params[2]); + if (!pHook) + { + return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]); + } + pNotify = pCtx->GetFunctionById(params[4]); + + intercept = (params[3]) ? true : false; + listener = s_UsrMessageNatives.GetNewListener(pCtx); + listener->InitListener(msgid, pHook, pNotify, intercept); + g_UserMsgs.HookUserMessage(msgid, listener, intercept); + + return 1; +} + +static cell_t smn_UnHookUserMessage(IPluginContext *pCtx, const cell_t *params) +{ + IPluginFunction *pFunc; + MsgListenerWrapper *listener; + bool intercept; + int msgid = params[1]; + + if (msgid < 0 || msgid >= 255) + { + return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); + } + + pFunc = pCtx->GetFunctionById(params[2]); + if (!pFunc) + { + return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + intercept = (params[3]) ? true : false; + listener = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept); + if (!listener) + { + return pCtx->ThrowNativeError("Unable to unhook the current user message"); + } + + if (!g_UserMsgs.UnhookUserMessage(msgid, listener, intercept)) + { + return pCtx->ThrowNativeError("Unable to unhook the current user message"); + } + + s_UsrMessageNatives.RemoveListener(pCtx, listener, intercept); + + return 1; +} REGISTER_NATIVES(usrmsgnatives) { @@ -186,5 +315,7 @@ REGISTER_NATIVES(usrmsgnatives) {"StartMessage", smn_StartMessage}, {"StartMessageEx", smn_StartMessageEx}, {"EndMessage", smn_EndMessage}, + {"HookUserMessage", smn_HookUserMessage}, + {"UnHookUserMessage", smn_UnHookUserMessage}, {NULL, NULL} }; diff --git a/plugins/include/usermessages.inc b/plugins/include/usermessages.inc index c6ad7395..29d38ed1 100644 --- a/plugins/include/usermessages.inc +++ b/plugins/include/usermessages.inc @@ -85,6 +85,7 @@ native EndMessage(); * @brief Called when a message is hooked * * @param msg_id Message index. + * @param bf Handle to the input bit buffer of the message. * @param players Array containing player indexes. * @param playersNum Number of players in the array. * @param reliable True if message is reliable, false otherwise. @@ -92,7 +93,14 @@ native EndMessage(); * @return Ignored for normal hooks. For intercept hooks, false blocks * the message from being sent, and true continues. */ -functag MsgHook bool:public(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init); +functag MsgHook Action:public(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init); + +/** + * @brief Called when a message is finished sending. + * + * @param msg_id Message index. + */ +functag MsgSentNotify public(UserMsg:msg_id); /** * @brief Hooks a user message. @@ -102,10 +110,11 @@ functag MsgHook bool:public(UserMsg:msg_id, Handle:bf, const players[], playersN * @param intercept If intercept is true, message will be fully intercepted, * allowing the user to block the message. Otherwise, * the hook is normal and ignores the return value. + * @param notify Notification function. * @noreturn * @error Invalid message index. */ -native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false); +native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false, MsgSentNotify:notify=MsgSentNotify:-1); /** * @brief Removes one usermessage hook. @@ -118,33 +127,6 @@ native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false); */ native UnHookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false); -/** - * @brief Called when a message is finished sending. - * - * @param msg_id Message index. - */ -functag MsgSentNotify public(UserMsg:msg_id); - -/** - * @brief Notifies when a message has finished sending. - * - * @param msg_id Message index. - * @param notify Notification function. - * @noreturn - * @error Invalid message index. - */ -native NotifyUserMessage(UserMsg:msg_id, MsgSentNotify:notify); - -/** - * @brief Removes a user message notification. - * - * @param msg_id Message index. - * @param notify Notification function. - * @noreturn - * @error Invalid message index. - */ -native RemoveNotifyUserMessage(UserMsg:msg_id, MsgSentNotify:notify); - /** * Starts a usermessage (network message) that broadcasts to all clients. * @note See StartMessage or StartMessageEx(). diff --git a/public/IUserMessages.h b/public/IUserMessages.h index ce6e5919..6307c336 100644 --- a/public/IUserMessages.h +++ b/public/IUserMessages.h @@ -19,7 +19,9 @@ #ifndef _INCLUDE_SOURCEMOD_INTERFACE_USERMESSAGES_H_ #define _INCLUDE_SOURCEMOD_INTERFACE_USERMESSAGES_H_ +#include #include +#include #include #include @@ -59,9 +61,9 @@ namespace SourceMod * @param pFtiler Recipient filter. * @return True to allow message, false to scrap it. */ - virtual bool InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) + virtual ResultType InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) { - return true; + return Pl_Continue; } /** @@ -73,6 +75,11 @@ namespace SourceMod } }; + #define USERMSG_PASSTHRU (1<<0) /**< Message will pass through other SourceMM plugins */ + #define USERMSG_PASSTHRU_ALL (1<<1) /**< Message will pass through other SourceMM plugins AND SourceMod */ + #define USERMSG_RELIABLE (1<<2) /**< Message will be set to reliable */ + #define USERMSG_INITMSG (1<<3) /**< Message will be considered to be an initmsg */ + /** * @brief Contains functions for hooking user messages. */ @@ -127,14 +134,15 @@ namespace SourceMod * @param players Array containing player indexes. * @param playersNum Number of players in the array. * @param flags Flags to use for sending the message. - * @return bf_write structure to write message with. + * @return bf_write structure to write message with, or NULL on failure. */ virtual bf_write *StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags) =0; /** * @brief Wrapper around UserMessageEnd for use with StartMessage(). + * @return True on success, false otherwise. */ - virtual void EndMessage() =0; + virtual bool EndMessage() =0; }; }