From 1b47fad299a88a732b1c3a917134bf8de2cef93a Mon Sep 17 00:00:00 2001 From: xen Date: Mon, 21 Sep 2026 18:43:34 +0300 Subject: [PATCH 01/12] Implement basic thirdperson I tried to avoid having to use a new hook hence the funny hack --- CS2Fixes.vcxproj | 1 + CS2Fixes.vcxproj.filters | 3 + cfg/cs2fixes/cs2fixes.cfg | 1 + src/commands.cpp | 17 +++++- src/cs2_sdk/entity/ccsplayerpawn.h | 1 + src/cs2_sdk/entity/customplayercamera.h | 45 +++++++++++++++ src/playermanager.cpp | 76 +++++++++++++++++++++++++ src/playermanager.h | 3 + 8 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/cs2_sdk/entity/customplayercamera.h diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj index 40a1b174..797abd0e 100644 --- a/CS2Fixes.vcxproj +++ b/CS2Fixes.vcxproj @@ -261,6 +261,7 @@ + diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters index 8da178c1..d03d6a9b 100644 --- a/CS2Fixes.vcxproj.filters +++ b/CS2Fixes.vcxproj.filters @@ -466,5 +466,8 @@ Header Files\cs2_sdk\entity + + Header Files\cs2_sdk\entity + \ No newline at end of file diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index f052491a..fcfcb268 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -28,6 +28,7 @@ cs2f_cvarwhitelist_enable 0 // Whether to enable the custom ConVar whitelist f cs2f_block_particle_msgs 0 // Whether to block CUserMsg_ParticleManager messages to fix lag/crashes, experimental cs2f_disable_setmodel 0 // Whether to disable SetModel usage from maps (custom input, cs_script function) cs2f_allow_duck_spam 0 // Whether to allow duck spamming by removing the duck slowdown, clients will only partially predict [0 = disabled, 1 = both teams, 2 = T only, 3 = CT only] +cs2f_thirdperson_enable 0 // Whether to enable thirdperson cs2f_beacon_particle "particles/cs2fixes/admin_beacon.vpcf" // .vpcf file to be precached and used for player beacon cs2f_motd_url "" // Server MOTD URL, shows up as a "Server Website" button in scoreboard diff --git a/src/commands.cpp b/src/commands.cpp index 020ae6a3..442e7834 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -833,7 +833,22 @@ CON_COMMAND_CHAT(vc, "- Display players that are using voice chat") VoiceChatPrintCmd(args, player); } -#if _DEBUG +CConVar g_cvarThirdPersonEnable("cs2f_thirdperson_enable", FCVAR_NONE, "Whether to enable thirdperson", false); + +CON_COMMAND_CHAT(tp, "- Thirdperson") +{ + if (!g_cvarThirdPersonEnable.Get() || !player) + return; + + ZEPlayer* pPlayer = player->GetZEPlayer(); + + if (!pPlayer) + return; + + pPlayer->ToggleThirdPerson(); +} + +#if 1 CON_COMMAND_CHAT(myuid, "- Test") { if (!player) diff --git a/src/cs2_sdk/entity/ccsplayerpawn.h b/src/cs2_sdk/entity/ccsplayerpawn.h index f418c230..b0b3c71a 100644 --- a/src/cs2_sdk/entity/ccsplayerpawn.h +++ b/src/cs2_sdk/entity/ccsplayerpawn.h @@ -62,6 +62,7 @@ class CCSPlayerPawn : public CCSPlayerPawnBase SCHEMA_FIELD(float, m_flVelocityModifier) SCHEMA_FIELD(CCSPlayer_ActionTrackingServices*, m_pActionTrackingServices) SCHEMA_FIELD(QAngle, m_angEyeAngles) + SCHEMA_FIELD(bool, m_bLeftHanded) [[nodiscard]] CCSPlayer_CameraServices* GetCameraService() { diff --git a/src/cs2_sdk/entity/customplayercamera.h b/src/cs2_sdk/entity/customplayercamera.h new file mode 100644 index 00000000..c8d3f97d --- /dev/null +++ b/src/cs2_sdk/entity/customplayercamera.h @@ -0,0 +1,45 @@ +/** + * ============================================================================= + * CS2Fixes + * Copyright (C) 2023-2026 Source2ZE + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "entity/ccsplayerpawn.h" + +enum CustomCameraMode_t : uint8_t +{ + CUSTOM_CAMERA_MODE_DISABLED = 0, + CUSTOM_CAMERA_MODE_CONTROLLED = 1, + CUSTOM_CAMERA_MODE_CONTROLLED_POSITION = 2, + CUSTOM_CAMERA_MODE_FOLLOW_POSITION = 3, +}; + +class CCSCustomPlayerCamera : public CBaseEntity +{ +public: + DECLARE_SCHEMA_CLASS(CCSCustomPlayerCamera) + + SCHEMA_FIELD(CHandle, m_hPawn) + SCHEMA_FIELD(CustomCameraMode_t, m_nCameraMode) + SCHEMA_FIELD(CHandle, m_hFollowEntity) + SCHEMA_FIELD(bool, m_bFollowEyes) + SCHEMA_FIELD(Vector, m_vecFollowOffset) + SCHEMA_FIELD(Vector, m_vecCameraOffset) + SCHEMA_FIELD(bool, m_bClipCameraOffset) + SCHEMA_FIELD(float32, m_flCameraOffsetReturnStrength) +}; \ No newline at end of file diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 3bfe9c84..4d82eda3 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -637,6 +637,82 @@ void ZEPlayer::CreatePointOrient() pOrient->AcceptInput("SetTarget", "!activator", pPawn); } +void ZEPlayer::ToggleThirdPerson(float flDistance) +{ + CCSPlayerController* pController = CCSPlayerController::FromSlot(GetPlayerSlot()); + + if (!pController) + return; + + CCSPlayerPawn* pPawn = pController->GetPlayerPawn(); + + if (!pPawn) + return; + + auto pCameraService = pPawn->GetCameraService(); + + if (!pCameraService) + return; + + CCSCustomPlayerCamera* pCamera = (CCSCustomPlayerCamera*)pCameraService->m_hViewEntity().Get(); + + if (pCamera) + { + // Map is taking control of the camera, let it be + if (pCamera->AsPointViewControl() || !CloseEnough(pCamera->m_vecFollowOffset().z, 0.001337)) + return; + + // Camera is ours, disable it + pCamera->m_nCameraMode = CUSTOM_CAMERA_MODE_DISABLED; + pCameraService->m_hViewEntity = nullptr; + + ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Thirdperson off"); + + return; + } + else if (!m_hCustomCamera.Get()) + { + // Try to find the player's camera if the map spawned it + CCSCustomPlayerCamera* pEnt = nullptr; + + while (pEnt = (CCSCustomPlayerCamera*)UTIL_FindEntityByClassname(pEnt, "custom_player_camera")) + { + if (pEnt->m_hPawn.Get() == pPawn) + { + pCamera = pEnt; + break; + } + } + + if (!pCamera) + { + // No camera found, spawn one + pCamera = CreateEntityByName("custom_player_camera"); + pCamera->DispatchSpawn(); + } + + m_hCustomCamera = pCamera; + } + else + { + // Player is in first person and our camera exists, use it + pCamera = m_hCustomCamera; + } + + pCamera->m_hPawn = pPawn; + pCamera->m_hFollowEntity = pPawn; + pCamera->m_nCameraMode = CUSTOM_CAMERA_MODE_FOLLOW_POSITION; + pCamera->m_bFollowEyes = true; + pCamera->m_vecFollowOffset = Vector(0.f, 0.f, 0.001337f); // HACK: Random tiny value to mark this as our camera + pCamera->m_vecCameraOffset = Vector(flDistance, pPawn->m_bLeftHanded ? 15.f : -15.f, 0.f); + pCamera->m_bClipCameraOffset = true; + pCamera->m_flCameraOffsetReturnStrength = 1.f; + + pCameraService->m_hViewEntity = pCamera; + + ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Thirdperson on"); +} + void ZEPlayer::CreateEntwatchHud() { CCSPlayerController* pController = CCSPlayerController::FromSlot(GetPlayerSlot()); diff --git a/src/playermanager.h b/src/playermanager.h index f0581950..d09c7648 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -23,6 +23,7 @@ #include "entity/cparticlesystem.h" #include "entity/cpointorient.h" #include "entity/cpointworldtext.h" +#include "entity/customplayercamera.h" #include "entity/lights.h" #include "gamesystem.h" #include "in_buttons.h" @@ -344,6 +345,7 @@ class ZEPlayer void SetSteamIdAttribute(); void CreateEntwatchHud(); void CreatePointOrient(); + void ToggleThirdPerson(float flDistance = -65.f); private: bool m_bAuthenticated; @@ -396,6 +398,7 @@ class ZEPlayer int m_iButtonWatchMode; CHandle m_hPointOrient; CHandle m_hEntwatchHud; + CHandle m_hCustomCamera; int m_iEntwatchHudMode; Color m_colorEntwatchHud; float m_flEntwatchHudX; From 0a99fcef963514095902738456f42073b6697462 Mon Sep 17 00:00:00 2001 From: xen Date: Mon, 21 Sep 2026 18:52:02 +0300 Subject: [PATCH 02/12] Update sdk --- sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk b/sdk index bd17582b..936d985f 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit bd17582be4bc18970e4fe4c518359872fda8276d +Subproject commit 936d985f984ca5e1f15dec082be17db44e754124 From ee48b285f87a5d8391c4a5932ea03fade01eb3ec Mon Sep 17 00:00:00 2001 From: Vauff Date: Mon, 21 Sep 2026 22:46:28 -0400 Subject: [PATCH 03/12] Fix thirdperson issues & tweak msgs --- src/commands.cpp | 4 ++-- src/playermanager.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/commands.cpp b/src/commands.cpp index 442e7834..57df67cb 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -835,7 +835,7 @@ CON_COMMAND_CHAT(vc, "- Display players that are using voice chat") CConVar g_cvarThirdPersonEnable("cs2f_thirdperson_enable", FCVAR_NONE, "Whether to enable thirdperson", false); -CON_COMMAND_CHAT(tp, "- Thirdperson") +CON_COMMAND_CHAT(tp, "- Toggle thirdperson") { if (!g_cvarThirdPersonEnable.Get() || !player) return; @@ -848,7 +848,7 @@ CON_COMMAND_CHAT(tp, "- Thirdperson") pPlayer->ToggleThirdPerson(); } -#if 1 +#if _DEBUG CON_COMMAND_CHAT(myuid, "- Test") { if (!player) diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 4d82eda3..8b2b3637 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -666,7 +666,7 @@ void ZEPlayer::ToggleThirdPerson(float flDistance) pCamera->m_nCameraMode = CUSTOM_CAMERA_MODE_DISABLED; pCameraService->m_hViewEntity = nullptr; - ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Thirdperson off"); + ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "You have disabled thirdperson."); return; } @@ -675,7 +675,7 @@ void ZEPlayer::ToggleThirdPerson(float flDistance) // Try to find the player's camera if the map spawned it CCSCustomPlayerCamera* pEnt = nullptr; - while (pEnt = (CCSCustomPlayerCamera*)UTIL_FindEntityByClassname(pEnt, "custom_player_camera")) + while ((pEnt = (CCSCustomPlayerCamera*)UTIL_FindEntityByClassname(pEnt, "custom_player_camera"))) { if (pEnt->m_hPawn.Get() == pPawn) { @@ -710,7 +710,7 @@ void ZEPlayer::ToggleThirdPerson(float flDistance) pCameraService->m_hViewEntity = pCamera; - ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Thirdperson on"); + ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "You have enabled thirdperson."); } void ZEPlayer::CreateEntwatchHud() From 9d8d2d805fbc3e2af3bbdff76827f2d45f178148 Mon Sep 17 00:00:00 2001 From: Vauff Date: Tue, 22 Sep 2026 23:39:25 -0400 Subject: [PATCH 04/12] Fix memory issue edge case with cvars in new cfg parser --- src/cfgparser.cpp | 5 ++++- src/cfgparser.h | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cfgparser.cpp b/src/cfgparser.cpp index 5b80cf7a..906e1806 100644 --- a/src/cfgparser.cpp +++ b/src/cfgparser.cpp @@ -98,7 +98,10 @@ void CCfgParser::ParseCfg(const char* pszCfgPath) if (convar.IsValidRef()) { - if (args.ArgC() > 1 && !convar.SetString(args[1])) + // Hold on to ConVarRefAbstract's so they don't get destroyed on next iteration loop, this could cause memory issues with deferred FCVAR_PERFORMING_CALLBACKS cvar's + ConVarRefAbstract& cachedConvar = m_mapConVars.insert_or_assign(convar.GetAccessIndex(), convar).first->second; + + if (args.ArgC() > 1 && !cachedConvar.SetString(args[1])) Message("Failed to execute \"%s %s\"\n", args[0], args[1]); continue; diff --git a/src/cfgparser.h b/src/cfgparser.h index 53f3296c..baee9275 100644 --- a/src/cfgparser.h +++ b/src/cfgparser.h @@ -19,6 +19,9 @@ #pragma once +#include "convar.h" +#include + class CCfgParser { public: @@ -26,6 +29,9 @@ class CCfgParser void ApplyGameSettings(const char* pszMapName); void ExecuteConfigs(const char* pszMapName); void ParseCfg(const char* pszCfgPath); + +private: + std::map m_mapConVars; }; extern CCfgParser* g_pCfgParser; From 8fd76c5ec359fc854b654b7441d7e7608101772b Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 23 Sep 2026 06:57:34 -0400 Subject: [PATCH 05/12] Update signatures for 2026-09-22 CS2 update --- gamedata/cs2fixes.jsonc | 65 +++++++++++++++++++++-------------------- src/detours.cpp | 2 +- src/detours.h | 2 +- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index 186bea51..706dff6f 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -5,15 +5,15 @@ "UTIL_SayTextFilter": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 49 8B F0 48 89 4D", - "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 4C 8D 75 ? 41 55 49 89 D5" + "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 49 8B D8", + "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 4C 8D 75 ? 41 55 41 89 CD" }, // Called from Host_Say "UTIL_SayText2Filter": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 41 8B F8", - "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 41 89 D6 31 D2" + "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 41 8B D8", + "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 89 D7 31 D2" }, // idk a good way to find this again, i just brute forced the vtable. offset is 136 on CTriggerPush "TriggerPush_Touch": @@ -27,14 +27,14 @@ { "library": "server", "windows": "48 89 5C 24 ? 55 56 57 41 55 41 57 48 83 EC ? 44 8B 89", - "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 49 89 D4 53 48 89 F3 48 81 EC" + "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 49 89 D4 53 48 89 F3 48 81 EC ? ? ? ? 8B 97" }, // Check vauff's pin in #scripting "ServerMovementUnlock": { "library": "server", "windows": "0F 86 AF ? ? ? 0F 57 C0 0F 2E C2", - "linux": "0F 87 ? ? ? ? F3 0F 10 25 ? ? ? ? F3 0F 11 A5 ? ? ? ? 48 89 DE" + "linux": "76 ? F3 0F 51 C0 F3 0F 7E DB 49 8B 06" }, // String: "CCSPlayerPawnBase::SwitchTeam", just keep in mind this is actually CCSPlayerController::SwitchTeam "CCSPlayerController_SwitchTeam": @@ -47,28 +47,28 @@ "CheckJumpButtonWater": { "library": "server", - "windows": "C8 42 EB ? 4C 8B 77 ? 4D 39 66", - "linux": "C8 42 41 C7 85 ? ? ? ? ? ? ? ? 41 C7 85 ? ? ? ? ? ? ? ? E9" + "windows": "C8 42 EB ? 4C 8B 73 ? 4D 39 6E", + "linux": "C8 42 41 C7 84 24 ? ? ? ? ? ? ? ? 41 C7 84 24 ? ? ? ? ? ? ? ? E9" }, // "SetPosition" is passed to this "CEntitySystem_AddEntityIOEvent": { "library": "server", - "windows": "48 89 5C 24 18 4C 89 4C 24 20 48 89 4C 24 08 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 20", + "windows": "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC ? 49 8B F1 48 8B F9", "linux": "55 48 89 E5 41 55 49 89 CD 41 54 49 89 FC 53 BB" }, // "BombPlanted" is passed to this "CEntityInstance_AcceptInput": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC ? 49 8B F0 48 8B D9 48 8B 0D", - "linux": "55 48 89 E5 41 56 49 89 FE 41 55 48 8D 7D" + "windows": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 56 48 81 EC ? ? ? ? 4D 8B F0 48 8B F1", + "linux": "55 48 89 E5 41 57 49 89 FF 41 56 48 8D BD ? ? ? ? 41 55 4C 8D AD" }, // Called by CEntityInstance_AcceptInput "CEntityIdentity_AcceptInput": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 02", + "windows": "48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 55 41 56 41 57 48 8D 6C 24", "linux": "55 48 89 E5 41 57 41 56 4C 8D BD ? ? ? ? 4D 89 CE" }, // func_pushable inside CTriggerBrush::Use calls CEntityIOOutput::FireOutputInternal @@ -78,8 +78,8 @@ "CEntityIOOutput_FireOutputInternal": { "library": "server", - "windows": "4C 89 4C 24 ? 48 89 4C 24 ? 53 56", - "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 49 89 D4 53 48 89 F3 48 83 EC ? 48 8D 05" + "windows": "48 8B C4 48 89 58 ? 48 89 70 ? 48 89 48 ? 55", + "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 49 89 D4 53 48 89 F3 48 81 EC ? ? ? ? 48 8D 05" }, // "multi_manager" is passed to this "CGameEntitySystem_FindEntityByClassName": @@ -160,13 +160,13 @@ { "library": "server", "windows": "48 8B C4 4C 89 48 ? 48 89 48 ? 55 41 54", - "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 41 89 F4 53 48 8D 35 ? ? ? ? 48 89 FB" + "linux": "55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 53 48 81 EC ? ? ? ? 48 8D 05 ? ? ? ? F3 0F 11 85 ? ? ? ? 89 B5" }, "CCSPlayer_WeaponServices_CanUse": { "library": "server", "windows": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 54 41 56 41 57 48 83 EC ? 48 8B 01 48 8B FA", - "linux": "55 48 8D 15 ? ? ? ? 48 89 E5 41 55 41 54 49 89 FC 53 48 89 F3 48 83 EC ? 48 8B 07 48 8B 80" + "linux": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 54 41 56 41 57 48 83 EC ? 48 8B 01 48 8B FA" }, "CCSPlayer_WeaponServices_EquipWeapon": { @@ -198,7 +198,7 @@ { "library": "server", "windows": "48 89 5C 24 08 48 89 74 24 10 48 89 7C 24 18 4C 89 74 24 20 55 48 8D 6C 24 D1", - "linux": "55 48 89 E5 41 57 41 56 41 55 41 89 CD 41 54 53 48 89 D3" + "linux": "55 48 89 E5 41 57 41 56 41 55 41 89 CD 41 54 53 48 81 EC" }, // search for "land_%s", this is called after that string is referenced (the one with 4 parameters). // (function that calls it also contains "T_Default.SuitLand"). @@ -238,8 +238,8 @@ "CGamePlayerEquip_InputTriggerForActivatedPlayer": { "library": "server", - "windows": "48 89 5C 24 18 56 48 83 EC 20 48 8B 1A", - "linux": "55 48 89 E5 41 56 41 55 41 54 53 48 83 EC ? 48 8B 1E" + "windows": "48 89 5C 24 ? 56 48 83 EC ? 48 8B 1A", + "linux": "55 48 89 E5 41 56 41 55 41 54 53 48 8B 1E 48 85 DB 74" }, // Return value of this function is used to determine whether "NETWORK_DISCONNECT_REJECT_SERVERFULL to %s: Cannot get free client\n" gets printed "GetFreeClient": @@ -261,7 +261,7 @@ { "library": "server", "windows": "4C 89 44 24 ? F3 0F 11 4C 24 ? 55 53 56", - "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 49 89 FC 53 48 8D 3D ? ? ? ? 48 81 EC ? ? ? ? 48 89 B5 ? ? ? ? BE ? ? ? ? F3 0F 11 85" + "linux": "48 B9 ? ? ? ? ? ? ? ? 55 48 89 E5 41 57 49 89 FF" }, "TraceFunc": { @@ -273,8 +273,8 @@ "TraceShape": { "library": "server", - "windows": "48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 56 41 57 48 8D AC 24 ? ? ? ? B8", - "linux": "55 48 89 E5 41 57 49 89 CF 41 56 49 89 F6 41 55 4D 89 C5 41 54 49 89 D4" + "windows": "48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 54 41 56 41 57", + "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 49 89 F7 41 56 48 8D 35 ? ? ? ? 49 89 CE" }, "CBasePlayerPawn_GetEyePosition": { @@ -288,19 +288,20 @@ "windows": "48 89 5C 24 ? 57 48 81 EC ? ? ? ? 48 8B F9 48 8B DA 48 8B 89 ? ? ? ? 48 85 C9", "linux": "55 48 89 E5 41 54 53 48 89 FB 48 83 C4 ? 48 8B BF ? ? ? ? 48 85 FF" }, - // Only ever referenced right next to string "TestActivator" + // Called by function with "TestActivator" string "CBaseFilter_InputTestActivator": { "library": "server", - "windows": "48 89 5C 24 ? 57 48 83 EC ? 4C 8B 02", - "linux": "55 48 89 E5 41 54 49 89 F4 53 48 89 FB 48 83 EC ? 48 8B 07 48 8B 16" + // Inlined by MSVC as of 2026-09-22 CS2 update + //"windows": "48 89 5C 24 ? 57 48 83 EC ? 4C 8B 02", + "linux": "55 48 89 E5 41 55 41 54 49 89 F4 53 48 89 FB 48 81 EC ? ? ? ? 48 8B 07 48 8B 16" }, // "Kicking user %s (sv_kick_players_with_cooldown=%d)\n" "GameSystem_Think_CheckSteamBan": { "library": "server", - "windows": "41 54 48 81 EC ? ? ? ? BA ? ? ? ? 48 8D 0D ? ? ? ? E8 ? ? ? ? 48 85 C0", - "linux": "55 48 8D 3D ? ? ? ? BE ? ? ? ? 48 89 E5 41 57 41 56 41 55 41 54 53 48 83 EC" + "windows": "41 54 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 44 8B 60", + "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 53 48 83 EC ? 48 8B 05 ? ? ? ? 44 8B 68 ? 45 85 ED 0F 8E ? ? ? ? 8B 15" }, // Location to CUtlMap unk that is referenced on Windows by function with "Notification about user penalty: %u/%u (%u sec)\n" string // On Linux, a qword appears twice in GameSystem_Think_CheckSteamBan, and thrice in a sub-function of the function used for Windows (1 top, 2 bottom), the only other reference to this qword is some convar registration function with two unks above, sm_mapGcBanInformation is the unk further away @@ -322,8 +323,8 @@ "CCSPlayer_ItemServices_CanAcquire": { "library": "server", - "windows": "44 89 44 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 55 41 56 41 57 48 8B EC", - "linux": "55 48 89 E5 41 57 41 56 41 55 49 89 CD 41 54 49 89 FC 53 48 89 F3 48 83 EC" + "windows": "44 89 44 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 54 41 55 41 56 41 57 48 8B EC", + "linux": "55 48 89 E5 41 57 41 56 49 89 F6 41 55 41 54 49 89 CC 53 48 89 FB 48 83 EC ? 4C 8B 6F" }, "CTriggerGravity_GravityTouch": { @@ -343,13 +344,13 @@ { "library": "server", "windows": "48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 4C 8B E9", - "linux": "55 31 C0 48 89 E5 41 57 41 56 41 55 41 54 41 89 F4" + "linux": "55 31 C0 48 89 E5 41 57 41 56 41 55 41 54 53 48 89 FB 48 8D 3D ? ? ? ? 48 81 EC ? ? ? ? 89 B5" }, // Called in return by function with "Usage: setang_exact pitch yaw " string "CBasePlayerPawn_SnapViewAngles": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B DA 48 8B F1 48 8D 55", + "windows": "48 89 7C 24 ? 55 48 8B EC 48 81 EC ? ? ? ? 48 8B F9 48 8D 4D ? E8 ? ? ? ? F2 0F 10 87", "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8" }, // These two functions are called by CEnvBeam & CEnvLaser member functions, one of those functions has "env_beams cannot have the end entity be the beam itself" string @@ -500,7 +501,7 @@ "ServerMovementUnlock": { "windows": "E9 B0 00 00 00 90", - "linux": "90 90 90 90 90 90" + "linux": "EB" }, "FixWaterFloorJump": { diff --git a/src/detours.cpp b/src/detours.cpp index 6508f208..4f993e9d 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -752,7 +752,7 @@ KHook::Return Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, C return {KHook::Action::Ignore}; } -KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void* a6, void* a7) +KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, void* pParameterContainer, float flDelay, void* a6, const CVariant* value) { if (g_cvarEnableButtonWatch.Get()) ButtonWatch(pThis, pActivator, pCaller, value, flDelay); diff --git a/src/detours.h b/src/detours.h index 65da62a4..e2c776dc 100644 --- a/src/detours.h +++ b/src/detours.h @@ -102,7 +102,7 @@ KHook::Return Detour_FindUseEntity(CCSPlayer_UseServices* pThis, f KHook::Return Detour_FindUseEntity_Post(CCSPlayer_UseServices* pThis, float unk2, int64_t unk3); KHook::Return Detour_TraceFunc(int64*, int*, float*, uint64); KHook::Return Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64); -KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void*, void*); +KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, void* pParameterContainer, float flDelay, void*, const CVariant* value); #ifdef PLATFORM_WINDOWS KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*); KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*); From a7f4c6cffc57999caf4392f0ccb19793463d4b50 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 23 Sep 2026 12:45:01 -0400 Subject: [PATCH 06/12] Update offsets for 2026-09-22 CS2 update --- gamedata/cs2fixes.jsonc | 68 ++++++++++++++++++++--------------------- src/detours.cpp | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index 706dff6f..62a7a3c5 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -166,7 +166,7 @@ { "library": "server", "windows": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 54 41 56 41 57 48 83 EC ? 48 8B 01 48 8B FA", - "linux": "48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 54 41 56 41 57 48 83 EC ? 48 8B 01 48 8B FA" + "linux": "55 48 8D 15 ? ? ? ? 48 89 E5 41 55 41 54 49 89 F4 53 48 89 FB 48 83 EC ? 48 8B 07 48 8B 80 ? ? ? ? 48 39 D0 0F 85 ? ? ? ? 80 BF" }, "CCSPlayer_WeaponServices_EquipWeapon": { @@ -391,63 +391,63 @@ }, "CBaseEntity::SetOwner": { - "windows": 53, - "linux": 52 + "windows": 54, + "linux": 53 }, "CBaseEntity::Use": { - "windows": 145, - "linux": 144 + "windows": 147, + "linux": 146 }, "CBaseEntity::StartTouch": { - "windows": 148, - "linux": 147 + "windows": 150, + "linux": 149 }, "CBaseEntity::Touch": { - "windows": 149, - "linux": 148 + "windows": 151, + "linux": 150 }, "CBaseEntity::EndTouch": { - "windows": 150, - "linux": 149 + "windows": 152, + "linux": 151 }, "Teleport": { - "windows": 163, - "linux": 162 + "windows": 165, + "linux": 164 }, "CollisionRulesChanged": { - "windows": 186, - "linux": 185 + "windows": 188, + "linux": 187 }, "CCSPlayerController_Respawn": { - "windows": 270, - "linux": 272 + "windows": 274, + "linux": 276 }, // CBaseTrigger "PassesTriggerFilters": { - "windows": 269, - "linux": 270 + "windows": 271, + "linux": 272 }, // Actually a bit of a wrapper function now? Eventually calls a long function with "player_hurt" in the middle and then inserts userid, health, priority, attacker strings "CCSPlayerPawn::OnTakeDamage_Alive": { - "windows": 252, - "linux": 253 + "windows": 255, + "linux": 256 }, // Look for the kill command, go through its callback and you should a find call like this, with v9 being a pawn pointer: - // return (*(*v9 + 2976LL))(v9, v27, 0LL); - // 2976 (372 * 8) is the offset + // return (*(*v9 + 3096LL))(v9, v27, 0LL); + // 3096 (387 * 8) is the offset "CBasePlayerPawn_CommitSuicide": { - "windows": 384, - "linux": 384 + "windows": 387, + "linux": 387 }, "GameEntitySystem": { @@ -457,15 +457,15 @@ // In the function with "[%03d] Found: %s, firing\n", you'll find a call into a pointer offset just a bit higher, that's the offset * 8 "CGameRules_FindPickerEntity": { - "windows": 26, - "linux": 27 + "windows": 25, + "linux": 26 }, // engine - // "tried to sprint to a non-client", there will be a check above like this: if ( a2 >= *(v5 + 632) ), note that this is a CUtlVector + // "tried to sprint to a non-client", there will be a check above like this: if ( a2 >= *(v5 + 616) ), note that this is a CUtlVector "CNetworkGameServer_ClientList": { - "windows": 73, - "linux": 73 + "windows": 77, + "linux": 77 }, // Right above "mapgroup workshop;" string there is a virtual call to this on g_pGameTypes using "workshop" string "IGameTypes_CreateWorkshopMapGroup": @@ -476,8 +476,8 @@ // There's no easy way to find this, but it's a function that checks entity flags (0x370) and ends by calling RemoveFlag with 0x800000 (FL_BASEVELOCITY) "CCSPlayer_MovementServices::CheckMovingGround": { - "windows": 44, - "linux": 45 + "windows": 45, + "linux": 46 }, "CCSPlayer_WeaponServices::DropWeapon": { @@ -492,8 +492,8 @@ // server.dll -> xref 'sv_phys_stop_at_collision' first __fastcall "CVPhys2World::GetTouchingList": { - "windows": 121, - "linux": 121 + "windows": 122, + "linux": 122 } }, "Patches": diff --git a/src/detours.cpp b/src/detours.cpp index 4f993e9d..1de71033 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -941,7 +941,7 @@ KHook::Return Detour_CWorldRendererMgr_CreateWorld_Internal_Po return {KHook::Action::Ignore}; auto pWorld = singleWorld->m_pCWorld; - auto vecLumpData = (CUtlVector*)((uint8_t*)pWorld + 0x1E0); + auto vecLumpData = (CUtlVector*)((uint8_t*)pWorld + 0x298); FOR_EACH_VEC(*vecLumpData, i) { From dd7c4cb7a14e928671c54c183bc5084753536774 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 23 Sep 2026 12:51:15 -0400 Subject: [PATCH 07/12] Update SDK --- sdk | 2 +- src/addresses.h | 4 +++- src/detours.cpp | 4 ++-- src/detours.h | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sdk b/sdk index 936d985f..625bfd4e 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit 936d985f984ca5e1f15dec082be17db44e754124 +Subproject commit 625bfd4e39816ad0e1eae3b3144a3b5425c5c7a0 diff --git a/src/addresses.h b/src/addresses.h index 09ac769e..a168a8b1 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -60,6 +60,8 @@ class CTakeDamageInfo; class CCSPlayer_WeaponServices; class CBasePlayerWeapon; class CSpawnGroupMgrGameSystem; +struct CPulseArgumentPack; +struct CPulseInputParamMap; struct EmitSound_t; struct StartSoundEventInfo; struct CTakeDamageResult; @@ -88,7 +90,7 @@ namespace addresses inline void(FASTCALL* UTIL_Remove)(CEntityInstance*); inline void(FASTCALL* CEntitySystem_AddEntityIOEvent)(CEntitySystem* pEntitySystem, CEntityInstance* pTarget, const char* pszInput, - CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, float flDelay, void*, void*); + CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, float flDelay, CPulseArgumentPack* pArgumentPack, CPulseInputParamMap* pParamMap); inline void(FASTCALL* CEntityInstance_AcceptInput)(CEntityInstance* pThis, const char* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); diff --git a/src/detours.cpp b/src/detours.cpp index 1de71033..0a618f2e 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -441,7 +441,7 @@ bool PrepareMapSetModel(CBaseModelEntity* pModel) return true; } -KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void* a6, void* a7) +KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, CPulseArgumentPack* pArgumentPack, CPulseInputParamMap* pParamMap) { VPROF_SCOPE_BEGIN("Detour_CEntityIdentity_AcceptInput"); @@ -752,7 +752,7 @@ KHook::Return Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, C return {KHook::Action::Ignore}; } -KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, void* pParameterContainer, float flDelay, void* a6, const CVariant* value) +KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, CPulseArgumentPack* pArgumentPack, float flDelay, CPulseInputParamMap* pParamMap, const CVariant* value) { if (g_cvarEnableButtonWatch.Get()) ButtonWatch(pThis, pActivator, pCaller, value, flDelay); diff --git a/src/detours.h b/src/detours.h index e2c776dc..b0621ed7 100644 --- a/src/detours.h +++ b/src/detours.h @@ -88,7 +88,7 @@ KHook::Return Detour_UTIL_SayTextFilter(IRecipientFilter&, const char*, CC KHook::Return Detour_UTIL_SayText2Filter(IRecipientFilter&, CCSPlayerController*, int, const char*, const char*, const char*, const char*, const char*); KHook::Return Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices*, CBasePlayerWeapon*); KHook::Return Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices*, CBasePlayerWeapon*); -KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void*, void*); +KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, CPulseArgumentPack* pArgumentPack, CPulseInputParamMap* pParamMap); KHook::Return Detour_CNavMesh_GetNearestNavArea(CNavMesh* pNavMesh, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7); KHook::Return Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove); KHook::Return Detour_ProcessMovement_Post(CCSPlayer_MovementServices* pThis, void* pMove); @@ -102,7 +102,7 @@ KHook::Return Detour_FindUseEntity(CCSPlayer_UseServices* pThis, f KHook::Return Detour_FindUseEntity_Post(CCSPlayer_UseServices* pThis, float unk2, int64_t unk3); KHook::Return Detour_TraceFunc(int64*, int*, float*, uint64); KHook::Return Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64); -KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, void* pParameterContainer, float flDelay, void*, const CVariant* value); +KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, CPulseArgumentPack* pArgumentPack, float flDelay, CPulseInputParamMap* pParamMap, const CVariant* value); #ifdef PLATFORM_WINDOWS KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*); KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*); From 6f39d8a8103277afd918ff870dd84ee6292ab832 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 23 Sep 2026 14:35:51 -0400 Subject: [PATCH 08/12] New necessary null check? --- src/zombiereborn.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index e91796ae..3935ea95 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -971,7 +971,9 @@ void ZR_OnPlayerSpawn(CCSPlayerController* pController) // also set team immediately in case the spawn teleport is team filtered if (bInfect) { - pController->GetPawn()->m_bTakesDamage(false); + if (pController->GetPawn()) + pController->GetPawn()->m_bTakesDamage(false); + pController->SwitchTeam(CS_TEAM_T); } else From a9fa15e9180a5028cdd040f06bb4aa19d6403931 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 23 Sep 2026 15:18:07 -0400 Subject: [PATCH 09/12] Revert "EntWatch clantag colours (#462)" This reverts commit 02bd0b18703acdde698550a9fe1b084f2104b875. --- cfg/cs2fixes/cs2fixes.cfg | 2 -- src/entwatch.cpp | 18 +----------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index fcfcb268..d4a62789 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -162,8 +162,6 @@ cs2f_enable_button_watch 0 // Whether to enable button watch or not. entwatch_enable 0 // Whether to enable EntWatch features entwatch_auto_filter 1 // Whether to automatically block non-item holders from triggering uses entwatch_clantag 1 // Whether to set item holder's clantag and set 9999 score -entwatch_clantag_colour_mode 1 // Whether to set item clantag colours (0=No colour, 1=Use item colour, 2=Use entwatch_clantag_colour value) -entwatch_clantag_colour "FFFFFF" // Hex colour (RRGGBB) to use if entwatch_clantag_colour_mode 2 entwatch_hud 1 // Whether to enable the EntWatch hud and related commands entwatch_score 9999 // Score to give item holders (0 = dont change score at all) Requires entwatch_clantag 1 entwatch_glow 1000 // Distance that dropped item weapon glow will be visible (0 = glow disabled) diff --git a/src/entwatch.cpp b/src/entwatch.cpp index 55e51adb..fb4feb6e 100644 --- a/src/entwatch.cpp +++ b/src/entwatch.cpp @@ -65,8 +65,6 @@ CBaseEntity* g_pCTriggerMultipleVTable = nullptr; CConVar g_cvarEnableEntWatch("entwatch_enable", FCVAR_NONE, "INCOMPATIBLE WITH CS#. Whether to enable EntWatch features", false); CConVar g_cvarEnableFiltering("entwatch_auto_filter", FCVAR_NONE, "Whether to automatically block non-item holders from triggering uses", true); CConVar g_cvarUseEntwatchClantag("entwatch_clantag", FCVAR_NONE, "Whether to set item holder's clantag and set score", true); -CConVar g_cvarClantagColourMode("entwatch_clantag_colour_mode", FCVAR_NONE, "Whether to set item clantag colours (0=No colour, 1=Use item colour, 2=Use entwatch_clantag_colour value)", 1, true, 0, true, 2); -CConVar g_cvarClantagColour("entwatch_clantag_colour", FCVAR_NONE, "Hex colour (RRGGBB) to use if entwatch_clantag_colour_mode 2", "FFFFFF"); CConVar g_cvarItemHolderScore("entwatch_score", FCVAR_NONE, "Score to give item holders (0 = dont change score at all) Requires entwatch_clantag 1", 9999, true, 0, false, 0); CConVar g_cvarEnableEntwatchHud("entwatch_hud", FCVAR_NONE, "Whether to enable the EntWatch hud and related commands", true); @@ -1905,21 +1903,7 @@ float EW_UpdateHud() if (g_cvarUseEntwatchClantag.Get()) { - switch (g_cvarClantagColourMode.Get()) - { - case 0: - V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "[%s]%s:", - sItemText.c_str(), pItem->szShortName.c_str()); - break; - case 1: - V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "[%s]%s", - pItem->colorGlow.r(), pItem->colorGlow.g(), pItem->colorGlow.b(), sItemText.c_str(), pItem->szShortName.c_str()); - break; - case 2: - V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "[%s]%s", - g_cvarClantagColour.Get().String(), sItemText.c_str(), pItem->szShortName.c_str()); - break; - } + V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "[%s]%s:", sItemText.c_str(), pItem->szShortName.c_str()); if (pItem->bHasThisClantag) pOwner->SetClanTag(pItem->sClantag); } From b8eb85e4caed97244af48570d2504ffae9835c1d Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 24 Sep 2026 07:48:43 -0400 Subject: [PATCH 10/12] Fix EntWatch counter tracking --- src/buttonwatch.cpp | 2 +- src/buttonwatch.h | 2 +- src/detours.cpp | 4 ++-- src/entwatch.cpp | 4 ++-- src/entwatch.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/buttonwatch.cpp b/src/buttonwatch.cpp index 016a4e1d..d92d1968 100644 --- a/src/buttonwatch.cpp +++ b/src/buttonwatch.cpp @@ -75,7 +75,7 @@ CON_COMMAND_CHAT_FLAGS(bw, "- Toggle button watch display", ADMFLAG_GENERIC) } std::map mapRecentEnts; -void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay) +void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, float flDelay) { if (!GetGlobals() || V_stricmp(pThis->m_pDesc->m_pName, "OnPressed") || !pActivator || !((CBaseEntity*)pActivator)->IsPawn() || !pCaller || mapRecentEnts.contains(pCaller->GetEntityIndex().Get())) return; diff --git a/src/buttonwatch.h b/src/buttonwatch.h index d7e9a825..bb72ffa2 100644 --- a/src/buttonwatch.h +++ b/src/buttonwatch.h @@ -22,4 +22,4 @@ extern CConVar g_cvarEnableButtonWatch; -void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay); \ No newline at end of file +void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, float flDelay); \ No newline at end of file diff --git a/src/detours.cpp b/src/detours.cpp index 0a618f2e..a5eeb691 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -755,10 +755,10 @@ KHook::Return Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, C KHook::Return Detour_CEntityIOOutput_FireOutputInternal(CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, CPulseArgumentPack* pArgumentPack, float flDelay, CPulseInputParamMap* pParamMap, const CVariant* value) { if (g_cvarEnableButtonWatch.Get()) - ButtonWatch(pThis, pActivator, pCaller, value, flDelay); + ButtonWatch(pThis, pActivator, pCaller, flDelay); if (g_cvarEnableEntWatch.Get()) - EW_FireOutput(pThis, pActivator, pCaller, value, flDelay); + EW_FireOutput(pThis, pActivator, pCaller, flDelay); return {KHook::Action::Ignore}; } diff --git a/src/entwatch.cpp b/src/entwatch.cpp index fb4feb6e..609527c4 100644 --- a/src/entwatch.cpp +++ b/src/entwatch.cpp @@ -2223,7 +2223,7 @@ void EW_PlayerDisconnect(int slot) g_pEWHandler->PlayerDrop(EWDropReason::Disconnect, -1, pController); } -void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay) +void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, float flDelay) { if (!g_pEWHandler->IsConfigLoaded() || !pCaller) return; @@ -2248,7 +2248,7 @@ void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CE // Message("Output for item %s (instance:%d) handler:%d outputname:%s\n", g_pEWHandler->vecItems[i]->szItemName, i, j, pThis->m_pDesc->m_pName); if (handler->type == EWHandlerType::CounterDown || handler->type == EWHandlerType::CounterUp) - handler->Use(value->m_float32); + handler->Use(((CMathCounter*)pCaller)->GetCounterValue()); else handler->Use(0.0); } diff --git a/src/entwatch.h b/src/entwatch.h index a318446d..1526bb2f 100644 --- a/src/entwatch.h +++ b/src/entwatch.h @@ -315,6 +315,6 @@ void EW_DropWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* void EW_PlayerDeath(IGameEvent* pEvent); void EW_PlayerDeathPre(CCSPlayerController* pController); void EW_PlayerDisconnect(int slot); -void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay); +void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, float flDelay); int GetTemplateSuffixNumber(const char* szName); float EW_UpdateHud(); \ No newline at end of file From ecd042d84fccd38c9f360bf28df100e9e8566dbc Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 24 Sep 2026 08:12:54 -0400 Subject: [PATCH 11/12] Adjust clan tags for new format --- cfg/cs2fixes/cs2fixes.cfg | 2 +- src/entwatch.cpp | 2 +- src/topdefender.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index d4a62789..ab924a08 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -72,7 +72,7 @@ cs2f_topdefender_print 1 // Whether to print defender stats to console at rou cs2f_topdefender_score 5000 // Score given to the top defender cs2f_topdefender_threshold 1000 // Damage threshold for Top Defenders to be shown on round end cs2f_topdefender_rate 1.0 // How often TopDefender stats get updated -cs2f_topdefender_clantag "[Top Defender]" // Clan tag given to the top defender +cs2f_topdefender_clantag "Top Defender" // Clan tag given to the top defender // Chat flood settings cs2f_flood_interval 0.75 // Amount of time allowed between chat messages acquiring flood tokens diff --git a/src/entwatch.cpp b/src/entwatch.cpp index 609527c4..6b214016 100644 --- a/src/entwatch.cpp +++ b/src/entwatch.cpp @@ -1903,7 +1903,7 @@ float EW_UpdateHud() if (g_cvarUseEntwatchClantag.Get()) { - V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "[%s]%s:", sItemText.c_str(), pItem->szShortName.c_str()); + V_snprintf(pItem->sClantag, sizeof(EWItemInstance::sClantag), "%s: %s", sItemText.c_str(), pItem->szShortName.c_str()); if (pItem->bHasThisClantag) pOwner->SetClanTag(pItem->sClantag); } diff --git a/src/topdefender.cpp b/src/topdefender.cpp index ddc7d026..c9a4514a 100644 --- a/src/topdefender.cpp +++ b/src/topdefender.cpp @@ -41,7 +41,7 @@ CConVar g_cvarTopDefenderPrint("cs2f_topdefender_print", FCVAR_NONE, "Whet CConVar g_cvarTopDefenderRate("cs2f_topdefender_rate", FCVAR_NONE, "How often TopDefender stats get updated", 1.0f, true, 0.1f, false, 0.0f); CConVar g_cvarTopDefenderThreshold("cs2f_topdefender_threshold", FCVAR_NONE, "Damage threshold for Top Defenders to be shown on round end", 1000, true, 0, false, 0); CConVar g_cvarTopDefenderScore("cs2f_topdefender_score", FCVAR_NONE, "Score given to the top defender", 5000, true, 0, false, 0); -CConVar g_cvarTopDefenderClanTag("cs2f_topdefender_clantag", FCVAR_NONE, "Clan tag given to the top defender", "[Top Defender]"); +CConVar g_cvarTopDefenderClanTag("cs2f_topdefender_clantag", FCVAR_NONE, "Clan tag given to the top defender", "Top Defender"); // Array sorting function bool SortTD(ZEPlayerHandle a, ZEPlayerHandle b) From a62250e8afd42b3d18d8d3fe45f0feedf2a60a1e Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 24 Sep 2026 09:27:37 -0400 Subject: [PATCH 12/12] Add map migration for logic_case InValue float values --- cfg/cs2fixes/cs2fixes.cfg | 3 ++- src/detours.cpp | 3 +++ src/mapmigrations.cpp | 26 ++++++++++++++++++++++++++ src/mapmigrations.h | 2 ++ src/playermanager.cpp | 2 +- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index ab924a08..8d400411 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -174,4 +174,5 @@ cs2f_hud_duration_leeway 2 // Extra seconds duration to leave hud messages vis // Map migrations cs2f_mapmigrations_20260121 2 // Current mode for 2026-01-21 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-01-21 & disabled if updated after] -cs2f_mapmigrations_20260420 2 // Current mode for 2026-04-20 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-04-20 & disabled if updated after] \ No newline at end of file +cs2f_mapmigrations_20260420 2 // Current mode for 2026-04-20 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-04-20 & disabled if updated after] +cs2f_mapmigrations_20260922 2 // Current mode for 2026-09-22 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-09-22 & disabled if updated after] \ No newline at end of file diff --git a/src/detours.cpp b/src/detours.cpp index a5eeb691..7afd5352 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -534,6 +534,9 @@ KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, C return {KHook::Action::Supersede, CPointViewControlHandler::OnDisableAll(pViewControl)}; } + if (g_pMapMigrations->Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value)) + return {KHook::Action::Supersede, true}; + VPROF_SCOPE_END(); return {KHook::Action::Ignore}; diff --git a/src/mapmigrations.cpp b/src/mapmigrations.cpp index 7750b06d..8e57a8eb 100644 --- a/src/mapmigrations.cpp +++ b/src/mapmigrations.cpp @@ -29,9 +29,11 @@ CMapMigrations* g_pMapMigrations = nullptr; const time_t g_time20260121 = 1769036239; const time_t g_time20260420 = 1776725888; +const time_t g_time20260922 = 1790120204; CConVar g_cvarMapMigrations20260121("cs2f_mapmigrations_20260121", FCVAR_NONE, "Current mode for 2026-01-21 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-01-21 & disabled if updated after]", 2); CConVar g_cvarMapMigrations20260420("cs2f_mapmigrations_20260420", FCVAR_NONE, "Current mode for 2026-04-20 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-04-20 & disabled if updated after]", 2); +CConVar g_cvarMapMigrations20260922("cs2f_mapmigrations_20260922", FCVAR_NONE, "Current mode for 2026-09-22 CS2 update map migrations. [0 = Force disabled, 1 = Force enabled, 2 = Automatically enabled for maps updated before 2026-09-22 & disabled if updated after]", 2); void CMapMigrations::PreLevelLoad(uint64 iWorkshopId) { @@ -65,6 +67,14 @@ void CMapMigrations::OnEquipWeapon(CBasePlayerWeapon* pWeapon) Migrations_20260420(pWeapon); } +bool CMapMigrations::Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value) +{ + if (g_cvarMapMigrations20260922.Get() == 1 || (g_cvarMapMigrations20260922.Get() == 2 && m_timeMapUpdated < g_time20260922)) + return Migrations_20260922(pThis, pInputName, pActivator, pCaller, value); + + return false; +} + void CMapMigrations::RunMigrations(CUtlVector* pVecEntityKeyValues) { if (g_cvarMapMigrations20260121.Get() > 0) @@ -157,6 +167,22 @@ void CMapMigrations::Migrations_20260420(CBasePlayerWeapon* pWeapon) } } +bool CMapMigrations::Migrations_20260922(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value) +{ + // Fix stringified Pulse argument packs formatting float decimals breaking logic_case matching post 2026-09-22 CS2 update + if (value->m_type == FIELD_FLOAT32 && !V_strcasecmp(pInputName->String(), "InValue") && !V_strcasecmp(pThis->GetClassname(), "logic_case")) + { + char szValue[32]; + V_snprintf(szValue, sizeof(szValue), "%g", value->m_float32); + + // Back up through CEntityInstance_AcceptInput so we can just worry about variant_t + ((CBaseEntity*)pThis->m_pInstance)->AcceptInput(pInputName->String(), szValue, pActivator, pCaller); + return true; + } + + return false; +} + bool CMapMigrations::Migrations20260420Enabled() { return g_cvarMapMigrations20260420.Get() == 1 || (g_cvarMapMigrations20260420.Get() == 2 && m_timeMapUpdated < g_time20260420); diff --git a/src/mapmigrations.h b/src/mapmigrations.h index dfcf31e8..9c57da3a 100644 --- a/src/mapmigrations.h +++ b/src/mapmigrations.h @@ -37,10 +37,12 @@ class CMapMigrations void ApplyGameSettings(uint64 iWorkshopId); void OnRoundPrestart(); void OnEquipWeapon(CBasePlayerWeapon* pWeapon); + bool Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); void RunMigrations(CUtlVector* pVecEntityKeyValues); void Migrations_Rendermode(CUtlVector* pVecEntityKeyValues); void Migrations_20260121(CUtlVector* pVecEntityKeyValues); void Migrations_20260420(CBasePlayerWeapon* pWeapon); + bool Migrations_20260922(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); bool Migrations20260420Enabled(); void UpdateMapUpdateTime(uint64 iWorkshopId, time_t timeMapUpdated); diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 8b2b3637..9c95e59c 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -674,7 +674,7 @@ void ZEPlayer::ToggleThirdPerson(float flDistance) { // Try to find the player's camera if the map spawned it CCSCustomPlayerCamera* pEnt = nullptr; - + while ((pEnt = (CCSCustomPlayerCamera*)UTIL_FindEntityByClassname(pEnt, "custom_player_camera"))) { if (pEnt->m_hPawn.Get() == pPawn)