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:
parent
92c13f99e6
commit
b757be53f5
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -162,6 +162,7 @@ C_DLLEXPORT int AMXX_Attach(PFN_REQ_FNPTR reqFnptrFunc)
|
||||
OnAmxxAttach();
|
||||
|
||||
RegisterNatives_HookChains();
|
||||
RegisterNatives_HookMessage();
|
||||
RegisterNatives_Members();
|
||||
RegisterNatives_Misc();
|
||||
RegisterNatives_VTC();
|
||||
|
230
reapi/src/hook_message_manager.cpp
Normal file
230
reapi/src/hook_message_manager.cpp
Normal 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);
|
||||
}
|
64
reapi/src/hook_message_manager.h
Normal file
64
reapi/src/hook_message_manager.h
Normal 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;
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
@ -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
|
||||
{
|
||||
|
414
reapi/src/natives/natives_hookmessage.cpp
Normal file
414
reapi/src/natives/natives_hookmessage.cpp
Normal 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);
|
||||
}
|
3
reapi/src/natives/natives_hookmessage.h
Normal file
3
reapi/src/natives/natives_hookmessage.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void RegisterNatives_HookMessage();
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user