diff --git a/README.md b/README.md
index b824c153..9098f127 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
sv_net_incoming_decompression_max_size <16|65536> // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
sv_net_incoming_decompression_punish // Time in minutes for which the player will be banned for malformed/abnormal bzip2 fragments (0 - Permanent, use a negative number for a kick). Default: -1
sv_tags <comma-delimited string list of tags> // Sets a string defining the "gametags" for this server, this is optional, but if it is set it allows users/scripts to filter in the matchmaking/server-browser interfaces based on the value. Default: ""
+sv_filterban <-1|0|1>// Set packet filtering by IP mode. -1 - All players will be rejected without any exceptions. 0 - No checks will happen. 1 - All incoming players will be checked if they're IP banned (if they have an IP filter entry), if they are, they will be kicked. Default: 1
diff --git a/rehlds/CMakeLists.txt b/rehlds/CMakeLists.txt
index a9b371b2..e48e87d1 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/common/quakedef.h b/rehlds/common/quakedef.h
index 21f88e21..17f166d9 100644
--- a/rehlds/common/quakedef.h
+++ b/rehlds/common/quakedef.h
@@ -29,6 +29,9 @@
typedef int BOOL;
+// The maximum user messages
+#define MAX_USERMESSAGES 256
+
// user message
#define MAX_USER_MSG_DATA 192
diff --git a/rehlds/engine/host.cpp b/rehlds/engine/host.cpp
index ce21d5b2..dc81e93a 100644
--- a/rehlds/engine/host.cpp
+++ b/rehlds/engine/host.cpp
@@ -375,7 +375,8 @@ void EXT_FUNC SV_ClientPrintf_internal(const char *Dest)
{
char string[1024];
- Q_strlcpy(string, Dest, min(strlen(Dest) + 1, sizeof(string)));
+ Q_strlcpy(string, Dest);
+
MSG_WriteByte(&host_client->netchan.message, svc_print);
MSG_WriteString(&host_client->netchan.message, string);
}
diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp
index 71fc0f6d..b4b75e9c 100644
--- a/rehlds/engine/net_chan.cpp
+++ b/rehlds/engine/net_chan.cpp
@@ -38,7 +38,7 @@ cvar_t net_drawslider = { "net_drawslider", "0", 0, 0.0f, nullptr};
cvar_t net_chokeloopback = { "net_chokeloop", "0", 0, 0.0f, nullptr};
cvar_t sv_net_incoming_decompression = { "sv_net_incoming_decompression", "1", 0, 1.0f, nullptr };
-cvar_t sv_net_incoming_decompression_max_ratio = { "sv_net_incoming_decompression_max_ratio", "75.0", 0, 75.0f, nullptr };
+cvar_t sv_net_incoming_decompression_max_ratio = { "sv_net_incoming_decompression_max_ratio", "80.0", 0, 80.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_size = { "sv_net_incoming_decompression_max_size", "65536", 0, 65536.0f, nullptr };
cvar_t sv_net_incoming_decompression_punish = { "sv_net_incoming_decompression_punish", "-1", 0, -1.0f, NULL };
diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp
index dfd28468..d53a74bc 100644
--- a/rehlds/engine/pr_cmds.cpp
+++ b/rehlds/engine/pr_cmds.cpp
@@ -2124,7 +2124,7 @@ void EXT_FUNC PF_MessageBegin_I(int msg_dest, int msg_type, const float *pOrigin
if (msg_type == 0)
Sys_Error("%s: Tried to create a message with a bogus message type ( 0 )", __func__);
- gMsgStarted = 1;
+ gMsgStarted = TRUE;
gMsgType = msg_type;
gMsgEntity = ed;
gMsgDest = msg_dest;
@@ -2151,7 +2151,7 @@ void EXT_FUNC PF_MessageEnd_I(void)
qboolean MsgIsVarLength = 0;
if (!gMsgStarted)
Sys_Error("%s: called with no active message\n", __func__);
- gMsgStarted = 0;
+ gMsgStarted = FALSE;
if (gMsgEntity && (gMsgEntity->v.flags & FL_FAKECLIENT))
return;
@@ -2275,6 +2275,7 @@ void EXT_FUNC PF_WriteByte_I(int iValue)
{
if (!gMsgStarted)
Sys_Error("%s: called with no active message\n", __func__);
+
MSG_WriteByte(&gMsgBuffer, iValue);
}
diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h
index c59332e1..9446f146 100644
--- a/rehlds/engine/server.h
+++ b/rehlds/engine/server.h
@@ -95,13 +95,6 @@ typedef enum redirect_e
RD_PACKET = 2,
} redirect_t;
-typedef enum server_state_e
-{
- ss_dead = 0,
- ss_loading = 1,
- ss_active = 2,
-} server_state_t;
-
typedef struct server_s
{
qboolean active;
@@ -468,6 +461,7 @@ void SV_QueryMovevarsChanged(void);
void SV_SendServerinfo(sizebuf_t *msg, client_t *client);
void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client);
void SV_SendResources(sizebuf_t *msg);
+void SV_SendResources_internal(sizebuf_t *msg);
void SV_WriteClientdataToMessage(client_t *client, sizebuf_t *msg);
void SV_WriteSpawn(sizebuf_t *msg);
void SV_SendUserReg(sizebuf_t *msg);
diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp
index 8f99c0fa..5dd76f27 100644
--- a/rehlds/engine/sv_main.cpp
+++ b/rehlds/engine/sv_main.cpp
@@ -1199,6 +1199,11 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client)
}
void SV_SendResources(sizebuf_t *msg)
+{
+ g_RehldsHookchains.m_SV_SendResources.callChain(SV_SendResources_internal, msg);
+}
+
+void EXT_FUNC SV_SendResources_internal(sizebuf_t *msg)
{
unsigned char nullbuffer[32];
Q_memset(nullbuffer, 0, sizeof(nullbuffer));
@@ -3836,13 +3841,20 @@ void SV_ProcessFile(client_t *cl, char *filename)
qboolean SV_FilterPacket(void)
{
+ // sv_filterban filtering IP mode
+ // -1: all players will be rejected without any exceptions
+ // 0: no checks will happen
+ // 1: all incoming players will be checked if they're IP banned (if they have an IP filter entry), if they are, they will be kicked
+
+ qboolean bNegativeFilter = (sv_filterban.value == 1) ? TRUE : FALSE;
+
for (int i = numipfilters - 1; i >= 0; i--)
{
ipfilter_t* curFilter = &ipfilters[i];
if (curFilter->compare.u32 == 0xFFFFFFFF || curFilter->banEndTime == 0.0f || curFilter->banEndTime > realtime)
{
if ((*(uint32*)net_from.ip & curFilter->mask) == curFilter->compare.u32)
- return (int)sv_filterban.value;
+ return bNegativeFilter;
}
else
{
@@ -3852,7 +3864,8 @@ qboolean SV_FilterPacket(void)
--numipfilters;
}
}
- return sv_filterban.value == 0.0f;
+
+ return !bNegativeFilter;
}
void SV_SendBan(void)
@@ -6555,7 +6568,13 @@ void SV_ClearEntities(void)
}
int EXT_FUNC RegUserMsg(const char *pszName, int iSize)
{
- if (giNextUserMsg > 255 || !pszName || Q_strlen(pszName) > 11 || iSize > 192)
+ if (giNextUserMsg >= MAX_USERMESSAGES)
+ return 0;
+
+ if (iSize > MAX_USER_MSG_DATA)
+ return 0;
+
+ if (!pszName || Q_strlen(pszName) >= MAX_USERMESSAGES_LENGTH - 1)
return 0;
UserMsg *pUserMsgs = sv_gpUserMsgs;
diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp
index 425e439e..eced090b 100644
--- a/rehlds/engine/sv_upld.cpp
+++ b/rehlds/engine/sv_upld.cpp
@@ -207,14 +207,13 @@ void SV_Customization(client_t *pPlayer, resource_t *pResource, qboolean bSkipPl
// Creates customizations list for the current player and sends resources to other players.
void SV_RegisterResources(void)
{
- resource_t *pResource;
client_t *pHost = host_client;
pHost->uploading = FALSE;
#ifdef REHLDS_FIXES
SV_CreateCustomizationList(pHost); // FIXED: Call this function only once. It was crazy to call it for each resource available.
#else // REHLDS_FIXES
- for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext)
+ for (resource_t *pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext)
{
SV_CreateCustomizationList(pHost);
SV_Customization(pHost, pResource, TRUE);
diff --git a/rehlds/engine/sys_dll.cpp b/rehlds/engine/sys_dll.cpp
index 08431a49..ddfcef34 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/engine/usermsg.h b/rehlds/engine/usermsg.h
index ffff648d..b7520557 100644
--- a/rehlds/engine/usermsg.h
+++ b/rehlds/engine/usermsg.h
@@ -31,11 +31,14 @@
#include "maintypes.h"
#include "quakedef.h"
+// The maximum length of a usermessage name in a network transmission
+#define MAX_USERMESSAGES_LENGTH 16
+
typedef struct _UserMsg
{
int iMsg;
int iSize;
- char szName[16];
+ char szName[MAX_USERMESSAGES_LENGTH];
struct _UserMsg *next;
pfnUserMsgHook pfn;
} UserMsg;
diff --git a/rehlds/msvc/ReHLDS.vcxproj b/rehlds/msvc/ReHLDS.vcxproj
index 934a19cd..5d461b5e 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 a59f265d..9d0731a2 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 00000000..c93a2ec7
--- /dev/null
+++ b/rehlds/public/rehlds/IMessageManager.h
@@ -0,0 +1,337 @@
+/*
+*
+* 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
+ };
+
+ /**
+ * @brief Data types for message data
+ */
+ enum class DataType : uint8
+ {
+ Any, // Any part of the message
+ Dest, // Destination of the message
+ Index, // Index of the message
+ Origin, // Origin of the message
+ Edict, // Pointer to the edict of the recipient client
+ Param, // Parameter of the message
+ Max
+ };
+
+ virtual ~IMessage() = default;
+
+ /**
+ * @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 index of the message
+ * @return The index of the message
+ */
+ virtual int getId() 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 Checks if the specified type of message data has been modified
+ *
+ * This function allows you to check if any part of the message data, such as its
+ * destination, type, origin, edict, or any specific parameter, has been modified
+ *
+ * @param type The type of the data to check for modification
+ * This can be one of the following:
+ * - DataType::Any: Check if any part of the message has been modified
+ * - DataType::Dest: Check if the destination has been modified
+ * - DataType::Index: Check if the message ID has been modified
+ * - DataType::Origin: Check if the origin has been modified
+ * - DataType::Edict: Check if the edict pointer has been modified
+ * - DataType::Param: Check if a specific parameter has been modified
+ *
+ * @param index The index of the parameter to check for modification (used only when type is DataType::Param)
+ * Default value is -1, which means the parameter index is not applicable
+ *
+ * @return True if the specified data type has been modified, false otherwise
+ */
+ virtual bool isDataModified(DataType type = DataType::Any, size_t index = -1) const = 0;
+
+ /**
+ * @brief Resets a specific type of message data to its original value
+ *
+ * @param type The type of data to reset to its original value
+ * This can be one of the following:
+ * - DataType::Any: Reset all modified message data to its original values
+ * - DataType::Dest: Reset the destination to its original value
+ * - DataType::Index: Reset the message ID to its original value
+ * - DataType::Origin: Reset the origin to its original value
+ * - DataType::Edict: Reset the edict pointer of the recipient client to its original value
+ * - DataType::Param: Reset a specific parameter to its original value
+ *
+ * @param index The index of the parameter to reset (used only when type is DataType::Param)
+ * Default value is -1, which means the parameter index is not applicable
+ *
+ * @return True if the modified data type was reset, false otherwise
+ */
+ virtual bool resetModifiedData(DataType type = DataType::Any, size_t index = -1) = 0;
+
+ /**
+ * @brief Sets the destination of the message
+ */
+ virtual void setDest(Dest dest) = 0;
+
+ /**
+ * @brief Sets the index of the message
+ */
+ virtual void setId(int msg_id) = 0;
+
+ /**
+ * @brief Sets the origin of the message
+ */
+ virtual void setOrigin(const float *origin) = 0;
+
+ /**
+ * @brief Sets the edict associated with the message
+ */
+ virtual void setEdict(struct edict_s *pEdict) = 0;
+
+ /**
+ * @brief Returns the original destination of the message before any modifications
+ * @return The original destination of the message
+ */
+ virtual Dest getOriginalDest() const = 0;
+
+ /**
+ * @brief Returns the original type of the message before any modifications
+ * @return The original type of the message
+ */
+ virtual int getOriginalId() const = 0;
+
+ /**
+ * @brief Returns the original origin of the message before any modifications
+ * @return The original origin of the message
+ */
+ virtual const float* getOriginalOrigin() const = 0;
+
+ /**
+ * @brief Returns the original edict associated with the message before any modifications
+ * @return The original edict associated with the message
+ */
+ virtual struct edict_s* getOriginalEdict() const = 0;
+
+ /**
+ * @brief Returns the original integer value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original integer value of the parameter
+ */
+ virtual int getOriginalParamInt(size_t index) const = 0;
+
+ /**
+ * @brief Returns the original float value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original float value of the parameter
+ */
+ virtual float getOriginalParamFloat(size_t index) const = 0;
+
+ /**
+ * @brief Returns the original string value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original string value of the parameter
+ */
+ virtual const char* getOriginalParamString(size_t index) 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 2
+#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() = default;
+
+ /**
+ * @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 msg_id The message type
+ * @return The blocking behavior for the given message type
+ */
+ virtual IMessage::BlockType getMessageBlock(int msg_id) const = 0;
+
+ /**
+ * @brief Sets the blocking behavior for the given message type
+ * @param msg_id The message type
+ * @param blockType The blocking behavior to set
+ */
+ virtual void setMessageBlock(int msg_id, IMessage::BlockType blockType) = 0;
+
+ /**
+ * @brief Registers a hook function for the given message type
+ * @param msg_id 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 msg_id, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0;
+
+ /**
+ * @brief Unregisters a hook function for the given message type
+ * @param msg_id The message type to unregister the hook for
+ * @param handler The hook function to unregister
+ */
+ virtual void unregisterHook(int msg_id, hookfunc_t handler) = 0;
+};
diff --git a/rehlds/public/rehlds/maintypes.h b/rehlds/public/rehlds/maintypes.h
index b211e2d8..158a3542 100644
--- a/rehlds/public/rehlds/maintypes.h
+++ b/rehlds/public/rehlds/maintypes.h
@@ -67,4 +67,12 @@ typedef enum sv_delta_s
sv_packet_delta,
} sv_delta_t;
+// From engine/server.h
+typedef enum server_state_e
+{
+ ss_dead = 0,
+ ss_loading = 1,
+ ss_active = 2,
+} server_state_t;
+
#endif // MAINTYPES_H
diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h
index bc835819..743e47d2 100644
--- a/rehlds/public/rehlds/rehlds_api.h
+++ b/rehlds/public/rehlds/rehlds_api.h
@@ -31,13 +31,14 @@
#include "rehlds_interfaces.h"
#include "hookchains.h"
#include "FlightRecorder.h"
+#include "IMessageManager.h"
#include "interface.h"
#include "model.h"
#include "ObjectList.h"
#include "pr_dlls.h"
#define REHLDS_API_VERSION_MAJOR 3
-#define REHLDS_API_VERSION_MINOR 13
+#define REHLDS_API_VERSION_MINOR 14
//Steam_NotifyClientConnect hook
typedef IHookChain IRehldsHook_Steam_NotifyClientConnect;
@@ -259,6 +260,10 @@ typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_ClientPrintf
typedef IHookChain IRehldsHook_SV_AllowPhysent;
typedef IHookChainRegistry IRehldsHookRegistry_SV_AllowPhysent;
+//SV_SendResources hook
+typedef IVoidHookChain IRehldsHook_SV_SendResources;
+typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_SendResources;
+
class IRehldsHookchains {
public:
virtual ~IRehldsHookchains() { }
@@ -318,6 +323,7 @@ class IRehldsHookchains {
virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource() = 0;
virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf() = 0;
virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent() = 0;
+ virtual IRehldsHookRegistry_SV_SendResources* SV_SendResources() = 0;
};
struct RehldsFuncs_t {
@@ -441,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/public/rehlds/rehlds_interfaces.h b/rehlds/public/rehlds/rehlds_interfaces.h
index da3768c6..438838c1 100644
--- a/rehlds/public/rehlds/rehlds_interfaces.h
+++ b/rehlds/public/rehlds/rehlds_interfaces.h
@@ -36,6 +36,7 @@ class IGameClient;
#include "common_rehlds.h"
#include "userid_rehlds.h"
+#include "FileSystem.h"
#ifdef REHLDS_SELF
#include "server.h"
diff --git a/rehlds/rehlds/hookchains_impl.cpp b/rehlds/rehlds/hookchains_impl.cpp
index 13f6e36e..eaa49fcc 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 dd2523c9..0ab78009 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 de99a030..a947fcf2 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 8d7f9dad..62a39207 100644
--- a/rehlds/rehlds/rehlds_api_impl.cpp
+++ b/rehlds/rehlds/rehlds_api_impl.cpp
@@ -885,6 +885,10 @@ IRehldsHookRegistry_SV_AllowPhysent* CRehldsHookchains::SV_AllowPhysent() {
return &m_SV_AllowPhysent;
}
+IRehldsHookRegistry_SV_SendResources* CRehldsHookchains::SV_SendResources() {
+ return &m_SV_SendResources;
+}
+
int EXT_FUNC CRehldsApi::GetMajorVersion()
{
return REHLDS_API_VERSION_MAJOR;
@@ -913,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 699bd164..cda2b5a1 100644
--- a/rehlds/rehlds/rehlds_api_impl.h
+++ b/rehlds/rehlds/rehlds_api_impl.h
@@ -254,6 +254,10 @@ typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_ClientPri
typedef IHookChainImpl CRehldsHook_SV_AllowPhysent;
typedef IHookChainRegistryImpl CRehldsHookRegistry_SV_AllowPhysent;
+//SV_SendResources hook
+typedef IVoidHookChainImpl CRehldsHook_SV_SendResources;
+typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_SendResources;
+
class CRehldsHookchains : public IRehldsHookchains {
public:
CRehldsHookRegistry_Steam_NotifyClientConnect m_Steam_NotifyClientConnect;
@@ -311,6 +315,7 @@ class CRehldsHookchains : public IRehldsHookchains {
CRehldsHookRegistry_SV_AddResource m_SV_AddResource;
CRehldsHookRegistry_SV_ClientPrintf m_SV_ClientPrintf;
CRehldsHookRegistry_SV_AllowPhysent m_SV_AllowPhysent;
+ CRehldsHookRegistry_SV_SendResources m_SV_SendResources;
public:
EXT_FUNC virtual IRehldsHookRegistry_Steam_NotifyClientConnect* Steam_NotifyClientConnect();
@@ -368,6 +373,7 @@ class CRehldsHookchains : public IRehldsHookchains {
EXT_FUNC virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource();
EXT_FUNC virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf();
EXT_FUNC virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent();
+ EXT_FUNC virtual IRehldsHookRegistry_SV_SendResources* SV_SendResources();
};
extern CRehldsHookchains g_RehldsHookchains;
@@ -385,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 00000000..53405193
--- /dev/null
+++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp
@@ -0,0 +1,1066 @@
+/*
+*
+* 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"
+
+/**
+ * @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 Returns the original integer value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original integer value of the parameter
+ */
+ int getOriginalParamInt(size_t index) const;
+
+ /**
+ * @brief Returns the original float value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original float value of the parameter
+ */
+ float getOriginalParamFloat(size_t index) const;
+
+ /**
+ * @brief Returns the original string value of the parameter at the given index before any modifications
+ * @param index The index of the parameter
+ * @return The original string value of the parameter
+ */
+ const char* getOriginalParamString(size_t index) const;
+
+ /**
+ * @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 Sets the destination of the message
+ */
+ void setDest(Dest dest);
+
+ /**
+ * @brief Returns the destination of the message
+ * @return The destination of the message
+ */
+ Dest getDest() const { return m_Storage[BACK].dest; }
+ Dest getOriginalDest() const { return m_Storage[FRONT].dest; }
+
+ /**
+ * @brief Sets the index of the message
+ */
+ void setId(int msg_id);
+
+ /**
+ * @brief Returns the index of the message
+ * @return The index of the message
+ */
+ int getId() const { return m_Storage[BACK].msgid; }
+ int getOriginalId() const { return m_Storage[FRONT].msgid; }
+
+ /**
+ * @brief Sets the origin of the message
+ */
+ void setOrigin(const float *origin);
+
+ /**
+ * @brief Returns the origin of the message
+ * @return The origin of the message
+ */
+ const float *getOrigin() const { return m_Storage[BACK].origin; }
+ const float *getOriginalOrigin() const { return m_Storage[FRONT].origin; }
+
+ /**
+ * @brief Sets the edict associated with the message
+ */
+ void setEdict(edict_t *pEdict);
+
+ /**
+ * @brief Returns the edict associated with the message
+ * @return The edict associated with the message
+ */
+ edict_t *getEdict() const { return m_Storage[BACK].edict; }
+ edict_t *getOriginalEdict() const { return m_Storage[FRONT].edict; }
+
+ /**
+ * @brief Checks if the specified type of message data has been modified
+ *
+ * This function allows you to check if any part of the message data, such as its
+ * destination, type, origin, edict, or any specific parameter, has been modified
+ *
+ * @param type The type of the data to check for modification
+ * This can be one of the following:
+ * - DataType::Any: Check if any part of the message has been modified
+ * - DataType::Dest: Check if the destination has been modified
+ * - DataType::Index: Check if the message ID has been modified
+ * - DataType::Origin: Check if the origin has been modified
+ * - DataType::Edict: Check if the edict pointer has been modified
+ * - DataType::Param: Check if a specific parameter has been modified
+ *
+ * @param index The index of the parameter to check for modification (used only when type is DataType::Param)
+ * Default value is -1, which means the parameter index is not applicable
+ *
+ * @return True if the specified data type has been modified, false otherwise
+ */
+ bool isDataModified(DataType type, size_t index) const;
+
+ /**
+ * @brief Resets a specific type of message data to its original value
+ *
+ * @param type The type of data to reset to its original value
+ * This can be one of the following:
+ * - DataType::Any: Reset all modified message data to its original values
+ * - DataType::Dest: Reset the destination to its original value
+ * - DataType::Index: Reset the message ID to its original value
+ * - DataType::Origin: Reset the origin to its original value
+ * - DataType::Edict: Reset the edict pointer of the recipient client to its original value
+ * - DataType::Param: Reset a specific parameter to its original value
+ *
+ * @param index The index of the parameter to reset (used only when type is DataType::Param)
+ * Default value is -1, which means the parameter index is not applicable
+ *
+ * @return True if the modified data type was reset, false otherwise
+ */
+ bool resetModifiedData(DataType type, size_t index = -1);
+
+private:
+
+ friend class MessageManagerImpl;
+
+ // Sets the active state of the message with the given parameters
+ void setActive(int dest, int id, 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);
+
+ enum
+ {
+ FRONT, // Original buffer storage data
+ BACK, // Current modifiable buffer storage data
+ MAX_STORAGE
+ };
+
+ struct Storage_t
+ {
+ // The buffer storage data for the message 'm_buffer' (variable-length message limited to one byte is 256)
+ uint8 bufData[256]{};
+
+ // The buffer for the message
+ sizebuf_t buf = { "MsgMngr/Begin/End", SIZEBUF_ALLOW_OVERFLOW, bufData, sizeof(bufData), 0 };
+
+ // The destination of the message
+ Dest dest{};
+
+ // The index of the message
+ int msgid{0};
+
+ // The origin of the message
+ float origin[3]{0,0,0};
+
+ // The edict associated with the message
+ edict_t *edict{nullptr};
+ };
+
+ Storage_t m_Storage[MAX_STORAGE];
+
+#pragma pack(push, 1)
+ struct Param_t
+ {
+ bool modified : 1; // Flag indicating whether the message param has been modified
+ ParamType type : 3; // The type of the parameter
+ size_t posBack : 9; // The current position of the parameter in the buffer
+ size_t posFront : 9; // The stock position of the parameter in the buffer
+ size_t oldlen : 9; // The length of the parameter in the buffer
+ size_t newlen : 9; // The length of the parameter in the buffer
+ };
+#pragma pack(pop)
+
+ 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 : 4; // The number of parameters in the message
+
+ void resetParam(size_t index);
+
+ void setModifiedDataBit(DataType type);
+ void unsetModifiedDataBit(DataType type);
+ bool isDataTypeModified(DataType type) const;
+ uint8 m_modifiedDataBits : static_cast(DataType::Max);
+};
+
+/**
+* Inline methods
+*/
+inline void MessageImpl::setModifiedDataBit(DataType type)
+{
+ m_modifiedDataBits |= ((1 << static_cast(DataType::Any)) | (1 << static_cast(type)));
+}
+
+inline void MessageImpl::unsetModifiedDataBit(DataType type)
+{
+ m_modifiedDataBits &= ~((1 << static_cast(DataType::Any)) | (1 << static_cast(type)));
+}
+
+inline bool MessageImpl::isDataTypeModified(DataType type) const
+{
+ return (m_modifiedDataBits & (1 << static_cast(type))) != 0;
+}
+
+// Constructs a Message object
+MessageImpl::MessageImpl()
+{
+ m_paramCount = 0;
+ m_modifiedDataBits = 0;
+}
+
+// Sets the active state of the message with the given parameters
+void MessageImpl::setActive(int dest, int id, const float *origin, edict_t *edict)
+{
+ // Initialize storage buffers
+ for (int i = 0; i < MAX_STORAGE; i++)
+ {
+ Storage_t &storage = m_Storage[i];
+ storage.buf.cursize = 0;
+ storage.dest = static_cast(dest);
+ storage.msgid = id;
+ storage.edict = edict;
+
+ // Copy origin vector if provided
+ if (origin)
+ VectorCopy(origin, storage.origin);
+ else
+ VectorClear(storage.origin);
+ }
+
+ m_paramCount = 0;
+ m_modifiedDataBits = 0;
+}
+
+// Sets the buffer for the message
+void MessageImpl::setBuffer(sizebuf_t *pbuf)
+{
+ // Copy data from the provided buffer to the message buffer
+ for (int i = 0; i < MAX_STORAGE; i++)
+ {
+ Storage_t &storage = m_Storage[i];
+ Q_memcpy(storage.buf.data, pbuf->data, pbuf->cursize);
+ storage.buf.cursize = pbuf->cursize;
+ }
+}
+
+// Sets the copyback buffer for the message
+void MessageImpl::setCopybackBuffer(sizebuf_t *pbuf)
+{
+ const Storage_t &storage = m_Storage[BACK];
+
+ // Copy data from the message buffer back to the provided buffer
+ Q_memcpy(pbuf->data, storage.buf.data, storage.buf.cursize);
+ pbuf->cursize = storage.buf.cursize;
+}
+
+// Clears the message parameters
+void MessageImpl::clear()
+{
+ m_paramCount = 0;
+ m_modifiedDataBits = 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.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length;
+ param.posBack = param.posFront = 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 >= m_paramCount)
+ return;
+
+ Param_t ¶m = m_params[index];
+ void *pbuf = m_Storage[BACK].buf.data + param.posBack;
+
+ // 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:
+ *(uint32 *)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
+ param.modified = true;
+
+ // Mark the overall status as changed
+ setModifiedDataBit(DataType::Param);
+}
+
+// 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)
+ {
+ sizebuf_t &buf = m_Storage[BACK].buf;
+
+ // Check if the buffer size limit will be exceeded
+ if (buf.cursize + diffLength > buf.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 = buf.cursize - (startPos + oldLength);
+ if (moveLength > 0)
+ Q_memmove(buf.data + startPos + newLength, buf.data + startPos + oldLength, moveLength);
+
+ buf.cursize += diffLength;
+
+ if (newLength < oldLength)
+ Q_memset(buf.data + startPos + newLength + moveLength, 0, oldLength - newLength);
+
+ // Update the position of all subsequent parameters
+ for (size_t i = index + 1; i < m_paramCount; i++)
+ m_params[i].posBack += 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 >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ const Param_t ¶m = m_params[index];
+ const void *buf = m_Storage[BACK].buf.data + param.posBack;
+ switch (param.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 *(uint32 *)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 >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ const Param_t ¶m = m_params[index];
+ const void *buf = m_Storage[BACK].buf.data + param.posBack;
+ 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 >= 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_Storage[BACK].buf.data + param.posBack;
+
+ return nullptr;
+}
+
+int MessageImpl::getOriginalParamInt(size_t index) const
+{
+ // Ensure index is within bounds
+ if (index >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ const Param_t ¶m = m_params[index];
+ const void *buf = m_Storage[FRONT].buf.data + param.posFront;
+ switch (param.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 *(uint32 *)buf;
+ default:
+ return 0; // bad type
+ }
+}
+
+float MessageImpl::getOriginalParamFloat(size_t index) const
+{
+ // Ensure index is within bounds
+ if (index >= m_paramCount)
+ return 0;
+
+ // Get the parameter value based on its type
+ const Param_t ¶m = m_params[index];
+ const void *buf = m_Storage[FRONT].buf.data + param.posFront;
+ 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;
+}
+
+const char *MessageImpl::getOriginalParamString(size_t index) const
+{
+ // Ensure index is within bounds
+ if (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_Storage[FRONT].buf.data + param.posFront;
+
+ 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 + 3) >= m_paramCount)
+ return;
+
+ // Get the parameter position in the buffer
+ Param_t ¶m = m_params[index];
+
+ int16 *pbuf = (int16 *)m_Storage[BACK].buf.data + param.posBack;
+
+ // 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
+ param.modified = true;
+
+ // Mark the overall status as modified
+ setModifiedDataBit(DataType::Param);
+}
+
+// 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 >= m_paramCount)
+ return;
+
+ // Calculate the length of the string
+ Param_t ¶m = m_params[index];
+
+ param.newlen = Q_strlen(value) + 1;
+
+ // Transform buffer to accommodate the new string length
+ setTxformBuffer(index, param.posBack, param.oldlen, param.newlen);
+
+ // Copy the string value to the buffer
+ Q_memcpy(m_Storage[BACK].buf.data + param.posBack, value, param.newlen);
+
+ // Mark message as modified
+ param.modified = true;
+
+ // Mark the overall status as modified
+ setModifiedDataBit(DataType::Param);
+}
+
+// Sets the destination of the message
+void MessageImpl::setDest(Dest dest)
+{
+ m_Storage[BACK].dest = dest;
+ setModifiedDataBit(DataType::Dest);
+}
+
+// Sets the type of the message
+void MessageImpl::setId(int msg_id)
+{
+ m_Storage[BACK].msgid = msg_id;
+ setModifiedDataBit(DataType::Index);
+}
+
+// Sets the origin of the message
+void MessageImpl::setOrigin(const float *origin)
+{
+ // Copy origin vector if provided
+ if (origin)
+ VectorCopy(origin, m_Storage[BACK].origin);
+ else
+ VectorClear(m_Storage[BACK].origin);
+
+ setModifiedDataBit(DataType::Origin);
+}
+
+//Sets the edict associated with the message
+void MessageImpl::setEdict(edict_t *pEdict)
+{
+ m_Storage[BACK].edict = pEdict;
+ setModifiedDataBit(DataType::Edict);
+}
+
+bool MessageImpl::isDataModified(DataType type, size_t index) const
+{
+ if (!isDataTypeModified(type))
+ return false;
+
+ if (type == DataType::Param && index != -1)
+ {
+ // Ensure index is within bounds
+ if (index >= m_paramCount)
+ return false;
+
+ const Param_t ¶m = m_params[index];
+ return param.modified;
+ }
+
+ return true;
+}
+
+void MessageImpl::resetParam(size_t index)
+{
+ Param_t ¶m = m_params[index];
+
+ void *pbackbuf = m_Storage[BACK].buf.data + param.posBack;
+ const void *pfrontbuf = m_Storage[FRONT].buf.data + param.posFront;
+
+ // Set value based on parameter type
+ switch (param.type)
+ {
+ case IMessage::ParamType::Byte:
+ *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::Char:
+ *(int8 *)pbackbuf = *(int8 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::Short:
+ case IMessage::ParamType::Entity:
+ *(int16 *)pbackbuf = *(int16 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::Long:
+ *(uint32 *)pbackbuf = *(uint32 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::Angle:
+ *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::Coord:
+ *(int16 *)pbackbuf = *(int16 *)pfrontbuf;
+ break;
+ case IMessage::ParamType::String:
+ // Return the original string value from the front buffer
+ setTxformBuffer(index, param.posBack, param.newlen, param.oldlen);
+ Q_memcpy(pbackbuf, pfrontbuf, param.oldlen);
+ param.newlen = param.oldlen;
+ break;
+ default:
+ return; // bad type
+ }
+
+ // Unmark message as modified
+ param.modified = false;
+}
+
+// Resets a specific message parameter to its original value
+bool MessageImpl::resetModifiedData(DataType type, size_t index)
+{
+ Storage_t &storageBack = m_Storage[BACK];
+ const Storage_t &storageFront = m_Storage[FRONT];
+
+ unsetModifiedDataBit(type);
+
+ switch (type)
+ {
+ // Resets all message parameters and storage data to their original values
+ case DataType::Any:
+ {
+ // Update the position of all subsequent parameters
+ for (size_t i = 0; i < m_paramCount; i++)
+ {
+ Param_t ¶m = m_params[i];
+ param.posBack = param.posFront;
+ param.newlen = param.oldlen;
+ param.modified = false; // Unmark message as modified
+ }
+
+ // Copy front storage data to back buffer data
+ Q_memcpy(storageBack.buf.data, storageFront.buf.data, storageFront.buf.maxsize);
+
+ storageBack.dest = storageFront.dest;
+ storageBack.msgid = storageFront.msgid;
+ storageBack.edict = storageFront.edict;
+
+ VectorCopy(storageFront.origin, storageBack.origin);
+
+ m_modifiedDataBits = 0;
+
+ break;
+ }
+ case DataType::Dest:
+ storageBack.dest = storageFront.dest;
+ break;
+ case DataType::Index:
+ storageBack.msgid = storageFront.msgid;
+ break;
+ case DataType::Origin:
+ VectorCopy(storageFront.origin, storageBack.origin);
+ break;
+ case DataType::Edict:
+ storageBack.edict = storageFront.edict;
+ break;
+ case DataType::Param:
+ {
+ // Reset a specific parameter
+ if (index != -1)
+ {
+ // Ensure index is within bounds
+ if (index < m_paramCount)
+ resetParam(index);
+ }
+ else
+ {
+ for (size_t i = 0; i < m_paramCount; i++)
+ resetParam(i);
+ }
+
+ // Recalc modified data bits
+ for (size_t i = 0; i < m_paramCount; i++)
+ {
+ const Param_t ¶m = m_params[i];
+ if (param.modified)
+ {
+ setModifiedDataBit(DataType::Param);
+ break;
+ }
+ }
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ // If there was any other modified data, mark Any as overall modified data
+ if (m_modifiedDataBits != 0)
+ setModifiedDataBit(DataType::Any);
+
+ return true;
+}
+
+MessageManagerImpl::MessageManagerImpl() : m_stack(m_pool)
+{
+ m_inblock = false;
+ m_inhook = false;
+}
+
+// Register hook function for the game message type
+void MessageManagerImpl::registerHook(int msg_id, hookfunc_t handler, int priority)
+{
+ if (!m_hooks[msg_id])
+ m_hooks[msg_id] = new HookRegistry_t;
+
+ if (m_hooks[msg_id]->findHook((void *)handler))
+ return; // already registered
+
+ m_hooks[msg_id]->registerHook(handler, priority);
+}
+
+// Unregister hook function for the game message type
+void MessageManagerImpl::unregisterHook(int msg_id, hookfunc_t handler)
+{
+ if (!m_hooks[msg_id])
+ return;
+
+ m_hooks[msg_id]->unregisterHook(handler);
+
+ if (m_hooks[msg_id]->getCount() == 0)
+ {
+ delete m_hooks[msg_id];
+ m_hooks[msg_id] = nullptr;
+ m_pool.clear();
+ }
+}
+
+// Get the block type for the game message type
+IMessage::BlockType MessageManagerImpl::getMessageBlock(int msg_id) const
+{
+ return m_blocks[msg_id];
+}
+
+// Set the block type for the game message type
+void MessageManagerImpl::setMessageBlock(int msg_id, IMessage::BlockType blockType)
+{
+ m_blocks[msg_id] = blockType;
+}
+
+bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed)
+{
+ // Check if the message type is blocked
+ if (m_blocks[msg_id] != IMessage::BlockType::Not)
+ {
+ m_inblock = true;
+ return false;
+ }
+
+ // Check if there are hooks registered for the message type
+ m_inhook = (m_hooks[msg_id] && m_hooks[msg_id]->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_id);
+
+ // Push a new game message onto the stack
+ MessageImpl &msg = m_stack.push();
+
+ // Initialize the message
+ msg.setActive(msg_dest, msg_id, pOrigin, ed);
+ }
+
+ return true;
+}
+
+static void EXT_FUNC SendUserMessageData(IMessage *msg)
+{
+ // Set global variables with message data
+ gMsgType = msg->getId();
+ 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.top();
+
+ // Set buffer from global buffer and call hookchain
+ msg.setBuffer(&gMsgBuffer);
+ m_hooks[msg.getId()]->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.top();
+ msg.addParam(type, length);
+ }
+
+ return true;
+}
+
+//
+// Functions intercept to handle messages
+//
+
+void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed)
+{
+ // Set global message type
+ gMsgType = msg_id;
+
+ // Begin message manager
+ if (MessageManager().MessageBegin(msg_dest, msg_id, pOrigin, ed))
+ PF_MessageBegin_I(msg_dest, msg_id, 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 00000000..f016b193
--- /dev/null
+++ b/rehlds/rehlds/rehlds_messagemngr_impl.h
@@ -0,0 +1,183 @@
+/*
+*
+* 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"
+
+#include // std::unique_ptr
+#include // std::move
+
+/**
+ * @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 msg_id The message type
+ * @return The blocking behavior for the given message type
+ */
+ IMessage::BlockType getMessageBlock(int msg_id) const;
+
+ /**
+ * @brief Sets the blocking behavior for the given message type
+ * @param msg_id The message type
+ * @param blockType The blocking behavior to set
+ */
+ void setMessageBlock(int msg_id, IMessage::BlockType blockType);
+
+ /**
+ * @brief Registers a hook function for the given message type
+ * @param msg_id 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 msg_id, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT);
+
+ /**
+ * @brief Unregisters a hook function for the given message type
+ * @param msg_id The message type to unregister the hook for
+ * @param handler The hook function to unregister
+ */
+ void unregisterHook(int msg_id, hookfunc_t handler);
+
+private:
+ friend void PF_MessageBegin_Intercept(int msg_dest, int msg_id, 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_id, 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 The fixed-size memory pool holds a list of free objects
+ * @tparam T The type of objects stored in the stack
+ * @tparam MAX The maximum size of the stack
+ */
+ template
+ class MessagePool
+ {
+ public:
+ std::unique_ptr acquire()
+ {
+ if (_size > 0)
+ return std::move(_freeObjects[--_size]); // reusing
+ return std::make_unique(); // initialize constructor for new element
+ }
+
+ void release(std::unique_ptr obj)
+ {
+ if (_size < MAX)
+ _freeObjects[_size++] = std::move(obj);
+ }
+
+ void clear()
+ {
+ while (_size > 0)
+ _freeObjects[--_size].reset();
+ }
+
+ private:
+ size_t _size{0u};
+ std::unique_ptr _freeObjects[MAX]{};
+ };
+
+ /**
+ * @brief Helper a templated MessageStack class to manage a stack of Message objects with fixed size
+ * @tparam T The type of objects stored in the stack
+ * @tparam MAX The maximum size of the stack
+ */
+ template
+ class MessageStack
+ {
+ public:
+ MessageStack(MessagePool &pool) : _pool(pool) {}
+
+ T &push()
+ {
+ std::unique_ptr msg = _pool.acquire();
+ _activeObjects[_size++] = std::move(msg);
+ return *_activeObjects[_size - 1];
+ }
+
+ void pop()
+ {
+ if (_size > 0)
+ _pool.release(std::move(_activeObjects[--_size]));
+ }
+
+ size_t size() const { return _size; }
+ size_t max_size() const { return MAX; }
+
+ const T &top() const { return *_activeObjects[_size - 1]; }
+ T &top() { return *_activeObjects[_size - 1]; }
+
+ private:
+ size_t _size{0u};
+ std::unique_ptr _activeObjects[MAX]{};
+ MessagePool &_pool;
+ };
+
+ static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough
+ MessagePool m_pool; // A fixed-size memory pool stack for reusable
+ MessageStack m_stack;
+
+ using HookRegistry_t = IVoidHookChainRegistryImpl;
+ HookRegistry_t *m_hooks[MAX_USERMESSAGES]{};
+
+ 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();
diff --git a/rehlds/version/version.h b/rehlds/version/version.h
index 55a454b1..3bed179d 100644
--- a/rehlds/version/version.h
+++ b/rehlds/version/version.h
@@ -6,5 +6,5 @@
#pragma once
#define VERSION_MAJOR 3
-#define VERSION_MINOR 13
+#define VERSION_MINOR 14
#define VERSION_MAINTENANCE 0