2
0
mirror of https://github.com/rehlds/reapi.git synced 2024-12-28 07:35:31 +03:00

Implement message hook using ReHLDS API

This commit is contained in:
s1lentq 2024-05-13 21:41:59 +07:00
parent 92c13f99e6
commit b757be53f5
15 changed files with 897 additions and 1 deletions

View File

@ -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"

View File

@ -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);

View File

@ -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
};

View File

@ -146,6 +146,7 @@
<ClInclude Include="..\include\cssdk\engine\hookchains.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="..\include\cssdk\engine\IMessageManager.h" />
<ClInclude Include="..\include\cssdk\engine\keydefs.h" />
<ClInclude Include="..\include\cssdk\engine\maintypes.h" />
<ClInclude Include="..\include\cssdk\engine\model.h" />
@ -217,6 +218,7 @@
<ClInclude Include="..\src\hook_list.h" />
<ClInclude Include="..\src\main.h" />
<ClInclude Include="..\src\member_list.h" />
<ClInclude Include="..\src\hook_message_manager.h" />
<ClInclude Include="..\src\mods\mod_rechecker_api.h" />
<ClInclude Include="..\src\mods\mod_regamedll_api.h" />
<ClInclude Include="..\src\mods\mod_rehlds_api.h" />
@ -227,6 +229,7 @@
<ClInclude Include="..\src\natives\natives_helper.h" />
<ClInclude Include="..\src\natives\natives_hookchains.h" />
<ClInclude Include="..\src\natives\natives_members.h" />
<ClInclude Include="..\src\natives\natives_hookmessage.h" />
<ClInclude Include="..\src\natives\natives_misc.h" />
<ClInclude Include="..\src\natives\natives_rechecker.h" />
<ClInclude Include="..\src\natives\natives_reunion.h" />
@ -266,6 +269,7 @@
</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\src\main.cpp" />
<ClCompile Include="..\src\hook_message_manager.cpp" />
<ClCompile Include="..\src\meta_api.cpp" />
<ClCompile Include="..\src\mods\mod_rechecker_api.cpp" />
<ClCompile Include="..\src\mods\mod_regamedll_api.cpp" />
@ -276,6 +280,7 @@
<ClCompile Include="..\src\natives\natives_common.cpp" />
<ClCompile Include="..\src\natives\natives_hookchains.cpp" />
<ClCompile Include="..\src\natives\natives_members.cpp" />
<ClCompile Include="..\src\natives\natives_hookmessage.cpp" />
<ClCompile Include="..\src\natives\natives_misc.cpp" />
<ClCompile Include="..\src\natives\natives_rechecker.cpp" />
<ClCompile Include="..\src\natives\natives_reunion.cpp" />

View File

@ -726,6 +726,15 @@
<ClInclude Include="..\include\cssdk\dlls\gib.h">
<Filter>include\cssdk\dlls</Filter>
</ClInclude>
<ClInclude Include="..\include\cssdk\engine\IMessageManager.h">
<Filter>include\cssdk\engine</Filter>
</ClInclude>
<ClInclude Include="..\src\hook_message_manager.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\src\natives\natives_hookmessage.h">
<Filter>src\natives</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\include\cssdk\common\parsemsg.cpp">
@ -830,6 +839,12 @@
<ClCompile Include="..\src\amx_hook.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\hook_message_manager.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\natives\natives_hookmessage.cpp">
<Filter>src\natives</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\extra\amxmodx\scripting\include\reapi.inc">

View File

@ -162,6 +162,7 @@ C_DLLEXPORT int AMXX_Attach(PFN_REQ_FNPTR reqFnptrFunc)
OnAmxxAttach();
RegisterNatives_HookChains();
RegisterNatives_HookMessage();
RegisterNatives_Members();
RegisterNatives_Misc();
RegisterNatives_VTC();

View File

@ -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<IMessage *> *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<CAmxxHookBase *> &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<CAmxxHookBase *> &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<CAmxxHookBase *> &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<IMessage *> *chain, IMessage *params)
{
g_messageHookManager.DispatchCallbacks(chain, params);
}

View File

@ -0,0 +1,64 @@
#pragma once
#include <array>
// 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<CAmxxHookBase *> pre, post;
};
// Dispatches the callbacks for the message hooks
void DispatchCallbacks(IVoidHookChain<IMessage *> *chain, IMessage *params);
// Routine function for dispatching message callbacks
static void RoutineMessageCallbacks(IVoidHookChain<IMessage *> *chain, IMessage *params);
// Getter the message hook by ID
MessageHook *getHook(size_t id);
// Array of message hooks
std::array<MessageHook, MAX_USERMESSAGES> 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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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();

View File

@ -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 *>((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
{

View File

@ -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<cell>(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<cell>(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<IMessage::BlockType>(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<cell>(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);
}

View File

@ -0,0 +1,3 @@
#pragma once
void RegisterNatives_HookMessage();

View File

@ -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"