From c9f9bbfff98cdb6e8ca45b9ab626f40d0c7bb22b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 13 May 2024 19:20:19 +0700 Subject: [PATCH] Implement API interface game message manager --- rehlds/CMakeLists.txt | 1 + rehlds/engine/sys_dll.cpp | 4 + rehlds/msvc/ReHLDS.vcxproj | 3 + rehlds/msvc/ReHLDS.vcxproj.filters | 9 + rehlds/public/rehlds/IMessageManager.h | 223 ++++++++++ rehlds/public/rehlds/rehlds_api.h | 2 + rehlds/rehlds/hookchains_impl.cpp | 4 + rehlds/rehlds/hookchains_impl.h | 12 +- rehlds/rehlds/precompiled.h | 1 + rehlds/rehlds/rehlds_api_impl.cpp | 4 + rehlds/rehlds/rehlds_api_impl.h | 1 + rehlds/rehlds/rehlds_messagemngr_impl.cpp | 508 ++++++++++++++++++++++ rehlds/rehlds/rehlds_messagemngr_impl.h | 277 ++++++++++++ 13 files changed, 1048 insertions(+), 1 deletion(-) create mode 100644 rehlds/public/rehlds/IMessageManager.h create mode 100644 rehlds/rehlds/rehlds_messagemngr_impl.cpp create mode 100644 rehlds/rehlds/rehlds_messagemngr_impl.h diff --git a/rehlds/CMakeLists.txt b/rehlds/CMakeLists.txt index a9b371b28..e48e87d16 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 08431a490..ddfcef34d 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 dc153551e..6b2a50bce 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 a59f265d8..9d0731a25 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 000000000..cf818dbc2 --- /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 b82f332f3..743e47d21 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 @@ class IRehldsApi { 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 13f6e36e6..eaa49fcc7 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 dd2523c91..0ab780097 100644 --- a/rehlds/rehlds/hookchains_impl.h +++ b/rehlds/rehlds/hookchains_impl.h @@ -109,10 +109,11 @@ class AbstractHookChainRegistry { 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 @@ class IHookChainRegistryImpl : public IHookChainRegistry < t_ret, t_args...>, pu 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 @@ class IVoidHookChainRegistryImpl : public IVoidHookChainRegistry , pu 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 de99a0301..a947fcf29 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 2b4ede0b2..62a392070 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 6aece98cf..cda2b5a13 100644 --- a/rehlds/rehlds/rehlds_api_impl.h +++ b/rehlds/rehlds/rehlds_api_impl.h @@ -391,6 +391,7 @@ class CRehldsApi : public IRehldsApi { 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 000000000..617ceb1a7 --- /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 000000000..b0ad18cb8 --- /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();