Skip to content

Commit c18f453

Browse files
fix: aggregate BG queue announcer on burst + enforce strict N vs N teams for all match sizes (#162)
1 parent 5a34d9a commit c18f453

2 files changed

Lines changed: 60 additions & 206 deletions

File tree

conf/CFBG.conf.dist

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@
7777
# 1 - (Enabled. Get even teams.)
7878
#
7979
# CFBG.EvenTeams.MaxPlayersThreshold
80-
# Description: team member quantity until the EvenTeams rule is valid
81-
# Default: 5 - (Quantity of players per team after the EvenTeams rule will be ignored)
82-
# 0 - (No treshold)
80+
# Description: Per-team player count at which the EvenTeams rule stops being
81+
# enforced. While each team is below this size, teams are kept
82+
# strictly even (N vs N) and any surplus player waits in queue.
83+
# Default: 0 - (EvenTeams enforced for all match sizes, no upper limit)
84+
# >0 - (Once both teams reach this size, one team may gain an extra player)
8385
#
8486
# CFBG.RandomRaceSelection
8587
# Description: allows players to choose the race they will be morphed into when joining battlegrounds as the opposite team.
@@ -107,7 +109,7 @@ CFBG.BalancedTeams.Class.LevelDiff = 2
107109
CFBG.Include.Avg.Ilvl.Enable = 0
108110
CFBG.Players.Count.In.Group = 3
109111
CFBG.EvenTeams.Enabled = 0
110-
CFBG.EvenTeams.MaxPlayersThreshold = 5
112+
CFBG.EvenTeams.MaxPlayersThreshold = 0
111113
CFBG.ResetCooldowns = 0
112114
CFBG.Show.PlayerName = 0
113115
CFBG.RandomRaceSelection = 1

src/CFBG.cpp

Lines changed: 54 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "Config.h"
1212
#include "Containers.h"
1313
#include "Language.h"
14-
#include "Log.h"
1514
#include "Opcodes.h"
1615
#include "ReputationMgr.h"
1716
#include "ScriptMgr.h"
@@ -160,7 +159,7 @@ void CFBG::LoadConfig()
160159
_IsEnableResetCooldowns = sConfigMgr->GetOption<bool>("CFBG.ResetCooldowns", false);
161160
_IsEnableBalanceTeamsOnEntry = sConfigMgr->GetOption<bool>("CFBG.BalanceTeamsOnEntry.Enabled", true);
162161
_showPlayerName = sConfigMgr->GetOption<bool>("CFBG.Show.PlayerName", false);
163-
_EvenTeamsMaxPlayersThreshold = sConfigMgr->GetOption<uint32>("CFBG.EvenTeams.MaxPlayersThreshold", 5);
162+
_EvenTeamsMaxPlayersThreshold = sConfigMgr->GetOption<uint32>("CFBG.EvenTeams.MaxPlayersThreshold", 0);
164163
_MaxPlayersCountInGroup = sConfigMgr->GetOption<uint32>("CFBG.Players.Count.In.Group", 3);
165164
_balanceClassMinLevel = sConfigMgr->GetOption<uint8>("CFBG.BalancedTeams.Class.MinLevel", 10);
166165
_balanceClassMaxLevel = sConfigMgr->GetOption<uint8>("CFBG.BalancedTeams.Class.MaxLevel", 19);
@@ -796,95 +795,6 @@ bool CFBG::FillPlayersToCFBG(BattlegroundQueue* bgqueue, Battleground* bg, Battl
796795

797796
GroupsList groups{ bgqueue->m_QueuedGroups[bracket_id][BG_QUEUE_CFBG].begin(), bgqueue->m_QueuedGroups[bracket_id][BG_QUEUE_CFBG].end() };
798797

799-
// Sort for check same groups
800-
std::sort(groups.begin(), groups.end(), [](GroupQueueInfo const* a, GroupQueueInfo const* b) { return a->Players.size() < b->Players.size(); });
801-
802-
std::array<std::size_t, 2> playersInvitedToBGCount{};
803-
804-
if (IsEnableEvenTeams())
805-
{
806-
std::vector<std::pair<GroupQueueInfo*, GroupQueueInfo*>> sameGroups;
807-
808-
// Check groups with equal players
809-
for (auto itr = groups.begin(); itr != groups.end();)
810-
{
811-
if ((*itr)->IsInvitedToBGInstanceGUID)
812-
{
813-
itr++;
814-
continue;
815-
}
816-
817-
auto nextItr{ itr + 1 };
818-
if (nextItr != groups.end())
819-
{
820-
if ((*nextItr)->IsInvitedToBGInstanceGUID || (*itr)->Players.size() != (*nextItr)->Players.size())
821-
{
822-
itr++;
823-
continue;
824-
}
825-
826-
sameGroups.emplace_back(*itr, *nextItr);
827-
itr = itr + 2;
828-
829-
if (itr == groups.end())
830-
break;
831-
else
832-
continue;
833-
}
834-
835-
itr++;
836-
}
837-
838-
if (!sameGroups.empty())
839-
{
840-
auto InviteGroupToBG = [this, bg, bgqueue, maxAli, maxHorde](GroupQueueInfo* gInfo)
841-
{
842-
TeamId targetTeam = GetLowerTeamIdInBG(bg, bgqueue, gInfo);
843-
gInfo->teamId = targetTeam;
844-
845-
if (bgqueue->m_SelectionPools[targetTeam].AddGroup(gInfo, targetTeam == TEAM_ALLIANCE ? maxAli : maxHorde))
846-
return targetTeam;
847-
848-
return TEAM_NEUTRAL;
849-
};
850-
851-
for (auto& [group1, group2] : sameGroups)
852-
{
853-
auto team1{ InviteGroupToBG(group1) };
854-
auto team2{ InviteGroupToBG(group2) };
855-
856-
if (team1 != TEAM_NEUTRAL && team2 != TEAM_NEUTRAL)
857-
{
858-
std::erase(groups, group1);
859-
std::erase(groups, group2);
860-
playersInvitedToBGCount.at(team1) += group1->Players.size();
861-
playersInvitedToBGCount.at(team2) += group2->Players.size();
862-
}
863-
}
864-
}
865-
866-
if (groups.empty())
867-
return true; // we invited all players, done
868-
}
869-
870-
// Sort with join time (default)
871-
std::sort(groups.begin(), groups.end(), [](GroupQueueInfo const* a, GroupQueueInfo const* b) { return a->JoinTime < b->JoinTime; });
872-
873-
if (IsEnableEvenTeams())
874-
{
875-
InviteSameCountGroups(groups, bgqueue, maxAli, maxHorde, bg);
876-
877-
if (groups.empty())
878-
return true; // we invited all players, done
879-
}
880-
881-
// Check invited players to bg
882-
for (auto const& gInfo : bgqueue->m_QueuedGroups[bracket_id][BG_QUEUE_CFBG])
883-
{
884-
if (gInfo->IsInvitedToBGInstanceGUID)
885-
playersInvitedToBGCount.at(gInfo->teamId) += gInfo->Players.size();
886-
}
887-
888798
auto DefaultInvitePlayersToBG = [this, bg, bgqueue, &groups, maxAli, maxHorde]()
889799
{
890800
GroupsList toDeleteGroups;
@@ -905,79 +815,68 @@ bool CFBG::FillPlayersToCFBG(BattlegroundQueue* bgqueue, Battleground* bg, Battl
905815
std::erase(groups, itr);
906816
};
907817

908-
auto playersInBGAli{ bg->GetPlayersCountByTeam(TEAM_ALLIANCE) + playersInvitedToBGCount.at(TEAM_ALLIANCE) };
909-
auto playersInBGHorde{ bg->GetPlayersCountByTeam(TEAM_HORDE) + playersInvitedToBGCount.at(TEAM_HORDE) };
910-
auto playersInBG{ static_cast<std::size_t>(playersInBGAli + playersInBGHorde) };
911-
auto evenTeamsCount{ EvenTeamsMaxPlayersThreshold() };
912-
913-
if (IsEnableEvenTeams() && evenTeamsCount && playersInBG < evenTeamsCount * 2)
818+
if (IsEnableEvenTeams())
914819
{
915-
int32 aliNeed = evenTeamsCount - playersInBGAli;
916-
int32 hordeNeed = evenTeamsCount - playersInBGHorde;
820+
// Projected team sizes: players already in the BG plus players invited in
821+
// earlier queue updates that have not entered yet.
822+
uint32 playersInBGAli{ bg->GetPlayersCountByTeam(TEAM_ALLIANCE) };
823+
uint32 playersInBGHorde{ bg->GetPlayersCountByTeam(TEAM_HORDE) };
917824

918-
if (aliNeed < 0)
919-
aliNeed = 0;
825+
for (auto const& gInfo : bgqueue->m_QueuedGroups[bracket_id][BG_QUEUE_CFBG])
826+
if (gInfo->IsInvitedToBGInstanceGUID)
827+
(gInfo->teamId == TEAM_ALLIANCE ? playersInBGAli : playersInBGHorde) += gInfo->Players.size();
920828

921-
if (hordeNeed < 0)
922-
hordeNeed = 0;
829+
uint32 evenTeamsCount{ EvenTeamsMaxPlayersThreshold() };
830+
uint32 playersInBG{ playersInBGAli + playersInBGHorde };
923831

924-
if ((aliNeed || hordeNeed) && (aliNeed != hordeNeed))
832+
// A threshold of 0 enforces even teams for all match sizes. A non-zero
833+
// threshold stops enforcing once both teams reach it (total >= threshold * 2).
834+
if (evenTeamsCount && playersInBG >= evenTeamsCount * 2)
925835
{
926-
uint32 playersNeed{ 0 };
927-
TeamId targetTeam = TEAM_NEUTRAL;
836+
DefaultInvitePlayersToBG();
837+
return true;
838+
}
928839

929-
if (aliNeed && aliNeed > hordeNeed)
930-
{
931-
playersNeed = aliNeed - hordeNeed;
932-
targetTeam = TEAM_ALLIANCE;
933-
}
934-
else if (hordeNeed && hordeNeed > aliNeed)
935-
{
936-
playersNeed = hordeNeed - aliNeed;
937-
targetTeam = TEAM_HORDE;
938-
}
840+
// 1. Close any existing imbalance by topping up the smaller team only.
841+
if (playersInBGAli != playersInBGHorde)
842+
{
843+
TeamId targetTeam{ playersInBGAli < playersInBGHorde ? TEAM_ALLIANCE : TEAM_HORDE };
844+
uint32 gap{ targetTeam == TEAM_ALLIANCE ? playersInBGHorde - playersInBGAli : playersInBGAli - playersInBGHorde };
845+
uint32 targetCount{ std::min<uint32>(gap, targetTeam == TEAM_ALLIANCE ? maxAli : maxHorde) };
846+
847+
std::sort(groups.begin(), groups.end(), [](GroupQueueInfo const* a, GroupQueueInfo const* b) { return a->Players.size() < b->Players.size(); });
848+
849+
GroupsList toDeleteGroups;
939850

940-
if (playersNeed > 0 && targetTeam != TEAM_NEUTRAL)
851+
for (auto const& gInfo : groups)
941852
{
942-
GroupsList toDeleteGroups;
943-
944-
// #1. Try fill players to even team
945-
for (auto const& gInfo : groups)
946-
{
947-
// We can add only single players
948-
if (gInfo->IsInvitedToBGInstanceGUID)
949-
continue;
950-
951-
gInfo->teamId = targetTeam;
952-
953-
if (bgqueue->m_SelectionPools[targetTeam].AddGroup(gInfo, playersNeed))
954-
{
955-
auto groupPlayerSize{ gInfo->Players.size() };
956-
playersNeed -= groupPlayerSize;
957-
toDeleteGroups.emplace_back(gInfo);
958-
targetTeam == TEAM_ALLIANCE ? aliNeed -= groupPlayerSize : hordeNeed -= groupPlayerSize;
959-
}
960-
961-
// Stop invited if found players for even teams
962-
if (!playersNeed)
963-
break;
964-
}
965-
966-
// Delete invited groups
967-
for (auto const& gInfo : toDeleteGroups)
968-
std::erase(groups, gInfo);
853+
if (gInfo->IsInvitedToBGInstanceGUID)
854+
continue;
855+
856+
if (bgqueue->m_SelectionPools[targetTeam].GetPlayerCount() >= targetCount)
857+
break;
858+
859+
gInfo->teamId = targetTeam;
860+
auto before{ bgqueue->m_SelectionPools[targetTeam].GetPlayerCount() };
861+
bgqueue->m_SelectionPools[targetTeam].AddGroup(gInfo, targetCount);
862+
863+
if (bgqueue->m_SelectionPools[targetTeam].GetPlayerCount() > before)
864+
toDeleteGroups.emplace_back(gInfo);
969865
}
970-
else
971-
LOG_FATAL("module", "> CFBG: Incorrect conditions for check even teams. Players need: {}. Target team: {}", playersNeed, targetTeam);
866+
867+
for (auto const& gInfo : toDeleteGroups)
868+
std::erase(groups, gInfo);
972869
}
973870

974-
// #2 if all teams even and `MaxPlayersThreshold` complete
975-
if (!aliNeed && !hordeNeed)
976-
DefaultInvitePlayersToBG();
871+
// 2. Invite the rest only in balanced same-size pairs. Any group that would
872+
// break N vs N stays in the queue for the next update.
873+
std::sort(groups.begin(), groups.end(), [](GroupQueueInfo const* a, GroupQueueInfo const* b) { return a->Players.size() > b->Players.size(); });
874+
InviteSameCountGroups(groups, bgqueue, maxAli, maxHorde, bg);
875+
876+
return true;
977877
}
978-
else
979-
DefaultInvitePlayersToBG();
980878

879+
DefaultInvitePlayersToBG();
981880
return true;
982881
}
983882

@@ -1009,7 +908,6 @@ void CFBG::UpdateForget(Player* player)
1009908
}
1010909
}
1011910

