From 7149512aab59ceb112ea21af47f65c899effac54 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 20 May 2026 23:41:08 -0400 Subject: [PATCH 01/30] Simplify forced next map & FinishVote flows --- src/map_votes.cpp | 60 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/map_votes.cpp b/src/map_votes.cpp index 5ce879c0..148b159a 100644 --- a/src/map_votes.cpp +++ b/src/map_votes.cpp @@ -246,33 +246,34 @@ void CMapVoteSystem::StartVote() m_iVoteSize = std::min((int)vecPossibleMaps.size(), g_cvarVoteMaxMaps.Get()); bool bAbort = false; + std::shared_ptr pAlternativeMap; if (GetForcedNextMap()) { - CTimer::Create(6.0f, TIMERFLAG_MAP, []() { - g_pMapVoteSystem->FinishVote(); - return -1.0f; - }); - + pAlternativeMap = GetForcedNextMap(); bAbort = true; + ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "The vote was overriden. \x06%s\x01 will be the next map!\n", GetForcedNextMap()->GetName()); + Message("The vote was overriden. \x06%s\x01 will be the next map!\n", GetForcedNextMap()->GetName()); } else if (m_iVoteSize < 2) { - ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "Not enough maps available for map vote, aborting! Please have an admin loosen map limits."); - Message("Not enough maps available for map vote, aborting!\n"); - m_bIsVoteOngoing = false; - bAbort = true; - // Reload the current map as a fallback // Previously we fell back to game behaviour which could choose a random map in mapgroup, but a crash bug with default map changes was introduced in 2025-05-07 CS2 update - CTimer::Create(6.0f, TIMERFLAG_MAP, []() { - g_pMapVoteSystem->GetCurrentMap()->Load(); - return -1.0f; - }); + pAlternativeMap = GetCurrentMap(); + bAbort = true; + ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "Not enough maps available for map vote, aborting! Please have an admin loosen map limits."); + Message("Not enough maps available for map vote, aborting!\n"); } if (bAbort) { + CTimer::Create(6.0f, TIMERFLAG_MAP, [pAlternativeMap]() { + if (pAlternativeMap) + pAlternativeMap->Load(); + + return -1.0f; + }); + // Disable the map vote for (int i = 0; i < 10; i++) { @@ -283,6 +284,7 @@ void CMapVoteSystem::StartVote() g_pGameRules->m_nEndMatchMapGroupVoteOptions.NetworkStateChanged(); } + m_bIsVoteOngoing = false; return; } @@ -383,28 +385,18 @@ void CMapVoteSystem::FinishVote() bool bIsNextMapVoted = UpdateWinningMap(); int iNextMapVoteIndex = WinningMapIndex(); char buffer[256]; - std::shared_ptr pNextMap; - if (GetForcedNextMap()) + if (iNextMapVoteIndex == -1) { - pNextMap = GetForcedNextMap(); + Panic("Failed to count map votes, file a bug\n"); + iNextMapVoteIndex = 0; } - else - { - if (iNextMapVoteIndex == -1) - { - Panic("Failed to count map votes, file a bug\n"); - iNextMapVoteIndex = 0; - } - g_pGameRules->m_nEndMatchMapVoteWinner = iNextMapVoteIndex; - pNextMap = GetMapByIndex(g_pGameRules->m_nEndMatchMapGroupVoteOptions[iNextMapVoteIndex]); - } + std::shared_ptr pNextMap = GetMapByIndex(g_pGameRules->m_nEndMatchMapGroupVoteOptions[iNextMapVoteIndex]); + g_pGameRules->m_nEndMatchMapVoteWinner = iNextMapVoteIndex; // Print out the map we're changing to - if (GetForcedNextMap()) - V_snprintf(buffer, sizeof(buffer), "The vote was overriden. \x06%s\x01 will be the next map!\n", pNextMap->GetName()); - else if (bIsNextMapVoted) + if (bIsNextMapVoted) V_snprintf(buffer, sizeof(buffer), "The vote has ended. \x06%s\x01 will be the next map!\n", pNextMap->GetName()); else V_snprintf(buffer, sizeof(buffer), "No map was chosen. \x06%s\x01 will be the next map!\n", pNextMap->GetName()); @@ -413,7 +405,7 @@ void CMapVoteSystem::FinishVote() Message(buffer); // Print vote result information: how many votes did each map get? - if (!GetForcedNextMap() && GetGlobals()) + if (GetGlobals()) { int arrMapVotes[10] = {0}; Message("Map vote result --- total votes per map:\n"); @@ -837,6 +829,12 @@ void CMapVoteSystem::PrintMapList(CCSPlayerController* pController) void CMapVoteSystem::ForceNextMap(CCSPlayerController* pController, const char* pszMapSubstring) { + if (m_bIntermissionStarted) + { + ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Cannot force next map because the map vote has already started!"); + return; + } + if (pszMapSubstring[0] == '\0') { if (!GetForcedNextMap()) From ad89e67dad91b51ba55e529811323791ccd7ba53 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 21 May 2026 20:59:25 -0400 Subject: [PATCH 02/30] Fix weapon parented entities migration being reset on some entities --- src/cs2_sdk/entity/cbasetoggle.h | 3 +++ src/mapmigrations.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/cs2_sdk/entity/cbasetoggle.h b/src/cs2_sdk/entity/cbasetoggle.h index 66ae7c86..f939c728 100644 --- a/src/cs2_sdk/entity/cbasetoggle.h +++ b/src/cs2_sdk/entity/cbasetoggle.h @@ -24,4 +24,7 @@ class CBaseToggle : CBaseModelEntity { DECLARE_SCHEMA_CLASS(CBaseToggle); + + SCHEMA_FIELD(Vector, m_vecPosition1); + SCHEMA_FIELD(Vector, m_vecPosition2); }; \ No newline at end of file diff --git a/src/mapmigrations.cpp b/src/mapmigrations.cpp index 2a934050..c57fd07a 100644 --- a/src/mapmigrations.cpp +++ b/src/mapmigrations.cpp @@ -21,6 +21,7 @@ #include "cs2fixes.h" #include "entity.h" #include "entity/cbasemodelentity.h" +#include "entity/cbasetoggle.h" #include "utils.h" #include "vprof.h" @@ -133,8 +134,20 @@ void CMapMigrations::Migrations_20260420(CBasePlayerWeapon* pWeapon) if (pParentSceneNode && pParentSceneNode->m_pOwner() == pWeapon) { Vector newOrigin = pTarget->GetAbsOrigin(); + const char* pszClass = pTarget->GetClassname(); + newOrigin.z -= 40.0f; pTarget->Teleport(&newOrigin, nullptr, nullptr); + + // If child inherits CBaseToggle, there's further bullshit we have to offset + // There's also some more obscure entities + all of CBaseTrigger, but these seem unnecessary to fixup + if (!V_strcasecmp(pszClass, "func_button") || !V_strcasecmp(pszClass, "func_physical_button") || !V_strcasecmp(pszClass, "func_rot_button") || !V_strcasecmp(pszClass, "momentary_rot_button") || !V_strcasecmp(pszClass, "func_movelinear") || !V_strcasecmp(pszClass, "func_door") || !V_strcasecmp(pszClass, "func_door_rotating")) + { + CBaseToggle* pToggle = (CBaseToggle*)pTarget; + + pToggle->m_vecPosition1().z -= 40.0f; + pToggle->m_vecPosition2().z -= 40.0f; + } } } } From 93045c53faf825093077cf02147ddf6e38b3f63a Mon Sep 17 00:00:00 2001 From: Vauff Date: Fri, 22 May 2026 01:28:41 -0400 Subject: [PATCH 03/30] Migrate discord bot config to JSONC --- PackageScript | 2 +- configs/discordbots.cfg.example | 14 ---- configs/discordbots.jsonc.example | 13 ++++ src/discord.cpp | 106 ++++++++++++++++++++++++++---- src/discord.h | 3 + 5 files changed, 110 insertions(+), 28 deletions(-) delete mode 100644 configs/discordbots.cfg.example create mode 100644 configs/discordbots.jsonc.example diff --git a/PackageScript b/PackageScript index ab59251e..6e96f562 100644 --- a/PackageScript +++ b/PackageScript @@ -113,7 +113,7 @@ for task in MMSPlugin.binaries: mapcfg_folder = builder.AddFolder(os.path.join(packages[sdk_name].sdk_name, 'cfg', MMSPlugin.metadata['name'], 'maps')) gamedata_folder = builder.AddFolder(os.path.join(packages[sdk_name].sdk_name, 'addons', MMSPlugin.metadata['name'], 'gamedata')) builder.AddCopy(os.path.join('configs', 'admins.jsonc.example'), configs_folder) - builder.AddCopy(os.path.join('configs', 'discordbots.cfg.example'), configs_folder) + builder.AddCopy(os.path.join('configs', 'discordbots.jsonc.example'), configs_folder) builder.AddCopy(os.path.join('configs', 'maplist.jsonc.example'), configs_folder) builder.AddCopy(os.path.join('cfg', MMSPlugin.metadata['name'], 'cs2fixes.cfg'), cfg_folder) builder.AddCopy(os.path.join('cfg', MMSPlugin.metadata['name'], 'maps', 'de_somemap.cfg'), mapcfg_folder) diff --git a/configs/discordbots.cfg.example b/configs/discordbots.cfg.example deleted file mode 100644 index 4d8f43c0..00000000 --- a/configs/discordbots.cfg.example +++ /dev/null @@ -1,14 +0,0 @@ -"discordbots" -{ - "YourFirstBot" - { - "webhook" "DISCORD_WEBHOOK_1" - "override_name" "false" - } - "YourSecondBot" - { - "webhook" "DISCORD_WEBHOOK_2" - "avatar" "AVATAR_IMAGE_2" - "override_name" "true" - } -} \ No newline at end of file diff --git a/configs/discordbots.jsonc.example b/configs/discordbots.jsonc.example new file mode 100644 index 00000000..7e06d670 --- /dev/null +++ b/configs/discordbots.jsonc.example @@ -0,0 +1,13 @@ +{ + "YourFirstBot": + { + "webhook": "DISCORD_WEBHOOK_1", + "override_name": false + }, + "YourSecondBot": + { + "webhook": "DISCORD_WEBHOOK_2", + "avatar": "AVATAR_IMAGE_2", + "override_name": true + } +} diff --git a/src/discord.cpp b/src/discord.cpp index 3a0fc4a3..cad81a75 100644 --- a/src/discord.cpp +++ b/src/discord.cpp @@ -23,10 +23,12 @@ #include "httpmanager.h" #include "interfaces/interfaces.h" #include "utlstring.h" +#include #include "vendor/nlohmann/json.hpp" using json = nlohmann::json; +using ordered_json = nlohmann::ordered_json; CDiscordBotManager* g_pDiscordBotManager = nullptr; @@ -73,38 +75,116 @@ void CDiscordBot::PostMessage(std::string strMessage) bool CDiscordBotManager::LoadDiscordBotsConfig() { m_vecDiscordBots.clear(); + + const char* pszJsonPath = "addons/cs2fixes/configs/discordbots.jsonc"; + char szPath[MAX_PATH]; + V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszJsonPath); + std::ifstream jsonFile(szPath); + + if (!jsonFile.is_open()) + { + if (!ConvertDiscordBotsKVToJSON()) + { + Panic("Failed to open %s and convert KV1 discordbots.cfg to JSON format, discord bots are not loaded!\n", pszJsonPath); + return false; + } + + jsonFile.open(szPath); + } + + ordered_json jDiscordBots = ordered_json::parse(jsonFile, nullptr, false, true); + + if (jDiscordBots.is_discarded()) + { + Panic("Failed parsing JSON from %s, discord bots are not loaded!\n", pszJsonPath); + return false; + } + + for (auto it = jDiscordBots.cbegin(); it != jDiscordBots.cend(); ++it) + { + const json& jDiscordBot = it.value(); + + if (!jDiscordBot.contains("webhook")) + { + Panic("Discord bot entry %s is missing 'webhook' key\n", it.key().c_str()); + return false; + } + + std::string strWebhookUrl = jDiscordBot.value("webhook", ""); + std::string strAvatarUrl = jDiscordBot.value("avatar", ""); + bool bOverrideName = jDiscordBot.value("override_name", false); + + // We just append the bots as-is + std::shared_ptr pBot = std::make_shared(it.key(), strWebhookUrl, strAvatarUrl, bOverrideName); + Message("Loaded DiscordBot config %s\n", pBot->GetName()); + Message(" - Webhook URL: %s\n", pBot->GetWebhookUrl()); + Message(" - Avatar URL: %s\n", pBot->GetAvatarUrl()); + m_vecDiscordBots.push_back(pBot); + } + + return true; +} + +// TODO: Remove this once servers have been given a few months to update cs2fixes +bool CDiscordBotManager::ConvertDiscordBotsKVToJSON() +{ KeyValues* pKV = new KeyValues("discordbots"); KeyValues::AutoDelete autoDelete(pKV); const char* pszPath = "addons/cs2fixes/configs/discordbots.cfg"; if (!pKV->LoadFromFile(g_pFullFileSystem, pszPath)) { - Warning("Failed to load %s\n", pszPath); + Panic("Failed to load %s\n", pszPath); return false; } + + ordered_json jDiscordBots; + for (KeyValues* pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) { - const char* pszName = pKey->GetName(); const char* pszWebhookUrl = pKey->GetString("webhook", nullptr); - const char* pszAvatarUrl = pKey->GetString("avatar", nullptr); - bool bOverrideName = pKey->GetBool("override_name", false); if (!pszWebhookUrl) { - Warning("Discord bot entry %s is missing 'webhook' key\n", pszName); + Panic("Discord bot entry %s is missing 'webhook' key\n", pKey->GetName()); return false; } - if (!pszAvatarUrl) - pszAvatarUrl = ""; + ordered_json jDiscordBot; + jDiscordBot["webhook"] = pszWebhookUrl; - // We just append the bots as-is - std::shared_ptr pBot = std::make_shared(pszName, pszWebhookUrl, pszAvatarUrl, bOverrideName); - ConMsg("Loaded DiscordBot config %s\n", pBot->GetName()); - ConMsg(" - Webhook URL: %s\n", pBot->GetWebhookUrl()); - ConMsg(" - Avatar URL: %s\n", pBot->GetAvatarUrl()); - m_vecDiscordBots.push_back(pBot); + if (pKey->FindKey("avatar")) + jDiscordBot["avatar"] = pKey->GetString("avatar", ""); + + jDiscordBot["override_name"] = pKey->GetBool("override_name", false); + jDiscordBots[pKey->GetName()] = jDiscordBot; + } + + const char* pszJsonPath = "addons/cs2fixes/configs/discordbots.jsonc"; + const char* pszKVConfigRenamePath = "addons/cs2fixes/configs/discordbots_old.cfg"; + char szPath[MAX_PATH]; + V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszJsonPath); + std::ofstream jsonFile(szPath); + + if (!jsonFile.is_open()) + { + Panic("Failed to open %s\n", pszJsonPath); + return false; } + jsonFile << std::setfill('\t') << std::setw(1) << jDiscordBots << std::endl; + + char szKVRenamePath[MAX_PATH]; + V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszPath); + V_snprintf(szKVRenamePath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszKVConfigRenamePath); + + std::rename(szPath, szKVRenamePath); + + // remove old cfg example if it exists + const char* pszKVExamplePath = "addons/cs2fixes/configs/discordbots.cfg.example"; + V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszKVExamplePath); + std::remove(szPath); + + Message("Successfully converted KV1 discordbots.cfg to JSON format at %s\n", pszJsonPath); return true; } diff --git a/src/discord.h b/src/discord.h index ea97ad96..f3113b10 100644 --- a/src/discord.h +++ b/src/discord.h @@ -53,6 +53,9 @@ class CDiscordBotManager void PostDiscordMessage(const char* pszDiscordBotName, const char* pszMessage); bool LoadDiscordBotsConfig(); + // TODO: Remove this once servers have been given a few months to update cs2fixes + bool ConvertDiscordBotsKVToJSON(); + private: std::vector> m_vecDiscordBots; }; From be25b862b2dd60115832ff9055b30f467af3e8b5 Mon Sep 17 00:00:00 2001 From: Vauff Date: Fri, 22 May 2026 01:33:54 -0400 Subject: [PATCH 04/30] Remove KV1 admin config converter --- src/adminsystem.cpp | 77 ++------------------------------------------- src/adminsystem.h | 3 -- 2 files changed, 2 insertions(+), 78 deletions(-) diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp index 1cdc6726..de883c24 100644 --- a/src/adminsystem.cpp +++ b/src/adminsystem.cpp @@ -1338,74 +1338,6 @@ CAdminSystem::CAdminSystem() m_iDCPlyIndex = 0; } -// TODO: Remove this once servers have been given a few months to update cs2fixes -bool CAdminSystem::ConvertAdminsKVToJSON() -{ - KeyValues* pKV = new KeyValues("admins"); - KeyValues::AutoDelete autoDelete(pKV); - - const char* pszPath = "addons/cs2fixes/configs/admins.cfg"; - - if (!pKV->LoadFromFile(g_pFullFileSystem, pszPath)) - { - Warning("Failed to load %s\n", pszPath); - return false; - } - - ordered_json jAdmins; - - jAdmins["Admins"] = ordered_json(ordered_json::value_t::object); - - for (KeyValues* pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) - { - ordered_json jAdmin; - - if (!pKey->FindKey("steamid")) - { - Warning("Admin entry %s is missing 'steam' key\n", pKey->GetName()); - return false; - } - - jAdmin["name"] = pKey->GetName(); - - if (pKey->FindKey("flags")) - jAdmin["flags"] = pKey->GetString("flags", nullptr); - - if (pKey->FindKey("immunity")) - jAdmin["immunity"] = pKey->GetInt("immunity", 0); - - jAdmins["Admins"][pKey->GetString("steamid", "")] = jAdmin; - } - - const char* pszJsonPath = "addons/cs2fixes/configs/admins.jsonc"; - const char* pszKVConfigRenamePath = "addons/cs2fixes/configs/admins_old.cfg"; - char szPath[MAX_PATH]; - V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszJsonPath); - std::ofstream jsonFile(szPath); - - if (!jsonFile.is_open()) - { - Panic("Failed to open %s\n", pszJsonPath); - return false; - } - - jsonFile << std::setfill('\t') << std::setw(1) << jAdmins << std::endl; - - char szKVRenamePath[MAX_PATH]; - V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszPath); - V_snprintf(szKVRenamePath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszKVConfigRenamePath); - - std::rename(szPath, szKVRenamePath); - - // remove old cfg example if it exists - const char* pszKVExamplePath = "addons/cs2fixes/configs/admins.cfg.example"; - V_snprintf(szPath, sizeof(szPath), "%s%s%s", Plat_GetGameDirectory(), "/csgo/", pszKVExamplePath); - std::remove(szPath); - - Message("Successfully converted KV1 admins.cfg to JSON format at %s\n", pszJsonPath); - return true; -} - bool CAdminSystem::LoadAdmins() { m_mapAdmins.clear(); @@ -1418,13 +1350,8 @@ bool CAdminSystem::LoadAdmins() if (!jsonFile.is_open()) { - if (!ConvertAdminsKVToJSON()) - { - Panic("Failed to open %s and convert KV1 admins.cfg to JSON format, admins are not loaded!\n", pszJsonPath); - return false; - } - - jsonFile.open(szPath); + Panic("Failed to open %s, admins are not loaded!\n", pszJsonPath); + return false; } ordered_json jAdminConfig = ordered_json::parse(jsonFile, nullptr, false, true); diff --git a/src/adminsystem.h b/src/adminsystem.h index 76fcd740..34c3f311 100644 --- a/src/adminsystem.h +++ b/src/adminsystem.h @@ -189,9 +189,6 @@ class CAdminSystem void AddDisconnectedPlayer(const char* pszName, uint64 xuid, const char* pszIP); void ShowDisconnectedPlayers(CCSPlayerController* const pAdmin); - // TODO: Remove this once servers have been given a few months to update cs2fixes - bool ConvertAdminsKVToJSON(); - private: std::map m_mapAdminGroups; std::map m_mapAdmins; From ff451b8e8fa9c3a6351ccdaeb4d6764d2ae888f7 Mon Sep 17 00:00:00 2001 From: tilgep Date: Sat, 23 May 2026 04:36:45 +0100 Subject: [PATCH 05/30] Add a mode to zsounds to only hear infect scream (#446) * Add mode to zsounds to only hear infect scream * fix missing var initialization --------- Co-authored-by: Vauff --- src/playermanager.cpp | 38 ++++++++++++++++++++++++++++++-------- src/playermanager.h | 8 ++++++-- src/user_preferences.cpp | 5 +++-- src/zombiereborn.cpp | 27 ++++++++++++++++++++++----- src/zombiereborn.h | 7 +++++++ 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 4bd47367..2cbc3584 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -36,6 +36,7 @@ #include "utils/entity.h" #include "utlstring.h" #include "votemanager.h" +#include "zombiereborn.h" #include <../cs2fixes.h> #include "tier0/memdbgon.h" @@ -1775,22 +1776,43 @@ void CPlayerManager::SetPlayerSilenceSound(int slot, bool set) g_pUserPreferencesSystem->SetPreferenceInt(slot, SOUND_STATUS_PREF_KEY_NAME, iStopPreferenceStatus + iSilencePreferenceStatus); } -void CPlayerManager::SetPlayerZSounds(int slot, bool set) +void CPlayerManager::SetPlayerZSounds(int slot, EZSoundsType mode) { - if (set) - m_nUsingZSounds |= ((uint64)1 << slot); - else - m_nUsingZSounds &= ~((uint64)1 << slot); + switch (mode) + { + case EZSoundsType::ON: + m_nUsingZSounds |= ((uint64)1 << slot); + m_nUsingZSoundsInfect |= ((uint64)1 << slot); + break; + case EZSoundsType::INFECT_ONLY: + m_nUsingZSounds &= ~((uint64)1 << slot); + m_nUsingZSoundsInfect |= ((uint64)1 << slot); + break; + case EZSoundsType::OFF: + m_nUsingZSounds &= ~((uint64)1 << slot); + m_nUsingZSoundsInfect &= ~((uint64)1 << slot); + break; + } // Set the user prefs if the player is ingame ZEPlayer* pPlayer = m_vecPlayers[slot]; if (!pPlayer) return; - uint64 iSlotMask = (uint64)1 << slot; - int iZSoundsPreferenceStatus = (m_nUsingZSounds & iSlotMask) ? 1 : 0; + int iZSoundsPreferenceStatus = (int)mode; g_pUserPreferencesSystem->SetPreferenceInt(slot, ZSOUNDS_PREF_KEY_NAME, iZSoundsPreferenceStatus); } +EZSoundsType CPlayerManager::GetPlayerZSoundsMode(int slot) +{ + if (m_nUsingZSounds & ((uint64)1 << slot)) + return EZSoundsType::ON; + + if (m_nUsingZSoundsInfect & ((uint64)1 << slot)) + return EZSoundsType::INFECT_ONLY; + + return EZSoundsType::OFF; +} + void CPlayerManager::SetPlayerStopDecals(int slot, bool set) { if (set) @@ -1827,7 +1849,7 @@ void CPlayerManager::ResetPlayerFlags(int slot) { SetPlayerStopSound(slot, true); SetPlayerSilenceSound(slot, false); - SetPlayerZSounds(slot, true); + SetPlayerZSounds(slot, EZSoundsType::ON); SetPlayerStopDecals(slot, true); SetPlayerNoShake(slot, false); } diff --git a/src/playermanager.h b/src/playermanager.h index 458a21a0..757b9589 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -110,6 +110,7 @@ enum class ETargetError class ZEPlayer; struct ZRClass; struct ZRModelEntry; +enum class EZSoundsType; class ZEPlayerHandle { @@ -404,6 +405,7 @@ class CPlayerManager m_nUsingStopSound = -1; // On by default m_nUsingSilenceSound = 0; m_nUsingZSounds = -1; // On by default + m_nUsingZSoundsInfect = -1; // On by default m_nUsingStopDecals = -1; // On by default m_nUsingNoShake = 0; } @@ -432,12 +434,13 @@ class CPlayerManager uint64 GetStopSoundMask() { return m_nUsingStopSound; } uint64 GetSilenceSoundMask() { return m_nUsingSilenceSound; } uint64 GetZSoundsMask() { return m_nUsingZSounds; } + uint64 GetZSoundsInfectMask() { return m_nUsingZSoundsInfect; } uint64 GetStopDecalsMask() { return m_nUsingStopDecals; } uint64 GetNoShakeMask() { return m_nUsingNoShake; } void SetPlayerStopSound(int slot, bool set); void SetPlayerSilenceSound(int slot, bool set); - void SetPlayerZSounds(int slot, bool set); + void SetPlayerZSounds(int slot, EZSoundsType mode); void SetPlayerStopDecals(int slot, bool set); void SetPlayerNoShake(int slot, bool set); @@ -445,7 +448,7 @@ class CPlayerManager bool IsPlayerUsingStopSound(int slot) { return m_nUsingStopSound & ((uint64)1 << slot); } bool IsPlayerUsingSilenceSound(int slot) { return m_nUsingSilenceSound & ((uint64)1 << slot); } - bool IsPlayerUsingZSounds(int slot) { return m_nUsingZSounds & ((uint64)1 << slot); } + EZSoundsType GetPlayerZSoundsMode(int slot); bool IsPlayerUsingStopDecals(int slot) { return m_nUsingStopDecals & ((uint64)1 << slot); } bool IsPlayerUsingNoShake(int slot) { return m_nUsingNoShake & ((uint64)1 << slot); } @@ -461,6 +464,7 @@ class CPlayerManager uint64 m_nUsingStopSound; uint64 m_nUsingSilenceSound; uint64 m_nUsingZSounds; + uint64 m_nUsingZSoundsInfect; uint64 m_nUsingStopDecals; uint64 m_nUsingNoShake; }; diff --git a/src/user_preferences.cpp b/src/user_preferences.cpp index 04f251e7..27f41e47 100644 --- a/src/user_preferences.cpp +++ b/src/user_preferences.cpp @@ -24,6 +24,7 @@ #include "httpmanager.h" #include "playermanager.h" #include "strtools.h" +#include "zombiereborn.h" #include #undef snprintf #include "vendor/nlohmann/json.hpp" @@ -112,7 +113,7 @@ void CUserPreferencesSystem::OnPutPreferences(int iSlot) bool bHideDecals = (bool)GetPreferenceInt(iSlot, DECAL_PREF_KEY_NAME, 1); bool bNoShake = (bool)GetPreferenceInt(iSlot, NO_SHAKE_PREF_KEY_NAME, 0); int iButtonWatchMode = GetPreferenceInt(iSlot, BUTTON_WATCH_PREF_KEY_NAME, 0); - bool bZSounds = (bool)GetPreferenceInt(iSlot, ZSOUNDS_PREF_KEY_NAME, 1); + int iZSounds = GetPreferenceInt(iSlot, ZSOUNDS_PREF_KEY_NAME, (int)EZSoundsType::ON); // EntWatch int iEntwatchMode = GetPreferenceInt(iSlot, EW_PREF_HUD_MODE, 0); @@ -125,7 +126,7 @@ void CUserPreferencesSystem::OnPutPreferences(int iSlot) // Set the values that we just loaded --- the player is guaranteed available g_playerManager->SetPlayerStopSound(iSlot, bStopSound); g_playerManager->SetPlayerSilenceSound(iSlot, bSilenceSound); - g_playerManager->SetPlayerZSounds(iSlot, bZSounds); + g_playerManager->SetPlayerZSounds(iSlot, (EZSoundsType)iZSounds); g_playerManager->SetPlayerStopDecals(iSlot, bHideDecals); g_playerManager->SetPlayerNoShake(iSlot, bNoShake); diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index 00de8278..be60f539 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -1800,16 +1800,19 @@ void ZR_FinishRound(int iTeamNum) void ZR_PostEventAbstract_SosStartSoundEvent(const uint64* pClients, CNetMessagePB* pMsg) { + static uint32 screamHash; static std::set soundEventHashes; ExecuteOnce( - soundEventHashes.insert(GetSoundEventHash("zr.amb.scream")); + screamHash = GetSoundEventHash("zr.amb.scream"); soundEventHashes.insert(GetSoundEventHash("zr.amb.zombie_die")); soundEventHashes.insert(GetSoundEventHash("zr.amb.zombie_pain")); soundEventHashes.insert(GetSoundEventHash("zr.amb.zombie_voice_idle"));); // Filter out people with zsounds disabled from hearing this sound - if (soundEventHashes.contains(pMsg->soundevent_hash())) + if (pMsg->soundevent_hash() == screamHash) + *(uint64*)pClients &= g_playerManager->GetZSoundsInfectMask(); + else if (soundEventHashes.contains(pMsg->soundevent_hash())) *(uint64*)pClients &= g_playerManager->GetZSoundsMask(); } @@ -1822,11 +1825,25 @@ CON_COMMAND_CHAT(zsounds, "- Toggle zombie sounds") } int iPlayer = player->GetPlayerSlot(); - bool bSet = !g_playerManager->IsPlayerUsingZSounds(iPlayer); + EZSoundsType state = g_playerManager->GetPlayerZSoundsMode(iPlayer); - g_playerManager->SetPlayerZSounds(iPlayer, bSet); + if (state == EZSoundsType::ON) + { + state = EZSoundsType::INFECT_ONLY; + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Zombie sounds\x04 enabled\x10 (Infect sounds only)."); + } + else if (state == EZSoundsType::INFECT_ONLY) + { + state = EZSoundsType::OFF; + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Zombie sounds\x07 disabled."); + } + else if (state == EZSoundsType::OFF) + { + state = EZSoundsType::ON; + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Zombie sounds\x04 enabled."); + } - ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "You have %s zombie sounds.", bSet ? "enabled" : "disabled"); + g_playerManager->SetPlayerZSounds(iPlayer, state); } CON_COMMAND_CHAT(ztele, "- Teleport to spawn") diff --git a/src/zombiereborn.h b/src/zombiereborn.h index 8a548dfc..c42eadb0 100644 --- a/src/zombiereborn.h +++ b/src/zombiereborn.h @@ -50,6 +50,13 @@ enum EZRSpawnType RESPAWN, }; +enum class EZSoundsType +{ + OFF, // No zombie sounds heard + ON, // All zombie sounds heard + INFECT_ONLY, // Only the infect scream heard +}; + // model entries in zr classes struct ZRModelEntry { From 173fcb9e9299968baac1c5ac63153dea077c31b0 Mon Sep 17 00:00:00 2001 From: tilgep Date: Mon, 1 Jun 2026 16:58:08 +0100 Subject: [PATCH 06/30] Add !motherzombies command (#447) * Add !motherzombies command * grammar tweak --------- Co-authored-by: Vauff --- src/zombiereborn.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index be60f539..5cd9f2bc 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -56,6 +56,7 @@ static bool g_bRespawnEnabled = true; static CHandle g_hRespawnToggler; static CHandle g_hTeamCT; static CHandle g_hTeamT; +std::vector g_MotherZombies; CZRPlayerClassManager* g_pZRPlayerClassManager = nullptr; ZRWeaponConfig* g_pZRWeaponConfig = nullptr; @@ -911,6 +912,8 @@ void ZR_OnRoundPrestart(IGameEvent* pEvent) if (pPawn) pPawn->m_bTakesDamage = false; } + + g_MotherZombies.clear(); } void SetupRespawnToggler() @@ -1258,6 +1261,8 @@ void ZR_InfectMotherZombie(CCSPlayerController* pVictimController, std::vectorGetHandle(); CTimer::Create(rand() % (int)g_cvarMoanInterval.Get(), TIMERFLAG_MAP | TIMERFLAG_ROUND, [hPlayer]() { return ZR_MoanTimer(hPlayer); }); + + g_MotherZombies.push_back(hPlayer); } // make players who've been picked as MZ recently less likely to be picked again @@ -2090,4 +2095,43 @@ CON_COMMAND_CHAT_FLAGS(revive, "- Revive a player", ADMFLAG_GENERIC) } if (iNumClients > 1) PrintMultiAdminAction(nType, strCommandPlayerName, "revived", "", ZR_PREFIX); +} + +CON_COMMAND_CHAT(motherzombies, "- Print the current mother zombies to chat") +{ + if (g_ZRRoundState == EZRRoundState::ROUND_START || g_MotherZombies.size() == 0) + { + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "There are no mother zombies."); + return; + } + + bool first = true; + std::string names = ""; + for (int i = g_MotherZombies.size() - 1; i >= 0; i--) + { + if (!g_MotherZombies[i].IsValid()) + { + g_MotherZombies.erase(g_MotherZombies.begin() + i); + continue; + } + + CCSPlayerController* pMZ = CCSPlayerController::FromSlot(g_MotherZombies[i].GetPlayerSlot()); + if (!pMZ) + continue; + + if (first) + { + names = pMZ->GetPlayerName(); + first = false; + } + else + { + names += ", " + pMZ->GetPlayerName(); + } + } + + if (first) + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "There are no mother zombies."); + else + ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Mother zombies: %s", names.c_str()); } \ No newline at end of file From ced74d2dae16fe8c17ffa5abf89c89837909565b Mon Sep 17 00:00:00 2001 From: u Date: Tue, 2 Jun 2026 16:06:56 +0900 Subject: [PATCH 07/30] feat: duck spam (#448) * feat: duck spam * Match default duck speed, rename cvar & add to .cfg --------- Co-authored-by: Vauff --- cfg/cs2fixes/cs2fixes.cfg | 3 ++- src/detours.cpp | 7 +++++++ src/patches.cpp | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index b3819b0e..f5a8ab08 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -7,7 +7,7 @@ cs2f_stopsound_enable 0 // Whether to enable stopsound cs2f_noblock_enable 0 // Whether to use player noblock, which sets debris collision on every player cs2f_noblock_grenades 0 // Whether to use noblock on grenade projectiles cs2f_block_team_messages 0 // Whether to block team join messages -cs2f_movement_unlocker_enable 0 // Whether to enable movement unlocker +cs2f_movement_unlocker_enable 0 // Whether to enable movement unlocker, clients will not predict cs2f_use_old_push 0 // Whether to use the old CSGO trigger_push behavior (Necessary for surf and other modes that heavily use ported pushes) cs2f_hide_enable 0 // Whether to enable hide (WARNING: randomly crashes clients since 2023-12-13 CS2 update) cs2f_votemanager_enable 0 // Whether to enable votemanager features such as map vote fix, nominations, RTV and extends @@ -26,6 +26,7 @@ cs2f_fix_game_bans 0 // Whether to fix CS2 game bans spreading to all new jo cs2f_free_armor 0 // Whether kevlar (1+) and/or helmet (2) are given automatically 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_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/detours.cpp b/src/detours.cpp index 683881c7..17d4aef0 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -538,6 +538,8 @@ void* FASTCALL Detour_CNavMesh_GetNearestNavArea(int64_t unk1, float* unk2, unsi return CNavMesh_GetNearestNavArea(unk1, unk2, unk3, unk4, unk5, unk6, unk7); } +CConVar g_cvarAllowDuckSpam("cs2f_allow_duck_spam", FCVAR_NONE, "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]", 0, true, 0, true, CS_TEAM_CT); + void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove) { CCSPlayerPawn* pPawn = pThis->GetPawn(); @@ -550,6 +552,11 @@ void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pM if (!pController || !pController->IsConnected()) return ProcessMovement(pThis, pMove); + int iAllowDuckSpam = g_cvarAllowDuckSpam.Get(); + + if ((iAllowDuckSpam == 1 || pPawn->m_iTeamNum() == iAllowDuckSpam) && pThis->m_flDuckSpeed() != 8.0f) + pThis->m_flDuckSpeed = 8.0f; + float flSpeedMod = pController->GetZEPlayer()->GetSpeedMod(); if (flSpeedMod == 1.f) diff --git a/src/patches.cpp b/src/patches.cpp index 86e6cbbd..573820cd 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -36,7 +36,7 @@ CMemPatch g_CommonPatches[] = CMemPatch("SetSchemaHammerUniqueId", "SetSchemaHammerUniqueId"), }; -CConVar cs2f_movement_unlocker_enable("cs2f_movement_unlocker_enable", FCVAR_NONE, "Whether to enable movement unlocker", false, +CConVar cs2f_movement_unlocker_enable("cs2f_movement_unlocker_enable", FCVAR_NONE, "Whether to enable movement unlocker, clients will not predict", false, [](CConVar* cvar, CSplitScreenSlot slot, const bool* new_val, const bool* old_val) { // Movement unlocker is always the first patch if (*new_val) From 9b1f7772fc04a1f28095d949d0a0b641954a6828 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 4 Jun 2026 14:53:56 -0400 Subject: [PATCH 08/30] Convert remaining unnecessary CUtlVector usages Other than in detour system, because that's removed in KHook branch --- src/entities.cpp | 85 ++++++++++++++++++++----------------------- src/eventlistener.h | 5 +-- src/events.cpp | 16 +++----- src/playermanager.cpp | 22 ++++------- src/playermanager.h | 6 +-- src/zombiereborn.cpp | 2 +- 6 files changed, 59 insertions(+), 77 deletions(-) diff --git a/src/entities.cpp b/src/entities.cpp index 7a5b1321..a9e4f61e 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -494,7 +494,7 @@ namespace CPointViewControlHandler { struct ViewControl { - CUtlVector> m_players; + std::vector> m_players; std::string m_viewTarget; std::string m_name; }; @@ -590,7 +590,8 @@ namespace CPointViewControlHandler for (auto& [vk, vc] : s_repository) { - if (const auto index = vc.m_players.Find(handle); index > -1) + const auto iterator = std::find(vc.m_players.begin(), vc.m_players.end(), handle); + if (iterator != vc.m_players.end()) { if (vk == static_cast(key)) { @@ -598,14 +599,15 @@ namespace CPointViewControlHandler return false; } - vc.m_players.Remove(index); + vc.m_players.erase(iterator); UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); Warning("PointViewControl %s already enabled for %s\n", vc.m_name.c_str(), pController->GetPlayerName().c_str()); break; } } - return it->second.m_players.AddToTail(handle) >= 0; + it->second.m_players.push_back(handle); + return true; } bool OnDisable(CPointViewControl* pEntity, CBaseEntity* pActivator) { @@ -632,7 +634,14 @@ namespace CPointViewControlHandler UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); - return it->second.m_players.FindAndRemove(handle); + auto& vecPlayers = it->second.m_players; + auto iterator = std::find(vecPlayers.begin(), vecPlayers.end(), handle); + + if (iterator == vecPlayers.end()) + return false; + + vecPlayers.erase(iterator); + return true; } bool OnEnableAll(CPointViewControl* pEntity) { @@ -655,9 +664,11 @@ namespace CPointViewControlHandler for (auto& [vk, vc] : s_repository) { - if (const auto index = vc.m_players.Find(handle); index > -1) + auto iterator = std::find(vc.m_players.begin(), vc.m_players.end(), handle); + + if (iterator != vc.m_players.end()) { - vc.m_players.Remove(index); + vc.m_players.erase(iterator); if (vk == static_cast(key)) continue; UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV); @@ -665,7 +676,7 @@ namespace CPointViewControlHandler } } - it->second.m_players.AddToTail(handle); + it->second.m_players.push_back(handle); } return true; @@ -677,15 +688,11 @@ namespace CPointViewControlHandler if (it == s_repository.end()) return false; - FOR_EACH_VEC(it->second.m_players, i) - { - const auto& handle = it->second.m_players.Element(i); - - if (const auto player = handle.Get()) + for (auto hPawn : it->second.m_players) + if (CCSPlayerPawn* player = hPawn.Get()) UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); - } - it->second.m_players.Purge(); + it->second.m_players.clear(); return true; } @@ -698,13 +705,9 @@ namespace CPointViewControlHandler const auto entity = CHandle(it->first).Get(); if (!entity) { - FOR_EACH_VEC(it->second.m_players, i) - { - const auto& handle = it->second.m_players.Element(i); - - if (const auto player = handle.Get()) + for (auto hPawn : it->second.m_players) + if (CCSPlayerPawn* player = hPawn.Get()) UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); - } it = s_repository.erase(it); } @@ -725,53 +728,47 @@ namespace CPointViewControlHandler continue; } - if (vc.m_players.Count() == 0) + if (vc.m_players.empty()) continue; const auto pTarget = entity->GetTargetCameraEntity(); if (!pTarget) { - FOR_EACH_VEC(vc.m_players, i) - { - const auto& handle = vc.m_players.Element(i); - - if (const auto player = handle.Get()) + for (auto hPawn : vc.m_players) + if (CCSPlayerPawn* player = hPawn.Get()) UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); - } - vc.m_players.Purge(); + vc.m_players.clear(); continue; } - FOR_EACH_VEC(vc.m_players, i) + for (auto iterator = vc.m_players.begin(); iterator != vc.m_players.end();) { - const auto& handle = vc.m_players.Element(i); - const auto player = handle.Get(); + auto hPawn = *iterator; + CCSPlayerPawn* player = hPawn.Get(); if (!player) { - vc.m_players.Remove(i--); + iterator = vc.m_players.erase(iterator); continue; } if (!player->IsAlive()) { UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); - vc.m_players.Remove(i--); + iterator = vc.m_players.erase(iterator); continue; } UpdatePlayerState(player, pTarget->GetHandle(), entity->HasFrozen(), entity->HasFOV() ? entity->GetFOV() : INVALID_FOV, entity->HasDisarm()); + iterator++; } } } bool IsViewControl(CCSPlayerPawn* pPawn) { - const auto handle = pPawn->GetHandle().ToInt(); for (const auto& [vk, vc] : s_repository) { - FOR_EACH_VEC(vc.m_players, i) - { - if (vc.m_players.Element(i).ToInt() == handle) + for (auto hPawn : vc.m_players) + if (hPawn == pPawn->GetHandle()) return true; - } } return false; } @@ -779,14 +776,10 @@ namespace CPointViewControlHandler { for (auto& [vk, vc] : s_repository) { - FOR_EACH_VEC(vc.m_players, i) - { - const auto& handle = vc.m_players.Element(i); - - if (const auto player = handle.Get()) + for (auto hPawn : vc.m_players) + if (CCSPlayerPawn* player = hPawn.Get()) UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV); - } - vc.m_players.Purge(); + vc.m_players.clear(); } s_repository.clear(); } diff --git a/src/eventlistener.h b/src/eventlistener.h index 5ba1d09a..0c482b8d 100644 --- a/src/eventlistener.h +++ b/src/eventlistener.h @@ -21,11 +21,10 @@ #include "common.h" #include "igameevents.h" #include "utlstring.h" -#include "utlvector.h" class CGameEventListener; -extern CUtlVector g_vecEventListeners; +extern std::vector g_vecEventListeners; extern CConVar g_cvarFreeArmor; typedef void (*FnEventListenerCallback)(IGameEvent* event); @@ -36,7 +35,7 @@ class CGameEventListener : public IGameEventListener2 CGameEventListener(FnEventListenerCallback callback, const char* pszEventName) : m_Callback(callback), m_pszEventName(pszEventName) { - g_vecEventListeners.AddToTail(this); + g_vecEventListeners.push_back(this); } ~CGameEventListener() override diff --git a/src/events.cpp b/src/events.cpp index b8c2af45..a945929c 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -39,7 +39,7 @@ #include "tier0/memdbgon.h" -CUtlVector g_vecEventListeners; +std::vector g_vecEventListeners; void RegisterEventListeners() { @@ -48,10 +48,8 @@ void RegisterEventListeners() if (bRegistered || !g_gameEventManager) return; - FOR_EACH_VEC(g_vecEventListeners, i) - { - g_gameEventManager->AddListener(g_vecEventListeners[i], g_vecEventListeners[i]->GetEventName(), true); - } + for (CGameEventListener* pListener : g_vecEventListeners) + g_gameEventManager->AddListener(pListener, pListener->GetEventName(), true); bRegistered = true; } @@ -61,12 +59,10 @@ void UnregisterEventListeners() if (!g_gameEventManager) return; - FOR_EACH_VEC(g_vecEventListeners, i) - { - g_gameEventManager->RemoveListener(g_vecEventListeners[i]); - } + for (CGameEventListener* pListener : g_vecEventListeners) + g_gameEventManager->RemoveListener(pListener); - g_vecEventListeners.Purge(); + g_vecEventListeners.clear(); } GAME_EVENT_F(round_prestart) diff --git a/src/playermanager.cpp b/src/playermanager.cpp index 2cbc3584..14212c5f 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -421,36 +421,30 @@ void ZEPlayer::CreateMark(float fDuration, Vector vecOrigin) int ZEPlayer::GetLeaderVoteCount() { - int iValidVoteCount = 0; - - for (int i = m_vecLeaderVotes.Count() - 1; i >= 0; i--) - if (m_vecLeaderVotes[i].IsValid()) - iValidVoteCount++; - else - m_vecLeaderVotes.Remove(i); + std::erase_if(m_vecLeaderVotes, [](ZEPlayerHandle hPlayer) { + return !hPlayer.IsValid(); + }); - return iValidVoteCount; + return m_vecLeaderVotes.size(); } bool ZEPlayer::HasPlayerVotedLeader(ZEPlayer* pPlayer) { - FOR_EACH_VEC(m_vecLeaderVotes, i) - { - if (m_vecLeaderVotes[i] == pPlayer) + for (ZEPlayerHandle hPlayer : m_vecLeaderVotes) + if (hPlayer == pPlayer) return true; - } return false; } void ZEPlayer::AddLeaderVote(ZEPlayer* pPlayer) { - m_vecLeaderVotes.AddToTail(pPlayer->GetHandle()); + m_vecLeaderVotes.push_back(pPlayer->GetHandle()); } void ZEPlayer::PurgeLeaderVotes() { - m_vecLeaderVotes.Purge(); + m_vecLeaderVotes.clear(); } void ZEPlayer::StartGlow(Color color, int duration) diff --git a/src/playermanager.h b/src/playermanager.h index 757b9589..108767dd 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -376,7 +376,7 @@ class ZEPlayer Color m_colorTracer; Color m_colorGlow; Color m_colorBeacon; - CUtlVector m_vecLeaderVotes; + std::vector m_vecLeaderVotes; float m_flLeaderVoteTime; CHandle m_hGlowModel; float m_flSpeedMod; @@ -404,9 +404,9 @@ class CPlayerManager V_memset(m_vecPlayers, 0, sizeof(m_vecPlayers)); m_nUsingStopSound = -1; // On by default m_nUsingSilenceSound = 0; - m_nUsingZSounds = -1; // On by default + m_nUsingZSounds = -1; // On by default m_nUsingZSoundsInfect = -1; // On by default - m_nUsingStopDecals = -1; // On by default + m_nUsingStopDecals = -1; // On by default m_nUsingNoShake = 0; } diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index 5cd9f2bc..d18da547 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -1261,7 +1261,7 @@ void ZR_InfectMotherZombie(CCSPlayerController* pVictimController, std::vectorGetHandle(); CTimer::Create(rand() % (int)g_cvarMoanInterval.Get(), TIMERFLAG_MAP | TIMERFLAG_ROUND, [hPlayer]() { return ZR_MoanTimer(hPlayer); }); - + g_MotherZombies.push_back(hPlayer); } From 3b486cac8070965d007e83b9a3aba76555fd8a53 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 24 Jun 2026 18:33:22 -0400 Subject: [PATCH 09/30] Push fix may no longer need subtick movement disabled Needs further testing --- src/detours.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/detours.cpp b/src/detours.cpp index 17d4aef0..eb49a932 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -593,8 +593,7 @@ void* FASTCALL Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd for (int i = 0; i < numcmds; i++) { - // Push fix only works properly if subtick movement is also disabled - if (g_cvarDisableSubtickMovement.Get() || g_cvarUseOldPush.Get()) + if (g_cvarDisableSubtickMovement.Get()) { auto subtickMoves = cmds[i].cmd.mutable_base()->mutable_subtick_moves(); auto iterator = subtickMoves->begin(); From ee2a1180f102d4f1f2af77a3949770b42c4a8af1 Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 24 Jun 2026 23:21:29 -0400 Subject: [PATCH 10/30] Replace UTIL_Remove gamedata with runtime lookup --- gamedata/cs2fixes.jsonc | 7 ------- sdk | 2 +- src/addresses.cpp | 1 - src/entitylistener.cpp | 3 +++ 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index 014e536a..2fbcb39e 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -56,13 +56,6 @@ "windows": "C8 42 EB ? 4C 8B 77 ? 4D 39 66", "linux": "C8 42 41 C7 85 ? ? ? ? ? ? ? ? 41 C7 85 ? ? ? ? ? ? ? ? E9" }, - // Called right after "Removed %s(%s)\n" - "UTIL_Remove": - { - "library": "server", - "windows": "48 85 C9 74 ? 48 8B D1 48 8B 0D ? ? ? ?", - "linux": "48 89 FE 48 85 FF 74 ? 48 8D 05 ? ? ? ? 48" - }, // "SetPosition" is passed to this "CEntitySystem_AddEntityIOEvent": { diff --git a/sdk b/sdk index a6649509..9ab16fa9 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit a664950956626e7690ee9c84f37a18ccbeb6fc7a +Subproject commit 9ab16fa9fcdeeb30565dfdbf6fbb312356978a0b diff --git a/src/addresses.cpp b/src/addresses.cpp index b13538fc..c1ca413a 100644 --- a/src/addresses.cpp +++ b/src/addresses.cpp @@ -55,7 +55,6 @@ bool addresses::Initialize(CGameConfig* g_GameConfig) RESOLVE_SIG(g_GameConfig, "CCSPlayerController_SwitchTeam", addresses::CCSPlayerController_SwitchTeam); RESOLVE_SIG(g_GameConfig, "CBasePlayerController_SetPawn", addresses::CBasePlayerController_SetPawn); RESOLVE_SIG(g_GameConfig, "CBaseModelEntity_SetModel", addresses::CBaseModelEntity_SetModel); - RESOLVE_SIG(g_GameConfig, "UTIL_Remove", addresses::UTIL_Remove); RESOLVE_SIG(g_GameConfig, "CEntitySystem_AddEntityIOEvent", addresses::CEntitySystem_AddEntityIOEvent); RESOLVE_SIG(g_GameConfig, "CEntityInstance_AcceptInput", addresses::CEntityInstance_AcceptInput); RESOLVE_SIG(g_GameConfig, "CGameEntitySystem_FindEntityByClassName", addresses::CGameEntitySystem_FindEntityByClassName); diff --git a/src/entitylistener.cpp b/src/entitylistener.cpp index 61a672b7..7e13e57d 100644 --- a/src/entitylistener.cpp +++ b/src/entitylistener.cpp @@ -23,6 +23,7 @@ #include "cs2fixes.h" #include "entities.h" #include "entity/cgamerules.h" +#include "entityclass.h" #include "entwatch.h" #include "gameconfig.h" #include "plat.h" @@ -49,6 +50,8 @@ void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity) void CEntityListener::OnEntityCreated(CEntityInstance* pEntity) { + ExecuteOnce(addresses::UTIL_Remove = pEntity->m_pEntity->m_pClass->m_NameToThinkFunc("CBaseEntitySUB_Remove")); + if (!V_strcmp("cs_gamerules", pEntity->GetClassname())) g_pGameRules = ((CCSGameRulesProxy*)pEntity)->m_pGameRules; } From e55c96e8f0b15712f3e8f703b2eb68ae75df23fd Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 24 Jun 2026 23:32:45 -0400 Subject: [PATCH 11/30] Fix Spawn hook crash after SDK update Apparently we were actually hooking SortEntities all this time, which supported post-hooks while Spawn didn't --- src/cs2fixes.cpp | 4 ++-- src/cs2fixes.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index cac649b1..48ce7fb8 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -320,7 +320,7 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool g_iLoadEventsFromFileId = SH_ADD_DVPHOOK(IGameEventManager2, LoadEventsFromFile, pCGameEventManagerVTable, SH_MEMBER(this, &CS2Fixes::Hook_LoadEventsFromFile), false); auto pCEntitySystemVTable = (CEntitySystem*)modules::server->FindVirtualTable("CGameEntitySystem"); - g_iSpawnId = SH_ADD_DVPHOOK(CEntitySystem, Spawn, pCEntitySystemVTable, SH_MEMBER(this, &CS2Fixes::Hook_SpawnPost), true); + g_iSpawnId = SH_ADD_DVPHOOK(CEntitySystem, Spawn, pCEntitySystemVTable, SH_MEMBER(this, &CS2Fixes::Hook_Spawn), false); if (!bRequiredInitLoaded) { @@ -1185,7 +1185,7 @@ void CS2Fixes::Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr) g_pSpawnGroupMgr = (CSpawnGroupMgrGameSystem*)pSpawnGroupMgr; } -void CS2Fixes::Hook_SpawnPost(int nCount, const EntitySpawnInfo_t* pInfo) +void CS2Fixes::Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo) { for (int i = 0; i < nCount; i++) g_pMapMigrations->OnEntitySpawned(pInfo[i].m_pEntity->m_pInstance, pInfo[i].m_pKeyValues); diff --git a/src/cs2fixes.h b/src/cs2fixes.h index 18b6eb2b..da948aa4 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -106,7 +106,7 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes void Hook_DropWeaponPost(CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity); int Hook_LoadEventsFromFile(const char* filename, bool bSearchAll); void Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr); - void Hook_SpawnPost(int nCount, const EntitySpawnInfo_t* pInfo); + void Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo); public: // MetaMod API void* OnMetamodQuery(const char* iface, int* ret); From ed2c2861a2c82bbad8ec270a30189081857627ce Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 25 Jun 2026 17:16:20 -0400 Subject: [PATCH 12/30] Re-add support for full view angles in Teleport --- gamedata/cs2fixes.jsonc | 7 ++++++ src/addresses.cpp | 1 + src/addresses.h | 2 ++ src/cs2_sdk/entity/cbaseplayerpawn.h | 5 +++++ src/cs2fixes.cpp | 33 +++++++++++++++++++++------- src/cs2fixes.h | 1 + 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index 2fbcb39e..d97eaafa 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -379,6 +379,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" + }, + // 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", + "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC" } }, "Offsets": diff --git a/src/addresses.cpp b/src/addresses.cpp index c1ca413a..6a0fe6d7 100644 --- a/src/addresses.cpp +++ b/src/addresses.cpp @@ -70,6 +70,7 @@ bool addresses::Initialize(CGameConfig* g_GameConfig) RESOLVE_SIG(g_GameConfig, "CTakeDamageInfo", addresses::CTakeDamageInfo_Constructor); RESOLVE_SIG(g_GameConfig, "CCSPlayer_WeaponServices_EquipWeapon", addresses::CCSPlayer_WeaponServices_EquipWeapon); RESOLVE_SIG(g_GameConfig, "GetSpawnGroups", addresses::GetSpawnGroups); + RESOLVE_SIG(g_GameConfig, "CBasePlayerPawn_SnapViewAngles", addresses::CBasePlayerPawn_SnapViewAngles); return InitializeBanMap(g_GameConfig); } diff --git a/src/addresses.h b/src/addresses.h index 04c8ccb8..cd994951 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -44,6 +44,7 @@ class CEntityInstance; class CEntityIdentity; class CBasePlayerController; class CCSPlayerController; +class CBasePlayerPawn; class CCSPlayerPawn; class CBaseModelEntity; class CBaseEntity; @@ -106,4 +107,5 @@ namespace addresses const Vector* vecDamageForce, const Vector* vecDamagePosition, float flDamage, int bitsDamageType, int iCustomDamage, void* a10); inline void(FASTCALL* CCSPlayer_WeaponServices_EquipWeapon)(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon); inline void(FASTCALL* GetSpawnGroups)(CSpawnGroupMgrGameSystem* pSpawnGroupMgr, CUtlVector* pList); + inline void(FASTCALL* CBasePlayerPawn_SnapViewAngles)(CBasePlayerPawn* pPawn, QAngle* pAngles); } // namespace addresses \ No newline at end of file diff --git a/src/cs2_sdk/entity/cbaseplayerpawn.h b/src/cs2_sdk/entity/cbaseplayerpawn.h index ac1a70e6..555537ab 100644 --- a/src/cs2_sdk/entity/cbaseplayerpawn.h +++ b/src/cs2_sdk/entity/cbaseplayerpawn.h @@ -96,5 +96,10 @@ class CBasePlayerPawn : public CBaseModelEntity CALL_VIRTUAL(void, offset, this, bExplode, bForce); } + void SnapViewAngles(QAngle* pAngles) + { + addresses::CBasePlayerPawn_SnapViewAngles(this, pAngles); + } + CBasePlayerController* GetController() { return m_hController.Get(); } }; \ No newline at end of file diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 48ce7fb8..8068a1ae 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -116,7 +116,8 @@ int g_iPhysicsTouchShuffle = -1; int g_iWeaponServiceDropWeaponId = -1; int g_iSetGameSpawnGroupMgrId = -1; int g_iSpawnId = -1; -int g_iTeleportId = -1; +int g_iTeleportPreId = -1; +int g_iTeleportPostId = -1; double g_flUniversalTime = 0.0; float g_flLastTickedTime = 0.0f; @@ -283,7 +284,8 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool bRequiredInitLoaded = false; } SH_MANUALHOOK_RECONFIGURE(Teleport, offset, 0, 0); - g_iTeleportId = SH_ADD_MANUALDVPHOOK(Teleport, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_CCSPlayerPawn_Teleport), false); + g_iTeleportPreId = SH_ADD_MANUALDVPHOOK(Teleport, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_CCSPlayerPawn_Teleport), false); + g_iTeleportPostId = SH_ADD_MANUALDVPHOOK(Teleport, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_CCSPlayerPawn_Teleport_Post), true); const auto pCCSPlayer_MovementServicesVTable = modules::server->FindVirtualTable("CCSPlayer_MovementServices"); offset = g_GameConfig->GetOffset("CCSPlayer_MovementServices::CheckMovingGround"); @@ -427,7 +429,8 @@ bool CS2Fixes::Unload(char* error, size_t maxlen) SH_REMOVE_HOOK_ID(g_iCTriggerGravityPrecacheId); SH_REMOVE_HOOK_ID(g_iCTriggerGravityEndTouchId); SH_REMOVE_HOOK_ID(g_iSpawnId); - SH_REMOVE_HOOK_ID(g_iTeleportId); + SH_REMOVE_HOOK_ID(g_iTeleportPreId); + SH_REMOVE_HOOK_ID(g_iTeleportPostId); if (g_iSetGameSpawnGroupMgrId != -1) SH_REMOVE_HOOK_ID(g_iSetGameSpawnGroupMgrId); @@ -1191,20 +1194,34 @@ void CS2Fixes::Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo) g_pMapMigrations->OnEntitySpawned(pInfo[i].m_pEntity->m_pInstance, pInfo[i].m_pKeyValues); } +float g_fTeleportXAngle; + void CS2Fixes::Hook_CCSPlayerPawn_Teleport(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity) { if (!pAngles) RETURN_META(MRES_IGNORED); + g_fTeleportXAngle = pAngles->x; + QAngle* pCastAngles = const_cast(pAngles); - // Post-AG2, changing x or z angles on a playermodel will bug out, and never did anything pre-AG2 anyways - if (pCastAngles->x != 0.0f) - pCastAngles->x = 0.0f; + // Post-AG2, changing x or z angles via Teleport on a playermodel will bug out, go through SnapViewAngles for x later instead + pCastAngles->x = 0.0f; + pCastAngles->z = 0.0f; + + RETURN_META(MRES_HANDLED); +} + +void CS2Fixes::Hook_CCSPlayerPawn_Teleport_Post(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity) +{ + if (!pAngles) + RETURN_META(MRES_IGNORED); - if (pCastAngles->z != 0.0f) - pCastAngles->z = 0.0f; + // Revert the x edit, z should always be 0 + QAngle* pCastAngles = const_cast(pAngles); + pCastAngles->x = g_fTeleportXAngle; + META_IFACEPTR(CCSPlayerPawn)->SnapViewAngles(pCastAngles); RETURN_META(MRES_HANDLED); } diff --git a/src/cs2fixes.h b/src/cs2fixes.h index da948aa4..dcfec20d 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -95,6 +95,7 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes bool Hook_OnTakeDamage_Alive(CTakeDamageResult* pDamageResult); void Hook_PhysicsTouchShuffle(CUtlVector* pList, bool unknown); void Hook_CCSPlayerPawn_Teleport(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity); + void Hook_CCSPlayerPawn_Teleport_Post(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity); #ifdef PLATFORM_WINDOWS Vector* Hook_GetEyePosition(Vector*); QAngle* Hook_GetEyeAngles(QAngle*); From 3077becc66ffd3845c91c3a79bfe2b8f16214b49 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 25 Jun 2026 18:09:21 -0400 Subject: [PATCH 13/30] Split CMapMigrations::OnEntitySpawned into pre/post --- src/cs2fixes.cpp | 2 +- src/entitylistener.cpp | 2 ++ src/mapmigrations.cpp | 13 +++++++------ src/mapmigrations.h | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 8068a1ae..98904b71 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -1191,7 +1191,7 @@ void CS2Fixes::Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr) void CS2Fixes::Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo) { for (int i = 0; i < nCount; i++) - g_pMapMigrations->OnEntitySpawned(pInfo[i].m_pEntity->m_pInstance, pInfo[i].m_pKeyValues); + g_pMapMigrations->OnEntitySpawned_Pre(reinterpret_cast(pInfo[i].m_pEntity->m_pInstance), pInfo[i].m_pKeyValues); } float g_fTeleportXAngle; diff --git a/src/entitylistener.cpp b/src/entitylistener.cpp index 7e13e57d..72e6fd61 100644 --- a/src/entitylistener.cpp +++ b/src/entitylistener.cpp @@ -26,6 +26,7 @@ #include "entityclass.h" #include "entwatch.h" #include "gameconfig.h" +#include "mapmigrations.h" #include "plat.h" CEntityListener* g_pEntityListener = nullptr; @@ -43,6 +44,7 @@ void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity) reinterpret_cast(pEntity)->SetCollisionGroup(COLLISION_GROUP_DEBRIS); EntityHandler_OnEntitySpawned(reinterpret_cast(pEntity)); + g_pMapMigrations->OnEntitySpawned_Post(reinterpret_cast(pEntity)); if (g_cvarEnableEntWatch.Get()) EW_OnEntitySpawned(pEntity); diff --git a/src/mapmigrations.cpp b/src/mapmigrations.cpp index c57fd07a..5de64a0e 100644 --- a/src/mapmigrations.cpp +++ b/src/mapmigrations.cpp @@ -50,16 +50,17 @@ void CMapMigrations::OnRoundPrestart() m_vecEquippedWeapons.clear(); } -void CMapMigrations::OnEntitySpawned(CEntityInstance* pEntity, const CEntityKeyValues* pKeyValues) +void CMapMigrations::OnEntitySpawned_Pre(CBaseEntity* pEntity, const CEntityKeyValues* pKeyValues) { - CBaseEntity* pBaseEntity = (CBaseEntity*)pEntity; - // Stupid workaround for CEntityKeyValues being inaccessible after entity spawn // We need access to this in 2026-01-21 rendermode migrations when called from UpdateMapUpdateTime - if (pBaseEntity->AsBaseModelEntity() && V_StringToInt32(pKeyValues->GetString("rendermode"), -1, NULL, NULL, PARSING_FLAG_SKIP_WARNING) == -1) - m_vecModelEntitiesUsingRendermodeEnum.push_back(pBaseEntity->GetHandle()); + if (pEntity->AsBaseModelEntity() && V_StringToInt32(pKeyValues->GetString("rendermode"), -1, NULL, NULL, PARSING_FLAG_SKIP_WARNING) == -1) + m_vecModelEntitiesUsingRendermodeEnum.push_back(pEntity->GetHandle()); +} - RunMigrations(pBaseEntity); +void CMapMigrations::OnEntitySpawned_Post(CBaseEntity* pEntity) +{ + RunMigrations(pEntity); } void CMapMigrations::OnEquipWeapon(CBasePlayerWeapon* pWeapon) diff --git a/src/mapmigrations.h b/src/mapmigrations.h index 18ba3404..c19fde12 100644 --- a/src/mapmigrations.h +++ b/src/mapmigrations.h @@ -54,7 +54,8 @@ class CMapMigrations public: void ApplyGameSettings(KeyValues* pKV); void OnRoundPrestart(); - void OnEntitySpawned(CEntityInstance* pEntity, const CEntityKeyValues* pKeyValues); + void OnEntitySpawned_Pre(CBaseEntity* pEntity, const CEntityKeyValues* pKeyValues); + void OnEntitySpawned_Post(CBaseEntity* pEntity); void OnEquipWeapon(CBasePlayerWeapon* pWeapon); void RunMigrations(CBaseEntity* pEntity); void Migrations_20260121(CBaseEntity* pEntity); From cf6a9cf6a3d6776887ff198e183ec00a32cd35db Mon Sep 17 00:00:00 2001 From: Fara <44729057+Faramour@users.noreply.github.com> Date: Fri, 26 Jun 2026 02:49:32 +0200 Subject: [PATCH 14/30] Add !voicechat command (#451) * Add !voicechat command * simplify format --------- Co-authored-by: Vauff --- src/commands.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/cs2fixes.cpp | 27 ++++++++++++++++++++++ src/cs2fixes.h | 2 ++ src/playermanager.h | 4 ++++ 4 files changed, 88 insertions(+) diff --git a/src/commands.cpp b/src/commands.cpp index 51642931..391973c6 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -750,6 +750,61 @@ CON_COMMAND_F(cs2f_fullupdate, "- Force a full update for all clients.", FCVAR_L g_playerManager->FullUpdateAllClients(); } +void VoiceChatPrintCmd(const CCommand& args, CCSPlayerController* player) +{ + if (!GetGlobals()) return; + + std::vector vecActivePlayers; + for (int i = 0; i < GetGlobals()->maxClients; i++) + { + ZEPlayer* pPlayer = g_playerManager->GetPlayer(i); + + if (pPlayer && !pPlayer->GetVoiceTimer().expired()) + vecActivePlayers.push_back(i); + } + + if (vecActivePlayers.empty()) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "There are no players currently using voice chat."); + return; + } + + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "List of players using voice chat:"); + if (player) + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "List of players using voice chat:"); + + for (const auto& slot : vecActivePlayers) + { + CCSPlayerController* pController = CCSPlayerController::FromSlot(slot); + ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot); + + bool bSteamIDAuthed = pPlayer->IsAuthenticated(); + uint64 uSteamID = bSteamIDAuthed ? pPlayer->GetSteamId64() : pPlayer->GetUnauthenticatedSteamId64(); + + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "\x04%s \x06[#%hu] \x05[%llu%s\x05]", + pController->GetPlayerName().c_str(), + g_pEngineServer2->GetPlayerUserId(slot), + uSteamID, + bSteamIDAuthed ? "" : " \x02(No Auth)"); + if (!player) continue; + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "%s [#%hu] [%llu%s]", + pController->GetPlayerName().c_str(), + g_pEngineServer2->GetPlayerUserId(slot), + uSteamID, + bSteamIDAuthed ? "" : " (No Auth)"); + } +} + +CON_COMMAND_CHAT(voicechat, "- Display players that are using voice chat") +{ + VoiceChatPrintCmd(args, player); +} + +CON_COMMAND_CHAT(vc, "- Display players that are using voice chat") +{ + VoiceChatPrintCmd(args, player); +} + #if _DEBUG CON_COMMAND_CHAT(myuid, "- Test") { diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 98904b71..988946e3 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -95,6 +95,7 @@ SH_DECL_MANUALHOOK3_void(DropWeapon, 0, 0, 0, CBasePlayerWeapon*, Vector*, Vecto SH_DECL_HOOK1_void(IServer, SetGameSpawnGroupMgr, SH_NOATTRIB, 0, IGameSpawnGroupMgr*); SH_DECL_HOOK2_void(CEntitySystem, Spawn, SH_NOATTRIB, 0, int, const EntitySpawnInfo_t*); SH_DECL_MANUALHOOK3_void(Teleport, 0, 0, 0, const Vector*, const QAngle*, const Vector*); +SH_DECL_HOOK1(CServerSideClient, ProcessVoiceData, SH_NOATTRIB, 0, bool, const CCLCMsg_VoiceData_t&); CS2Fixes g_CS2Fixes; IGameEventSystem* g_gameEventSystem = nullptr; @@ -118,6 +119,7 @@ int g_iSetGameSpawnGroupMgrId = -1; int g_iSpawnId = -1; int g_iTeleportPreId = -1; int g_iTeleportPostId = -1; +int g_iProcessVoiceDataId = -1; double g_flUniversalTime = 0.0; float g_flLastTickedTime = 0.0f; @@ -324,6 +326,9 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool auto pCEntitySystemVTable = (CEntitySystem*)modules::server->FindVirtualTable("CGameEntitySystem"); g_iSpawnId = SH_ADD_DVPHOOK(CEntitySystem, Spawn, pCEntitySystemVTable, SH_MEMBER(this, &CS2Fixes::Hook_Spawn), false); + auto pCServerSideClientVTable = (CServerSideClient*)modules::engine->FindVirtualTable("CServerSideClient"); + g_iProcessVoiceDataId = SH_ADD_DVPHOOK(CServerSideClient, ProcessVoiceData, pCServerSideClientVTable, SH_MEMBER(this, &CS2Fixes::Hook_ProcessVoiceData), false); + if (!bRequiredInitLoaded) { snprintf(error, maxlen, "One or more address lookups, patches or detours failed, please refer to startup logs for more information"); @@ -431,6 +436,7 @@ bool CS2Fixes::Unload(char* error, size_t maxlen) SH_REMOVE_HOOK_ID(g_iSpawnId); SH_REMOVE_HOOK_ID(g_iTeleportPreId); SH_REMOVE_HOOK_ID(g_iTeleportPostId); + SH_REMOVE_HOOK_ID(g_iProcessVoiceDataId); if (g_iSetGameSpawnGroupMgrId != -1) SH_REMOVE_HOOK_ID(g_iSetGameSpawnGroupMgrId); @@ -1225,6 +1231,27 @@ void CS2Fixes::Hook_CCSPlayerPawn_Teleport_Post(const Vector* pPosition, const Q RETURN_META(MRES_HANDLED); } +bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg) +{ + CServerSideClient* client = META_IFACEPTR(CServerSideClient); + + if (!client) + RETURN_META_VALUE(MRES_IGNORED, true); + + ZEPlayer* pPlayer = g_playerManager->GetPlayer(client->GetPlayerSlot()); + + if (!pPlayer) + RETURN_META_VALUE(MRES_IGNORED, true); + + // logic following sourcemod's implementation of OnClientSpeaking + if (auto timer = pPlayer->GetVoiceTimer().lock()) + timer->Cancel(); + + pPlayer->SetVoiceTimer(CTimer::Create(0.3f, TIMERFLAG_NONE, [](){ return -1.0f; })); + + RETURN_META_VALUE(MRES_IGNORED, true); +} + void* CS2Fixes::OnMetamodQuery(const char* iface, int* ret) { if (V_strcmp(iface, CS2FIXES_INTERFACE)) diff --git a/src/cs2fixes.h b/src/cs2fixes.h index dcfec20d..ea4115fd 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -30,6 +30,7 @@ #include #include #include +#include "cs2_sdk/netmessages.h" #ifdef AMBUILD #include "version_gen.h" @@ -107,6 +108,7 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes void Hook_DropWeaponPost(CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity); int Hook_LoadEventsFromFile(const char* filename, bool bSearchAll); void Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr); + bool Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg); void Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo); public: // MetaMod API diff --git a/src/playermanager.h b/src/playermanager.h index 108767dd..3519defa 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -30,6 +30,7 @@ #include "steam/steamclientpublic.h" #include "utlvector.h" #include +#include "ctimer.h" extern CConVar g_cvarFlashLightTransmitOthers; extern CConVar g_cvarFlashLightAttachment; @@ -270,6 +271,7 @@ class ZEPlayer void SetEntwatchHudPos(float x, float y); void SetEntwatchHudSize(float flSize); void SetTopDefenderStatus(bool bStatus) { m_bTopDefender = bStatus; } + void SetVoiceTimer(std::weak_ptr timer) { m_pVoiceTimer = timer; } uint64 GetAdminFlags() { return m_iAdminFlags; } int GetAdminImmunity() { return m_iAdminImmunity; } @@ -320,6 +322,7 @@ class ZEPlayer float GetEntwatchHudY() { return m_flEntwatchHudY; } float GetEntwatchHudSize() { return m_flEntwatchHudSize; } bool GetTopDefenderStatus() { return m_bTopDefender; } + std::weak_ptr GetVoiceTimer() { return m_pVoiceTimer; } void OnSpawn(); void OnAuthenticated(); @@ -394,6 +397,7 @@ class ZEPlayer float m_flEntwatchHudY; float m_flEntwatchHudSize; bool m_bTopDefender; + std::weak_ptr m_pVoiceTimer; }; class CPlayerManager From 2e103e596130c7e0aa1c5de5bd86ca026e8b172a Mon Sep 17 00:00:00 2001 From: Vauff Date: Mon, 29 Jun 2026 02:35:35 -0400 Subject: [PATCH 15/30] Fix health regen interval being accessed as an int --- src/zombiereborn.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index d18da547..f8d942ac 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -246,8 +246,8 @@ ZRHumanClass::ZRHumanClass(ordered_json jsonKeys, std::string szClassname) : ZRZombieClass::ZRZombieClass(ordered_json jsonKeys, std::string szClassname) : ZRClass(jsonKeys, szClassname, CS_TEAM_T), iHealthRegenCount(jsonKeys.value("health_regen_count", 0)), - flHealthRegenInterval(jsonKeys.value("health_regen_interval", 0)), - flKnockback(jsonKeys.value("knockback", 1.0)){}; + flHealthRegenInterval(jsonKeys.value("health_regen_interval", 0.0f)), + flKnockback(jsonKeys.value("knockback", 1.0f)){}; void ZRZombieClass::Override(ordered_json jsonKeys, std::string szClassname) { From 8d95caf2c397e8afc7c9be2081864802be0d4faf Mon Sep 17 00:00:00 2001 From: Vauff Date: Mon, 29 Jun 2026 02:50:24 -0400 Subject: [PATCH 16/30] Add !mz alias --- src/zombiereborn.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index f8d942ac..dfe75d34 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -2097,7 +2097,7 @@ CON_COMMAND_CHAT_FLAGS(revive, "- Revive a player", ADMFLAG_GENERIC) PrintMultiAdminAction(nType, strCommandPlayerName, "revived", "", ZR_PREFIX); } -CON_COMMAND_CHAT(motherzombies, "- Print the current mother zombies to chat") +void MotherZombiesCommand(CCSPlayerController* player) { if (g_ZRRoundState == EZRRoundState::ROUND_START || g_MotherZombies.size() == 0) { @@ -2134,4 +2134,14 @@ CON_COMMAND_CHAT(motherzombies, "- Print the current mother zombies to chat") ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "There are no mother zombies."); else ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Mother zombies: %s", names.c_str()); +} + +CON_COMMAND_CHAT(motherzombies, "- Print the current mother zombies to chat") +{ + MotherZombiesCommand(player); +} + +CON_COMMAND_CHAT(mz, "- Print the current mother zombies to chat") +{ + MotherZombiesCommand(player); } \ No newline at end of file From 55162231d5dae07d8260da18951eaf83294cc894 Mon Sep 17 00:00:00 2001 From: Vauff Date: Mon, 29 Jun 2026 03:21:52 -0400 Subject: [PATCH 17/30] Prevent beacon spam --- src/commands.cpp | 16 ++++++++-------- src/cs2fixes.cpp | 2 +- src/cs2fixes.h | 2 +- src/leader.cpp | 18 ++++++++++++++++-- src/playermanager.h | 6 +++++- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/commands.cpp b/src/commands.cpp index 391973c6..147ebd56 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -782,16 +782,16 @@ void VoiceChatPrintCmd(const CCommand& args, CCSPlayerController* player) uint64 uSteamID = bSteamIDAuthed ? pPlayer->GetSteamId64() : pPlayer->GetUnauthenticatedSteamId64(); ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "\x04%s \x06[#%hu] \x05[%llu%s\x05]", - pController->GetPlayerName().c_str(), - g_pEngineServer2->GetPlayerUserId(slot), - uSteamID, - bSteamIDAuthed ? "" : " \x02(No Auth)"); + pController->GetPlayerName().c_str(), + g_pEngineServer2->GetPlayerUserId(slot), + uSteamID, + bSteamIDAuthed ? "" : " \x02(No Auth)"); if (!player) continue; ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "%s [#%hu] [%llu%s]", - pController->GetPlayerName().c_str(), - g_pEngineServer2->GetPlayerUserId(slot), - uSteamID, - bSteamIDAuthed ? "" : " (No Auth)"); + pController->GetPlayerName().c_str(), + g_pEngineServer2->GetPlayerUserId(slot), + uSteamID, + bSteamIDAuthed ? "" : " (No Auth)"); } } diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 988946e3..a6324a98 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -1247,7 +1247,7 @@ bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg) if (auto timer = pPlayer->GetVoiceTimer().lock()) timer->Cancel(); - pPlayer->SetVoiceTimer(CTimer::Create(0.3f, TIMERFLAG_NONE, [](){ return -1.0f; })); + pPlayer->SetVoiceTimer(CTimer::Create(0.3f, TIMERFLAG_NONE, []() { return -1.0f; })); RETURN_META_VALUE(MRES_IGNORED, true); } diff --git a/src/cs2fixes.h b/src/cs2fixes.h index ea4115fd..9fa6c05a 100644 --- a/src/cs2fixes.h +++ b/src/cs2fixes.h @@ -19,6 +19,7 @@ #pragma once +#include "cs2_sdk/netmessages.h" #include "engine/igameeventsystem.h" #include "entity/cgamerules.h" #include "gamesystems/spawngroup_manager.h" @@ -30,7 +31,6 @@ #include #include #include -#include "cs2_sdk/netmessages.h" #ifdef AMBUILD #include "version_gen.h" diff --git a/src/leader.cpp b/src/leader.cpp index a079040f..c01dcd3a 100644 --- a/src/leader.cpp +++ b/src/leader.cpp @@ -785,7 +785,7 @@ CON_COMMAND_CHAT_LEADER(beacon, "[name] [color] - Toggle beacon on a player") if (args.ArgC() >= 2 && (bIsAdmin || g_cvarLeaderCanTargetPlayers.Get())) pszTarget = args[1]; - if (!g_playerManager->CanTargetPlayers(player, pszTarget, iNumClients, pSlots, iTargetFlags, nType)) + if (!g_playerManager->CanTargetPlayers(player, pszTarget, iNumClients, pSlots, iTargetFlags, nType) || !GetGlobals()) return; for (int i = 0; i < iNumClients; i++) @@ -802,8 +802,15 @@ CON_COMMAND_CHAT_LEADER(beacon, "[name] [color] - Toggle beacon on a player") return; } + if (pPlayerTarget->GetBeaconEnabledTime() + 2.0f > GetGlobals()->curtime) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Wait before you can enable beacon again."); + return; + } + Color color = Leader_GetColor(args.ArgC() < 3 ? "" : args[2], pPlayer, pTarget); pPlayerTarget->StartBeacon(color, pPlayer ? pPlayer->GetHandle() : 0); + pPlayerTarget->SetBeaconEnabledTime(GetGlobals()->curtime); } else pPlayerTarget->EndBeacon(); @@ -886,7 +893,7 @@ CON_COMMAND_CHAT(leaderhelp, "- List leader commands in chat") CON_COMMAND_CHAT(leadercolor, "[color] - List leader colors in chat or change your leader color") { - if (!g_cvarEnableLeader.Get()) + if (!g_cvarEnableLeader.Get() || !GetGlobals()) return; ZEPlayer* zpPlayer = player ? player->GetZEPlayer() : nullptr; @@ -896,6 +903,12 @@ CON_COMMAND_CHAT(leadercolor, "[color] - List leader colors in chat or change yo std::transform(strColor.begin(), strColor.end(), strColor.begin(), [](unsigned char c) { return std::tolower(c); }); if (strColor.length() > 0 && mapColorPresets.contains(strColor)) { + if (zpPlayer->GetBeaconEnabledTime() + 2.0f > GetGlobals()->curtime) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Wait before you can change leader color again."); + return; + } + auto const& colorPreset = mapColorPresets.at(strColor); Color color = colorPreset.color; zpPlayer->SetLeaderColor(color); @@ -914,6 +927,7 @@ CON_COMMAND_CHAT(leadercolor, "[color] - List leader colors in chat or change yo { zpPlayer->EndBeacon(); zpPlayer->StartBeacon(color, zpPlayer ? zpPlayer->GetHandle() : 0); + zpPlayer->SetBeaconEnabledTime(GetGlobals()->curtime); } CCSPlayerPawn* pawnPlayer = (CCSPlayerPawn*)player->GetPawn(); diff --git a/src/playermanager.h b/src/playermanager.h index 3519defa..aacd56f8 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -20,6 +20,7 @@ #pragma once #include "bitvec.h" #include "common.h" +#include "ctimer.h" #include "entity/cparticlesystem.h" #include "entity/cpointorient.h" #include "entity/cpointworldtext.h" @@ -30,7 +31,6 @@ #include "steam/steamclientpublic.h" #include "utlvector.h" #include -#include "ctimer.h" extern CConVar g_cvarFlashLightTransmitOthers; extern CConVar g_cvarFlashLightAttachment; @@ -200,6 +200,7 @@ class ZEPlayer m_flEntwatchHudY = -2.0f; m_flEntwatchHudSize = 60.0f; m_bTopDefender = false; + m_flBeaconEnabledTime = -2.0f; } ~ZEPlayer() @@ -272,6 +273,7 @@ class ZEPlayer void SetEntwatchHudSize(float flSize); void SetTopDefenderStatus(bool bStatus) { m_bTopDefender = bStatus; } void SetVoiceTimer(std::weak_ptr timer) { m_pVoiceTimer = timer; } + void SetBeaconEnabledTime(float flTime) { m_flBeaconEnabledTime = flTime; } uint64 GetAdminFlags() { return m_iAdminFlags; } int GetAdminImmunity() { return m_iAdminImmunity; } @@ -323,6 +325,7 @@ class ZEPlayer float GetEntwatchHudSize() { return m_flEntwatchHudSize; } bool GetTopDefenderStatus() { return m_bTopDefender; } std::weak_ptr GetVoiceTimer() { return m_pVoiceTimer; } + float GetBeaconEnabledTime() { return m_flBeaconEnabledTime; } void OnSpawn(); void OnAuthenticated(); @@ -398,6 +401,7 @@ class ZEPlayer float m_flEntwatchHudSize; bool m_bTopDefender; std::weak_ptr m_pVoiceTimer; + float m_flBeaconEnabledTime; }; class CPlayerManager From 38bebf3d4e941eb1ca7999426dd96f1ab5ae5033 Mon Sep 17 00:00:00 2001 From: Vauff Date: Fri, 3 Jul 2026 01:44:57 -0400 Subject: [PATCH 18/30] Include recent VC users in !voicechat as well --- src/commands.cpp | 76 +++++++++++++++++++++++++++++++-------------- src/cs2fixes.cpp | 8 ++--- src/playermanager.h | 8 ++--- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/commands.cpp b/src/commands.cpp index 147ebd56..2d8799b1 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -750,48 +750,76 @@ CON_COMMAND_F(cs2f_fullupdate, "- Force a full update for all clients.", FCVAR_L g_playerManager->FullUpdateAllClients(); } +void VoiceChatPrintPlayer(CCSPlayerController* player, int slot) +{ + CCSPlayerController* pController = CCSPlayerController::FromSlot(slot); + ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot); + + bool bSteamIDAuthed = pPlayer->IsAuthenticated(); + uint64 uSteamID = bSteamIDAuthed ? pPlayer->GetSteamId64() : pPlayer->GetUnauthenticatedSteamId64(); + std::string strPlayerName = pController->GetPlayerName(); + CPlayerUserId userId = g_pEngineServer2->GetPlayerUserId(slot); + + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "\x04%s \x06[#%hu] \x05[%llu%s\x05]", strPlayerName.c_str(), userId, uSteamID, bSteamIDAuthed ? "" : " \x02(No Auth)"); + + if (player) + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "%s [#%hu] [%llu%s]", strPlayerName.c_str(), userId, uSteamID, bSteamIDAuthed ? "" : " (No Auth)"); +} + void VoiceChatPrintCmd(const CCommand& args, CCSPlayerController* player) { if (!GetGlobals()) return; std::vector vecActivePlayers; + std::vector> vecRecentPlayers; + for (int i = 0; i < GetGlobals()->maxClients; i++) { ZEPlayer* pPlayer = g_playerManager->GetPlayer(i); - if (pPlayer && !pPlayer->GetVoiceTimer().expired()) + if (!pPlayer) + continue; + + float flLastVoiceTime = pPlayer->GetLastVoiceTime(); + + if (flLastVoiceTime + 0.3f > GetGlobals()->curtime) + { vecActivePlayers.push_back(i); + } + else if (flLastVoiceTime + 15.0f > GetGlobals()->curtime) + { + auto iter = std::lower_bound(vecRecentPlayers.begin(), vecRecentPlayers.end(), flLastVoiceTime, [](const std::pair& recentPlayer, float flCompareVoiceTime) { + return recentPlayer.second < flCompareVoiceTime; + }); + + vecRecentPlayers.insert(iter, {i, flLastVoiceTime}); + } } - if (vecActivePlayers.empty()) + if (vecActivePlayers.empty() && vecRecentPlayers.empty()) { - ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "There are no players currently using voice chat."); + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "No players have recently used the voice chat."); return; } - ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "List of players using voice chat:"); - if (player) - ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "List of players using voice chat:"); + if (!vecRecentPlayers.empty()) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Players who recently used voice chat:"); + if (player) + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "Players who recently used voice chat:"); + + for (const auto& recentPlayer : vecRecentPlayers) + VoiceChatPrintPlayer(player, recentPlayer.first); + } - for (const auto& slot : vecActivePlayers) + if (!vecActivePlayers.empty()) { - CCSPlayerController* pController = CCSPlayerController::FromSlot(slot); - ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot); - - bool bSteamIDAuthed = pPlayer->IsAuthenticated(); - uint64 uSteamID = bSteamIDAuthed ? pPlayer->GetSteamId64() : pPlayer->GetUnauthenticatedSteamId64(); - - ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "\x04%s \x06[#%hu] \x05[%llu%s\x05]", - pController->GetPlayerName().c_str(), - g_pEngineServer2->GetPlayerUserId(slot), - uSteamID, - bSteamIDAuthed ? "" : " \x02(No Auth)"); - if (!player) continue; - ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "%s [#%hu] [%llu%s]", - pController->GetPlayerName().c_str(), - g_pEngineServer2->GetPlayerUserId(slot), - uSteamID, - bSteamIDAuthed ? "" : " (No Auth)"); + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Players using voice chat:"); + if (player) + ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "Players using voice chat:"); + + for (int slot : vecActivePlayers) + VoiceChatPrintPlayer(player, slot); } } diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index a6324a98..6dee2ab3 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -1235,7 +1235,7 @@ bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg) { CServerSideClient* client = META_IFACEPTR(CServerSideClient); - if (!client) + if (!client || !GetGlobals()) RETURN_META_VALUE(MRES_IGNORED, true); ZEPlayer* pPlayer = g_playerManager->GetPlayer(client->GetPlayerSlot()); @@ -1243,11 +1243,7 @@ bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg) if (!pPlayer) RETURN_META_VALUE(MRES_IGNORED, true); - // logic following sourcemod's implementation of OnClientSpeaking - if (auto timer = pPlayer->GetVoiceTimer().lock()) - timer->Cancel(); - - pPlayer->SetVoiceTimer(CTimer::Create(0.3f, TIMERFLAG_NONE, []() { return -1.0f; })); + pPlayer->SetLastVoiceTime(GetGlobals()->curtime); RETURN_META_VALUE(MRES_IGNORED, true); } diff --git a/src/playermanager.h b/src/playermanager.h index aacd56f8..835b0468 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -20,7 +20,6 @@ #pragma once #include "bitvec.h" #include "common.h" -#include "ctimer.h" #include "entity/cparticlesystem.h" #include "entity/cpointorient.h" #include "entity/cpointworldtext.h" @@ -200,6 +199,7 @@ class ZEPlayer m_flEntwatchHudY = -2.0f; m_flEntwatchHudSize = 60.0f; m_bTopDefender = false; + m_flLastVoiceTime = -15.0f; m_flBeaconEnabledTime = -2.0f; } @@ -272,7 +272,7 @@ class ZEPlayer void SetEntwatchHudPos(float x, float y); void SetEntwatchHudSize(float flSize); void SetTopDefenderStatus(bool bStatus) { m_bTopDefender = bStatus; } - void SetVoiceTimer(std::weak_ptr timer) { m_pVoiceTimer = timer; } + void SetLastVoiceTime(float flTime) { m_flLastVoiceTime = flTime; } void SetBeaconEnabledTime(float flTime) { m_flBeaconEnabledTime = flTime; } uint64 GetAdminFlags() { return m_iAdminFlags; } @@ -324,7 +324,7 @@ class ZEPlayer float GetEntwatchHudY() { return m_flEntwatchHudY; } float GetEntwatchHudSize() { return m_flEntwatchHudSize; } bool GetTopDefenderStatus() { return m_bTopDefender; } - std::weak_ptr GetVoiceTimer() { return m_pVoiceTimer; } + float GetLastVoiceTime() { return m_flLastVoiceTime; } float GetBeaconEnabledTime() { return m_flBeaconEnabledTime; } void OnSpawn(); @@ -400,7 +400,7 @@ class ZEPlayer float m_flEntwatchHudY; float m_flEntwatchHudSize; bool m_bTopDefender; - std::weak_ptr m_pVoiceTimer; + float m_flLastVoiceTime; float m_flBeaconEnabledTime; }; From 51370d54878ff49fb3354ef7e1c2282202bfdebd Mon Sep 17 00:00:00 2001 From: Vauff Date: Wed, 8 Jul 2026 23:56:46 -0400 Subject: [PATCH 19/30] Update signatures for 2026-07-08 CS2 update --- gamedata/cs2fixes.jsonc | 53 +++++++++++++++++++++-------------------- src/addresses.h | 2 +- src/detours.cpp | 8 ++++--- src/detours.h | 2 +- src/zombiereborn.cpp | 2 +- src/zombiereborn.h | 2 +- 6 files changed, 36 insertions(+), 33 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index d97eaafa..4bf162a2 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -19,13 +19,13 @@ { "library": "engine", "windows": "40 53 48 83 EC ? 48 8B D9 3B 51", - "linux": "55 48 89 E5 41 56 41 55 41 54 53 48 89 FB 39 77" + "linux": "55 48 89 E5 41 55 41 54 53 48 89 FB 48 83 EC ? 39 77" }, // idk a good way to find this again, i just brute forced the vtable. offset is 136 on CTriggerPush "TriggerPush_Touch": { "library": "server", - "windows": "40 55 53 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 02 48 8B F9", + "windows": "40 55 53 57 48 8B EC 48 81 EC ? ? ? ? 48 8B 02", "linux": "55 48 89 E5 41 57 41 56 41 55 49 89 F5 41 54 53 48 89 FB 48 83 EC ? E8 ? ? ? ? 84 C0" }, // this is called in CTriggerPush::Touch, using IDA pseudocode look in an `if ( ( v & 0x80 ) != 0 )` and then `if ( v > 0.0 ) SetGroundEntity()` @@ -68,14 +68,14 @@ { "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 F0 48 89 E5 41 57 49 89 FF 41 56 48 8D 7D" + "linux": "55 48 89 E5 41 56 49 89 FE 41 55 48 8D 7D" }, // 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 AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 02", - "linux": "55 49 89 D3 48 89 E5 41 57 45 89 CF 41 56" + "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", + "linux": "55 48 89 E5 41 57 41 56 4C 8D BD ? ? ? ? 4D 89 CE" }, // func_pushable inside CTriggerBrush::Use calls CEntityIOOutput::FireOutputInternal // Windows - https://imgur.com/a/A3zcxQm @@ -106,13 +106,13 @@ { "library": "server", "windows": "40 55 53 56 57 41 54 48 8D 6C 24 ? 48 81 EC ? ? ? ? 4D 8B E0", - "linux": "55 48 89 E5 41 57 41 56 41 55 49 89 FD 31 FF" + "linux": "55 66 0F EF C0 48 89 E5 41 57 41 56 41 55 49 89 FD 31 FF" }, // Should be xref'd right above "flGravity", takes a float arg "CBaseEntity::SetGravityScale": { "library": "server", - "windows": "48 89 5C 24 ? 57 48 83 EC ? F3 0F 10 81 ? ? ? ? 48 8B F9 0F 29 74 24 ? 0F 28 F1 0F 2E C6 7A ? 74", + "windows": "48 89 5C 24 ? 57 48 83 EC ? F3 0F 10 81 ? ? ? ? 48 8B F9 0F 29 74 24 ? 0F 28 F1 0F 2E C6 7A ? 74 ? BA ? ? ? ? 41 B8 ? ? ? ? 48 81 C1 ? ? ? ? E8 ? ? ? ? 48 8B CF F3 0F 11 B7 ? ? ? ? E8 ? ? ? ? 48 8B 5C 24 ? 0F 28 74 24 ? 48 83 C4 ? 5F C3 CC CC CC CC CC 48 89 5C 24", "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 53 48 89 FB 48 81 EC ? ? ? ? 0F 2E 87 ? ? ? ? 7A ? 75 ? 48 81 C4 ? ? ? ? 5B 41 5C 41 5D 41 5E 41 5F 5D C3 0F 1F 40 ? 31 C9 BE ? ? ? ? 66 0F EF C9 F3 0F 11 85 ? ? ? ? 48 8D BD ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 0F 29 8D ? ? ? ? 4C 8D A5 ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? C7 85 ? ? ? ? ? ? ? ? 66 89 8D ? ? ? ? E8 ? ? ? ? 48 8B 85 ? ? ? ? 48 8D 15 ? ? ? ? 83 85 ? ? ? ? ? F3 0F 10 85 ? ? ? ? C7 00 ? ? ? ? 48 8B 03 48 8B 80 ? ? ? ? 48 39 D0 0F 85 ? ? ? ? 8B 95 ? ? ? ? 4C 8D 7B ? 85 D2 0F 85 ? ? ? ? 80 BB ? ? ? ? ? 75" }, // "Game System %s is defined twice!\n" @@ -150,8 +150,8 @@ "CNavMesh_GetNearestNavArea": { "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 AC 24 ? ? ? ? 48 81 EC ? ? ? ? 4C 8B 2D", - "linux": "55 48 89 E5 41 57 49 89 D7 41 56 41 55 49 89 FD 41 54 4D 89 C4" + "windows": "48 89 5C 24 ? 44 89 4C 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24", + "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 89 CE" }, // Has "SetModel" string, but is also referenced by cs_script registration function using the same string "CS_Script_SetModel": @@ -168,16 +168,17 @@ "windows": "40 53 48 83 EC ? 48 8B D9 4C 8B C2 48 8B 0D ? ? ? ? 48 8D 54 24 ? 48 8B 01 FF 50 ? 48 8B 54 24 ? 48 8B CB E8 ? ? ? ? 48 83 C4 ? 5B C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24", "linux": "55 48 89 E5 53 48 89 FB 48 83 EC ? 48 8D 05 ? ? ? ? 48 8B 38 48 8B 07 FF 50 ? 48 89 DF 48 8B 5D ? C9 48 89 C6 E9 ? ? ? ? CC CC CC CC 55" }, + // "TerminateRound" "CGameRules_TerminateRound": { "library": "server", - "windows": "48 8B C4 4C 89 48 ? 48 89 48 ? 55 56 41 56", - "linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 53 48 81 EC ? ? ? ? 48 8D 05 ? ? ? ? F3 0F 11 85" + "windows": "48 8B C4 4C 89 48 ? 48 89 48 ? 55 41 54", + "linux": "55 48 89 E5 41 57 41 89 F7 41 56 48 8D 35" }, "CCSPlayer_WeaponServices_CanUse": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC ? 48 8B 01 48 8B FA", + "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" }, "CCSPlayer_WeaponServices_EquipWeapon": @@ -267,8 +268,8 @@ "CGamePlayerEquip_InputTriggerForAllPlayers": { "library": "server", - "windows": "40 55 53 41 54 41 56 48 8B EC 48 83 EC ? 4C 8B F1", - "linux": "55 48 89 E5 41 57 41 56 41 55 49 89 FD 41 54 53 48 83 EC ? E8 ? ? ? ? C7 45" + "windows": "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 54 41 56 41 57 48 8B EC 48 83 EC ? 4C 8B F1", + "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 49 89 FC 53 48 83 EC ? E8 ? ? ? ? C7 45 ? ? ? ? ? 89 C7 66 89 45 ? 66 83 F8 ? 75 ? 48 C7 45 ? ? ? ? ? 66 83 FF ? 0F 84 ? ? ? ? 0F B7 7D ? E8 ? ? ? ? 66 89 45 ? 66 83 F8 ? 0F 84 ? ? ? ? 89 C7 E8 ? ? ? ? 0F B7 7D ? 48 89 C3 48 85 C0 74 ? 48 89 C7 E8 ? ? ? ? 48 8D 7D ? 48 89 C6 E8 ? ? ? ? 0F B7 7D ? 84 C0 0F 85 ? ? ? ? EB ? 66 0F 1F 44 00 ? E8 ? ? ? ? 48 89 C3 48 85 C0 0F 84 ? ? ? ? 48 89 C7 E8 ? ? ? ? 48 8D 7D ? 48 89 C6 E8 ? ? ? ? 84 C0 0F 84 ? ? ? ? 0F B7 7D ? 48 89 5D ? 66 83 FF" }, "CGamePlayerEquip_InputTriggerForActivatedPlayer": { @@ -281,7 +282,7 @@ { "library": "engine", "windows": "48 89 54 24 ? 53 56 57 41 56 48 83 EC", - "linux": "55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 49 89 F4 53 48 81 EC ? ? ? ? 41 C6 01" + "linux": "55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 53 48 83 EC ? 8B 87" }, // The only function with "weapon_shield" and does fminf with 260.0 earlier "CCSPlayerPawn_GetMaxSpeed": @@ -296,7 +297,7 @@ { "library": "server", "windows": "4C 89 44 24 ? F3 0F 11 4C 24 ? 55 53 56", - "linux": "55 48 89 E5 41 57 49 89 FF 41 56 48 8D 3D ? ? ? ? 41 55 41 54 53 48 81 EC" + "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" }, "TraceFunc": { @@ -308,20 +309,20 @@ "TraceShape": { "library": "server", - "windows": "48 89 5C 24 ? 48 89 4C 24 ? 55 57", - "linux": "55 48 89 E5 41 57 49 89 CF 41 56 49 89 F6 41 55 4D 89 C5" + "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" }, "CBasePlayerPawn_GetEyePosition": { "library": "server", - "windows": "48 89 74 24 ? 57 48 83 EC ? 48 8B F1 48 8B FA 48 8B 89 ? ? ? ? 48 85 C9 74 ? 48 8B 01", + "windows": "48 89 5C 24 ? 57 48 83 EC ? 48 8B F9 48 8B DA 48 8B 89 ? ? ? ? 48 85 C9 74 ? 48 8B 01", "linux": "55 48 89 E5 53 48 89 FB 48 83 EC ? 48 8B BF ? ? ? ? 48 85 FF 0F 84" }, "CBasePlayerPawn_GetEyeAngles": { "library": "server", "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 EC ? 48 8B BF ? ? ? ? 48 85 FF 0F 84 ? ? ? ? 48 8B 07 48 8D 15" + "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" "CBaseFilter_InputTestActivator": @@ -335,15 +336,15 @@ { "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 81 EC ? ? ? ? E8 ? ? ? ? 48 85 C0 0F 84 ? ? ? ? 8B 00" + "linux": "55 48 8D 3D ? ? ? ? BE ? ? ? ? 48 89 E5 41 57 41 56 41 55 41 54 53 48 83 EC" }, // 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 "CCSGameRules__sm_mapGcBanInformation": { "library": "server", - "windows": "48 8D 0D ? ? ? ? 48 89 45 ? 0F 11 45", - "linux": "48 8D 0D ? ? ? ? 48 63 51 ? 83 FA ? 0F 84 ? ? ? ? F7 41 ? ? ? ? ? 74" + "windows": "48 8D 0D ? ? ? ? 0F 11 44 24 ? 0F 11 44 24 ? E8", + "linux": "48 8D 35 ? ? ? ? 89 C2 48 63 46" }, // Called right before "%d spawn groups:\n" "GetSpawnGroups": @@ -371,21 +372,21 @@ { "library": "server", "windows": "75 ? 48 8B 03 48 8B CB FF 90 ? ? ? ? 84 C0 74 ? 48 8D 05", - "linux": "75 ? 48 8B 03 48 8D 15 ? ? ? ? 48 8B 80 ? ? ? ? 48 39 D0 75 ? 48 83 C4" + "linux": "75 ? 48 8B 03 48 89 DF FF 90 ? ? ? ? 84 C0 74 ? 48 8D 05" }, // "Going to intermission...\n" "CCSGameRules_GoToIntermission": { "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 49 89 FD 41 54 48 8D 3D" }, // 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", - "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC" + "linux": "55 48 89 E5 41 57 49 89 FF 48 89 F7 41 56 41 55 41 54 53 48 81 EC ? ? ? ? E8" } }, "Offsets": diff --git a/src/addresses.h b/src/addresses.h index cd994951..47fe2d87 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -87,7 +87,7 @@ namespace addresses inline void(FASTCALL* CEntitySystem_AddEntityIOEvent)(CEntitySystem* pEntitySystem, CEntityInstance* pTarget, const char* pszInput, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, float flDelay, int outputID, void*, void*); inline void(FASTCALL* CEntityInstance_AcceptInput)(CEntityInstance* pThis, const char* pInputName, - CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID, void*); + CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); inline CBaseEntity*(FASTCALL* CGameEntitySystem_FindEntityByClassName)(CEntitySystem* pEntitySystem, CEntityInstance* pStartEntity, const char* szName); diff --git a/src/detours.cpp b/src/detours.cpp index eb49a932..9769eef0 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -430,13 +430,13 @@ bool PrepareMapSetModel(CBaseModelEntity* pModel) return true; } -bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID, void* a7, void* a8) +bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void* a6, void* a7) { VPROF_SCOPE_BEGIN("Detour_CEntityIdentity_AcceptInput"); if (g_cvarEnableZR.Get()) { - bool result = ZR_Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID); + bool result = ZR_Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value); if (!result) return result; @@ -525,7 +525,7 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym VPROF_SCOPE_END(); - return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID, a7, a8); + return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, a6, a7); } CConVar g_cvarBlockNavLookup("cs2f_block_nav_lookup", FCVAR_NONE, "Whether to block navigation mesh lookup, improves server performance but breaks bot navigation", false); @@ -657,6 +657,8 @@ void FASTCALL Detour_CTriggerGravity_GravityTouch(CBaseEntity* pEntity, CBaseEnt CTriggerGravity_GravityTouch(pEntity, pOther); } +// todo: did these args change? or just linux decompile shit +// also which os did we even grab these from initially CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6) { // Not sure if this function can even be called in this state, but if it is, we can't do shit anyways diff --git a/src/detours.h b/src/detours.h index 2700e6ef..982083d7 100644 --- a/src/detours.h +++ b/src/detours.h @@ -91,7 +91,7 @@ void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther) int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult); bool FASTCALL Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices*, CBasePlayerWeapon*); void FASTCALL Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices*, CBasePlayerWeapon*); -bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID, void*, void*); +bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void*, void*); void* FASTCALL Detour_CNavMesh_GetNearestNavArea(int64_t unk1, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7); void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove); void* FASTCALL Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin); diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index dfe75d34..e91796ae 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -1485,7 +1485,7 @@ AcquireResult ZR_Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices return AcquireResult::Allowed; } -bool ZR_Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID) +bool ZR_Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value) { const char* inputName = pInputName->String(); diff --git a/src/zombiereborn.h b/src/zombiereborn.h index c42eadb0..84b6005f 100644 --- a/src/zombiereborn.h +++ b/src/zombiereborn.h @@ -280,7 +280,7 @@ void ZR_OnRoundFreezeEnd(IGameEvent* pEvent); void ZR_OnRoundTimeWarning(IGameEvent* pEvent); bool ZR_Hook_OnTakeDamage_Alive(CTakeDamageInfo* pInfo, CCSPlayerPawn* pVictimPawn); AcquireResult ZR_Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices* pItemServices, CEconItemView* pEconItem); -bool ZR_Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID); +bool ZR_Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); void ZR_Hook_ClientPutInServer(CPlayerSlot slot, char const* pszName, int type, uint64 xuid); void ZR_Hook_ClientCommand_JoinTeam(CPlayerSlot slot, const CCommand& args); void ZR_Precache(IEntityResourceManifest* pResourceManifest); From 111229bad2168a065afebcd727390a98c751fb38 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 09:41:30 -0400 Subject: [PATCH 20/30] Update SDK --- sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk b/sdk index 9ab16fa9..7a247626 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit 9ab16fa9fcdeeb30565dfdbf6fbb312356978a0b +Subproject commit 7a247626c342c91808daacabb9b5c417dcbab594 From a74093eff65da10ae1d1fc50e4b460a3fd7c95f5 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 10:18:31 -0400 Subject: [PATCH 21/30] Update offsets for 2026-07-08 CS2 update --- gamedata/cs2fixes.jsonc | 32 ++++++++++++++++---------------- src/cs2_sdk/entity/services.h | 3 +++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc index 4bf162a2..f18794d8 100644 --- a/gamedata/cs2fixes.jsonc +++ b/gamedata/cs2fixes.jsonc @@ -451,28 +451,28 @@ }, "CCSPlayerController_Respawn": { - "windows": 273, - "linux": 275 + "windows": 270, + "linux": 272 }, // CBaseTrigger "PassesTriggerFilters": { - "windows": 273, - "linux": 274 + "windows": 269, + "linux": 270 }, // 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": 256, - "linux": 257 + "windows": 252, + "linux": 253 }, // 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 "CBasePlayerPawn_CommitSuicide": { - "windows": 390, - "linux": 390 + "windows": 384, + "linux": 384 }, "GameEntitySystem": { @@ -494,8 +494,8 @@ // "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 "CNetworkGameServer_ClientList": { - "windows": 74, - "linux": 74 + "windows": 73, + "linux": 73 }, // Right above "mapgroup workshop;" string there is a virtual call to this on g_pGameTypes using "workshop" string "IGameTypes_CreateWorkshopMapGroup": @@ -506,18 +506,18 @@ // 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": 41, - "linux": 42 + "windows": 44, + "linux": 45 }, "CCSPlayer_WeaponServices::DropWeapon": { - "windows": 25, - "linux": 26 + "windows": 28, + "linux": 29 }, "CCSPlayer_WeaponServices::SelectItem": { - "windows": 27, - "linux": 28 + "windows": 30, + "linux": 31 }, // server.dll -> xref 'sv_phys_stop_at_collision' first __fastcall "CVPhys2World::GetTouchingList": diff --git a/src/cs2_sdk/entity/services.h b/src/cs2_sdk/entity/services.h index bd9d3024..8f52b483 100644 --- a/src/cs2_sdk/entity/services.h +++ b/src/cs2_sdk/entity/services.h @@ -86,6 +86,9 @@ class CPlayerPawnComponent virtual void unk_17() = 0; virtual void unk_18() = 0; virtual void unk_19() = 0; + virtual void unk_20() = 0; + virtual void unk_21() = 0; + virtual void unk_22() = 0; public: DECLARE_SCHEMA_CLASS(CPlayerPawnComponent); From f6de81c094b0e3d71919ff54c644a5b878c22a62 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 10:18:45 -0400 Subject: [PATCH 22/30] Fix compile errors --- AMBuilder | 1 + src/cs2_sdk/entity/cbaseentity.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AMBuilder b/AMBuilder index bb02216f..033d025c 100644 --- a/AMBuilder +++ b/AMBuilder @@ -78,6 +78,7 @@ for sdk_target in MMSPlugin.sdk_targets: ] protoc_builder = builder.tools.Protoc(protoc = sdk_target.protoc, sources = [ + os.path.join(sdk['path'], 'common', 'valveextensions.proto'), os.path.join(sdk['path'], 'common', 'network_connection.proto'), os.path.join(sdk['path'], 'common', 'source2_steam_stats.proto'), os.path.join(sdk['path'], 'common', 'netmessages.proto'), diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h index c7655657..f285b0b5 100644 --- a/src/cs2_sdk/entity/cbaseentity.h +++ b/src/cs2_sdk/entity/cbaseentity.h @@ -209,7 +209,7 @@ class CBaseEntity : public CEntityInstance void AcceptInput(const char* pInputName, variant_t value = variant_t(""), CEntityInstance* pActivator = nullptr, CEntityInstance* pCaller = nullptr) { - addresses::CEntityInstance_AcceptInput(this, pInputName, pActivator, pCaller, &value, 0, nullptr); + addresses::CEntityInstance_AcceptInput(this, pInputName, pActivator, pCaller, &value); } bool IsAlive() { return m_lifeState == LifeState_t::LIFE_ALIVE; } From eddd4b7e9a789895e1ae1f330509ed89bf01f4ca Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 10:45:58 -0400 Subject: [PATCH 23/30] Update CTakeDamageResult --- src/cs2_sdk/entity/ctakedamageinfo.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cs2_sdk/entity/ctakedamageinfo.h b/src/cs2_sdk/entity/ctakedamageinfo.h index 389a0bde..3ea47aa7 100644 --- a/src/cs2_sdk/entity/ctakedamageinfo.h +++ b/src/cs2_sdk/entity/ctakedamageinfo.h @@ -153,6 +153,7 @@ struct CTakeDamageResult int32_t m_nHealthBefore; float m_flDamageDealt; float m_flPreModifiedDamage; + VectorWS m_vDamagePosition; int32_t m_nTotalledHealthLost; float m_flTotalledDamageDealt; float m_flTotalledPreModifiedDamage; @@ -192,4 +193,4 @@ struct CTakeDamageResult { } }; -static_assert(sizeof(CTakeDamageResult) == 80); \ No newline at end of file +static_assert(sizeof(CTakeDamageResult) == 96); \ No newline at end of file From 740d354f642c9a3b2c0cd475d968d9ec0aa28264 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 11:54:26 -0400 Subject: [PATCH 24/30] Move m_flSpeed schema field --- src/cs2_sdk/entity/cbaseentity.h | 1 - src/cs2_sdk/entity/ctriggerpush.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h index f285b0b5..bcf1955f 100644 --- a/src/cs2_sdk/entity/cbaseentity.h +++ b/src/cs2_sdk/entity/cbaseentity.h @@ -148,7 +148,6 @@ class CBaseEntity : public CEntityInstance SCHEMA_FIELD(float, m_flFriction) SCHEMA_FIELD(float, m_flGravityScale) SCHEMA_FIELD(float, m_flTimeScale) - SCHEMA_FIELD(float, m_flSpeed) SCHEMA_FIELD(CUtlString, m_sUniqueHammerID); SCHEMA_FIELD(CUtlSymbolLarge, m_target); SCHEMA_FIELD(CUtlSymbolLarge, m_iGlobalname); diff --git a/src/cs2_sdk/entity/ctriggerpush.h b/src/cs2_sdk/entity/ctriggerpush.h index 9901afaa..76d7f2a2 100644 --- a/src/cs2_sdk/entity/ctriggerpush.h +++ b/src/cs2_sdk/entity/ctriggerpush.h @@ -31,4 +31,5 @@ class CTriggerPush : public CBaseTrigger SCHEMA_FIELD(Vector, m_vecPushDirEntitySpace) SCHEMA_FIELD(bool, m_bTriggerOnStartTouch) + SCHEMA_FIELD(float, m_flSpeed) }; From bc6393c8c22cdef1cfbf015a91080b5d989e9e53 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 12:38:31 -0400 Subject: [PATCH 25/30] Update AddEntityIOEvent args too --- src/addresses.h | 2 +- src/utils/entity.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/addresses.h b/src/addresses.h index 47fe2d87..18a43c00 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -85,7 +85,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, int outputID, void*, void*); + CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, float flDelay, void*, void*); inline void(FASTCALL* CEntityInstance_AcceptInput)(CEntityInstance* pThis, const char* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value); diff --git a/src/utils/entity.cpp b/src/utils/entity.cpp index 8f363055..94f46ad1 100644 --- a/src/utils/entity.cpp +++ b/src/utils/entity.cpp @@ -60,5 +60,5 @@ CBaseEntity* UTIL_FindEntityByName(CEntityInstance* pStartEntity, const char* sz void UTIL_AddEntityIOEvent(CEntityInstance* pTarget, const char* pszInput, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t value, float flDelay) { - addresses::CEntitySystem_AddEntityIOEvent(g_pEntitySystem, pTarget, pszInput, pActivator, pCaller, &value, flDelay, 0, nullptr, nullptr); + addresses::CEntitySystem_AddEntityIOEvent(g_pEntitySystem, pTarget, pszInput, pActivator, pCaller, &value, flDelay, nullptr, nullptr); } From e89744615261b444f36b522b3b7b5af518991ff8 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 12:45:35 -0400 Subject: [PATCH 26/30] remove todo comment --- src/detours.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/detours.cpp b/src/detours.cpp index 9769eef0..4af38bd6 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -657,8 +657,6 @@ void FASTCALL Detour_CTriggerGravity_GravityTouch(CBaseEntity* pEntity, CBaseEnt CTriggerGravity_GravityTouch(pEntity, pOther); } -// todo: did these args change? or just linux decompile shit -// also which os did we even grab these from initially CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6) { // Not sure if this function can even be called in this state, but if it is, we can't do shit anyways From 0f4cd74156e164abd71153ae7fc0444102da960f Mon Sep 17 00:00:00 2001 From: xen Date: Thu, 9 Jul 2026 22:36:39 +0300 Subject: [PATCH 27/30] Fix CGlowProperty's NetworkStateChanged --- src/cs2_sdk/entity/globaltypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cs2_sdk/entity/globaltypes.h b/src/cs2_sdk/entity/globaltypes.h index 4729963c..7d8f42cf 100644 --- a/src/cs2_sdk/entity/globaltypes.h +++ b/src/cs2_sdk/entity/globaltypes.h @@ -215,7 +215,7 @@ class CInButtonState class CGlowProperty { public: - DECLARE_SCHEMA_CLASS_INLINE(CGlowProperty) + DECLARE_SCHEMA_CLASS_BASE(CGlowProperty, 4) SCHEMA_FIELD(Vector, m_fGlowColor) SCHEMA_FIELD(int, m_iGlowType) From 3c1ac662e6d430c482ceff7b31282c45eb96c7f0 Mon Sep 17 00:00:00 2001 From: Vauff Date: Thu, 9 Jul 2026 16:05:25 -0400 Subject: [PATCH 28/30] Update SDK --- sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk b/sdk index 7a247626..5f891c90 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit 7a247626c342c91808daacabb9b5c417dcbab594 +Subproject commit 5f891c9026230cce0fc0a3fc4b5fef1c467a1385 From b9d82317a99690a685e904bc237464b68f1cc49b Mon Sep 17 00:00:00 2001 From: notkoen <45914779+notkoen@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:40:04 -0700 Subject: [PATCH 29/30] Update README (#452) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2782d8e7..876910c7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ CS2Fixes is a Metamod plugin with fixes and features aimed but not limited to zo - Download the [latest release package](https://github.com/Source2ZE/CS2Fixes/releases) for your OS - Extract the package contents into `game/csgo` on your server - Configure the plugin cvars as desired in `cfg/cs2fixes/cs2fixes.cfg`, many features are disabled by default -- OPTIONAL: If you want to setup admins, rename `admins.cfg.example` to `admins.cfg` which can be found in `addons/cs2fixes/configs` and follow the instructions within to add admins +- OPTIONAL: If you want to setup admins, rename `admins.jsonc.example` to `admins.jsonc` which can be found in `addons/cs2fixes/configs` and follow the instructions within to add admins ## Fixes and Features You can find the documentation of the fixes and features [here](../../wiki/Home). From 92ca0b70df25a98da562497700ca6ba3b6dc225a Mon Sep 17 00:00:00 2001 From: Vauff Date: Fri, 10 Jul 2026 17:09:20 -0400 Subject: [PATCH 30/30] Add prop damage multiplier configuration --- cfg/cs2fixes/cs2fixes.cfg | 3 ++- src/detours.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index f5a8ab08..e7cdac87 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -46,10 +46,11 @@ cs2f_flashlight_color "255 255 255 0" // What color to use for flashlights cs2f_flashlight_attachment "clip_limit" // Which attachment to parent a flashlight to cs2f_flashlight_particle "particles/cs2fixes/simple_flashlight.vpcf" // Path to flashlight particle -// Damage block settings +// Damage settings cs2f_block_molotov_self_dmg 0 // Whether to block self-damage from molotovs cs2f_block_all_dmg 0 // Whether to block all damage to players cs2f_fix_block_dmg 0 // Whether to fix block-damage on players +cs2f_prop_dmg_scale 1.0 // Multiplier on prop damage // Custom burn settings cs2f_burn_particle "particles/cs2fixes/napalm_fire.vpcf" // The particle to use for burning players diff --git a/src/detours.cpp b/src/detours.cpp index 4af38bd6..61005420 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -93,6 +93,7 @@ DECLARE_DETOUR(CCSGameRules_GoToIntermission, Detour_CCSGameRules_GoToIntermissi CConVar g_cvarBlockMolotovSelfDmg("cs2f_block_molotov_self_dmg", FCVAR_NONE, "Whether to block self-damage from molotovs", false); CConVar g_cvarBlockAllDamage("cs2f_block_all_dmg", FCVAR_NONE, "Whether to block all damage to players", false); CConVar g_cvarFixBlockDamage("cs2f_fix_block_dmg", FCVAR_NONE, "Whether to fix block-damage on players", false); +CConVar g_cvarPropDamageScale("cs2f_prop_dmg_scale", FCVAR_NONE, "Multiplier on prop damage", 1.0f, true, 0.0f, false, 0.0f); int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult) { @@ -149,6 +150,12 @@ int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageI if (!V_strcasecmp(pszInflictorClass, "hegrenade_projectile") && pInfo->m_AttackerInfo.m_bIsPawn && pInfo->m_AttackerInfo.m_nTeam == 0) return 1; + if (!V_strncasecmp(pszInflictorClass, "prop_physics", 12)) + { + pInfo->m_flDamage *= g_cvarPropDamageScale.Get(); + pInfo->m_flTotalledDamage *= g_cvarPropDamageScale.Get(); + } + // maybe call in flow CTakeDamageResult damageResult(0);