2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-14 15:48:04 +03:00

Enhanced API interface game message manager

Bump major API version
Minor refactoring
This commit is contained in:
s1lentq 2024-05-27 05:01:37 +07:00
parent c9f9bbfff9
commit 0f45ec09fa
3 changed files with 745 additions and 167 deletions

View File

@ -44,9 +44,9 @@ public:
*/
enum class BlockType : uint8
{
Not, // Not a block
Once, // Block once
Set // Set block
Not, // Not a block
Once, // Block once
Set // Set block
};
/**
@ -66,99 +66,213 @@ public:
SPEC, // Sends to all spectator proxies
};
virtual ~IMessage() {};
/**
* @brief Data types for message data
*/
enum class DataType : uint8_t
{
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 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 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
*/
/**
* @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;
/**
* @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
@ -167,7 +281,7 @@ public:
#endif
};
#define MESSAGEMNGR_VERSION_MAJOR 1
#define MESSAGEMNGR_VERSION_MAJOR 2
#define MESSAGEMNGR_VERSION_MINOR 0
/**
@ -178,7 +292,7 @@ class IMessageManager
public:
using hookfunc_t = void (*)(IVoidHookChain<IMessage *> *chain, IMessage *msg);
virtual ~IMessageManager() {};
virtual ~IMessageManager() = default;
/**
* @brief Returns the major version of the MessageManager
@ -194,30 +308,30 @@ public:
/**
* @brief Returns the blocking behavior for the given message type
* @param msgType The message type
* @param msg_id The message type
* @return The blocking behavior for the given message type
*/
virtual IMessage::BlockType getMessageBlock(int msgType) const = 0;
virtual IMessage::BlockType getMessageBlock(int msg_id) const = 0;
/**
* @brief Sets the blocking behavior for the given message type
* @param msgType The message type
* @param msg_id The message type
* @param blockType The blocking behavior to set
*/
virtual void setMessageBlock(int msgType, IMessage::BlockType blockType) = 0;
virtual void setMessageBlock(int msg_id, 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 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 msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0;
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 msgType The message type to unregister the hook for
* @param msg_id The message type to unregister the hook for
* @param handler The hook function to unregister
*/
virtual void unregisterHook(int msgType, hookfunc_t handler) = 0;
virtual void unregisterHook(int msg_id, hookfunc_t handler) = 0;
};

View File

@ -21,53 +21,60 @@
// 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;
m_paramCount = 0;
m_modifiedDataBits = 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)
void MessageImpl::setActive(int dest, int id, const float *origin, edict_t *edict)
{
m_dest = static_cast<Dest>(dest);
m_type = type;
m_edict = 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>(dest);
storage.msgid = id;
storage.edict = edict;
// Reset buffer size
m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW;
m_buffer.cursize = 0;
// Copy origin vector if provided
if (origin)
VectorCopy(origin, storage.origin);
else
VectorClear(storage.origin);
}
// Copy origin vector if provided
if (origin)
VectorCopy(origin, m_origin);
else
VectorClear(m_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
memcpy(m_buffer.data, pbuf->data, pbuf->cursize);
m_buffer.cursize = pbuf->cursize;
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
memcpy(pbuf->data, m_buffer.data, m_buffer.cursize);
pbuf->cursize = m_buffer.cursize;
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
@ -87,9 +94,9 @@ static size_t SIZEOF_PARAMTYPE[] =
void MessageImpl::addParam(IMessage::ParamType type, size_t length)
{
Param_t &param = m_params[m_paramCount++];
param.type = type;
param.len = (length == -1) ? SIZEOF_PARAMTYPE[static_cast<size_t>(type)] : length;
param.pos = gMsgBuffer.cursize;
param.type = type;
param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast<size_t>(type)] : length;
param.posBack = param.posFront = gMsgBuffer.cursize;
}
// Sets the value of a primitive parameter at the given index
@ -97,12 +104,11 @@ template <typename T>
void MessageImpl::setParamPrimitive(size_t index, T value)
{
// Ensure index is within bounds
if (index < 0 || index >= m_paramCount)
if (index >= m_paramCount)
return;
const Param_t &param = m_params[index];
void *pbuf = m_buffer.data + param.pos;
Param_t &param = m_params[index];
void *pbuf = m_Storage[BACK].buf.data + param.posBack;
// Set value based on parameter type
switch (param.type)
@ -118,7 +124,7 @@ void MessageImpl::setParamPrimitive(size_t index, T value)
*(int16 *)pbuf = value;
break;
case IMessage::ParamType::Long:
*(uint16 *)pbuf = value;
*(uint32 *)pbuf = value;
break;
case IMessage::ParamType::Angle:
// Convert angle value to byte representation with loss of precision
@ -133,7 +139,10 @@ void MessageImpl::setParamPrimitive(size_t index, T value)
}
// Mark message as modified
m_modified = true;
param.modified = true;
// Mark the overall status as changed
setModifiedDataBit(DataType::Param);
}
// Transforms the buffer after setting a string parameter at the given index
@ -143,8 +152,10 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt
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 (m_buffer.cursize + diffLength > m_buffer.maxsize)
if (buf.cursize + diffLength > buf.maxsize)
{
Sys_Error(
"%s: Refusing to transform string with %i param of user message of %i bytes, "
@ -152,13 +163,18 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt
}
// 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;
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].pos += diffLength;
m_params[i].posBack += diffLength;
}
}
@ -166,12 +182,13 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt
int MessageImpl::getParamInt(size_t index) const
{
// Ensure index is within bounds
if (index < 0 || index >= m_paramCount)
if (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)
const Param_t &param = m_params[index];
const void *buf = m_Storage[BACK].buf.data + param.posBack;
switch (param.type)
{
case IMessage::ParamType::Byte:
return *(uint8 *)buf;
@ -181,7 +198,7 @@ int MessageImpl::getParamInt(size_t index) const
case IMessage::ParamType::Entity:
return *(int16 *)buf;
case IMessage::ParamType::Long:
return *(uint16 *)buf;
return *(uint32 *)buf;
default:
return 0; // bad type
}
@ -191,12 +208,12 @@ int MessageImpl::getParamInt(size_t index) const
float MessageImpl::getParamFloat(size_t index) const
{
// Ensure index is within bounds
if (index < 0 || index >= m_paramCount)
if (index >= m_paramCount)
return 0;
// Get the parameter value based on its type
const Param_t &param = m_params[index];
void *buf = m_buffer.data + param.pos;
const void *buf = m_Storage[BACK].buf.data + param.posBack;
switch (param.type)
{
case IMessage::ParamType::Angle:
@ -214,13 +231,74 @@ float MessageImpl::getParamFloat(size_t index) const
const char *MessageImpl::getParamString(size_t index) const
{
// Ensure index is within bounds
if (index < 0 || index >= m_paramCount)
if (index >= m_paramCount)
return nullptr;
// Get the parameter value if it is a string
const Param_t &param = m_params[index];
if (param.type == IMessage::ParamType::String)
return (const char *)m_buffer.data + param.pos;
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 &param = 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 &param = 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 &param = m_params[index];
if (param.type == IMessage::ParamType::String)
return (const char *)m_Storage[FRONT].buf.data + param.posFront;
return nullptr;
}
@ -244,13 +322,13 @@ void MessageImpl::setParamVec(size_t index, const float *pos)
return;
// Ensure index is within bounds
if (index < 0 || (index + 3) >= m_paramCount)
if ((index + 3) >= m_paramCount)
return;
// Get the parameter position in the buffer
const Param_t &param = m_params[index];
Param_t &param = m_params[index];
int16 *pbuf = (int16 *)m_buffer.data + param.pos;
int16 *pbuf = (int16 *)m_Storage[BACK].buf.data + param.posBack;
// Set each component of the vector parameter
*(int16 *)pbuf++ = (int16)(pos[0] * 8.0);
@ -258,7 +336,10 @@ void MessageImpl::setParamVec(size_t index, const float *pos)
*(int16 *)pbuf++ = (int16)(pos[2] * 8.0);
// Mark message as modified
m_modified = true;
param.modified = true;
// Mark the overall status as modified
setModifiedDataBit(DataType::Param);
}
// Sets the string value of the parameter at the given index
@ -268,77 +349,276 @@ void MessageImpl::setParamString(size_t index, const char *value)
return;
// Ensure index is within bounds
if (index < 0 || index >= m_paramCount)
if (index >= m_paramCount)
return;
// Calculate the length of the string
size_t length = Q_strlen(value) + 1;
const Param_t &param = m_params[index];
Param_t &param = m_params[index];
param.newlen = Q_strlen(value) + 1;
// Transform buffer to accommodate the new string length
setTxformBuffer(index, param.pos, param.len, length);
setTxformBuffer(index, param.posBack, param.oldlen, param.newlen);
// Copy the string value to the buffer
memcpy(m_buffer.data + param.pos, value, length);
Q_memcpy(m_Storage[BACK].buf.data + param.posBack, value, param.newlen);
// Mark message as modified
m_modified = true;
param.modified = true;
// Mark the overall status as modified
setModifiedDataBit(DataType::Param);
}
MessageManagerImpl::MessageManagerImpl()
// 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 &param = m_params[index];
return param.modified;
}
return true;
}
void MessageImpl::resetParam(size_t index)
{
Param_t &param = 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 &param = 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 &param = 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 msgType, hookfunc_t handler, int priority)
void MessageManagerImpl::registerHook(int msg_id, hookfunc_t handler, int priority)
{
m_hooks[msgType].registerHook(handler, priority);
if (!m_hooks[msg_id])
m_hooks[msg_id] = new HookRegistry_t;
if (m_hooks[msg_id]->findHook(handler))
return; // already registered
m_hooks[msg_id]->registerHook(handler, priority);
}
// Unregister hook function for the game message type
void MessageManagerImpl::unregisterHook(int msgType, hookfunc_t handler)
void MessageManagerImpl::unregisterHook(int msg_id, hookfunc_t handler)
{
m_hooks[msgType].unregisterHook(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 msgType) const
IMessage::BlockType MessageManagerImpl::getMessageBlock(int msg_id) const
{
return m_blocks[msgType];
return m_blocks[msg_id];
}
// Set the block type for the game message type
void MessageManagerImpl::setMessageBlock(int msgType, IMessage::BlockType blockType)
void MessageManagerImpl::setMessageBlock(int msg_id, IMessage::BlockType blockType)
{
m_blocks[msgType] = blockType;
m_blocks[msg_id] = blockType;
}
bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
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_type] != IMessage::BlockType::Not)
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_type].getCount() > 0;
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_type);
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
m_stack.push();
MessageImpl &msg = m_stack.push();
// Initialize the message
MessageImpl &msg = m_stack.back();
msg.setActive(msg_dest, msg_type, pOrigin, ed);
msg.setActive(msg_dest, msg_id, pOrigin, ed);
}
return true;
@ -347,7 +627,7 @@ bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *p
static void EXT_FUNC SendUserMessageData(IMessage *msg)
{
// Set global variables with message data
gMsgType = msg->getType();
gMsgType = msg->getId();
gMsgEntity = msg->getEdict();
gMsgDest = static_cast<int>(msg->getDest());
@ -383,11 +663,11 @@ bool MessageManagerImpl::MessageEnd()
gMsgStarted = FALSE;
// Get the message from the top of the stack
MessageImpl &msg = m_stack.back();
MessageImpl &msg = m_stack.top();
// Set buffer from global buffer and call hookchain
msg.setBuffer(&gMsgBuffer);
m_hooks[msg.getType()].callChain(SendUserMessageData, &msg);
m_hooks[msg.getId()]->callChain(SendUserMessageData, &msg);
m_inhook = false;
// Clear the message and pop from the stack
@ -407,7 +687,7 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length)
if (m_inhook)
{
// Add parameter to top stack message
MessageImpl &msg = m_stack.back();
MessageImpl &msg = m_stack.top();
msg.addParam(type, length);
}
@ -418,14 +698,14 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length)
// Functions intercept to handle messages
//
void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed)
{
// Set global message type
gMsgType = msg_type;
gMsgType = msg_id;
// Begin message manager
if (MessageManager().MessageBegin(msg_dest, msg_type, pOrigin, ed))
PF_MessageBegin_I(msg_dest, msg_type, pOrigin, ed);
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)

