diff --git a/reapi/CMakeLists.txt b/reapi/CMakeLists.txt index cfe8f930..95244736 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 ba71180a..24dee320 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 a2e47f94..d32b1cee 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 41b87982..4f753b31 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 501a60fc..53558f91 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 0b5c8fcf..d8895af4 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 00000000..9ecb35d7 --- /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 00000000..0cef91c1 --- /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 d82b4252..c9bd0773 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 109c582f..670de741 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 768dc8ca..cb2cb051 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 f6c664e4..19fef5dd 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 00000000..3c251829 --- /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 00000000..74a20739 --- /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 e9a4ae4d..21a2e7cd 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"