diff --git a/reapi/CMakeLists.txt b/reapi/CMakeLists.txt
index cfe8f93..9524473 100644
--- a/reapi/CMakeLists.txt
+++ b/reapi/CMakeLists.txt
@@ -115,6 +115,7 @@ set(REAPI_SRCS
"src/hook_callback.cpp"
"src/hook_list.cpp"
"src/hook_manager.cpp"
+ "src/hook_message_manager.cpp"
"src/api_config.cpp"
"src/member_list.cpp"
"src/meta_api.cpp"
@@ -122,6 +123,7 @@ set(REAPI_SRCS
"src/sdk_util.cpp"
"src/natives/natives_common.cpp"
"src/natives/natives_hookchains.cpp"
+ "src/natives/natives_message.cpp"
"src/natives/natives_members.cpp"
"src/natives/natives_misc.cpp"
"src/natives/natives_rechecker.cpp"
diff --git a/reapi/extra/amxmodx/scripting/include/reapi_engine.inc b/reapi/extra/amxmodx/scripting/include/reapi_engine.inc
index ba71180..24dee32 100644
--- a/reapi/extra/amxmodx/scripting/include/reapi_engine.inc
+++ b/reapi/extra/amxmodx/scripting/include/reapi_engine.inc
@@ -266,3 +266,124 @@ native rh_get_net_from(output[], len);
* @return Netchan connection time in seconds or 0 if client index is invalid or client is not connected
*/
native rh_get_client_connect_time(const index);
+
+
+enum MessageHook
+{
+ INVALID_MESSAGEHOOK = 0
+};
+
+/**
+* Registers a callback function to be called when a game message with the specified ID is received.
+*
+* @param msg_id The ID of the message to register the callback for.
+* @param callback The name of the callback function.
+* @param post Whether the callback should be invoked before or after processing the message. (optional)
+*
+* @note You can modify the message content using SetMessageParam native before the original function is invoked.
+* Also can reading the message content using GetMessageParam native.
+*
+* In the callback function, use the return values from Hookchain return types, such as HC_CONTINUE, HC_SUPERCEDE, etc.
+* to control the flow of message processing.
+*
+* @return Returns a handle to the registered message hook.
+*/
+native MessageHook:RegisterMessage(const msg_id, const callback[], post = 0);
+
+/**
+* Unregisters a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to unregister.
+*
+* @return Returns true if the message hook is successfully unregistered, otherwise false.
+*/
+native bool:UnregisterMessage(const MessageHook:handle);
+
+/**
+* Enables a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to enable.
+*
+* @return Returns true if the message hook is successfully enabled, otherwise false.
+*/
+native bool:EnableHookMessage(const MessageHook:handle);
+
+/**
+* Disables a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to disable.
+*
+* @return Returns true if the message hook is successfully disabled, otherwise false.
+*/
+native bool:DisableHookMessage(const MessageHook:handle);
+
+/**
+* Sets the parameter value for the specified index in the current game message.
+*
+* @param index The index of the parameter to set.
+* @param value The value to set for the parameter.
+*
+* @return Returns true if the parameter value is successfully set, otherwise false.
+*/
+native bool:SetMessageParam(const number, any:...);
+
+/**
+* Retrieves the parameter value for the specified index in the current game message.
+*
+* @param index The index of the parameter to retrieve.
+* @param ... Additional parameters depending on the type of the parameter being retrieved.
+*
+* @return Returns the retrieved parameter value.
+*/
+native any:GetMessageParam(const number, any:...);
+
+/**
+* Retrieves the type of the parameter at the specified index in the current game message.
+*
+* @param index The index of the parameter to retrieve the type for.
+*
+* @return Returns the type of the parameter, look at the enum MsgParamType
+*/
+native MsgParamType:GetMessageParamType(const number);
+
+/**
+* Retrieves the number of parameters in the current game message.
+*
+* @return Returns the number of parameters in the current game message.
+*/
+native GetMessageParamCount();
+
+/**
+* Retrieves the origin of the current game message.
+*
+* @param origin An array to store the origin coordinates of the game message.
+*
+* @return Returns true if the origin is successfully retrieved, otherwise false.
+*/
+native bool:GetMessageOrigin(Float:origin[3]);
+
+/**
+* Retrieves the destination of the current message.
+*
+* @return Returns the destination of the current message.
+*/
+native GetMessageDest();
+
+/**
+* Sets the block type for the specified message ID.
+*
+* @param msgid The ID of the message to set the block type for.
+* @param type The type of block to set for the message, look at the enum MsgBlockType
+*
+* @return Returns true if the block type is successfully set, otherwise false.
+*/
+native bool:SetMessageBlock(const msgid, MsgBlockType:type);
+
+/**
+* Retrieves the block type for the specified message ID.
+*
+* @param msgid The ID of the message to retrieve the block type for.
+*
+* @return Returns the block type of the specified message, look at the enum MsgBlockType
+*/
+native MsgBlockType:GetMessageBlock(const msgid);
diff --git a/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc b/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc
index a2e47f9..d32b1ce 100644
--- a/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc
+++ b/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc
@@ -22,7 +22,7 @@ enum CheckVisibilityType
VisibilityInPAS // Check in Potentially Audible Set (PAS)
};
-/*
+/**
* For RH_SV_AddResource hook
*/
enum ResourceType_t
@@ -1321,3 +1321,29 @@ enum NetAdrVars
*/
netadr_port
};
+
+/**
+* Message param types used with GetMessageParamType()
+*/
+enum MsgParamType
+{
+ ParamByte,
+ ParamChar,
+ ParamShort,
+ ParamLong,
+ ParamAngle,
+ ParamCoord,
+ ParamString,
+ ParamEntity,
+};
+
+/**
+* Blocking behavior types for messages
+* Flags for natives SetMessageBlock()/GetMessageBlock()
+*/
+enum MsgBlockType
+{
+ MSG_BLOCK_NOT, // Not a block
+ MSG_BLOCK_ONCE, // Block once
+ MSG_BLOCK_SET // Set block
+};
diff --git a/reapi/msvc/reapi.vcxproj b/reapi/msvc/reapi.vcxproj
index 41b8798..4f753b3 100644
--- a/reapi/msvc/reapi.vcxproj
+++ b/reapi/msvc/reapi.vcxproj
@@ -146,6 +146,7 @@
true
+
@@ -217,6 +218,7 @@
+
@@ -227,6 +229,7 @@
+
@@ -266,6 +269,7 @@
+
@@ -276,6 +280,7 @@
+
diff --git a/reapi/msvc/reapi.vcxproj.filters b/reapi/msvc/reapi.vcxproj.filters
index 501a60f..53558f9 100644
--- a/reapi/msvc/reapi.vcxproj.filters
+++ b/reapi/msvc/reapi.vcxproj.filters
@@ -726,6 +726,15 @@
include\cssdk\dlls
+
+ include\cssdk\engine
+
+
+ src
+
+
+ src\natives
+
@@ -830,6 +839,12 @@
src
+
+ src
+
+
+ src\natives
+
diff --git a/reapi/src/amxxmodule.cpp b/reapi/src/amxxmodule.cpp
index 0b5c8fc..d8895af 100644
--- a/reapi/src/amxxmodule.cpp
+++ b/reapi/src/amxxmodule.cpp
@@ -162,6 +162,7 @@ C_DLLEXPORT int AMXX_Attach(PFN_REQ_FNPTR reqFnptrFunc)
OnAmxxAttach();
RegisterNatives_HookChains();
+ RegisterNatives_HookMessage();
RegisterNatives_Members();
RegisterNatives_Misc();
RegisterNatives_VTC();
diff --git a/reapi/src/hook_message_manager.cpp b/reapi/src/hook_message_manager.cpp
new file mode 100644
index 0000000..9ecb35d
--- /dev/null
+++ b/reapi/src/hook_message_manager.cpp
@@ -0,0 +1,230 @@
+#include "precompiled.h"
+
+// Global instance of the message hook manager
+MessageHookManager g_messageHookManager;
+
+// Pointer to the message context within an active hook
+IMessage *g_activeMessageContext = nullptr;
+
+/**
+* @brief Dispatches the callbacks associated with the given message
+*
+* This function executes the hooks associated with the given message type,
+* allowing modifications or interception of the message's behavior
+*
+* @param chain Pointer to the hook chain for the message
+* @param message Pointer to the message parameters
+*/
+void MessageHookManager::DispatchCallbacks(IVoidHookChain *chain, IMessage *params)
+{
+ // Get the hook associated with the given message type
+ MessageHook *msg = getHook(params->getType());
+
+ // If somehow no hook is found, just continue hookchain
+ if (!msg) {
+ chain->callNext(params);
+ return;
+ }
+
+ // Save the current message context and set the new one
+ IMessage *savedContext = g_activeMessageContext;
+ g_activeMessageContext = params;
+
+ // Get the entity index of the message (if applicable)
+ edict_t *entityEdict = params->getEdict();
+ int entityIndex = entityEdict ? indexOfEdict(entityEdict) : 0;
+
+ int hookState = HC_CONTINUE;
+
+ // Execute pre-hooks
+ for (const CAmxxHookBase *fwd : msg->pre)
+ {
+ if (likely(fwd->GetState() == FSTATE_ENABLED))
+ {
+ int ret = g_amxxapi.ExecuteForward(fwd->GetFwdIndex(), entityIndex, (cell)params);
+ if (unlikely(ret == HC_BREAK)) {
+ g_activeMessageContext = savedContext;
+ return;
+ }
+
+ if (unlikely(ret > hookState))
+ hookState = ret;
+ }
+ }
+
+ // If the hook state is not superseded, continue hookchain
+ if (hookState != HC_SUPERCEDE) {
+ g_activeMessageContext = nullptr;
+ chain->callNext(params);
+ g_activeMessageContext = savedContext;
+ }
+
+ // Execute post-hooks
+ for (const CAmxxHookBase *fwd : msg->post)
+ {
+ if (likely(fwd->GetState() == FSTATE_ENABLED))
+ {
+ int ret = g_amxxapi.ExecuteForward(fwd->GetFwdIndex(), entityIndex, (cell)params);
+ if (unlikely(ret == HC_BREAK))
+ break;
+ }
+ }
+
+ // Restore the original message context
+ g_activeMessageContext = savedContext;
+}
+
+/**
+* @brief Adds a hook for the specified message ID
+*
+* This function registers a new hook for the specified message ID and associates it with the provided function
+*
+* @param msg_id ID of the message to hook
+* @param funcname Name of the function to call when the hook is triggered
+* @param post Indicates whether the hook should be executed after the default behavior (true) or before (false)
+*
+* @return Returns a handle to the registered hook, or 0 if the registration fails
+*/
+cell MessageHookManager::addHook(AMX *amx, int msg_id, const char *funcname, bool post)
+{
+ int fwid = g_amxxapi.RegisterSPForwardByName(amx, funcname, FP_CELL, FP_DONE);
+ if (unlikely(fwid == -1)) {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: register forward failed.", __FUNCTION__);
+ return 0;
+ }
+
+ // Get the message hook associated with the ID
+ MessageHook &msg = hooks[msg_id];
+
+ // If it's the first hook for this message, register it with message manager
+ if (!msg.post.size() && !msg.pre.size())
+ {
+ msg.id = msg_id;
+ g_RehldsMessageManager->registerHook(msg_id, RoutineMessageCallbacks);
+ }
+
+ // Determine whether to add the hook to the pre or post vector
+ std::vector &dest = post ? msg.post : msg.pre;
+
+ // Pack msg_id and forward ID into the handle value
+ cell handle = (msg_id * MAX_USERMESSAGES) + dest.size() + 1;
+
+ dest.emplace_back(new CAmxxHookBase(amx, funcname, fwid, -1));
+
+ return post ? -handle : handle;
+}
+
+/**
+* @brief Removes a hook associated with the specified handle
+*
+* This function removes the hook associated with the given handle from the hook manager
+*
+* @param handle Handle to the hook to be removed
+*
+* @return Returns true if the hook is successfully removed, false otherwise
+*/
+bool MessageHookManager::removeHook(AMX *amx, cell handle)
+{
+ // Determine whether the hook is a post-hook
+ bool post = handle < 0;
+ if (post)
+ handle = ~handle;
+ else
+ handle--;
+
+ // Unpack the message ID and forward ID from the handle
+ const size_t id = handle / MAX_USERMESSAGES;
+ const size_t fwid = handle & (MAX_USERMESSAGES - 1);
+
+ // Get the message hook by ID, returns nullptr if the ID is invalid
+ MessageHook *msg = getHook(id);
+ if (msg)
+ {
+ // Get the appropriate vector of hooks (pre or post)
+ std::vector &forwards = post ? msg->post : msg->pre;
+
+ // Check if the forward ID is within the vector bounds
+ if (fwid < forwards.size())
+ {
+ // Delete the hook and erase it from the vector
+ delete forwards[fwid];
+ forwards.erase(forwards.begin() + fwid);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+* @brief Retrieves the AMXX hook associated with the specified handle
+*
+* This function returns the AMXX hook associated with the provided handle
+*
+* @param handle Handle of the hook to retrieve
+*
+* @return Pointer to the AMXX hook if found, nullptr otherwise
+*/
+CAmxxHookBase *MessageHookManager::getAmxxHook(cell handle)
+{
+ // Determine whether the hook is a post-hook
+ bool post = handle < 0;
+ if (post)
+ handle = ~handle;
+ else
+ handle--;
+
+ // Unpack the message ID and forward ID from the handle
+ const size_t id = handle / MAX_USERMESSAGES;
+ const size_t fwid = handle & (MAX_USERMESSAGES - 1);
+
+ // Get the message hook by ID, returns nullptr if the ID is invalid
+ MessageHook *msg = getHook(id);
+ if (msg)
+ {
+ // Get the appropriate vector of hooks (pre or post)
+ std::vector &forwards = post ? msg->post : msg->pre;
+
+ // Check if the forward ID is within the vector bounds
+ if (fwid < forwards.size())
+ return forwards[fwid];
+ }
+
+ return nullptr;
+}
+
+/**
+* @brief Get the message hook by ID.
+*
+* This function safely retrieves the message hook associated with the provided ID.
+*
+* @param id ID of the message hook to retrieve.
+*
+* @return Pointer to the message hook if found, nullptr otherwise.
+*/
+MessageHookManager::MessageHook *MessageHookManager::getHook(size_t id)
+{
+ if (id <= 0 || id >= MAX_USERMESSAGES)
+ return nullptr;
+
+ return &hooks[id];
+}
+
+/**
+* @brief Clears all registered message hooks
+*
+* This function clears all registered message hooks from the hook manager
+*/
+void MessageHookManager::Clear()
+{
+ for (auto &h : hooks)
+ h.clear();
+}
+
+/**
+* @brief Dispatches the message callbacks as a routine
+*/
+void MessageHookManager::RoutineMessageCallbacks(IVoidHookChain *chain, IMessage *params)
+{
+ g_messageHookManager.DispatchCallbacks(chain, params);
+}
diff --git a/reapi/src/hook_message_manager.h b/reapi/src/hook_message_manager.h
new file mode 100644
index 0000000..0cef91c
--- /dev/null
+++ b/reapi/src/hook_message_manager.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include
+
+// Class managing hooks for game messages
+class MessageHookManager
+{
+public:
+ // Adds a hook for a game message
+ cell addHook(AMX *amx, int msg_id, const char *funcname, bool post);
+
+ // Removes a hook with the given handle
+ bool removeHook(AMX *amx, cell handle);
+
+ // Clears all hooks
+ void Clear();
+
+ // Get the AMXX hook with the given handle
+ CAmxxHookBase *getAmxxHook(cell handle);
+
+private:
+ class MessageHook
+ {
+ public:
+ // Clears all hooks associated with this message
+ void clear()
+ {
+ if (post.size() || pre.size()) {
+ for (CAmxxHookBase *h : post)
+ delete h;
+ post.clear();
+
+ for (CAmxxHookBase *h : pre)
+ delete h;
+ pre.clear();
+
+ // Unregister the message hook
+ if (g_RehldsMessageManager)
+ g_RehldsMessageManager->unregisterHook(id, RoutineMessageCallbacks);
+ }
+ }
+
+ int id;
+ std::vector pre, post;
+ };
+
+ // Dispatches the callbacks for the message hooks
+ void DispatchCallbacks(IVoidHookChain *chain, IMessage *params);
+
+ // Routine function for dispatching message callbacks
+ static void RoutineMessageCallbacks(IVoidHookChain *chain, IMessage *params);
+
+ // Getter the message hook by ID
+ MessageHook *getHook(size_t id);
+
+ // Array of message hooks
+ std::array hooks;
+};
+
+// Global instance of the message hook manager
+extern MessageHookManager g_messageHookManager;
+
+// Pointer to the message context within an active hook
+extern IMessage *g_activeMessageContext;
diff --git a/reapi/src/main.cpp b/reapi/src/main.cpp
index d82b425..c9bd077 100644
--- a/reapi/src/main.cpp
+++ b/reapi/src/main.cpp
@@ -42,6 +42,7 @@ void OnMetaDetach()
{
// clear all hooks?
g_hookManager.Clear();
+ g_messageHookManager.Clear();
g_queryFileManager.Clear();
if (api_cfg.hasVTC()) {
@@ -68,6 +69,7 @@ void ServerDeactivate_Post()
g_pEdicts = nullptr;
api_cfg.ServerDeactivate();
g_hookManager.Clear();
+ g_messageHookManager.Clear();
g_queryFileManager.Clear();
EntityCallbackDispatcher().DeleteAllCallbacks();
diff --git a/reapi/src/mods/mod_rehlds_api.cpp b/reapi/src/mods/mod_rehlds_api.cpp
index 109c582..670de74 100644
--- a/reapi/src/mods/mod_rehlds_api.cpp
+++ b/reapi/src/mods/mod_rehlds_api.cpp
@@ -5,6 +5,7 @@ const RehldsFuncs_t* g_RehldsFuncs;
IRehldsServerData* g_RehldsData;
IRehldsHookchains* g_RehldsHookchains;
IRehldsServerStatic* g_RehldsSvs;
+IMessageManager* g_RehldsMessageManager;
bool RehldsApi_Init()
{
@@ -74,6 +75,14 @@ bool RehldsApi_Init()
g_RehldsData = g_RehldsApi->GetServerData();
g_RehldsHookchains = g_RehldsApi->GetHookchains();
g_RehldsSvs = g_RehldsApi->GetServerStatic();
+ g_RehldsMessageManager = nullptr;
+
+ IMessageManager *messageManager = g_RehldsApi->GetMessageManager();
+ if (messageManager->getMajorVersion() == MESSAGEMNGR_VERSION_MAJOR &&
+ messageManager->getMinorVersion() >= MESSAGEMNGR_VERSION_MINOR)
+ {
+ g_RehldsMessageManager = messageManager;
+ }
return true;
}
diff --git a/reapi/src/mods/mod_rehlds_api.h b/reapi/src/mods/mod_rehlds_api.h
index 768dc8c..cb2cb05 100644
--- a/reapi/src/mods/mod_rehlds_api.h
+++ b/reapi/src/mods/mod_rehlds_api.h
@@ -5,5 +5,6 @@ extern const RehldsFuncs_t* g_RehldsFuncs;
extern IRehldsServerData* g_RehldsData;
extern IRehldsHookchains* g_RehldsHookchains;
extern IRehldsServerStatic* g_RehldsSvs;
+extern IMessageManager* g_RehldsMessageManager;
extern bool RehldsApi_Init();
\ No newline at end of file
diff --git a/reapi/src/natives/natives_helper.h b/reapi/src/natives/natives_helper.h
index f6c664e..19fef5d 100644
--- a/reapi/src/natives/natives_helper.h
+++ b/reapi/src/natives/natives_helper.h
@@ -7,6 +7,7 @@
#define CHECK_GAMERULES() if (unlikely(!g_pGameRules)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: gamerules not initialized", __FUNCTION__); return FALSE; }
#define CHECK_CONNECTED(x, y) if (unlikely(x == nullptr || x->has_disconnected)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; }
#define CHECK_INSTANCE_OF(x, y) if (unlikely(dynamic_cast((x::BaseClass *)y) == nullptr)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity %d ('%s'), is not an instance of the base class '%s'", __FUNCTION__, indexOfEdict(y->pev), STRING(y->pev->classname), #x); return FALSE; }
+#define CHECK_REQUIREMENTS(x) if (unlikely(!api_cfg.has##x())) { AMXX_LogError(amx, AMX_ERR_NATIVE, "Native '%s' is not available, %s required.", __FUNCTION__, #x); return FALSE; } if (!g_RehldsMessageManager) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: %s message manager not initialized.", __FUNCTION__, #x); return FALSE; }
class CAmxArg
{
diff --git a/reapi/src/natives/natives_hookmessage.cpp b/reapi/src/natives/natives_hookmessage.cpp
new file mode 100644
index 0000000..3c25182
--- /dev/null
+++ b/reapi/src/natives/natives_hookmessage.cpp
@@ -0,0 +1,414 @@
+#include "precompiled.h"
+
+enum MessageHook
+{
+ INVALID_MESSAGEHOOK = 0
+};
+
+/**
+* Registers a callback function to be called when a game message with the specified ID is received.
+*
+* @param msg_id The ID of the message to register the callback for.
+* @param callback The name of the callback function.
+* @param post Whether the callback should be invoked before or after processing the message. (optional)
+*
+* @note You can modify the message content using SetMessageParam native before the original function is invoked.
+* Also can reading the message content using GetMessageParam native.
+*
+* In the callback function, use the return values from Hookchain return types, such as HC_CONTINUE, HC_SUPERCEDE, etc.
+* to control the flow of message processing.
+*
+* @return Returns a handle to the registered message hook.
+*
+* native MessageHook:RegisterMessage(const msg_id, const callback[], post = 0);
+*/
+cell AMX_NATIVE_CALL RegisterMessage(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_id, arg_handler, arg_post };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ int msg_id = params[arg_id];
+
+ // svc_bad (0) is not allowed for hook
+ if (msg_id <= 0 || msg_id >= MAX_USERMESSAGES)
+ return INVALID_MESSAGEHOOK;
+
+ char namebuf[256];
+ const char *funcname = getAmxString(amx, params[arg_handler], namebuf);
+
+ int funcid;
+ if (unlikely(g_amxxapi.amx_FindPublic(amx, funcname, &funcid) != AMX_ERR_NONE))
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: public function \"%s\" not found.", __FUNCTION__, funcname);
+ return INVALID_MESSAGEHOOK;
+ }
+
+ int post = params[arg_post];
+ return g_messageHookManager.addHook(amx, msg_id, funcname, post != 0);
+}
+
+/**
+* Unregisters a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to unregister.
+*
+* @return Returns true if the message hook is successfully unregistered, otherwise false.
+*
+* native bool:UnregisterMessage(const MessageHook:handle);
+*/
+cell AMX_NATIVE_CALL UnregisterMessage(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_handle };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_messageHookManager.removeHook(amx, params[arg_handle]))
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: Failed to remove message hook with handle %d.", __FUNCTION__, params[arg_handle]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+* Enables a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to enable.
+*
+* @return Returns true if the message hook is successfully enabled, otherwise false.
+*
+* native bool:EnableHookMessage(const MessageHook:handle);
+*/
+cell AMX_NATIVE_CALL EnableHookMessage(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_handle };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ cell handle = params[arg_handle];
+ auto hook = g_messageHookManager.getAmxxHook(handle);
+ if (!hook)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: Message hook with handle %d not found.", __FUNCTION__, handle);
+ return FALSE;
+ }
+
+ hook->SetState(FSTATE_ENABLED);
+ return TRUE;
+}
+
+/**
+* Disables a game message hook identified by the specified handle.
+*
+* @param handle The handle of the message hook to disable.
+*
+* @return Returns true if the message hook is successfully disabled, otherwise false.
+*
+* native bool:DisableHookMessage(const MessageHook:handle);
+*/
+cell AMX_NATIVE_CALL DisableHookMessage(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_handle };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ cell handle = params[arg_handle];
+ auto hook = g_messageHookManager.getAmxxHook(handle);
+ if (!hook)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: Message hook with handle %d not found.", __FUNCTION__, handle);
+ return FALSE;
+ }
+
+ hook->SetState(FSTATE_STOPPED);
+ return TRUE;
+}
+
+/**
+* Sets the parameter value for the specified index in the current game message.
+*
+* @param index The index of the parameter to set.
+* @param value The value to set for the parameter.
+*
+* @return Returns true if the parameter value is successfully set, otherwise false.
+*
+* native bool:SetMessageParam(const number, any:...);
+*/
+cell AMX_NATIVE_CALL SetMessageParam(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number, arg_value };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to set argument without active hook.", __FUNCTION__);
+ return FALSE;
+ }
+
+ size_t number = params[arg_number] - 1;
+
+ cell *value = getAmxAddr(amx, params[arg_value]);
+
+ switch (g_activeMessageContext->getParamType(number))
+ {
+ case IMessage::ParamType::String:
+ char stringbuf[256];
+ g_activeMessageContext->setParamString(number, getAmxString(amx, params[arg_value], stringbuf));
+ break;
+ case IMessage::ParamType::Angle:
+ case IMessage::ParamType::Coord:
+ g_activeMessageContext->setParamFloat(number, *(float *)value);
+ break;
+ default:
+ g_activeMessageContext->setParamInt(number, *value);
+ break;
+ }
+
+ return TRUE;
+}
+
+/**
+* Retrieves the parameter value for the specified index in the current game message.
+*
+* @param index The index of the parameter to retrieve.
+* @param ... Additional parameters depending on the type of the parameter being retrieved.
+*
+* @return Returns the retrieved parameter value.
+*
+* native any:GetMessageParam(const number, any:...);
+*/
+cell AMX_NATIVE_CALL GetMessageParam(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number, arg_value, arg_maxlen };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to get argument without active hook.", __FUNCTION__);
+ return FALSE;
+ }
+
+ size_t number = params[arg_number] - 1;
+
+ cell *dstAddr = getAmxAddr(amx, params[arg_value]);
+
+ switch (g_activeMessageContext->getParamType(number))
+ {
+ case IMessage::ParamType::String:
+ {
+ if (PARAMS_COUNT != 3)
+ return FALSE;
+
+ const char *argString = g_activeMessageContext->getParamString(number);
+ setAmxString(dstAddr, argString ? argString : "", params[arg_maxlen]);
+ return TRUE;
+ }
+ case IMessage::ParamType::Angle:
+ case IMessage::ParamType::Coord:
+ {
+ float flValue = g_activeMessageContext->getParamFloat(number);
+ if (PARAMS_COUNT > 1)
+ *dstAddr = flValue;
+ return flValue;
+ }
+ case IMessage::ParamType::Entity:
+ case IMessage::ParamType::Byte:
+ case IMessage::ParamType::Char:
+ case IMessage::ParamType::Short:
+ case IMessage::ParamType::Long:
+ return g_activeMessageContext->getParamInt(number);
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/**
+* Retrieves the type of the parameter at the specified index in the current game message.
+*
+* @param index The index of the parameter to retrieve the type for.
+*
+* @return Returns the type of the parameter, look at the enum MsgParamType
+*
+* native MsgParamType:GetMessageParamType(const number);
+*/
+cell AMX_NATIVE_CALL GetMessageParamType(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to get argument without active hook.", __FUNCTION__);
+ return FALSE;
+ }
+
+ size_t number = params[arg_number] - 1;
+
+ size_t paramCount = g_activeMessageContext->getParamCount();
+ if (number < 0 || number >= paramCount)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid message argument %d.", __FUNCTION__, number);
+ return FALSE;
+ }
+
+ return static_cast(g_activeMessageContext->getParamType(number));
+}
+
+/**
+* Retrieves the number of parameters in the current game message.
+*
+* @return Returns the number of parameters in the current game message.
+*
+* native GetMessageParamCount();
+*/
+cell AMX_NATIVE_CALL GetMessageParamCount(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to get argument without active hook.", __FUNCTION__);
+ return FALSE;
+ }
+
+ return g_activeMessageContext->getParamCount();
+}
+
+/**
+* Retrieves the origin of the current game message.
+*
+* @param origin An array to store the origin coordinates of the game message.
+*
+* @return Returns true if the origin is successfully retrieved, otherwise false.
+*
+* native bool:GetMessageOrigin(Float:origin[3]);
+*/
+cell AMX_NATIVE_CALL GetMessageOrigin(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number, arg_origin };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to get argument without active hook.", __FUNCTION__);
+ return FALSE;
+ }
+
+ const float *messageOrigin = g_activeMessageContext->getOrigin();
+ if (!messageOrigin)
+ return FALSE;
+
+ Vector &outVec = *(Vector *)getAmxAddr(amx, params[arg_origin]);
+ outVec = messageOrigin;
+ return TRUE;
+}
+
+/**
+* Retrieves the destination of the current message.
+*
+* @return Returns the destination of the current message.
+*
+* native GetMessageDest();
+*/
+cell AMX_NATIVE_CALL GetMessageDest(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_number, arg_origin };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ if (!g_activeMessageContext)
+ {
+ AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: trying to get argument without active hook.", __FUNCTION__);
+ return -1;
+ }
+
+ return static_cast(g_activeMessageContext->getDest());
+}
+
+/**
+* Sets the block type for the specified message ID.
+*
+* @param msgid The ID of the message to set the block type for.
+* @param type The type of block to set for the message, look at the enum MsgBlockType
+*
+* @return Returns true if the block type is successfully set, otherwise false.
+*
+* native bool:SetMessageBlock(const msgid, MsgBlockType:type);
+*/
+cell AMX_NATIVE_CALL SetMessageBlock(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_id, arg_type };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ int msg_id = params[arg_id];
+
+ // svc_bad (0) is not allowed for hook
+ if (msg_id <= 0 || msg_id >= MAX_USERMESSAGES)
+ return FALSE;
+
+ g_RehldsMessageManager->setMessageBlock(msg_id, static_cast(params[arg_type]));
+ return TRUE;
+}
+
+/**
+* Retrieves the block type for the specified message ID.
+*
+* @param msgid The ID of the message to retrieve the block type for.
+*
+* @return Returns the block type of the specified message, look at the enum MsgBlockType
+*
+* native MsgBlockType:GetMessageBlock(const msgid);
+*/
+cell AMX_NATIVE_CALL GetMessageBlock(AMX *amx, cell *params)
+{
+ enum args_e { arg_count, arg_id };
+
+ CHECK_REQUIREMENTS(ReHLDS);
+
+ int msg_id = params[arg_id];
+
+ // svc_bad (0) is not allowed for hook
+ if (msg_id <= 0 || msg_id >= MAX_USERMESSAGES)
+ return FALSE;
+
+ return static_cast(g_RehldsMessageManager->getMessageBlock(params[arg_id]));
+}
+
+AMX_NATIVE_INFO HookMessage_Natives[] =
+{
+ { "RegisterMessage", RegisterMessage },
+ { "UnregisterMessage", UnregisterMessage },
+
+ { "EnableHookMessage", EnableHookMessage },
+ { "DisableHookMessage", DisableHookMessage },
+
+ { "GetMessageParam", GetMessageParam },
+ { "GetMessageParamType", GetMessageParamType },
+ { "GetMessageParamCount", GetMessageParamCount },
+
+ { "GetMessageOrigin", GetMessageOrigin },
+ { "GetMessageDest", GetMessageDest },
+
+ { "SetMessageParam", SetMessageParam },
+
+ { "SetMessageBlock", SetMessageBlock },
+ { "GetMessageBlock", GetMessageBlock },
+
+ { nullptr, nullptr }
+};
+
+void RegisterNatives_HookMessage()
+{
+ g_amxxapi.AddNatives(HookMessage_Natives);
+}
diff --git a/reapi/src/natives/natives_hookmessage.h b/reapi/src/natives/natives_hookmessage.h
new file mode 100644
index 0000000..74a2073
--- /dev/null
+++ b/reapi/src/natives/natives_hookmessage.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void RegisterNatives_HookMessage();
diff --git a/reapi/src/precompiled.h b/reapi/src/precompiled.h
index e9a4ae4..21a2e7c 100644
--- a/reapi/src/precompiled.h
+++ b/reapi/src/precompiled.h
@@ -60,12 +60,14 @@
#include "main.h"
#include "api_config.h"
#include "hook_manager.h"
+#include "hook_message_manager.h"
#include "hook_callback.h"
#include "entity_callback_dispatcher.h"
#include "member_list.h"
// natives
#include "natives_hookchains.h"
+#include "natives_hookmessage.h"
#include "natives_members.h"
#include "natives_misc.h"
#include "natives_common.h"
| | |