View File

@ -21,6 +21,9 @@
#include "IMessageManager.h"
#include "hookchains.h"
#include <memory> // std::unique_ptr
#include <algorithm> // std::move
/**
* @brief Implementation interface for defining message parameters and behavior for a game message object
*/
@ -71,6 +74,27 @@ public:
*/
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
@ -92,42 +116,101 @@ public:
*/
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_dest; }
Dest getDest() const { return m_Storage[BACK].dest; }
Dest getOriginalDest() const { return m_Storage[FRONT].dest; }
/**
* @brief Returns the type of the message
* @return The type of the message
* @brief Sets the index of the message
*/
int getType() const { return m_type; }
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_origin; }
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_edict; }
edict_t *getEdict() const { return m_Storage[BACK].edict; }
edict_t *getOriginalEdict() const { return m_Storage[FRONT].edict; }
/**
* @brief Returns whether the message has been modified
* @return True if the message has been modified, false otherwise
* @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 isModified() const { return m_modified; }
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 type, const float *origin, edict_t *edict);
void setActive(int dest, int id, const float *origin, edict_t *edict);
// Sets the buffer for the message
void setBuffer(sizebuf_t *pbuf);
@ -147,27 +230,78 @@ private:
// 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
enum
{
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
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_t 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; // The number of parameters 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 : DataType::Max;
};
/**
* Inline methods
*/
inline void MessageImpl::setModifiedDataBit(DataType type)
{
m_modifiedDataBits |= ((1 << static_cast<int>(DataType::Any)) | (1 << static_cast<int>(type)));
}
inline void MessageImpl::unsetModifiedDataBit(DataType type)
{
m_modifiedDataBits &= ~((1 << static_cast<int>(DataType::Any)) | (1 << static_cast<int>(type)));
}
inline bool MessageImpl::isDataTypeModified(DataType type) const
{
return (m_modifiedDataBits & (1 << static_cast<int>(type))) != 0;
}
/**
* @brief Implementation interface manages hooks and blocking behavior game messages
*/
@ -194,35 +328,35 @@ public:
/**
* @brief Returns the blocking behavior for the given message type
* @param msgType The message type
* @param msg_id The message type
* @return The blocking behavior for the given message type
*/
IMessage::BlockType getMessageBlock(int msgType) const;
IMessage::BlockType getMessageBlock(int msg_id) const;
/**
* @brief Sets the blocking behavior for the given message type
* @param msgType The message type
* @param msg_id The message type
* @param blockType The blocking behavior to set
*/
void setMessageBlock(int msgType, IMessage::BlockType blockType);
void setMessageBlock(int msg_id, IMessage::BlockType blockType);
/**
* @brief Registers a hook function for the given message type
* @param msgType The message type to register the hook for
* @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 msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT);
void registerHook(int msg_id, 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 msg_id The message type to unregister the hook for
* @param handler The hook function to unregister
*/
void unregisterHook(int msgType, hookfunc_t handler);
void unregisterHook(int msg_id, hookfunc_t handler);
private:
friend void PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
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);
@ -233,7 +367,7 @@ private:
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 MessageBegin(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed);
bool MessageEnd();
private:
@ -243,31 +377,81 @@ private:
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
* @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 <typename T, size_t MAX>
class Stack
class MessagePool
{
public:
void push() { _size++; }
void pop () { _size--; }
std::unique_ptr<T> acquire()
{
if (_size > 0)
return std::move(_freeObjects[--_size]); // reusing
return std::make_unique<T>(); // initialize constructor for new element
}
void release(std::unique_ptr<T> 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<T> _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 <typename T, size_t MAX>
class MessageStack
{
public:
MessageStack(MessagePool<T, MAX> &pool) : _pool(pool) {}
T &push()
{
std::unique_ptr<T> 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 &back() const { return _data[_size - 1]; }
T &back() { return _data[_size - 1]; }
const T &top() const { return *_activeObjects[_size - 1]; }
T &top() { return *_activeObjects[_size - 1]; }
private:
size_t _size = 0u;
T _data[MAX]{};
size_t _size{0u};
std::unique_ptr<T> _activeObjects[MAX]{};
MessagePool<T, MAX> &_pool;
};
static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough
Stack<MessageImpl, MAX_MSGSTACK> m_stack;
MessagePool<MessageImpl, MAX_MSGSTACK> m_pool; // A fixed-size memory pool stack for reusable
MessageStack<MessageImpl, MAX_MSGSTACK> m_stack;
using HookRegistry_t = IVoidHookChainRegistryImpl<IMessage *>;
HookRegistry_t *m_hooks[MAX_USERMESSAGES]{};
IVoidHookChainRegistryImpl<IMessage *> 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
};