diff --git a/extensions/sdktools/hooks.cpp b/extensions/sdktools/hooks.cpp index a9ef37be..d4900e70 100644 --- a/extensions/sdktools/hooks.cpp +++ b/extensions/sdktools/hooks.cpp @@ -44,6 +44,7 @@ CHookManager g_Hooks; static bool PRCH_enabled = false; static bool PRCH_used = false; +static bool PRCHPost_used = false; static bool FILE_used = false; SH_DECL_MANUALHOOK2_void(PlayerRunCmdHook, 0, 0, 0, CUserCmd *, IMoveHelper *); @@ -60,6 +61,7 @@ SourceHook::CallClass *basefilesystemPatch = NULL; CHookManager::CHookManager() { m_usercmdsFwd = NULL; + m_usercmdsPostFwd = NULL; m_netFileSendFwd = NULL; m_netFileReceiveFwd = NULL; m_pActiveNetChannel = NULL; @@ -99,11 +101,25 @@ void CHookManager::Initialize() Param_CellByRef, // tickcount Param_CellByRef, // seed Param_Array); // mouse[2] + + m_usercmdsPostFwd = forwards->CreateForward("OnPlayerRunCmdPost", ET_Ignore, 11, NULL, + Param_Cell, // client + Param_Cell, // buttons + Param_Cell, // impulse + Param_Array, // Float:vel[3] + Param_Array, // Float:angles[3] + Param_Cell, // weapon + Param_Cell, // subtype + Param_Cell, // cmdnum + Param_Cell, // tickcount + Param_Cell, // seed + Param_Array); // mouse[2] } void CHookManager::Shutdown() { forwards->ReleaseForward(m_usercmdsFwd); + forwards->ReleaseForward(m_usercmdsPostFwd); forwards->ReleaseForward(m_netFileSendFwd); forwards->ReleaseForward(m_netFileReceiveFwd); @@ -127,14 +143,14 @@ void CHookManager::OnClientConnect(int client) void CHookManager::OnClientPutInServer(int client) { - PlayerRunCmdHook(client); + if (PRCH_used) + PlayerRunCmdHook(client, false); + if (PRCHPost_used) + PlayerRunCmdHook(client, true); } -void CHookManager::PlayerRunCmdHook(int client) +void CHookManager::PlayerRunCmdHook(int client, bool post) { - if (!PRCH_used) - return; - edict_t *pEdict = PEntityOfEntIndex(client); if (!pEdict) { @@ -153,18 +169,24 @@ void CHookManager::PlayerRunCmdHook(int client) return; } + ke::Vector &runUserCmdHookVec = post ? m_runUserCmdPostHooks : m_runUserCmdHooks; CVTableHook hook(pEntity); - for (size_t i = 0; i < m_runUserCmdHooks.length(); ++i) + for (size_t i = 0; i < runUserCmdHookVec.length(); ++i) { - if (hook == m_runUserCmdHooks[i]) + if (hook == runUserCmdHookVec[i]) { return; } } - int hookid = SH_ADD_MANUALVPHOOK(PlayerRunCmdHook, pEntity, SH_MEMBER(this, &CHookManager::PlayerRunCmd), false); + int hookid; + if (post) + hookid = SH_ADD_MANUALVPHOOK(PlayerRunCmdHook, pEntity, SH_MEMBER(this, &CHookManager::PlayerRunCmdPost), true); + else + hookid = SH_ADD_MANUALVPHOOK(PlayerRunCmdHook, pEntity, SH_MEMBER(this, &CHookManager::PlayerRunCmd), false); + hook.SetHookID(hookid); - m_runUserCmdHooks.append(new CVTableHook(hook)); + runUserCmdHookVec.append(new CVTableHook(hook)); } void CHookManager::PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper) @@ -230,6 +252,48 @@ void CHookManager::PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper) RETURN_META(MRES_IGNORED); } +void CHookManager::PlayerRunCmdPost(CUserCmd *ucmd, IMoveHelper *moveHelper) +{ + if (m_usercmdsPostFwd->GetFunctionCount() == 0) + { + RETURN_META(MRES_IGNORED); + } + + CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); + + if (!pEntity) + { + RETURN_META(MRES_IGNORED); + } + + edict_t *pEdict = gameents->BaseEntityToEdict(pEntity); + + if (!pEdict) + { + RETURN_META(MRES_IGNORED); + } + + int client = IndexOfEdict(pEdict); + cell_t vel[3] = { sp_ftoc(ucmd->forwardmove), sp_ftoc(ucmd->sidemove), sp_ftoc(ucmd->upmove) }; + cell_t angles[3] = { sp_ftoc(ucmd->viewangles.x), sp_ftoc(ucmd->viewangles.y), sp_ftoc(ucmd->viewangles.z) }; + cell_t mouse[2] = { ucmd->mousedx, ucmd->mousedy }; + + m_usercmdsPostFwd->PushCell(client); + m_usercmdsPostFwd->PushCell(ucmd->buttons); + m_usercmdsPostFwd->PushCell(ucmd->impulse); + m_usercmdsPostFwd->PushArray(vel, 3); + m_usercmdsPostFwd->PushArray(angles, 3); + m_usercmdsPostFwd->PushCell(ucmd->weaponselect); + m_usercmdsPostFwd->PushCell(ucmd->weaponsubtype); + m_usercmdsPostFwd->PushCell(ucmd->command_number); + m_usercmdsPostFwd->PushCell(ucmd->tick_count); + m_usercmdsPostFwd->PushCell(ucmd->random_seed); + m_usercmdsPostFwd->PushArray(mouse, 2); + m_usercmdsPostFwd->Execute(); + + RETURN_META(MRES_IGNORED); +} + void CHookManager::NetChannelHook(int client) { if (!FILE_used) @@ -387,16 +451,30 @@ bool CHookManager::SendFile(const char *filename, unsigned int transferID) void CHookManager::OnPluginLoaded(IPlugin *plugin) { - if (PRCH_enabled && !PRCH_used && m_usercmdsFwd->GetFunctionCount()) + if (PRCH_enabled) { - PRCH_used = true; - - int MaxClients = playerhelpers->GetMaxClients(); - for (int i = 1; i <= MaxClients; i++) + bool changed = false; + if (!PRCH_used && (m_usercmdsFwd->GetFunctionCount() > 0)) { - if (playerhelpers->GetGamePlayer(i)->IsInGame()) + PRCH_used = true; + changed = true; + } + if (!PRCHPost_used && (m_usercmdsPostFwd->GetFunctionCount() > 0)) + { + PRCHPost_used = true; + changed = true; + } + + // Only check the hooks on the players if a new hook is used by this plugin. + if (changed) + { + int MaxClients = playerhelpers->GetMaxClients(); + for (int i = 1; i <= MaxClients; i++) { - OnClientPutInServer(i); + if (playerhelpers->GetGamePlayer(i)->IsInGame()) + { + OnClientPutInServer(i); + } } } } @@ -429,6 +507,17 @@ void CHookManager::OnPluginUnloaded(IPlugin *plugin) PRCH_used = false; } + if (PRCHPost_used && !m_usercmdsPostFwd->GetFunctionCount()) + { + for (size_t i = 0; i < m_runUserCmdPostHooks.length(); ++i) + { + delete m_runUserCmdPostHooks[i]; + } + + m_runUserCmdPostHooks.clear(); + PRCHPost_used = false; + } + if (FILE_used && !m_netFileSendFwd->GetFunctionCount() && !m_netFileReceiveFwd->GetFunctionCount()) { for (size_t i = 0; i < m_netChannelHooks.length(); ++i) diff --git a/extensions/sdktools/hooks.h b/extensions/sdktools/hooks.h index 6009e058..f536753b 100644 --- a/extensions/sdktools/hooks.h +++ b/extensions/sdktools/hooks.h @@ -49,6 +49,7 @@ public: void OnClientConnect(int client); void OnClientPutInServer(int client); void PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper); + void PlayerRunCmdPost(CUserCmd *ucmd, IMoveHelper *moveHelper); void OnMapStart(); public: /* NetChannel/Related Hooks */ bool FileExists(const char *filename, const char *pathID); @@ -66,14 +67,16 @@ public: //IFeatureProvider virtual FeatureStatus GetFeatureStatus(FeatureType type, const char *name); private: - void PlayerRunCmdHook(int client); + void PlayerRunCmdHook(int client, bool post); void NetChannelHook(int client); private: IForward *m_usercmdsFwd; + IForward *m_usercmdsPostFwd; IForward *m_netFileSendFwd; IForward *m_netFileReceiveFwd; ke::Vector m_runUserCmdHooks; + ke::Vector m_runUserCmdPostHooks; ke::Vector m_netChannelHooks; INetChannel *m_pActiveNetChannel; bool m_bFSTranHookWarned = false; diff --git a/plugins/include/sdktools_hooks.inc b/plugins/include/sdktools_hooks.inc index 315b058c..fc0f14a0 100644 --- a/plugins/include/sdktools_hooks.inc +++ b/plugins/include/sdktools_hooks.inc @@ -58,6 +58,23 @@ */ forward Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]); +/** + * @brief Called after a clients movement buttons were processed. + * + * @param client Index of the client. + * @param buttons The current commands (as bitflags - see entity_prop_stocks.inc). + * @param impulse The current impulse command. + * @param vel Players desired velocity. + * @param angles Players desired view angles. + * @param weapon Entity index of the new weapon if player switches weapon, 0 otherwise. + * @param subtype Weapon subtype when selected from a menu. + * @param cmdnum Command number. Increments from the first command sent. + * @param tickcount Tick count. A client's prediction based on the server's GetGameTickCount value. + * @param seed Random seed. Used to determine weapon recoil, spread, and other predicted elements. + * @param mouse Mouse direction (x, y). + */ +forward void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2]); + /** * @brief Called when a client requests a file from the server. *