From c922ef37072fd733949f0884d27bbbf04ded30a7 Mon Sep 17 00:00:00 2001 From: Frozen-H2O <43626458+Frozen-H2O@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:31:28 -0700 Subject: [PATCH] Add buttonwatch - Prints a message every time an entity fires OnPressed to players with buttonwatch enabled - Adds c_bw for players with ADMFLAG_GENERIC to toggle buttonwatch on and off (disabled by default) - Uses output hooking stuff from cs#: https://github.com/roflmuffin/CounterStrikeSharp/blob/f8451c2818a26380b49229655956e3058c3855ff/configs/addons/counterstrikesharp/gamedata/gamedata.json#L223-L228 --- gamedata/cs2fixes.games.txt | 6 ++++++ src/adminsystem.cpp | 20 ++++++++++++++++++++ src/detours.cpp | 29 +++++++++++++++++++++++++++++ src/detours.h | 35 ++++++++++++++++++++++++++++++++++- src/playermanager.cpp | 13 +++++++++++++ src/playermanager.h | 6 ++++++ src/user_preferences.cpp | 12 ++++++++++++ src/user_preferences.h | 4 +++- 8 files changed, 123 insertions(+), 2 deletions(-) diff --git a/gamedata/cs2fixes.games.txt b/gamedata/cs2fixes.games.txt index a47150b9..e56f592f 100644 --- a/gamedata/cs2fixes.games.txt +++ b/gamedata/cs2fixes.games.txt @@ -135,6 +135,12 @@ "windows" "\x48\x89\x54\x24\x10\x48\x89\x4C\x24\x08\x55\x53\x56\x57\x41\x55\x41\x56" "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x4C\x8D\xBD\xD0\xFE\xFF\xFF\x49\x89\xD6\x41\x55\x49\x89\xF5\x41\x54\x49\x89\xCC" } + "CEntityIOOutput_FireOutputInternal" + { + "library" "server" + "windows" "\x4C\x89\x4C\x24\x20\x53\x55\x57\x41\x54\x41\x56\x48\x81\xEC" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x49\x89\xD4\x53\x48\x89\xF3\x48\x83\xEC\x58" + } // Literally has "CGameRules::CGameRules constructed" in it "CGameRules_Constructor" { diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp index 87e37c17..d0acc94e 100644 --- a/src/adminsystem.cpp +++ b/src/adminsystem.cpp @@ -1414,6 +1414,26 @@ CON_COMMAND_CHAT_FLAGS(add_dc, " - Adds a fake p } #endif +CON_COMMAND_CHAT_FLAGS(bw, "- Toggle button watch display", ADMFLAG_GENERIC) +{ + if (!player) + { + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console."); + return; + } + + ZEPlayer* zpPlayer = player->GetZEPlayer(); + if (!zpPlayer) + { + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "Something went wrong..."); + return; + } + + zpPlayer->ToggleButtonWatch(); + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You have%s\1 button watch.", + zpPlayer->IsWatchingButtons() ? "\x04 enabled" : "\x02 disabled"); +} + CAdminSystem::CAdminSystem() { LoadAdmins(); diff --git a/src/detours.cpp b/src/detours.cpp index dc200d0b..88b9d520 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -68,6 +68,7 @@ DECLARE_DETOUR(CGameRules_Constructor, Detour_CGameRules_Constructor); DECLARE_DETOUR(CBaseEntity_TakeDamageOld, Detour_CBaseEntity_TakeDamageOld); DECLARE_DETOUR(CCSPlayer_WeaponServices_CanUse, Detour_CCSPlayer_WeaponServices_CanUse); DECLARE_DETOUR(CEntityIdentity_AcceptInput, Detour_CEntityIdentity_AcceptInput); +DECLARE_DETOUR(CEntityIOOutput_FireOutputInternal, Detour_CEntityIOOutput_FireOutputInternal); DECLARE_DETOUR(CNavMesh_GetNearestNavArea, Detour_CNavMesh_GetNearestNavArea); DECLARE_DETOUR(ProcessMovement, Detour_ProcessMovement); DECLARE_DETOUR(ProcessUsercmds, Detour_ProcessUsercmds); @@ -433,6 +434,34 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID); } + +void FASTCALL Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, CVariant* value, float flDelay) +{ + if (!V_stricmp(pThis->m_pDesc->m_pName, "OnPressed") && ((CBaseEntity*)pActivator)->IsPawn()) + { + std::string strMessage = CCSPlayerController::FromPawn(static_cast(pActivator))->GetPlayerName(); + strMessage = strMessage + "\1 pressed button \x0C" + std::to_string(pCaller->GetEntityIndex().Get()) + " "; + strMessage.append(((CBaseEntity*)pCaller)->GetName()); + + // Dont ask why this timer is here... + new CTimer(0.1f, false, false, [strMessage]() + { + for (int i = 0; i < gpGlobals->maxClients; i++) + { + CCSPlayerController* ccsPlayer = CCSPlayerController::FromSlot(i); + if (!ccsPlayer) + continue; + + ZEPlayer* zpPlayer = ccsPlayer->GetZEPlayer(); + if (zpPlayer && zpPlayer->IsWatchingButtons()) + ClientPrint(ccsPlayer, HUD_PRINTTALK, " \x02[BW]\x0C %s\1", strMessage.c_str()); + } + + return -1.0f; + }); + } +} + bool g_bBlockNavLookup = false; FAKE_BOOL_CVAR(cs2f_block_nav_lookup, "Whether to block navigation mesh lookup, improves server performance but breaks bot navigation", g_bBlockNavLookup, false, false) diff --git a/src/detours.h b/src/detours.h index cef09bf5..ea3fabe3 100644 --- a/src/detours.h +++ b/src/detours.h @@ -20,7 +20,7 @@ #pragma once #include "cdetour.h" #include - +#include "entitysystem.h" class CCheckTransmitInfo; class IRecipientFilter; @@ -47,6 +47,38 @@ class CGamePlayerEquip; class InputData_t; class CCSPlayerPawn; +struct EntityIOConnectionDesc_t +{ + const char* m_targetDesc; + const char* m_targetInput; + const char* m_valueOverride; + CEntityHandle m_hTarget; + EntityIOTargetType_t m_nTargetType; + int32 m_nTimesToFire; + float m_flDelay; +}; + +struct EntityIOConnection_t : EntityIOConnectionDesc_t +{ + bool m_bMarkedForRemoval; + EntityIOConnection_t* m_pNext; +}; + +struct EntityIOOutputDesc_t +{ + const char* m_pName; + uint32 m_nFlags; + uint32 m_nOutputOffset; +}; + +class CEntityIOOutput +{ +public: + void* vtable; + EntityIOConnection_t* m_pConnections; + EntityIOOutputDesc_t* m_pDesc; +}; + bool InitDetours(CGameConfig *gameConfig); void FlushAllDetours(); @@ -58,6 +90,7 @@ void FASTCALL Detour_CGameRules_Constructor(CGameRules *pThis); void FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity *pThis, CTakeDamageInfo *inputInfo); bool FASTCALL Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices *, CBasePlayerWeapon *); bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID); +void FASTCALL Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, CVariant* value, float flDelay); void* FASTCALL Detour_CNavMesh_GetNearestNavArea(int64_t unk1, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, int64_t unk6, float unk7, int64_t unk8); void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices *pThis, void *pMove); void *FASTCALL Detour_ProcessUsercmds(CBasePlayerPawn *pawn, CUserCmd *cmds, int numcmds, bool paused, float margin); diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 485fcfd1..3ed9fe4d 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -490,6 +490,19 @@ void ZEPlayer::EndGlow() addresses::UTIL_Remove(pModelParent); } +void ZEPlayer::ToggleButtonWatch() +{ + m_bIsWatchingButton = !m_bIsWatchingButton; + g_pUserPreferencesSystem->SetPreferenceBool(m_slot.Get(), BUTTON_WATCH_PREF_KEY_NAME, m_bIsWatchingButton); +} + +bool ZEPlayer::IsWatchingButtons() +{ + if (!IsAdminFlagSet(ADMFLAG_GENERIC) || IsFakeClient()) + return false; + return g_pUserPreferencesSystem->GetPreferenceBool(m_slot.Get(), BUTTON_WATCH_PREF_KEY_NAME, m_bIsWatchingButton); +} + void CPlayerManager::OnBotConnected(CPlayerSlot slot) { m_vecPlayers[slot.Get()] = new ZEPlayer(slot, true); diff --git a/src/playermanager.h b/src/playermanager.h index 037f5757..a9ad179f 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -28,10 +28,12 @@ #include "entity/lights.h" #include "entity/cparticlesystem.h" #include "gamesystem.h" +#include "user_preferences.h" #define DECAL_PREF_KEY_NAME "hide_decals" #define HIDE_DISTANCE_PREF_KEY_NAME "hide_distance" #define SOUND_STATUS_PREF_KEY_NAME "sound_status" +#define BUTTON_WATCH_PREF_KEY_NAME "button_watch" #define INVALID_ZEPLAYERHANDLE_INDEX 0u static uint32 iZEPlayerHandleSerial = 0u; // this should actually be 3 bytes large, but no way enough players join in servers lifespan for this to be an issue @@ -121,6 +123,7 @@ class ZEPlayer m_flMaxSpeed = 1.f; m_iLastInputs = IN_NONE; m_iLastInputTime = std::time(0); + m_bIsWatchingButton = g_pUserPreferencesSystem->GetPreferenceBool(m_slot.Get(), BUTTON_WATCH_PREF_KEY_NAME, false); } ~ZEPlayer() @@ -175,6 +178,7 @@ class ZEPlayer void SetLastInputs(uint64 iLastInputs) { m_iLastInputs = iLastInputs; } void UpdateLastInputTime() { m_iLastInputTime = std::time(0); } void SetMaxSpeed(float flMaxSpeed) { m_flMaxSpeed = flMaxSpeed; } + void ToggleButtonWatch(); bool IsMuted() { return m_bMuted; } bool IsGagged() { return m_bGagged; } @@ -208,6 +212,7 @@ class ZEPlayer float GetMaxSpeed() { return m_flMaxSpeed; } uint64 GetLastInputs() { return m_iLastInputs; } std::time_t GetLastInputTime() { return m_iLastInputTime; } + bool IsWatchingButtons(); void OnSpawn(); void OnAuthenticated(); @@ -261,6 +266,7 @@ class ZEPlayer float m_flMaxSpeed; uint64 m_iLastInputs; std::time_t m_iLastInputTime; + bool m_bIsWatchingButton; }; class CPlayerManager diff --git a/src/user_preferences.cpp b/src/user_preferences.cpp index b6dfbe5a..859964fe 100644 --- a/src/user_preferences.cpp +++ b/src/user_preferences.cpp @@ -177,6 +177,11 @@ float CUserPreferencesSystem::GetPreferenceFloat(int iSlot, const char* sKey, fl return V_StringToFloat32(GetPreference(iSlot, sKey, ""), fDefaultValue); } +float CUserPreferencesSystem::GetPreferenceBool(int iSlot, const char* sKey, bool bDefaultValue) +{ + return V_StringToBool(GetPreference(iSlot, sKey, ""), bDefaultValue); +} + void CUserPreferencesSystem::SetPreference(int iSlot, const char* sKey, const char* sValue) { uint32 iKeyHash = hash_32_fnv1a_const(sKey); @@ -212,6 +217,13 @@ void CUserPreferencesSystem::SetPreferenceFloat(int iSlot, const char* sKey, flo SetPreference(iSlot, sKey, (const char*) sPreferenceString); } +void CUserPreferencesSystem::SetPreferenceBool(int iSlot, const char* sKey, bool bValue) +{ + char sPreferenceString[MAX_PREFERENCE_LENGTH]; + V_snprintf(sPreferenceString, sizeof(sPreferenceString), "%b", bValue); + SetPreference(iSlot, sKey, (const char*) sPreferenceString); +} + bool CUserPreferencesSystem::CheckPreferencesLoaded(int iSlot) { ZEPlayer* player = g_playerManager->GetPlayer(CPlayerSlot(iSlot)); diff --git a/src/user_preferences.h b/src/user_preferences.h index c6dc64bc..6980dda5 100644 --- a/src/user_preferences.h +++ b/src/user_preferences.h @@ -17,7 +17,7 @@ * this program. If not, see . */ - +#pragma once #include "utlmap.h" #include "utlstring.h" #undef snprintf @@ -86,9 +86,11 @@ class CUserPreferencesSystem const char* GetPreference(int iSlot, const char* sKey, const char* sDefaultValue = ""); int GetPreferenceInt(int iSlot, const char* sKey, int iDefaultValue = 0); float GetPreferenceFloat(int iSlot, const char* sKey, float fDefaultValue = 0.0f); + float GetPreferenceBool(int iSlot, const char* sKey, bool bDefaultValue = false); void SetPreference(int iSlot, const char* sKey, const char* sValue); void SetPreferenceInt(int iSlot, const char* sKey, int iValue); void SetPreferenceFloat(int iSlot, const char* sKey, float fValue); + void SetPreferenceBool(int iSlot, const char* sKey, bool bValue); bool CheckPreferencesLoaded(int iSlot); bool PutPreferences(int iSlot, uint64 iSteamId, CUtlMap &preferenceData); void OnPutPreferences(int iSlot);