diff --git a/rehlds/CMakeLists.txt b/rehlds/CMakeLists.txt
index a9b371b..e48e87d 100644
--- a/rehlds/CMakeLists.txt
+++ b/rehlds/CMakeLists.txt
@@ -182,6 +182,7 @@ set(ENGINE_SRCS
rehlds/public_amalgamation.cpp
rehlds/rehlds_api_impl.cpp
rehlds/rehlds_interfaces_impl.cpp
+ rehlds/rehlds_messagemngr_impl.cpp
rehlds/rehlds_security.cpp
)
diff --git a/rehlds/engine/sys_dll.cpp b/rehlds/engine/sys_dll.cpp
index 08431a4..ddfcef3 100644
--- a/rehlds/engine/sys_dll.cpp
+++ b/rehlds/engine/sys_dll.cpp
@@ -1083,6 +1083,10 @@ void LoadThisDll(const char *szDllFilename)
goto IgnoreThisDLL;
}
+#ifdef REHLDS_API
+ MessageManager().Init();
+#endif
+
pfnGiveFnptrsToDll(&g_engfuncsExportedToDlls, &gGlobalVariables);
if (g_iextdllMac == MAX_EXTENSION_DLL)
{
diff --git a/rehlds/msvc/ReHLDS.vcxproj b/rehlds/msvc/ReHLDS.vcxproj
index dc15355..6b2a50b 100644
--- a/rehlds/msvc/ReHLDS.vcxproj
+++ b/rehlds/msvc/ReHLDS.vcxproj
@@ -119,6 +119,7 @@
+
@@ -386,6 +387,7 @@
+
@@ -439,6 +441,7 @@
+
diff --git a/rehlds/msvc/ReHLDS.vcxproj.filters b/rehlds/msvc/ReHLDS.vcxproj.filters
index a59f265..9d0731a 100644
--- a/rehlds/msvc/ReHLDS.vcxproj.filters
+++ b/rehlds/msvc/ReHLDS.vcxproj.filters
@@ -341,6 +341,9 @@
testsuite
+
+ rehlds
+
@@ -1060,5 +1063,11 @@
testsuite
+
+ rehlds
+
+
+ public\rehlds
+
\ No newline at end of file
diff --git a/rehlds/public/rehlds/IMessageManager.h b/rehlds/public/rehlds/IMessageManager.h
new file mode 100644
index 0000000..cf818db
--- /dev/null
+++ b/rehlds/public/rehlds/IMessageManager.h
@@ -0,0 +1,223 @@
+/*
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at
+* your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+*/
+
+#pragma once
+
+/**
+ * @brief Interface for defining message parameters and behavior for a individual message object
+ */
+class IMessage
+{
+public:
+ /**
+ * @brief The parameter types for a message
+ */
+ enum class ParamType : uint8
+ {
+ Byte,
+ Char,
+ Short,
+ Long,
+ Angle,
+ Coord,
+ String,
+ Entity,
+ };
+
+ /**
+ * @brief Blocking behavior types for messages
+ */
+ enum class BlockType : uint8
+ {
+ Not, // Not a block
+ Once, // Block once
+ Set // Set block
+ };
+
+ /**
+ * @brief Message destinations
+ */
+ enum class Dest : uint8
+ {
+ BROADCAST, // Unreliable to all
+ ONE, // Reliable to one (msg_entity)
+ ALL, // Reliable to all
+ INIT, // Write to the init string
+ PVS, // Ents in PVS of org
+ PAS, // Ents in PAS of org
+ PVS_R, // Reliable to PVS
+ PAS_R, // Reliable to PAS
+ ONE_UNRELIABLE, // Send to one client, but don't put in reliable stream, put in unreliable datagram
+ SPEC, // Sends to all spectator proxies
+ };
+
+ virtual ~IMessage() {};
+
+ /**
+ * @brief Returns the number of parameters in the message
+ * @return The number of parameters
+ */
+ virtual int getParamCount() const = 0;
+
+ /**
+ * @brief Returns the type of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The type of the parameter
+ */
+ virtual ParamType getParamType(size_t index) const = 0;
+
+ /**
+ * @brief Returns the integer value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The integer value of the parameter
+ */
+ virtual int getParamInt(size_t index) const = 0;
+
+ /**
+ * @brief Returns the float value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The float value of the parameter
+ */
+ virtual float getParamFloat(size_t index) const = 0;
+
+ /**
+ * @brief Returns the string value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The string value of the parameter
+ */
+ virtual const char* getParamString(size_t index) const = 0;
+
+ /**
+ * @brief Sets the integer value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param value The integer value to set
+ */
+ virtual void setParamInt(size_t index, int value) = 0;
+
+ /**
+ * @brief Sets the float value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param value The float value to set
+ */
+ virtual void setParamFloat(size_t index, float value) = 0;
+
+ /**
+ * @brief Sets the vector value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param pos The vector value to set
+ */
+ virtual void setParamVec(size_t index, const float *pos) = 0;
+
+ /**
+ * @brief Sets the string value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param string The string value to set
+ */
+ virtual void setParamString(size_t index, const char *string) = 0;
+
+ /**
+ * @brief Returns the destination of the message
+ * @return The destination of the message
+ */
+ virtual Dest getDest() const = 0;
+
+ /**
+ * @brief Returns the type of the message
+ * @return The type of the message
+ */
+ virtual int getType() const = 0;
+
+ /**
+ * @brief Returns the origin of the message
+ * @return The origin of the message
+ */
+ virtual const float* getOrigin() const = 0;
+
+ /**
+ * @brief Returns the edict associated with the message
+ * @return The edict associated with the message
+ */
+ virtual struct edict_s* getEdict() const = 0;
+
+ /**
+ * @brief Returns whether the message has been modified
+ * @return True if the message has been modified, false otherwise
+ */
+ virtual bool isModified() const = 0;
+
+ // This must be the last virtual function in class
+#ifdef REHLDS_SELF
+ // Set the copyback buffer for the message
+ virtual void setCopybackBuffer(struct sizebuf_s *pbuf) = 0;
+#endif
+};
+
+#define MESSAGEMNGR_VERSION_MAJOR 1
+#define MESSAGEMNGR_VERSION_MINOR 0
+
+/**
+ * @brief Interface manages hooks and blocking behavior game messages
+ */
+class IMessageManager
+{
+public:
+ using hookfunc_t = void (*)(IVoidHookChain *chain, IMessage *msg);
+
+ virtual ~IMessageManager() {};
+
+ /**
+ * @brief Returns the major version of the MessageManager
+ * @return The major version
+ */
+ virtual int getMajorVersion() const = 0;
+
+ /**
+ * @brief Returns the minor version of the MessageManager
+ * @return The minor version
+ */
+ virtual int getMinorVersion() const = 0;
+
+ /**
+ * @brief Returns the blocking behavior for the given message type
+ * @param msgType The message type
+ * @return The blocking behavior for the given message type
+ */
+ virtual IMessage::BlockType getMessageBlock(int msgType) const = 0;
+
+ /**
+ * @brief Sets the blocking behavior for the given message type
+ * @param msgType The message type
+ * @param blockType The blocking behavior to set
+ */
+ virtual void setMessageBlock(int msgType, IMessage::BlockType blockType) = 0;
+
+ /**
+ * @brief Registers a hook function for the given message type
+ * @param msgType The message type to register the hook for
+ * @param handler The hook function to register
+ * @param priority The priority of the hook function (see enum HookChainPriority)
+ */
+ virtual void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0;
+
+ /**
+ * @brief Unregisters a hook function for the given message type
+ * @param msgType The message type to unregister the hook for
+ * @param handler The hook function to unregister
+ */
+ virtual void unregisterHook(int msgType, hookfunc_t handler) = 0;
+};
diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h
index b82f332..743e47d 100644
--- a/rehlds/public/rehlds/rehlds_api.h
+++ b/rehlds/public/rehlds/rehlds_api.h
@@ -31,6 +31,7 @@
#include "rehlds_interfaces.h"
#include "hookchains.h"
#include "FlightRecorder.h"
+#include "IMessageManager.h"
#include "interface.h"
#include "model.h"
#include "ObjectList.h"
@@ -446,6 +447,7 @@ public:
virtual IRehldsServerStatic* GetServerStatic() = 0;
virtual IRehldsServerData* GetServerData() = 0;
virtual IRehldsFlightRecorder* GetFlightRecorder() = 0;
+ virtual IMessageManager *GetMessageManager() = 0;
};
#define VREHLDS_HLDS_API_VERSION "VREHLDS_HLDS_API_VERSION001"
diff --git a/rehlds/rehlds/hookchains_impl.cpp b/rehlds/rehlds/hookchains_impl.cpp
index 13f6e36..eaa49fc 100644
--- a/rehlds/rehlds/hookchains_impl.cpp
+++ b/rehlds/rehlds/hookchains_impl.cpp
@@ -90,3 +90,7 @@ void AbstractHookChainRegistry::removeHook(void* hookFunc) {
}
}
}
+
+int AbstractHookChainRegistry::getCount() const {
+ return m_NumHooks;
+}
diff --git a/rehlds/rehlds/hookchains_impl.h b/rehlds/rehlds/hookchains_impl.h
index dd2523c..0ab7800 100644
--- a/rehlds/rehlds/hookchains_impl.h
+++ b/rehlds/rehlds/hookchains_impl.h
@@ -109,10 +109,11 @@ protected:
protected:
void addHook(void* hookFunc, int priority);
- bool findHook(void* hookFunc) const;
void removeHook(void* hookFunc);
public:
+ int getCount() const;
+ bool findHook(void* hookFunc) const;
AbstractHookChainRegistry();
};
@@ -132,9 +133,14 @@ public:
EXT_FUNC virtual void registerHook(hookfunc_t hook, int priority) {
addHook((void*)hook, priority);
}
+
EXT_FUNC virtual void unregisterHook(hookfunc_t hook) {
removeHook((void*)hook);
}
+
+ bool isEmpty() const {
+ return getCount() == 0;
+ }
};
template
@@ -157,4 +163,8 @@ public:
EXT_FUNC virtual void unregisterHook(hookfunc_t hook) {
removeHook((void*)hook);
}
+
+ bool isEmpty() const {
+ return getCount() == 0;
+ }
};
diff --git a/rehlds/rehlds/precompiled.h b/rehlds/rehlds/precompiled.h
index de99a03..a947fcf 100644
--- a/rehlds/rehlds/precompiled.h
+++ b/rehlds/rehlds/precompiled.h
@@ -49,6 +49,7 @@
#include "rehlds_api_impl.h"
#include "FlightRecorderImpl.h"
#include "flight_recorder.h"
+#include "rehlds_messagemngr_impl.h"
#include "rehlds_security.h"
#include "dlls/cdll_dll.h"
diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp
index 2b4ede0..62a3920 100644
--- a/rehlds/rehlds/rehlds_api_impl.cpp
+++ b/rehlds/rehlds/rehlds_api_impl.cpp
@@ -917,6 +917,10 @@ IRehldsServerData* EXT_FUNC CRehldsApi::GetServerData() {
return &g_RehldsServerData;
}
+IMessageManager* EXT_FUNC CRehldsApi::GetMessageManager() {
+ return &MessageManager();
+}
+
IRehldsFlightRecorder* EXT_FUNC CRehldsApi::GetFlightRecorder() {
return g_FlightRecorder;
}
diff --git a/rehlds/rehlds/rehlds_api_impl.h b/rehlds/rehlds/rehlds_api_impl.h
index 6aece98..cda2b5a 100644
--- a/rehlds/rehlds/rehlds_api_impl.h
+++ b/rehlds/rehlds/rehlds_api_impl.h
@@ -391,6 +391,7 @@ public:
virtual IRehldsServerStatic* GetServerStatic();
virtual IRehldsServerData* GetServerData();
virtual IRehldsFlightRecorder* GetFlightRecorder();
+ virtual IMessageManager* GetMessageManager();
};
extern sizebuf_t* GetNetMessage_api();
diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp
new file mode 100644
index 0000000..617ceb1
--- /dev/null
+++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp
@@ -0,0 +1,508 @@
+/*
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at
+* your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+*/
+
+#include "precompiled.h"
+
+// Constructs a Message object
+MessageImpl::MessageImpl()
+{
+ m_buffer.buffername = "MessageManager/Begin/End";
+ m_buffer.data = m_bufferData;
+ m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW;
+ m_buffer.cursize = 0;
+ m_buffer.maxsize = sizeof(m_bufferData);
+
+ m_paramCount = 0;
+}
+
+// Sets the active state of the message with the given parameters
+void MessageImpl::setActive(int dest, int type, const float *origin, edict_t *edict)
+{
+ m_dest = static_cast(dest);
+ m_type = type;
+ m_edict = edict;
+
+ // Reset buffer size
+ m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW;
+ m_buffer.cursize = 0;
+
+ // Copy origin vector if provided
+ if (origin)
+ VectorCopy(origin, m_origin);
+ else
+ VectorClear(m_origin);
+}
+
+// Sets the buffer for the message
+void MessageImpl::setBuffer(sizebuf_t *pbuf)
+{
+ // Copy data from the provided buffer to the message buffer
+ memcpy(m_buffer.data, pbuf->data, pbuf->cursize);
+ m_buffer.cursize = pbuf->cursize;
+}
+
+// Sets the copyback buffer for the message
+void MessageImpl::setCopybackBuffer(sizebuf_t *pbuf)
+{
+ // Copy data from the message buffer back to the provided buffer
+ memcpy(pbuf->data, m_buffer.data, m_buffer.cursize);
+ pbuf->cursize = m_buffer.cursize;
+}
+
+// Clears the message parameters
+void MessageImpl::clear()
+{
+ m_paramCount = 0;
+}
+
+// An array containing fixed sizes for various types of parameters
+static size_t SIZEOF_PARAMTYPE[] =
+{
+ sizeof(uint8), // Byte
+ sizeof(int8), // Char
+ sizeof(int16), // Short
+ sizeof(uint32), // Long
+ sizeof(uint8), // Angle
+ sizeof(int16), // Coord
+ 0, // String
+ sizeof(int16), // Entity
+};
+
+// Adds a parameter to the message
+void MessageImpl::addParam(IMessage::ParamType type, size_t length)
+{
+ Param_t ¶m = m_params[m_paramCount++];
+ param.type = type;
+ param.len = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length;
+ param.pos = gMsgBuffer.cursize;
+}
+
+// Sets the value of a primitive parameter at the given index
+template
+void MessageImpl::setParamPrimitive(size_t index, T value)
+{
+ // Ensure index is within bounds
+ if (index < 0 || index >= m_paramCount)
+ return;
+
+ const Param_t ¶m = m_params[index];
+
+ void *pbuf = m_buffer.data + param.pos;
+
+ // Set value based on parameter type
+ switch (param.type)
+ {
+ case IMessage::ParamType::Byte:
+ *(uint8 *)pbuf = value;
+ break;
+ case IMessage::ParamType::Char:
+ *(int8 *)pbuf = value;
+ break;
+ case IMessage::ParamType::Short:
+ case IMessage::ParamType::Entity:
+ *(int16 *)pbuf = value;
+ break;
+ case IMessage::ParamType::Long:
+ *(uint16 *)pbuf = value;
+ break;
+ case IMessage::ParamType::Angle:
+ // Convert angle value to byte representation with loss of precision
+ *(uint8 *)pbuf = (int64)(fmod((double)value, 360.0) * 256.0 / 360.0) & 0xff;
+ break;
+ case IMessage::ParamType::Coord:
+ // Convert coordinate value to short integer representation with loss of precision
+ *(int16 *)pbuf = (int16)(int)(value * 8.0);
+ break;
+ default:
+ return; // bad type
+ }
+
+ // Mark message as modified
+ m_modified = true;
+}
+
+// Transforms the buffer after setting a string parameter at the given index
+void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLength, size_t newLength)
+{
+ // Calculate the difference in length
+ int32_t diffLength = newLength - oldLength;
+ if (diffLength != 0)
+ {
+ // Check if the buffer size limit will be exceeded
+ if (m_buffer.cursize + diffLength > m_buffer.maxsize)
+ {
+ Sys_Error(
+ "%s: Refusing to transform string with %i param of user message of %i bytes, "
+ "user message size limit is %i bytes\n", __func__, index, gMsgBuffer.cursize + diffLength, gMsgBuffer.maxsize);
+ }
+
+ // Move the data in the buffer
+ size_t moveLength = m_buffer.cursize - (startPos + oldLength);
+ Q_memmove(m_buffer.data + startPos + newLength, m_buffer.data + startPos + oldLength, moveLength);
+ m_buffer.cursize += diffLength;
+
+ // Update the position of all subsequent parameters
+ for (size_t i = index + 1; i < m_paramCount; i++)
+ m_params[i].pos += diffLength;
+ }
+}
+
+// Returns the integer value of the parameter at the given index
+int MessageImpl::getParamInt(size_t index) const
+{
+ // Ensure index is within bounds
+ if (index < 0 || index >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ void *buf = m_buffer.data + m_params[index].pos;
+ switch (m_params[index].type)
+ {
+ case IMessage::ParamType::Byte:
+ return *(uint8 *)buf;
+ case IMessage::ParamType::Char:
+ return *(int8 *)buf;
+ case IMessage::ParamType::Short:
+ case IMessage::ParamType::Entity:
+ return *(int16 *)buf;
+ case IMessage::ParamType::Long:
+ return *(uint16 *)buf;
+ default:
+ return 0; // bad type
+ }
+}
+
+// Returns the float value of the parameter at the given index
+float MessageImpl::getParamFloat(size_t index) const
+{
+ // Ensure index is within bounds
+ if (index < 0 || index >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ const Param_t ¶m = m_params[index];
+ void *buf = m_buffer.data + param.pos;
+ switch (param.type)
+ {
+ case IMessage::ParamType::Angle:
+ return (float)(*(uint8 *)buf * (360.0 / 256.0));
+ case IMessage::ParamType::Coord:
+ return (float)(*(int16 *)buf * (1.0 / 8));
+ default:
+ break; // bad type
+ }
+
+ return 0;
+}
+
+// Returns the string value of the parameter at the given index
+const char *MessageImpl::getParamString(size_t index) const
+{
+ // Ensure index is within bounds
+ if (index < 0 || index >= m_paramCount)
+ return nullptr;
+
+ // Get the parameter value if it is a string
+ const Param_t ¶m = m_params[index];
+ if (param.type == IMessage::ParamType::String)
+ return (const char *)m_buffer.data + param.pos;
+
+ return nullptr;
+}
+
+// Sets the integer value of the parameter at the given index
+void MessageImpl::setParamInt(size_t index, int value)
+{
+ setParamPrimitive(index, value);
+}
+
+// Sets the float value of the parameter at the given index
+void MessageImpl::setParamFloat(size_t index, float value)
+{
+ setParamPrimitive(index, value);
+}
+
+// Sets the vector value of the parameter at the given index
+void MessageImpl::setParamVec(size_t index, const float *pos)
+{
+ if (!pos)
+ return;
+
+ // Ensure index is within bounds
+ if (index < 0 || (index + 3) >= m_paramCount)
+ return;
+
+ // Get the parameter position in the buffer
+ const Param_t ¶m = m_params[index];
+
+ int16 *pbuf = (int16 *)m_buffer.data + param.pos;
+
+ // Set each component of the vector parameter
+ *(int16 *)pbuf++ = (int16)(pos[0] * 8.0);
+ *(int16 *)pbuf++ = (int16)(pos[1] * 8.0);
+ *(int16 *)pbuf++ = (int16)(pos[2] * 8.0);
+
+ // Mark message as modified
+ m_modified = true;
+}
+
+// Sets the string value of the parameter at the given index
+void MessageImpl::setParamString(size_t index, const char *value)
+{
+ if (!value)
+ return;
+
+ // Ensure index is within bounds
+ if (index < 0 || index >= m_paramCount)
+ return;
+
+ // Calculate the length of the string
+ size_t length = Q_strlen(value) + 1;
+ const Param_t ¶m = m_params[index];
+
+ // Transform buffer to accommodate the new string length
+ setTxformBuffer(index, param.pos, param.len, length);
+
+ // Copy the string value to the buffer
+ memcpy(m_buffer.data + param.pos, value, length);
+
+ // Mark message as modified
+ m_modified = true;
+}
+
+MessageManagerImpl::MessageManagerImpl()
+{
+ m_inblock = false;
+ m_inhook = false;
+}
+
+// Register hook function for the game message type
+void MessageManagerImpl::registerHook(int msgType, hookfunc_t handler, int priority)
+{
+ m_hooks[msgType].registerHook(handler, priority);
+}
+
+// Unregister hook function for the game message type
+void MessageManagerImpl::unregisterHook(int msgType, hookfunc_t handler)
+{
+ m_hooks[msgType].unregisterHook(handler);
+}
+
+// Get the block type for the game message type
+IMessage::BlockType MessageManagerImpl::getMessageBlock(int msgType) const
+{
+ return m_blocks[msgType];
+}
+
+// Set the block type for the game message type
+void MessageManagerImpl::setMessageBlock(int msgType, IMessage::BlockType blockType)
+{
+ m_blocks[msgType] = blockType;
+}
+
+bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
+{
+ // Check if the message type is blocked
+ if (m_blocks[msg_type] != IMessage::BlockType::Not)
+ {
+ m_inblock = true;
+ return false;
+ }
+
+ // Check if there are hooks registered for the message type
+ m_inhook = m_hooks[msg_type].getCount() > 0;
+
+ if (m_inhook)
+ {
+ // Check for stack overflow
+ if (m_stack.size() >= m_stack.max_size() - 1)
+ Sys_Error("%s: stack overflow in #%i user message.\nIndicate potential recursive calls...\n", __func__, msg_type);
+
+ // Push a new game message onto the stack
+ m_stack.push();
+
+ // Initialize the message
+ MessageImpl &msg = m_stack.back();
+ msg.setActive(msg_dest, msg_type, pOrigin, ed);
+ }
+
+ return true;
+}
+
+static void EXT_FUNC SendUserMessageData(IMessage *msg)
+{
+ // Set global variables with message data
+ gMsgType = msg->getType();
+ gMsgEntity = msg->getEdict();
+ gMsgDest = static_cast(msg->getDest());
+
+ gMsgOrigin[0] = msg->getOrigin()[0];
+ gMsgOrigin[1] = msg->getOrigin()[1];
+ gMsgOrigin[2] = msg->getOrigin()[2];
+
+ gMsgStarted = TRUE;
+
+ // Copy message data to global buffer and call end of the hookchain
+ msg->setCopybackBuffer(&gMsgBuffer);
+ PF_MessageEnd_I();
+}
+
+bool MessageManagerImpl::MessageEnd()
+{
+ // Check if in block mode
+ if (m_inblock)
+ {
+ m_inblock = false;
+
+ // Unblock the message type if it was blocked once
+ if (m_blocks[gMsgType] == IMessage::BlockType::Once)
+ m_blocks[gMsgType] = IMessage::BlockType::Not;
+
+ return false;
+ }
+
+ // Check if not in hook
+ if (!m_inhook)
+ return true;
+
+ gMsgStarted = FALSE;
+
+ // Get the message from the top of the stack
+ MessageImpl &msg = m_stack.back();
+
+ // Set buffer from global buffer and call hookchain
+ msg.setBuffer(&gMsgBuffer);
+ m_hooks[msg.getType()].callChain(SendUserMessageData, &msg);
+ m_inhook = false;
+
+ // Clear the message and pop from the stack
+ msg.clear();
+ m_stack.pop();
+
+ return false;
+}
+
+bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length)
+{
+ // Check if in block mode
+ if (m_inblock)
+ return false;
+
+ // Check if in hook mode
+ if (m_inhook)
+ {
+ // Add parameter to top stack message
+ MessageImpl &msg = m_stack.back();
+ msg.addParam(type, length);
+ }
+
+ return true;
+}
+
+//
+// Functions intercept to handle messages
+//
+
+void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
+{
+ // Set global message type
+ gMsgType = msg_type;
+
+ // Begin message manager
+ if (MessageManager().MessageBegin(msg_dest, msg_type, pOrigin, ed))
+ PF_MessageBegin_I(msg_dest, msg_type, pOrigin, ed);
+}
+
+void EXT_FUNC PF_MessageEnd_Intercept(void)
+{
+ // End message manager
+ if (MessageManager().MessageEnd())
+ PF_MessageEnd_I(); // Call original message end function if the manager allows it
+}
+
+void EXT_FUNC PF_WriteByte_Intercept(int iValue)
+{
+ // Write byte parameter to the message if the manager allows it
+ if (MessageManager().WriteParam(IMessage::ParamType::Byte))
+ PF_WriteByte_I(iValue);
+}
+
+void EXT_FUNC PF_WriteChar_Intercept(int iValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Char))
+ PF_WriteChar_I(iValue);
+}
+
+void EXT_FUNC PF_WriteShort_Intercept(int iValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Short))
+ PF_WriteShort_I(iValue);
+}
+
+void EXT_FUNC PF_WriteLong_Intercept(int iValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Long))
+ PF_WriteLong_I(iValue);
+}
+
+void EXT_FUNC PF_WriteAngle_Intercept(float flValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Angle))
+ PF_WriteAngle_I(flValue);
+}
+
+void EXT_FUNC PF_WriteCoord_Intercept(float flValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Coord))
+ PF_WriteCoord_I(flValue);
+}
+
+void EXT_FUNC PF_WriteString_Intercept(const char *sz)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::String, sz ? Q_strlen(sz) + 1 : 1))
+ PF_WriteString_I(sz);
+}
+
+void EXT_FUNC PF_WriteEntity_Intercept(int iValue)
+{
+ if (MessageManager().WriteParam(IMessage::ParamType::Entity))
+ PF_WriteEntity_I(iValue);
+}
+
+// Initialization function to set up function interception
+void MessageManagerImpl::Init()
+{
+ // Set function callback to intercept functions
+ g_engfuncsExportedToDlls.pfnMessageBegin = PF_MessageBegin_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteByte = PF_WriteByte_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteChar = PF_WriteChar_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteShort = PF_WriteShort_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteLong = PF_WriteLong_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteAngle = PF_WriteAngle_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteCoord = PF_WriteCoord_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteString = PF_WriteString_Intercept;
+ g_engfuncsExportedToDlls.pfnWriteEntity = PF_WriteEntity_Intercept;
+ g_engfuncsExportedToDlls.pfnMessageEnd = PF_MessageEnd_Intercept;
+}
+
+MessageManagerImpl &MessageManager()
+{
+ // Instance of the message manager singleton
+ static MessageManagerImpl instance{};
+ return instance;
+}
diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.h b/rehlds/rehlds/rehlds_messagemngr_impl.h
new file mode 100644
index 0000000..b0ad18c
--- /dev/null
+++ b/rehlds/rehlds/rehlds_messagemngr_impl.h
@@ -0,0 +1,277 @@
+/*
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at
+* your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+*/
+
+#pragma once
+
+#include "IMessageManager.h"
+#include "hookchains.h"
+
+/**
+ * @brief Implementation interface for defining message parameters and behavior for a game message object
+ */
+class MessageImpl: public IMessage
+{
+public:
+ MessageImpl();
+ ~MessageImpl() = default;
+
+ /**
+ * @brief Returns the number of parameters in the message
+ * @return The number of parameters
+ */
+ int getParamCount() const { return m_paramCount; }
+
+ /**
+ * @brief Returns the type of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The type of the parameter
+ */
+ ParamType getParamType(size_t index) const { return m_params[index].type; }
+
+ /**
+ * @brief Returns the integer value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The integer value of the parameter
+ */
+ int getParamInt(size_t index) const;
+
+ /**
+ * @brief Returns the float value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The float value of the parameter
+ */
+ float getParamFloat(size_t index) const;
+
+ /**
+ * @brief Returns the string value of the parameter at the given index
+ * @param index The index of the parameter
+ * @return The string value of the parameter
+ */
+ const char *getParamString(size_t index) const;
+
+ /**
+ * @brief Sets the integer value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param value The integer value to set
+ */
+ void setParamInt(size_t index, int value);
+
+ /**
+ * @brief Sets the float value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param value The float value to set
+ */
+ void setParamFloat(size_t index, float value);
+
+ /**
+ * @brief Sets the vector value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param pos The vector value to set
+ */
+ void setParamVec(size_t index, const float *pos);
+
+ /**
+ * @brief Sets the string value of the parameter at the given index
+ * @param index The index of the parameter
+ * @param string The string value to set
+ */
+ void setParamString(size_t index, const char *string);
+
+ /**
+ * @brief Returns the destination of the message
+ * @return The destination of the message
+ */
+ Dest getDest() const { return m_dest; }
+
+ /**
+ * @brief Returns the type of the message
+ * @return The type of the message
+ */
+ int getType() const { return m_type; }
+
+ /**
+ * @brief Returns the origin of the message
+ * @return The origin of the message
+ */
+ const float *getOrigin() const { return m_origin; }
+
+ /**
+ * @brief Returns the edict associated with the message
+ * @return The edict associated with the message
+ */
+ edict_t *getEdict() const { return m_edict; }
+
+ /**
+ * @brief Returns whether the message has been modified
+ * @return True if the message has been modified, false otherwise
+ */
+ bool isModified() const { return m_modified; }
+
+private:
+
+ friend class MessageManagerImpl;
+
+ // Sets the active state of the message with the given parameters
+ void setActive(int dest, int type, const float *origin, edict_t *edict);
+
+ // Sets the buffer for the message
+ void setBuffer(sizebuf_t *pbuf);
+
+ // Set the copyback buffer for the message
+ void setCopybackBuffer(sizebuf_t *pbuf);
+
+ // Adds a parameter to the message
+ void addParam(IMessage::ParamType type, size_t length);
+
+ // Clears the message after execution
+ void clear();
+
+ template
+ void setParamPrimitive(size_t index, T value);
+
+ // Transforms buffer after sets string for a parameter at the given index
+ void setTxformBuffer(size_t index, size_t startPos, size_t oldLength, size_t newLength);
+
+ bool m_modified; // Flag indicating whether the message has been modified
+ Dest m_dest; // The destination of the message
+ int m_type; // The type of the message
+ float m_origin[3]; // The origin of the message
+ edict_t* m_edict; // The edict associated with the message
+
+ uint8 m_bufferData[512]; // The buffer data for the message 'm_buffer'
+ sizebuf_t m_buffer; // The buffer for the message
+
+ struct Param_t
+ {
+ ParamType type; // The type of the parameter
+ size_t pos; // The position of the parameter in the buffer
+ size_t len; // The length of the parameter in the buffer
+ };
+
+ static const size_t MAX_PARAMS = 16; // The maximum number of parameters allowed in the message
+ Param_t m_params[MAX_PARAMS]; // The array of parameters in the message
+ size_t m_paramCount; // The number of parameters in the message
+};
+
+/**
+ * @brief Implementation interface manages hooks and blocking behavior game messages
+ */
+class MessageManagerImpl: public IMessageManager
+{
+public:
+
+ void Init();
+
+ MessageManagerImpl();
+ ~MessageManagerImpl() = default;
+
+ /**
+ * @brief Returns the major version of the MessageManager
+ * @return The major version
+ */
+ int getMajorVersion() const { return MESSAGEMNGR_VERSION_MAJOR; }
+
+ /**
+ * @brief Returns the minor version of the MessageManager
+ * @return The minor version
+ */
+ int getMinorVersion() const { return MESSAGEMNGR_VERSION_MINOR; }
+
+ /**
+ * @brief Returns the blocking behavior for the given message type
+ * @param msgType The message type
+ * @return The blocking behavior for the given message type
+ */
+ IMessage::BlockType getMessageBlock(int msgType) const;
+
+ /**
+ * @brief Sets the blocking behavior for the given message type
+ * @param msgType The message type
+ * @param blockType The blocking behavior to set
+ */
+ void setMessageBlock(int msgType, IMessage::BlockType blockType);
+
+ /**
+ * @brief Registers a hook function for the given message type
+ * @param msgType The message type to register the hook for
+ * @param handler The hook function to register
+ * @param priority The priority of the hook function (see enum HookChainPriority)
+ */
+ void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT);
+
+ /**
+ * @brief Unregisters a hook function for the given message type
+ * @param msgType The message type to unregister the hook for
+ * @param handler The hook function to unregister
+ */
+ void unregisterHook(int msgType, hookfunc_t handler);
+
+private:
+ friend void PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
+ friend void PF_MessageEnd_Intercept();
+ friend void PF_WriteByte_Intercept(int iValue);
+ friend void PF_WriteChar_Intercept(int iValue);
+ friend void PF_WriteShort_Intercept(int iValue);
+ friend void PF_WriteLong_Intercept(int iValue);
+ friend void PF_WriteAngle_Intercept(float flValue);
+ friend void PF_WriteCoord_Intercept(float flValue);
+ friend void PF_WriteString_Intercept(const char *sz);
+ friend void PF_WriteEntity_Intercept(int iValue);
+
+ bool MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
+ bool MessageEnd();
+
+private:
+ bool WriteParam(IMessage::ParamType type, size_t length = -1);
+
+ bool m_inblock; // Flag indicating whether a message block is currently active
+ bool m_inhook; // Flag indicating whether a message hook is currently active
+
+ /**
+ * @brief Helper a templated Stack class to manage a stack of Message objects
+ * @tparam T The type of objects stored in the stack
+ * @tparam MAX The maximum size of the stack
+ */
+ template
+ class Stack
+ {
+ public:
+ void push() { _size++; }
+ void pop () { _size--; }
+
+ size_t size() const { return _size; }
+ size_t max_size() const { return MAX; }
+
+ const T &back() const { return _data[_size - 1]; }
+ T &back() { return _data[_size - 1]; }
+ private:
+ size_t _size = 0u;
+ T _data[MAX]{};
+ };
+
+ static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough
+ Stack m_stack;
+
+ IVoidHookChainRegistryImpl m_hooks [MAX_USERMESSAGES]{}; // The array of hook chain registries for each message type
+ IMessage::BlockType m_blocks[MAX_USERMESSAGES]{}; // The array of blocking behaviors for each message type
+};
+
+/**
+ * @brief The singleton instance of the MessageManager
+ */
+extern MessageManagerImpl &MessageManager();