From 546a09331c55e22e011480d43c1937fd89cd3961 Mon Sep 17 00:00:00 2001 From: Faramour <44729057+Faramour@users.noreply.github.com> Date: Wed, 19 Aug 2026 20:50:29 +0200 Subject: [PATCH 1/3] Add low population features to Map Vote System --- cfg/cs2fixes/cs2fixes.cfg | 1 + src/map_votes.cpp | 40 +++++++++++++++++++++++++-------------- src/map_votes.h | 5 +++-- src/playermanager.cpp | 4 ++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index 3a7b746b..94046da7 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -98,6 +98,7 @@ cs2f_vote_maps_cooldown 6.0 // Default number of hours until a map can be pla cs2f_vote_maps_cooldown_rng 0.0 // Randomness range in both directions to apply to map cooldowns cs2f_vote_max_nominations 10 // Number of nominations to include per vote, out of a maximum of 10 cs2f_vote_max_maps 10 // Number of total maps to include per vote, including nominations, out of a maximum of 10 +cs2f_vote_lowpop_max_count 10 // Maximum amount of players required to enable low population features in map vote system // User preferences settings cs2f_user_prefs_api "" // User Preferences REST API endpoint diff --git a/src/map_votes.cpp b/src/map_votes.cpp index 4847bd6d..d087477e 100644 --- a/src/map_votes.cpp +++ b/src/map_votes.cpp @@ -43,6 +43,7 @@ CConVar g_cvarVoteMapsCooldown("cs2f_vote_maps_cooldown", FCVAR_NONE, "De CConVar g_cvarVoteMapsCooldownRng("cs2f_vote_maps_cooldown_rng", FCVAR_NONE, "Randomness range in both directions to apply to map cooldowns", 0.0f); CConVar g_cvarVoteMaxNominations("cs2f_vote_max_nominations", FCVAR_NONE, "Number of nominations to include per vote, out of a maximum of 10", 10, true, 0, true, 10); CConVar g_cvarVoteMaxMaps("cs2f_vote_max_maps", FCVAR_NONE, "Number of total maps to include per vote, including nominations, out of a maximum of 10", 10, true, 2, true, 10); +CConVar g_cvarVoteLowPopMaxCount("cs2f_vote_lowpop_max_count", FCVAR_NONE, "Maximum amount of players required to enable low population features in map vote system", 10, true, 0, false, 0); CON_COMMAND_CHAT_FLAGS(reload_map_list, "- Reload map list, also reloads current map on completion", ADMFLAG_ROOT) { @@ -168,11 +169,13 @@ CON_COMMAND_CHAT(maplist, "- List the maps in the server") g_pMapVoteSystem->PrintMapList(player); } -bool CMap::IsAvailable() +bool CMap::IsAvailable(int iOnlinePlayers) { auto pThis = shared_from_this(); + if (iOnlinePlayers == -1) + iOnlinePlayers = g_playerManager->GetOnlinePlayerCount(false); - if (GetCooldown()->IsOnCooldown()) + if (GetCooldown()->IsOnCooldown() && iOnlinePlayers > g_cvarVoteLowPopMaxCount.Get()) return false; if (*g_pMapVoteSystem->GetCurrentMap() == *pThis) @@ -181,7 +184,6 @@ bool CMap::IsAvailable() if (!IsEnabled()) return false; - int iOnlinePlayers = g_playerManager->GetOnlinePlayerCount(false); bool bMeetsMaxPlayers = iOnlinePlayers <= GetMaxPlayers(); bool bMeetsMinPlayers = iOnlinePlayers >= GetMinPlayers(); return bMeetsMaxPlayers && bMeetsMinPlayers; @@ -759,7 +761,7 @@ void CMapVoteSystem::AttemptNomination(CCSPlayerController* pController, const c return; } - if (pMap->GetCooldown()->IsOnCooldown()) + if (pMap->GetCooldown()->IsOnCooldown() && iPlayerCount > g_cvarVoteLowPopMaxCount.Get()) { ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Cannot nominate \x06%s\x01 because it's on a %s cooldown.", pMap->GetName(), pMap->GetCooldownText(false).c_str()); return; @@ -1108,11 +1110,16 @@ bool CMapVoteSystem::WriteMapCooldownsToFile() return true; } -void CMapVoteSystem::ClearInvalidNominations() +void CMapVoteSystem::OnPlayerCountChange() { if (!g_cvarVoteManagerEnable.Get() || m_bIsVoteOngoing || !GetGlobals()) return; + int iOnlinePlayers = g_playerManager->GetOnlinePlayerCount(false); + + if (iOnlinePlayers > m_iSessionMaxPlayerCount) + m_iSessionMaxPlayerCount = iOnlinePlayers; + for (int i = 0; i < GetGlobals()->maxClients; i++) { int iNominatedMapIndex = m_arrPlayerNominations[i]; @@ -1124,7 +1131,7 @@ void CMapVoteSystem::ClearInvalidNominations() auto pMap = GetMapByIndex(iNominatedMapIndex); // Check if nominated map still meets criteria for nomination - if (!pMap->IsAvailable()) + if (!pMap->IsAvailable(iOnlinePlayers)) { ClearPlayerInfo(i); CCSPlayerController* pPlayer = CCSPlayerController::FromSlot(i); @@ -1153,16 +1160,19 @@ void CMapVoteSystem::ApplyGameSettings(const char* pszMapName, uint64 iWorkshopI void CMapVoteSystem::OnLevelShutdown() { - // Put the map on cooldown as we transition to the next map - PutMapOnCooldown(GetCurrentMap()->GetName()); - - // Fully apply pending group cooldowns - for (std::shared_ptr pCooldown : m_vecCooldowns) + if (m_iSessionMaxPlayerCount > g_cvarVoteLowPopMaxCount.Get()) { - if (pCooldown->GetPendingCooldown() > 0.0f) + // Put the map on cooldown as we transition to the next map + PutMapOnCooldown(GetCurrentMap()->GetName()); + + // Fully apply pending group cooldowns + for (std::shared_ptr pCooldown : m_vecCooldowns) { - PutMapOnCooldown(pCooldown->GetMapName(), pCooldown->GetPendingCooldown()); - pCooldown->SetPendingCooldown(0.0f); + if (pCooldown->GetPendingCooldown() > 0.0f) + { + PutMapOnCooldown(pCooldown->GetMapName(), pCooldown->GetPendingCooldown()); + pCooldown->SetPendingCooldown(0.0f); + } } } @@ -1177,6 +1187,8 @@ void CMapVoteSystem::OnLevelShutdown() if (m_timeMapListModified != std::filesystem::last_write_time(szPath)) ReloadMapList(false); } + + m_iSessionMaxPlayerCount = 0; } std::string CMapVoteSystem::ConvertFloatToString(float fValue, int precision) diff --git a/src/map_votes.h b/src/map_votes.h index 178f65cc..935d2624 100644 --- a/src/map_votes.h +++ b/src/map_votes.h @@ -89,7 +89,7 @@ class CMap : public std::enable_shared_from_this std::vector GetGroups() { return m_vecGroups; }; bool HasGroup(std::string strGroup) { return std::find(m_vecGroups.begin(), m_vecGroups.end(), strGroup) != m_vecGroups.end(); }; - bool IsAvailable(); + bool IsAvailable(int iOnlinePlayers = -1); bool Load(); std::shared_ptr GetCooldown(); std::string GetCooldownText(bool bPlural); @@ -206,7 +206,7 @@ class CMapVoteSystem std::shared_ptr GetCurrentMap() { return m_pCurrentMap; } void SetCurrentMap(std::shared_ptr pCurrentMap) { m_pCurrentMap = pCurrentMap; } int GetDownloadQueueSize() { return m_DownloadQueue.size(); } - void ClearInvalidNominations(); + void OnPlayerCountChange(); std::shared_ptr GetForcedNextMap() { return m_pForcedNextMap; } void SetForcedNextMap(std::shared_ptr pForcedNextMap) { m_pForcedNextMap = pForcedNextMap; } std::unordered_map GetNominatedMaps(); @@ -247,6 +247,7 @@ class CMapVoteSystem std::weak_ptr m_pDownloadProgressTimer; std::weak_ptr m_pRateLimitedDownloadTimer; std::vector> m_vecWorkshopDetailsQueries; + int m_iSessionMaxPlayerCount = 0; }; extern CMapVoteSystem* g_pMapVoteSystem; \ No newline at end of file diff --git a/src/playermanager.cpp b/src/playermanager.cpp index ee61cdb2..3bfe9c84 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -804,7 +804,7 @@ bool CPlayerManager::OnClientConnected(CPlayerSlot slot, uint64 xuid, const char ResetPlayerFlags(slot.Get()); g_pMapVoteSystem->ClearPlayerInfo(slot.Get()); - g_pMapVoteSystem->ClearInvalidNominations(); + g_pMapVoteSystem->OnPlayerCountChange(); return true; } @@ -829,7 +829,7 @@ void CPlayerManager::OnClientDisconnect(CPlayerSlot slot) // One tick delay, to ensure player count decrements CTimer::Create(0.01f, TIMERFLAG_MAP, []() { g_pVoteManager->CheckRTVStatus(); - g_pMapVoteSystem->ClearInvalidNominations(); + g_pMapVoteSystem->OnPlayerCountChange(); return -1.0f; }); From a2e5c61894adf9259a8f0891eb15b4691bdbf135 Mon Sep 17 00:00:00 2001 From: Vauff Date: Tue, 25 Aug 2026 14:15:22 -0400 Subject: [PATCH 2/3] simplify a bit + cvar changes --- cfg/cs2fixes/cs2fixes.cfg | 2 +- src/map_votes.cpp | 20 ++++++++++++-------- src/map_votes.h | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg index 94046da7..1e7928d0 100644 --- a/cfg/cs2fixes/cs2fixes.cfg +++ b/cfg/cs2fixes/cs2fixes.cfg @@ -98,7 +98,7 @@ cs2f_vote_maps_cooldown 6.0 // Default number of hours until a map can be pla cs2f_vote_maps_cooldown_rng 0.0 // Randomness range in both directions to apply to map cooldowns cs2f_vote_max_nominations 10 // Number of nominations to include per vote, out of a maximum of 10 cs2f_vote_max_maps 10 // Number of total maps to include per vote, including nominations, out of a maximum of 10 -cs2f_vote_lowpop_max_count 10 // Maximum amount of players required to enable low population features in map vote system +cs2f_vote_bypass_cd_max_count 0 // Maximum amount of players allowed to enable bypassing map cooldowns // User preferences settings cs2f_user_prefs_api "" // User Preferences REST API endpoint diff --git a/src/map_votes.cpp b/src/map_votes.cpp index d087477e..78ddf69e 100644 --- a/src/map_votes.cpp +++ b/src/map_votes.cpp @@ -43,7 +43,7 @@ CConVar g_cvarVoteMapsCooldown("cs2f_vote_maps_cooldown", FCVAR_NONE, "De CConVar g_cvarVoteMapsCooldownRng("cs2f_vote_maps_cooldown_rng", FCVAR_NONE, "Randomness range in both directions to apply to map cooldowns", 0.0f); CConVar g_cvarVoteMaxNominations("cs2f_vote_max_nominations", FCVAR_NONE, "Number of nominations to include per vote, out of a maximum of 10", 10, true, 0, true, 10); CConVar g_cvarVoteMaxMaps("cs2f_vote_max_maps", FCVAR_NONE, "Number of total maps to include per vote, including nominations, out of a maximum of 10", 10, true, 2, true, 10); -CConVar g_cvarVoteLowPopMaxCount("cs2f_vote_lowpop_max_count", FCVAR_NONE, "Maximum amount of players required to enable low population features in map vote system", 10, true, 0, false, 0); +CConVar g_cvarVoteBypassCooldownMaxCount("cs2f_vote_bypass_cd_max_count", FCVAR_NONE, "Maximum amount of players allowed to enable bypassing map cooldowns", 0, true, 0, false, 0); CON_COMMAND_CHAT_FLAGS(reload_map_list, "- Reload map list, also reloads current map on completion", ADMFLAG_ROOT) { @@ -169,13 +169,11 @@ CON_COMMAND_CHAT(maplist, "- List the maps in the server") g_pMapVoteSystem->PrintMapList(player); } -bool CMap::IsAvailable(int iOnlinePlayers) +bool CMap::IsAvailable() { auto pThis = shared_from_this(); - if (iOnlinePlayers == -1) - iOnlinePlayers = g_playerManager->GetOnlinePlayerCount(false); - if (GetCooldown()->IsOnCooldown() && iOnlinePlayers > g_cvarVoteLowPopMaxCount.Get()) + if (GetCooldown()->IsOnCooldown()) return false; if (*g_pMapVoteSystem->GetCurrentMap() == *pThis) @@ -184,6 +182,7 @@ bool CMap::IsAvailable(int iOnlinePlayers) if (!IsEnabled()) return false; + int iOnlinePlayers = g_playerManager->GetOnlinePlayerCount(false); bool bMeetsMaxPlayers = iOnlinePlayers <= GetMaxPlayers(); bool bMeetsMinPlayers = iOnlinePlayers >= GetMinPlayers(); return bMeetsMaxPlayers && bMeetsMinPlayers; @@ -761,7 +760,7 @@ void CMapVoteSystem::AttemptNomination(CCSPlayerController* pController, const c return; } - if (pMap->GetCooldown()->IsOnCooldown() && iPlayerCount > g_cvarVoteLowPopMaxCount.Get()) + if (pMap->GetCooldown()->IsOnCooldown()) { ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "Cannot nominate \x06%s\x01 because it's on a %s cooldown.", pMap->GetName(), pMap->GetCooldownText(false).c_str()); return; @@ -1131,7 +1130,7 @@ void CMapVoteSystem::OnPlayerCountChange() auto pMap = GetMapByIndex(iNominatedMapIndex); // Check if nominated map still meets criteria for nomination - if (!pMap->IsAvailable(iOnlinePlayers)) + if (!pMap->IsAvailable()) { ClearPlayerInfo(i); CCSPlayerController* pPlayer = CCSPlayerController::FromSlot(i); @@ -1160,7 +1159,7 @@ void CMapVoteSystem::ApplyGameSettings(const char* pszMapName, uint64 iWorkshopI void CMapVoteSystem::OnLevelShutdown() { - if (m_iSessionMaxPlayerCount > g_cvarVoteLowPopMaxCount.Get()) + if (m_iSessionMaxPlayerCount > g_cvarVoteBypassCooldownMaxCount.Get()) { // Put the map on cooldown as we transition to the next map PutMapOnCooldown(GetCurrentMap()->GetName()); @@ -1328,6 +1327,11 @@ std::shared_ptr CMapVoteSystem::GetMapCooldown(const char* pszMapName return pCooldown; } +bool CCooldown::IsOnCooldown() +{ + return GetCurrentCooldown() > 0.0f && g_playerManager->GetOnlinePlayerCount(false) > g_cvarVoteBypassCooldownMaxCount.Get(); +} + float CCooldown::GetCurrentCooldown() { time_t timeCurrent = std::time(0); diff --git a/src/map_votes.h b/src/map_votes.h index 935d2624..ac55efac 100644 --- a/src/map_votes.h +++ b/src/map_votes.h @@ -50,8 +50,8 @@ class CCooldown void SetTimeCooldown(time_t timeCooldown) { m_timeCooldown = timeCooldown; }; float GetPendingCooldown() { return m_fPendingCooldown; }; void SetPendingCooldown(float fPendingCooldown) { m_fPendingCooldown = fPendingCooldown; }; - bool IsOnCooldown() { return GetCurrentCooldown() > 0.0f; } bool IsPending() { return m_fPendingCooldown > 0.0f && m_fPendingCooldown == GetCurrentCooldown(); }; + bool IsOnCooldown(); float GetCurrentCooldown(); private: @@ -89,7 +89,7 @@ class CMap : public std::enable_shared_from_this std::vector GetGroups() { return m_vecGroups; }; bool HasGroup(std::string strGroup) { return std::find(m_vecGroups.begin(), m_vecGroups.end(), strGroup) != m_vecGroups.end(); }; - bool IsAvailable(int iOnlinePlayers = -1); + bool IsAvailable(); bool Load(); std::shared_ptr GetCooldown(); std::string GetCooldownText(bool bPlural); From b280b9fa1b3f7a542aa143245c188fccc960fe3a Mon Sep 17 00:00:00 2001 From: Vauff Date: Tue, 25 Aug 2026 14:54:43 -0400 Subject: [PATCH 3/3] fix pending cooldowns not being properly reset --- src/map_votes.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/map_votes.cpp b/src/map_votes.cpp index 78ddf69e..72dd2479 100644 --- a/src/map_votes.cpp +++ b/src/map_votes.cpp @@ -1159,19 +1159,23 @@ void CMapVoteSystem::ApplyGameSettings(const char* pszMapName, uint64 iWorkshopI void CMapVoteSystem::OnLevelShutdown() { - if (m_iSessionMaxPlayerCount > g_cvarVoteBypassCooldownMaxCount.Get()) + bool bApplyCooldowns = m_iSessionMaxPlayerCount > g_cvarVoteBypassCooldownMaxCount.Get(); + + if (bApplyCooldowns) { // Put the map on cooldown as we transition to the next map PutMapOnCooldown(GetCurrentMap()->GetName()); + } - // Fully apply pending group cooldowns - for (std::shared_ptr pCooldown : m_vecCooldowns) + // Fully apply or discard pending group cooldowns + for (std::shared_ptr pCooldown : m_vecCooldowns) + { + if (pCooldown->GetPendingCooldown() > 0.0f) { - if (pCooldown->GetPendingCooldown() > 0.0f) - { + if (bApplyCooldowns) PutMapOnCooldown(pCooldown->GetMapName(), pCooldown->GetPendingCooldown()); - pCooldown->SetPendingCooldown(0.0f); - } + + pCooldown->SetPendingCooldown(0.0f); } }