1012-
std::unordered_map<ObjectGuid, Seconds> BGSpamProtectionCFBG;
1013911
void CFBG::SendMessageQueue(BattlegroundQueue* bgQueue, Battleground* bg, PvPDifficultyEntry const* bracketEntry, Player* leader)
1014912
{
1015913
BattlegroundBracketId bracketId = bracketEntry->GetBracketId();
@@ -1035,57 +933,11 @@ void CFBG::SendMessageQueue(BattlegroundQueue* bgQueue, Battleground* bg, PvPDif
1035933
}
1036934
else
1037935
{
1038-
auto searchGUID = BGSpamProtectionCFBG.find(leader->GetGUID());
1039-
1040-
if (searchGUID == BGSpamProtectionCFBG.end())
1041-
BGSpamProtectionCFBG[leader->GetGUID()] = 0s;
1042-
1043-
// Skip if spam time < 30 secs (default)
1044-
if (GameTime::GetGameTime() - BGSpamProtectionCFBG[leader->GetGUID()] < Seconds(sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY)))
1045-
{
1046-
return;
1047-
}
1048-
1049-
// When limited, it announces only if there are at least CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS in queue
1050-
auto limitQueueMinLevel = sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL);
1051-
if (limitQueueMinLevel != 0 && q_min_level >= limitQueueMinLevel)
1052-
{
1053-
// limit only RBG for 80, WSG for lower levels
1054-
auto bgTypeToLimit = q_min_level == 80 ? BATTLEGROUND_RB : BATTLEGROUND_WS;
1055-
1056-
if (bg->GetBgTypeID() == bgTypeToLimit && qTotal < sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS))
1057-
{
1058-
return;
1059-
}
1060-
}
1061-
1062-
BGSpamProtectionCFBG[leader->GetGUID()] = GameTime::GetGameTime();
1063-
1064-
if (_showPlayerName)
1065-
{
1066-
std::string msg = Acore::StringFormat("{} |cffffffffHas Joined|r |cffff0000{}|r|cffffffff(|r|cff00ffff{}|r|cffffffff/|r|cff00ffff{}|r|cffffffff)|r",
1067-
leader->GetPlayerName(), bg->GetName(), qTotal, MinPlayers);
1068-
1069-
auto const& sessions = sWorldSessionMgr->GetAllSessions();
1070-
for (auto const& session : sessions)
1071-
{
1072-
if (Player* player = session.second->GetPlayer())
1073-
{
1074-
if (player->GetPlayerSetting(AzerothcorePSSource, SETTING_ANNOUNCER_FLAGS).HasFlag(ANNOUNCER_FLAG_DISABLE_BG_QUEUE))
1075-
{
1076-
continue;
1077-
}
1078-
1079-
WorldPacket data(SMSG_CHAT_SERVER_MESSAGE, (msg.size() + 1));
1080-
data << uint32(3);
1081-
data << msg;
1082-
player->GetSession()->SendPacket(&data);
1083-
}
1084-
}
1085-
}
1086-
else
936+
// Defer to the shared core announcer (cross-faction); spam-window and
937+
// Limit gating now happen centrally in BattlegroundQueueAnnouncerUpdate.
938+
if (bgQueue->GetQueueAnnouncementTimer(bracketId) < 0)
1087939
{
1088-
ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName, q_min_level, q_max_level, qTotal, MinPlayers);
940+
bgQueue->SetQueueAnnouncementTimer(bracketId, BG_QUEUE_ANNOUNCER_IMMEDIATE_DEBOUNCE, true);
1089941
}
1090942
}
1091943
}

0 commit comments

Comments
 (0)