From 4d26913310ad6098c6a39e1cb9c23bbd07092bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Borz=C3=AC?= Date: Sun, 7 Jun 2026 16:20:29 +0200 Subject: [PATCH] fix(Core/Battlegrounds): aggregate BG queue announcer on burst (#26086) --- .gitignore | 1 + .../game/Battlegrounds/BattlegroundQueue.cpp | 73 +++++++++---------- .../game/Battlegrounds/BattlegroundQueue.h | 4 + .../Battlegrounds/BattlegroundSpamProtect.cpp | 47 ++++++++++++ .../Battlegrounds/BattlegroundSpamProtect.h | 2 + 5 files changed, 90 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index d41a6021f..c2aea7a19 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # AzerothCore # +.cache /conf/* !/conf/dist /modules/* diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 97a529327..295b8859f 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -1039,42 +1039,43 @@ void BattlegroundQueue::BattlegroundQueueAnnouncerUpdate(uint32 diff, Battlegrou return; } - if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED)) - { - uint32 qPlayers = 0; + // Armed per-bracket timer drives both Timed mode and the deferred immediate + // announcement; the spam-window/Limit throttle gates only immediate mode. + bool const isTimed = sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED); - if (_queueAnnouncementCrossfactioned) + uint32 qPlayers = 0; + + if (_queueAnnouncementCrossfactioned) + qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_CFBG); + else + qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_HORDE) + GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_ALLIANCE); + + if (!qPlayers) + { + _queueAnnouncementTimer[bracket_id] = -1; + return; + } + + if (_queueAnnouncementTimer[bracket_id] >= 0) + { + if (_queueAnnouncementTimer[bracket_id] <= static_cast(diff)) { - qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_CFBG); + _queueAnnouncementTimer[bracket_id] = -1; + + uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80); + + if (!isTimed && !sBGSpam->CanAnnounce(bg_template, bracket_id, q_min_level, qPlayers)) + return; + + auto bgName = bg_template->GetName(); + uint32 MaxPlayers = GetMinPlayersPerTeam(bg_template, bracketEntry) * 2; + uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80); + + ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qPlayers, MaxPlayers); } else { - qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_HORDE) + GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_ALLIANCE); - } - - if (!qPlayers) - { - _queueAnnouncementTimer[bracket_id] = -1; - return; - } - - if (_queueAnnouncementTimer[bracket_id] >= 0) - { - if (_queueAnnouncementTimer[bracket_id] <= static_cast(diff)) - { - _queueAnnouncementTimer[bracket_id] = -1; - - auto bgName = bg_template->GetName(); - uint32 MaxPlayers = GetMinPlayersPerTeam(bg_template, bracketEntry) * 2; - uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80); - uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80); - - ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qPlayers, MaxPlayers); - } - else - { - _queueAnnouncementTimer[bracket_id] -= static_cast(diff); - } + _queueAnnouncementTimer[bracket_id] -= static_cast(diff); } } } @@ -1145,12 +1146,10 @@ void BattlegroundQueue::SendMessageBGQueue(Player* leader, Battleground* bg, PvP } else { - if (!sBGSpam->CanAnnounce(leader, bg, q_min_level, qTotal)) - { - return; - } - - ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers); + // Arm the per-bracket debounce; first join arms, rest are no-ops. + // BattlegroundQueueAnnouncerUpdate emits the aggregated line later. + if (_queueAnnouncementTimer[bracketId] < 0) + SetQueueAnnouncementTimer(bracketId, BG_QUEUE_ANNOUNCER_IMMEDIATE_DEBOUNCE, false); } } } diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h index 475fae962..34575e539 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.h +++ b/src/server/game/Battlegrounds/BattlegroundQueue.h @@ -27,6 +27,10 @@ constexpr auto COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME = 10; +// Immediate-mode announcer debounce (ms): fires on the next periodic pass, by +// which point a same-tick queue burst has collapsed into one aggregated line. +constexpr int32 BG_QUEUE_ANNOUNCER_IMMEDIATE_DEBOUNCE = 1; + struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) { GuidSet Players; // player guid set diff --git a/src/server/game/Battlegrounds/BattlegroundSpamProtect.cpp b/src/server/game/Battlegrounds/BattlegroundSpamProtect.cpp index f917c61bc..ef47d0d0b 100644 --- a/src/server/game/Battlegrounds/BattlegroundSpamProtect.cpp +++ b/src/server/game/Battlegrounds/BattlegroundSpamProtect.cpp @@ -25,6 +25,7 @@ namespace { std::unordered_map _players; + std::unordered_map _brackets; void AddTime(ObjectGuid guid) { @@ -47,6 +48,26 @@ namespace // Skip if spam time < 30 secs (default) return GameTime::GetGameTime().count() - GetTime(guid) >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY); } + + void AddTime(uint32 key) + { + _brackets.insert_or_assign(key, GameTime::GetGameTime().count()); + } + + uint32 GetTime(uint32 key) + { + auto const& itr = _brackets.find(key); + if (itr != _brackets.end()) + return itr->second; + + return 0; + } + + bool IsCorrectDelay(uint32 key) + { + // Skip if spam time < 30 secs (default) + return GameTime::GetGameTime().count() - GetTime(key) >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY); + } } BGSpamProtect* BGSpamProtect::instance() @@ -84,3 +105,29 @@ bool BGSpamProtect::CanAnnounce(Player* player, Battleground* bg, uint32 minLeve AddTime(guid); return true; } + +bool BGSpamProtect::CanAnnounce(Battleground* bg, BattlegroundBracketId bracketId, uint32 minLevel, uint32 queueTotal) +{ + if (!bg) + return false; + + uint32 key = uint32(bg->GetBgTypeID()) * MAX_BATTLEGROUND_BRACKETS + uint32(bracketId); + + // Check prev time + if (!IsCorrectDelay(key)) + return false; + + // When limited, it announces only if there are at least CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS in queue + auto limitQueueMinLevel = sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL); + if (limitQueueMinLevel && minLevel >= limitQueueMinLevel) + { + // limit only RBG for 80, WSG for lower levels + auto bgTypeToLimit = minLevel == 80 ? BATTLEGROUND_RB : BATTLEGROUND_WS; + + if (bg->GetBgTypeID() == bgTypeToLimit && queueTotal < sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS)) + return false; + } + + AddTime(key); + return true; +} diff --git a/src/server/game/Battlegrounds/BattlegroundSpamProtect.h b/src/server/game/Battlegrounds/BattlegroundSpamProtect.h index 7990deb99..ecbdcb213 100644 --- a/src/server/game/Battlegrounds/BattlegroundSpamProtect.h +++ b/src/server/game/Battlegrounds/BattlegroundSpamProtect.h @@ -19,6 +19,7 @@ #define _BATTLEGROUND_SPAM_PROTECT_H_ #include "Define.h" +#include "DBCEnums.h" class Player; class Battleground; @@ -29,6 +30,7 @@ public: static BGSpamProtect* instance(); bool CanAnnounce(Player* player, Battleground* bg, uint32 minLevel, uint32 queueTotal); + bool CanAnnounce(Battleground* bg, BattlegroundBracketId bracketId, uint32 minLevel, uint32 queueTotal); }; #define sBGSpam BGSpamProtect::instance()