diff --git a/amxmodx/messages.cpp b/amxmodx/messages.cpp index 5480ed90..01899054 100755 --- a/amxmodx/messages.cpp +++ b/amxmodx/messages.cpp @@ -2,7 +2,8 @@ #include "messages.h" Message Msg; -CVector msgHooks[256]; +//CVector msgHooks[256]; +RegisteredMessage msgHooks[256]; int msgBlocks[256] = {BLOCK_NOT}; int msgDest; int msgType; @@ -16,7 +17,7 @@ void ClearMessages() { for (size_t i=0; i mres) mres = mresB; } + */ inhook = false; if (mres & 1) { @@ -477,7 +483,7 @@ static cell AMX_NATIVE_CALL register_message(AMX *amx, cell *params) int id = registerSPForwardByName(amx, name, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); if (id != -1) { - msgHooks[params[1]].push_back(id); + msgHooks[params[1]].AddHook(id); return id; } else { LogError(amx, AMX_ERR_NOTFOUND, "Could not find function \"%s\"", name); @@ -488,6 +494,29 @@ static cell AMX_NATIVE_CALL register_message(AMX *amx, cell *params) return 0; } +// unregister_message(msgid, msghandle) +static cell AMX_NATIVE_CALL unregister_message(AMX *amx, cell *params) +{ + if (!Msg.Ready()) + Msg.Init(); + + if (params[1]>0 && params[1] < 256) + { + int id = params[2]; + if (id != -1) + { + msgHooks[params[1]].RemoveHook(id); + return id; + } else { + LogError(amx, AMX_ERR_NOTFOUND, "Invalid registered message handle"); + return -1; + } + } + + return 0; +} + + static cell AMX_NATIVE_CALL set_msg_block(AMX *amx, cell *params) { int msgid = params[1]; @@ -775,6 +804,7 @@ AMX_NATIVE_INFO msg_Natives[] = {"write_string", write_string}, {"register_message", register_message}, + {"unregister_message", unregister_message}, {"set_msg_block", set_msg_block}, {"get_msg_block", get_msg_block}, diff --git a/amxmodx/messages.h b/amxmodx/messages.h index 19f5c2fd..9e357ec4 100755 --- a/amxmodx/messages.h +++ b/amxmodx/messages.h @@ -6,6 +6,7 @@ #include "amx.h" #include "CVector.h" #include "CString.h" +#include "sh_stack.h" #define MAX_MESSAGES 255 @@ -15,6 +16,136 @@ #define BLOCK_ONCE 1 #define BLOCK_SET 2 +class RegisteredMessage +{ +private: + CVector m_Forwards; + CStack m_InExecution; + bool m_Cleanup; + +public: + RegisteredMessage() : m_Cleanup(false) { } + ~RegisteredMessage() { this->Clear(); } + + void AddHook(int fwd) + { + m_Forwards.push_back(fwd); + } + bool RemoveHook(int fwd) + { + // Don't erase a forward if we're in the middle of execution; this + // could throw off the iterator that is going through the forwards + // and executing them. Instead, unregister the forward and set it + // to -1 from within the vector. + if (m_InExecution.size()) + { + this->m_Cleanup = true; + + CVector::iterator iter = m_Forwards.begin(); + CVector::iterator end = m_Forwards.end(); + while (iter != end) + { + if (*iter == fwd) + { + if (*iter != -1) + { + unregisterSPForward(*iter); + } + *iter = -1; + return true; + } + else + { + iter++; + } + } + + } + else + { + CVector::iterator iter = m_Forwards.begin(); + CVector::iterator end = m_Forwards.end(); + while (iter != end) + { + if (*iter == fwd) + { + if (fwd != -1) + { + unregisterSPForward(fwd); + + m_Forwards.erase(iter); + + return true; + } + else + { + // -1 could be in here more than once + m_Forwards.erase(iter); + } + } + else + { + iter++; + } + } + } + + return false; + } + + void Clear() + { + while (m_InExecution.size()) + { + m_InExecution.pop(); + } + for (size_t i = 0; i < m_Forwards.size(); i++) + { + int fwd = m_Forwards[i]; + + if (fwd != -1) + { + unregisterSPForward(m_Forwards[i]); + } + } + + m_Forwards.clear(); + } + + cell Execute(cell type, cell dest, cell entity) + { + m_InExecution.push(1); + cell res = 0; + cell thisres = 0; + for (size_t i = 0; i < m_Forwards.size(); i++) + { + int fwd = m_Forwards[i]; + + if (fwd != -1) + { + thisres = executeForwards(fwd, type, dest, entity); + if (thisres > res) + { + res = thisres; + } + + } + } + + m_InExecution.pop(); + + if (m_InExecution.size() == 0 && m_Cleanup) + { + this->RemoveHook(-1); + } + + return res; + } + bool Hooked() const + { + return m_Forwards.size() != 0; + } +}; enum msgtype { arg_byte = 1, @@ -76,7 +207,7 @@ void C_WriteString(const char *sz); void C_WriteEntity(int iValue); void C_MessageEnd(void); -extern CVector msgHooks[256]; +extern RegisteredMessage msgHooks[256]; extern int msgBlocks[256]; void ClearMessages(); diff --git a/plugins/include/messages.inc b/plugins/include/messages.inc index 760febc8..9a8652bc 100644 --- a/plugins/include/messages.inc +++ b/plugins/include/messages.inc @@ -55,9 +55,15 @@ native get_msg_block(iMessage); * or fully block it. Here is how it works: * If you hook a message, the message is stored but not sent. You have the opportunity to * not only execute code, but to get/set the contents of the message, before you choose to - * either block it or let it go on its way. The hooked function will be passed a msg_id, msg_dest, and entity index. */ + * either block it or let it go on its way. The hooked function will be passed a msg_id, msg_dest, and entity index. + * The return value can be passed to unregister_message() to stop the message from being hooked */ native register_message(iMsgId, const szFunction[]); +/* Unregisters a message hook previously created with register_message + * You must pass the proper message id, and return value from the message to unregister the message successfully. */ +native unregister_message(iMsgId, registeredmsg); + + /* The get/set _msg commands will fail if used outside a hooked message scope. * They should never be used unless inside a registered message function. * There are eight different ways of sending a message, five are ints, two are floats, and one is string.