Files
azerothcore-wotlk/src/server/game/Battlegrounds/BattlegroundQueue.cpp
M'Dic 7e58650cf5 revert(Core): ChrRace.dbc full implementation (#16114)
* revert (core): ChrRace.dbc full implementation

we revert this due to several issues arrising. Although the dbc reading is done in full and correctly. Azerothcore relied on the original handling (althought not propper) for so long that  there is

* revert

* Update remove_charrace_dbc.sql

* Update remove_charrace_dbc.sql

* Update remove_charrace_dbc.sql
2023-04-29 08:23:11 -03:00

1348 lines
54 KiB
C++

/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BattlegroundQueue.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
#include "BattlegroundMgr.h"
#include "BattlegroundSpamProtect.h"
#include "Channel.h"
#include "Chat.h"
#include "GameTime.h"
#include "Group.h"
#include "Language.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScriptMgr.h"
#include <unordered_map>
/*********************************************************/
/*** BATTLEGROUND QUEUE SYSTEM ***/
/*********************************************************/
BattlegroundQueue::BattlegroundQueue()
{
for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i)
{
for (uint32 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j)
{
m_WaitTimeLastIndex[i][j] = 0;
for (uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++k)
m_WaitTimes[i][j][k] = 0;
}
}
_queueAnnouncementTimer.fill(-1);
_queueAnnouncementCrossfactioned = false;
}
BattlegroundQueue::~BattlegroundQueue()
{
m_events.KillAllEvents(false);
m_QueuedPlayers.clear();
for (auto& m_QueuedGroup : m_QueuedGroups)
{
for (auto& j : m_QueuedGroup)
{
for (auto& itr : j)
delete itr;
j.clear();
}
}
}
/*********************************************************/
/*** BATTLEGROUND QUEUE SELECTION POOLS ***/
/*********************************************************/
// selection pool initialization, used to clean up from prev selection
void BattlegroundQueue::SelectionPool::Init()
{
SelectedGroups.clear();
PlayerCount = 0;
}
// returns true if we kicked more than requested
bool BattlegroundQueue::SelectionPool::KickGroup(const uint32 size)
{
if (SelectedGroups.empty())
return false;
// find last group with proper size or largest
bool foundProper = false;
GroupQueueInfo* groupToKick{ SelectedGroups.front() };
for (auto const& gInfo : SelectedGroups)
{
// if proper size - overwrite to kick last one
if (std::abs(int32(gInfo->Players.size()) - (int32)size) <= 1)
{
groupToKick = gInfo;
foundProper = true;
}
else if (!foundProper && gInfo->Players.size() >= groupToKick->Players.size())
groupToKick = gInfo;
}
// remove selected from pool
auto playersCountInGroup{ groupToKick->Players.size() };
PlayerCount -= playersCountInGroup;
std::erase(SelectedGroups, groupToKick);
if (foundProper)
return false;
return playersCountInGroup > size;
}
// returns true if added or desired count not yet reached
bool BattlegroundQueue::SelectionPool::AddGroup(GroupQueueInfo* ginfo, uint32 desiredCount)
{
// add if we don't exceed desiredCount
if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size())
{
SelectedGroups.push_back(ginfo);
PlayerCount += ginfo->Players.size();
return true;
}
return PlayerCount < desiredCount;
}
/*********************************************************/
/*** BATTLEGROUND QUEUES ***/
/*********************************************************/
// add group or player (grp == nullptr) to bg queue with the given leader and bg specifications
GroupQueueInfo* BattlegroundQueue::AddGroup(Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated, bool isPremade,
uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId /*= 0*/, uint32 opponentsArenaTeamId /*= 0*/)
{
BattlegroundBracketId bracketId = bracketEntry->GetBracketId();
// create new ginfo
auto* ginfo = new GroupQueueInfo;
ginfo->BgTypeId = bgTypeId;
ginfo->ArenaType = arenaType;
ginfo->ArenaTeamId = arenaTeamId;
ginfo->IsRated = isRated;
ginfo->IsInvitedToBGInstanceGUID = 0;
ginfo->JoinTime = GameTime::GetGameTimeMS().count();
ginfo->RemoveInviteTime = 0;
ginfo->teamId = leader->GetTeamId();
ginfo->RealTeamID = leader->GetTeamId(true);
ginfo->ArenaTeamRating = arenaRating;
ginfo->ArenaMatchmakerRating = matchmakerRating;
ginfo->PreviousOpponentsTeamId = opponentsArenaTeamId;
ginfo->OpponentsTeamRating = 0;
ginfo->OpponentsMatchmakerRating = 0;
ginfo->Players.clear();
//compute index (if group is premade or joined a rated match) to queues
uint32 index = 0;
if (!isRated && !isPremade)
index += PVP_TEAMS_COUNT;
if (ginfo->teamId == TEAM_HORDE)
index++;
sScriptMgr->OnAddGroup(this, ginfo, index, leader, group, bgTypeId, bracketEntry,
arenaType, isRated, isPremade, arenaRating, matchmakerRating, arenaTeamId, opponentsArenaTeamId);
LOG_DEBUG("bg.battleground", "Adding Group to BattlegroundQueue bgTypeId: {}, bracket_id: {}, index: {}", bgTypeId, bracketId, index);
// pussywizard: store indices at which GroupQueueInfo is in m_QueuedGroups
ginfo->BracketId = bracketId;
ginfo->GroupType = index;
//add players from group to ginfo
if (group)
{
group->DoForAllMembers([this, ginfo](Player* member)
{
ASSERT(m_QueuedPlayers.count(member->GetGUID()) == 0);
m_QueuedPlayers[member->GetGUID()] = ginfo;
ginfo->Players.emplace(member->GetGUID());
});
}
else
{
ASSERT(m_QueuedPlayers.count(leader->GetGUID()) == 0);
m_QueuedPlayers[leader->GetGUID()] = ginfo;
ginfo->Players.emplace(leader->GetGUID());
}
//add GroupInfo to m_QueuedGroups
m_QueuedGroups[bracketId][index].push_back(ginfo);
// announce world (this doesn't need mutex)
SendJoinMessageArenaQueue(leader, ginfo, bracketEntry, isRated);
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(ginfo->BgTypeId);
if (!bg)
return ginfo;
if (!isRated && !isPremade && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
SendMessageBGQueue(leader, bg, bracketEntry);
return ginfo;
}
void BattlegroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo)
{
uint32 timeInQueue = std::max<uint32>(1, getMSTimeDiff(ginfo->JoinTime, GameTime::GetGameTimeMS().count()));
// team_index: bg alliance - TEAM_ALLIANCE, bg horde - TEAM_HORDE, arena skirmish - TEAM_ALLIANCE, arena rated - TEAM_HORDE
uint8 team_index;
if (!ginfo->ArenaType)
team_index = ginfo->teamId;
else
team_index = (ginfo->IsRated ? TEAM_HORDE : TEAM_ALLIANCE);
// just to be sure
if (team_index >= TEAM_NEUTRAL)
return;
// pointer to last index
uint32* lastIndex = &m_WaitTimeLastIndex[team_index][ginfo->BracketId];
// set time at index to new value
m_WaitTimes[team_index][ginfo->BracketId][*lastIndex] = timeInQueue;
// set last index to next one
(*lastIndex)++;
(*lastIndex) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME;
}
uint32 BattlegroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo) const
{
// team_index: bg alliance - TEAM_ALLIANCE, bg horde - TEAM_HORDE, arena skirmish - TEAM_ALLIANCE, arena rated - TEAM_HORDE
uint8 team_index;
if (!ginfo->ArenaType)
team_index = ginfo->teamId;
else
team_index = (ginfo->IsRated ? TEAM_HORDE : TEAM_ALLIANCE);
// just to be sure
if (team_index >= TEAM_NEUTRAL)
return 0;
// if there are enough values:
if (m_WaitTimes[team_index][ginfo->BracketId][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1])
{
uint32 sum = 0;
for (uint32 i = 0; i < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++i)
sum += m_WaitTimes[team_index][ginfo->BracketId][i];
return sum / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME;
}
else
return 0;
}
//remove player from queue and from group info, if group info is empty then remove it too
void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount)
{
//remove player from map, if he's there
auto const& itr = m_QueuedPlayers.find(guid);
if (itr == m_QueuedPlayers.end())
{
//This happens if a player logs out while in a bg because WorldSession::LogoutPlayer() notifies the bg twice
std::string playerName = "Unknown";
if (Player* player = ObjectAccessor::FindPlayer(guid))
{
playerName = player->GetName();
}
LOG_ERROR("bg.battleground", "BattlegroundQueue: couldn't find player {} ({})", playerName, guid.ToString());
//ABORT("BattlegroundQueue: couldn't find player {} ({})", playerName, guid.ToString());
return;
}
GroupQueueInfo* groupInfo = itr->second;
uint32 _bracketId = groupInfo->BracketId;
uint32 _groupType = groupInfo->GroupType;
// find iterator
auto group_itr = m_QueuedGroups[_bracketId][_groupType].end();
for (auto k = m_QueuedGroups[_bracketId][_groupType].begin(); k != m_QueuedGroups[_bracketId][_groupType].end(); k++)
if ((*k) == groupInfo)
{
group_itr = k;
break;
}
// player can't be in queue without group, but just in case
if (group_itr == m_QueuedGroups[_bracketId][_groupType].end())
{
LOG_ERROR("bg.battleground", "BattlegroundQueue: ERROR Cannot find groupinfo for {}", guid.ToString());
//ABORT("BattlegroundQueue: ERROR Cannot find groupinfo for {}", guid.ToString());
return;
}
LOG_DEBUG("bg.battleground", "BattlegroundQueue: Removing {}, from bracket_id {}", guid.ToString(), _bracketId);
// remove player from group queue info
auto const& pitr = groupInfo->Players.find(guid);
ASSERT(pitr != groupInfo->Players.end());
if (pitr != groupInfo->Players.end())
groupInfo->Players.erase(pitr);
// if invited to bg, and should decrease invited count, then do it
if (decreaseInvitedCount && groupInfo->IsInvitedToBGInstanceGUID)
if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID, groupInfo->BgTypeId))
bg->DecreaseInvitedCount(groupInfo->teamId);
// remove player queue info
m_QueuedPlayers.erase(itr);
// announce to world if arena team left queue for rated match, show only once
SendExitMessageArenaQueue(groupInfo);
// if player leaves queue and he is invited to a rated arena match, then count it as he lost
if (groupInfo->IsInvitedToBGInstanceGUID && groupInfo->IsRated && decreaseInvitedCount)
{
if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(groupInfo->ArenaTeamId))
{
LOG_DEBUG("bg.battleground", "UPDATING memberLost's personal arena rating for {} by opponents rating: {}", guid.ToString(), groupInfo->OpponentsTeamRating);
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
{
at->MemberLost(player, groupInfo->OpponentsMatchmakerRating);
}
at->SaveToDB();
}
}
// remove group queue info no players left
if (groupInfo->Players.empty())
{
m_QueuedGroups[_bracketId][_groupType].erase(group_itr);
delete groupInfo;
return;
}
// group isn't yet empty, so not deleted yet
// if it's a rated arena and any member leaves when group not yet invited - everyone from group leaves too!
if (groupInfo->IsRated && !groupInfo->IsInvitedToBGInstanceGUID)
{
if (Player* plr = ObjectAccessor::FindConnectedPlayer(*(groupInfo->Players.begin())))
{
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(groupInfo->BgTypeId);
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(groupInfo->BgTypeId, groupInfo->ArenaType);
uint32 queueSlot = plr->GetBattlegroundQueueIndex(bgQueueTypeId);
plr->RemoveBattlegroundQueueId(bgQueueTypeId);
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL);
plr->SendDirectMessage(&data);
}
// recursive call
RemovePlayer(*groupInfo->Players.begin(), decreaseInvitedCount);
}
}
void BattlegroundQueue::AddEvent(BasicEvent* Event, uint64 e_time)
{
m_events.AddEvent(Event, m_events.CalculateTime(e_time));
}
bool BattlegroundQueue::IsPlayerInvitedToRatedArena(ObjectGuid pl_guid)
{
auto qItr = m_QueuedPlayers.find(pl_guid);
return qItr != m_QueuedPlayers.end() && qItr->second->IsRated && qItr->second->IsInvitedToBGInstanceGUID;
}
//returns true when player pl_guid is in queue and is invited to bgInstanceGuid
bool BattlegroundQueue::IsPlayerInvited(ObjectGuid pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime)
{
auto qItr = m_QueuedPlayers.find(pl_guid);
return qItr != m_QueuedPlayers.end() && qItr->second->IsInvitedToBGInstanceGUID == bgInstanceGuid && qItr->second->RemoveInviteTime == removeTime;
}
bool BattlegroundQueue::GetPlayerGroupInfoData(ObjectGuid guid, GroupQueueInfo* ginfo)
{
auto qItr = m_QueuedPlayers.find(guid);
if (qItr == m_QueuedPlayers.end())
return false;
*ginfo = *(qItr->second);
return true;
}
// this function is filling pools given free slots on both sides, result is ballanced
void BattlegroundQueue::FillPlayersToBG(Battleground* bg, BattlegroundBracketId bracket_id)
{
if (!sScriptMgr->CanFillPlayersToBG(this, bg, bracket_id))
{
return;
}
int32 hordeFree = bg->GetFreeSlotsForTeam(TEAM_HORDE);
int32 aliFree = bg->GetFreeSlotsForTeam(TEAM_ALLIANCE);
uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size();
uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size();
// try to get even teams
if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_EVEN)
{
// check if the teams are even
if (hordeFree == 1 && aliFree == 1)
{
// if we are here, the teams have the same amount of players
// then we have to allow to join the same amount of players
int32 hordeExtra = hordeCount - aliCount;
int32 aliExtra = aliCount - hordeCount;
hordeExtra = std::max(hordeExtra, 0);
aliExtra = std::max(aliExtra, 0);
if (aliCount != hordeCount)
{
aliFree -= aliExtra;
hordeFree -= hordeExtra;
aliFree = std::max(aliFree, 0);
hordeFree = std::max(hordeFree, 0);
}
}
}
GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin();
//count of groups in queue - used to stop cycles
//index to queue which group is current
uint32 aliIndex = 0;
for (; aliIndex < aliCount && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++)
++Ali_itr;
//the same thing for horde
GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin();
uint32 hordeIndex = 0;
for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++)
++Horde_itr;
//if ofc like BG queue invitation is set in config, then we are happy
if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_NO_BALANCE)
return;
/*
if we reached this code, then we have to solve NP - complete problem called Subset sum problem
So one solution is to check all possible invitation subgroups, or we can use these conditions:
1. Last time when BattlegroundQueue::Update was executed we invited all possible players - so there is only small possibility
that we will invite now whole queue, because only 1 change has been made to queues from the last BattlegroundQueue::Update call
2. Other thing we should consider is group order in queue
*/
// At first we need to compare free space in bg and our selection pool
int32 diffAli = aliFree - int32(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount());
int32 diffHorde = hordeFree - int32(m_SelectionPools[TEAM_HORDE].GetPlayerCount());
while (std::abs(diffAli - diffHorde) > 1 && (m_SelectionPools[TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() > 0))
{
//each cycle execution we need to kick at least 1 group
if (diffAli < diffHorde)
{
//kick alliance group, add to pool new group if needed
if (m_SelectionPools[TEAM_ALLIANCE].KickGroup(diffHorde - diffAli))
{
for (; aliIndex < aliCount && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++)
++Ali_itr;
}
//if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break;
if (!m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount())
{
if (aliFree <= diffHorde + 1)
break;
m_SelectionPools[TEAM_HORDE].KickGroup(diffHorde - diffAli);
}
}
else
{
//kick horde group, add to pool new group if needed
if (m_SelectionPools[TEAM_HORDE].KickGroup(diffAli - diffHorde))
{
for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++)
++Horde_itr;
}
if (!m_SelectionPools[TEAM_HORDE].GetPlayerCount())
{
if (hordeFree <= diffAli + 1)
break;
m_SelectionPools[TEAM_ALLIANCE].KickGroup(diffAli - diffHorde);
}
}
//count diffs after small update
diffAli = aliFree - int32(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount());
diffHorde = hordeFree - int32(m_SelectionPools[TEAM_HORDE].GetPlayerCount());
}
}
// this method checks if premade versus premade battleground is possible
// then after 30 mins (default) in queue it moves premade group to normal queue
bool BattlegroundQueue::CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam)
{
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty())
{
//start premade match
//if groups aren't invited
GroupsQueueType::const_iterator ali_group, horde_group;
for (ali_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group)
if (!(*ali_group)->IsInvitedToBGInstanceGUID)
break;
for (horde_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group)
if (!(*horde_group)->IsInvitedToBGInstanceGUID)
break;
// if found both groups
if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end())
{
m_SelectionPools[TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam);
m_SelectionPools[TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam);
//add groups/players from normal queue to size of bigger group
uint32 maxPlayers = std::min(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[TEAM_HORDE].GetPlayerCount());
GroupsQueueType::const_iterator itr;
for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++)
{
for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr)
{
//if itr can join BG and player count is less that maxPlayers, then add group to selectionpool
if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers))
break;
}
}
//premade selection pools are set
return true;
}
}
// now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!!
// this could be 2 cycles but i'm checking only first team in queue - it can cause problem -
// if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg
// and when they click or after 80 seconds the queue info is removed from queue
uint32 time_before = GameTime::GetGameTimeMS().count() - sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH);
for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++)
{
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty())
{
GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin();
if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam))
{
//we must insert group to normal queue and erase pointer from premade queue
(*itr)->GroupType = BG_QUEUE_NORMAL_ALLIANCE + i; // pussywizard: update GroupQueueInfo internal variable
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr));
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr);
}
}
}
//selection pools are not set
return false;
}
// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam
bool BattlegroundQueue::CheckNormalMatch(Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers)
{
auto CanStartMatch = [this, bgTemplate, minPlayers]()
{
//allow 1v0 if debug bg
if (sBattlegroundMgr->isTesting() && bgTemplate->isBattleground() && (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[TEAM_HORDE].GetPlayerCount()))
return true;
//return true if there are enough players in selection pools - enable to work .debug bg command correctly
return m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers;
};
if (sScriptMgr->IsCheckNormalMatch(this, bgTemplate, bracket_id, minPlayers, maxPlayers))
return CanStartMatch();
GroupsQueueType::const_iterator itr_team[PVP_TEAMS_COUNT];
for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++)
{
itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin();
for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i]))
{
if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID)
{
m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers);
if (m_SelectionPools[i].GetPlayerCount() >= minPlayers)
break;
}
}
}
//try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg
uint32 j = TEAM_ALLIANCE;
if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() < m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount())
j = TEAM_HORDE;
if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != BG_QUEUE_INVITATION_TYPE_NO_BALANCE
&& m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers)
{
//we will try to invite more groups to team with less players indexed by j
++(itr_team[j]); //this will not cause a crash, because for cycle above reached break;
for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j]))
{
if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID)
if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % PVP_TEAMS_COUNT].GetPlayerCount()))
break;
}
// do not allow to start bg with more than 2 players more on 1 faction
if (std::abs((int32)(m_SelectionPools[TEAM_HORDE].GetPlayerCount() - m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount())) > 2)
return false;
}
return CanStartMatch();
}
// this method will check if we can invite players to same faction skirmish match
bool BattlegroundQueue::CheckSkirmishForSameFaction(BattlegroundBracketId bracket_id, uint32 minPlayersPerTeam)
{
if (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam)
return false;
TeamId teamIndex = TEAM_ALLIANCE;
TeamId otherTeam = TEAM_HORDE;
if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam)
{
teamIndex = TEAM_HORDE;
otherTeam = TEAM_ALLIANCE;
}
//clear other team's selection
m_SelectionPools[otherTeam].Init();
//store last ginfo pointer
GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back();
//set itr_team to group that was added to selection pool latest
GroupsQueueType::iterator itr_team = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].begin();
for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].end(); ++itr_team)
if (ginfo == *itr_team)
break;
if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].end())
return false;
GroupsQueueType::iterator itr_team2 = itr_team;
++itr_team2;
//invite players to other selection pool
for (; itr_team2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].end(); ++itr_team2)
{
//if selection pool is full then break;
if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam))
break;
}
if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam)
return false;
//here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue
for (GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr)
{
//set correct team
(*itr)->teamId = otherTeam;
(*itr)->GroupType = static_cast<uint8>(BG_QUEUE_NORMAL_ALLIANCE) + static_cast<uint8>(otherTeam);
//add team to other queue
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(otherTeam)].push_front(*itr);
//remove team from old queue
GroupsQueueType::iterator itr2 = itr_team;
++itr2;
for (; itr2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].end(); ++itr2)
{
if (*itr2 == *itr)
{
m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast<uint8>(teamIndex)].erase(itr2);
break;
}
}
}
return true;
}
void BattlegroundQueue::UpdateEvents(uint32 diff)
{
m_events.Update(diff);
}
void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating)
{
// if no players in queue - do nothing
if (IsAllQueuesEmpty(bracket_id))
return;
auto InviteAllGroupsToBg = [this](Battleground* bg)
{
// invite those selection pools
for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++)
{
for (auto const& citr : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups)
{
InviteGroupToBG(citr, bg, citr->teamId);
}
}
};
// battleground with free slot for player should be always in the beggining of the queue
// maybe it would be better to create bgfreeslotqueue for each bracket_id
BGFreeSlotQueueContainer& bgQueues = sBattlegroundMgr->GetBGFreeSlotQueueStore(bgTypeId);
for (BGFreeSlotQueueContainer::iterator itr = bgQueues.begin(); itr != bgQueues.end();)
{
Battleground* bg = *itr; ++itr;
// DO NOT allow queue manager to invite new player to rated games
if (!bg->isRated() && bg->GetBgTypeID() == bgTypeId && bg->GetBracketId() == bracket_id &&
bg->GetStatus() > STATUS_WAIT_QUEUE && bg->GetStatus() < STATUS_WAIT_LEAVE)
{
// clear selection pools
m_SelectionPools[TEAM_ALLIANCE].Init();
m_SelectionPools[TEAM_HORDE].Init();
// call a function that does the job for us
FillPlayersToBG(bg, bracket_id);
// now everything is set, invite players
InviteAllGroupsToBg(bg);
if (!bg->HasFreeSlots())
bg->RemoveFromBGFreeSlotQueue();
}
}
Battleground* bg_template = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
if (!bg_template)
{
LOG_ERROR("bg.battleground", "Battleground: Update: bg template not found for {}", bgTypeId);
return;
}
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(), bracket_id);
if (!bracketEntry)
{
LOG_ERROR("bg.battleground", "Battleground: Update: bg bracket entry not found for map {} bracket id {}", bg_template->GetMapId(), bracket_id);
return;
}
// get min and max players per team
uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam();
uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam();
if (bg_template->isArena())
{
MinPlayersPerTeam = sBattlegroundMgr->isArenaTesting() ? 1 : arenaType;
MaxPlayersPerTeam = arenaType;
}
else if (sBattlegroundMgr->isTesting())
MinPlayersPerTeam = 1;
sScriptMgr->OnQueueUpdate(this, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating);
m_SelectionPools[TEAM_ALLIANCE].Init();
m_SelectionPools[TEAM_HORDE].Init();
// check if can start new premade battleground
if (bg_template->isBattleground() && bgTypeId != BATTLEGROUND_RB && CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam))
{
// create new battleground
Battleground* bg = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, 0, false);
if (!bg)
{
LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", bgTypeId);
return;
}
// invite those selection pools
InviteAllGroupsToBg(bg);
bg->StartBattleground();
// clear structures
m_SelectionPools[TEAM_ALLIANCE].Init();
m_SelectionPools[TEAM_HORDE].Init();
}
// check if can start new normal battleground or non-rated arena
if (!isRated)
{
if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) ||
(bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam)))
{
// create new battleground
Battleground* bg = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, arenaType, false);
if (!bg)
{
LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", bgTypeId);
return;
}
// invite players
InviteAllGroupsToBg(bg);
bg->StartBattleground();
}
}
// check if can start new rated arenas (can create many in single queue update)
else if (bg_template->isArena())
{
// found out the minimum and maximum ratings the newly added team should battle against
// arenaRating is the rating of the latest joined team, or 0
// 0 is on (automatic update call) and we must set it to team's with longest wait time
if (!arenaRating)
{
GroupQueueInfo* front1 = nullptr;
GroupQueueInfo* front2 = nullptr;
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty())
{
front1 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].front();
arenaRating = front1->ArenaMatchmakerRating;
}
if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty())
{
front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front();
arenaRating = front2->ArenaMatchmakerRating;
}
if (front1 && front2)
{
if (front1->JoinTime < front2->JoinTime)
arenaRating = front1->ArenaMatchmakerRating;
}
else if (!front1 && !front2)
return; // queues are empty
}
//set rating range
uint32 arenaMinRating = (arenaRating <= sBattlegroundMgr->GetMaxRatingDifference()) ? 0 : arenaRating - sBattlegroundMgr->GetMaxRatingDifference();
uint32 arenaMaxRating = arenaRating + sBattlegroundMgr->GetMaxRatingDifference();
// if max rating difference is set and the time past since server startup is greater than the rating discard time
// (after what time the ratings aren't taken into account when making teams) then
// the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
// else leave the discard time on 0, this way all ratings will be discarded
// this has to be signed value - when the server starts, this value would be negative and thus overflow
int32 discardTime = GameTime::GetGameTimeMS().count() - sBattlegroundMgr->GetRatingDiscardTimer();
// timer for previous opponents
int32 discardOpponentsTime = GameTime::GetGameTimeMS().count() - sWorld->getIntConfig(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER);
// we need to find 2 teams which will play next game
GroupsQueueType::iterator itr_teams[PVP_TEAMS_COUNT];
uint8 found = 0;
uint8 team = 0;
for (uint8 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++)
{
// take the group that joined first
GroupsQueueType::iterator itr2 = m_QueuedGroups[bracket_id][i].begin();
for (; itr2 != m_QueuedGroups[bracket_id][i].end(); ++itr2)
{
// if group match conditions, then add it to pool
if (!(*itr2)->IsInvitedToBGInstanceGUID
&& (((*itr2)->ArenaMatchmakerRating >= arenaMinRating && (*itr2)->ArenaMatchmakerRating <= arenaMaxRating)
|| (int32)(*itr2)->JoinTime < discardTime))
{
itr_teams[found++] = itr2;
team = i;
break;
}
}
}
if (!found)
return;
if (found == 1)
{
for (GroupsQueueType::iterator itr3 = itr_teams[0]; itr3 != m_QueuedGroups[bracket_id][team].end(); ++itr3)
{
if (!(*itr3)->IsInvitedToBGInstanceGUID
&& (((*itr3)->ArenaMatchmakerRating >= arenaMinRating && (*itr3)->ArenaMatchmakerRating <= arenaMaxRating) || (int32)(*itr3)->JoinTime < discardTime)
&& ((*(itr_teams[0]))->ArenaTeamId != (*itr3)->PreviousOpponentsTeamId || ((int32)(*itr3)->JoinTime < discardOpponentsTime))
&& (*(itr_teams[0]))->ArenaTeamId != (*itr3)->ArenaTeamId)
{
itr_teams[found++] = itr3;
break;
}
}
}
//if we have 2 teams, then start new arena and invite players!
if (found == 2)
{
GroupQueueInfo* aTeam = *(itr_teams[TEAM_ALLIANCE]);
GroupQueueInfo* hTeam = *(itr_teams[TEAM_HORDE]);
Battleground* arena = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, arenaType, true);
if (!arena)
{
LOG_ERROR("bg.battleground", "BattlegroundQueue::Update couldn't create arena instance for rated arena match!");
return;
}
aTeam->OpponentsTeamRating = hTeam->ArenaTeamRating;
hTeam->OpponentsTeamRating = aTeam->ArenaTeamRating;
aTeam->OpponentsMatchmakerRating = hTeam->ArenaMatchmakerRating;
hTeam->OpponentsMatchmakerRating = aTeam->ArenaMatchmakerRating;
LOG_DEBUG("bg.battleground", "setting oposite teamrating for team {} to {}", aTeam->ArenaTeamId, aTeam->OpponentsTeamRating);
LOG_DEBUG("bg.battleground", "setting oposite teamrating for team {} to {}", hTeam->ArenaTeamId, hTeam->OpponentsTeamRating);
// now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer
if (aTeam->teamId != TEAM_ALLIANCE)
{
aTeam->GroupType = BG_QUEUE_PREMADE_ALLIANCE;
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(aTeam);
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_teams[TEAM_ALLIANCE]);
}
if (hTeam->teamId != TEAM_HORDE)
{
hTeam->GroupType = BG_QUEUE_PREMADE_HORDE;
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(hTeam);
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_teams[TEAM_HORDE]);
}
arena->SetArenaMatchmakerRating(TEAM_ALLIANCE, aTeam->ArenaMatchmakerRating);
arena->SetArenaMatchmakerRating(TEAM_HORDE, hTeam->ArenaMatchmakerRating);
InviteGroupToBG(aTeam, arena, TEAM_ALLIANCE);
InviteGroupToBG(hTeam, arena, TEAM_HORDE);
LOG_DEBUG("bg.battleground", "Starting rated arena match!");
arena->StartBattleground();
}
}
}
void BattlegroundQueue::BattlegroundQueueAnnouncerUpdate(uint32 diff, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundBracketId bracket_id)
{
BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(bgQueueTypeId);
Battleground* bg_template = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
if (!bg_template)
{
return;
}
PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(), bracket_id);
if (!bracketEntry)
{
return;
}
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED))
{
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<int32>(diff))
{
_queueAnnouncementTimer[bracket_id] = -1;
auto bgName = bg_template->GetName();
uint32 MaxPlayers = bg_template->GetMinPlayersPerTeam() * 2;
uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80);
uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80);
sWorld->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<int32>(diff);
}
}
}
}
uint32 BattlegroundQueue::GetPlayersCountInGroupsQueue(BattlegroundBracketId bracketId, BattlegroundQueueGroupTypes bgqueue)
{
uint32 playersCount = 0;
for (auto const& itr : m_QueuedGroups[bracketId][bgqueue])
if (!itr->IsInvitedToBGInstanceGUID)
playersCount += static_cast<uint32>(itr->Players.size());
return playersCount;
}
bool BattlegroundQueue::IsAllQueuesEmpty(BattlegroundBracketId bracket_id)
{
uint8 queueEmptyCount = 0;
for (uint8 i = 0; i < BG_QUEUE_MAX; i++)
if (m_QueuedGroups[bracket_id][i].empty())
queueEmptyCount++;
return queueEmptyCount == BG_QUEUE_MAX;
}
void BattlegroundQueue::SendMessageBGQueue(Player* leader, Battleground* bg, PvPDifficultyEntry const* bracketEntry)
{
if (!sScriptMgr->CanSendMessageBGQueue(this, leader, bg, bracketEntry))
{
return;
}
if (bg->isArena())
{
// Skip announce for arena skirmish
return;
}
BattlegroundBracketId bracketId = bracketEntry->GetBracketId();
auto bgName = bg->GetName();
uint32 MinPlayers = bg->GetMinPlayersPerTeam();
uint32 MaxPlayers = MinPlayers * 2;
uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32)80);
uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32)80);
uint32 qHorde = GetPlayersCountInGroupsQueue(bracketId, BG_QUEUE_NORMAL_HORDE);
uint32 qAlliance = GetPlayersCountInGroupsQueue(bracketId, BG_QUEUE_NORMAL_ALLIANCE);
auto qTotal = qHorde + qAlliance;
LOG_DEBUG("bg.battleground", "> Queue status for {} (Lvl: {} to {}) Queued: {} (Need at least {} more)",
bgName, q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers - qTotal);
// Show queue status to player only (when joining battleground queue or Arena and arena world announcer is disabled)
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY))
{
ChatHandler(leader->GetSession()).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName.c_str(), q_min_level, q_max_level,
qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0,
qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0);
}
else // Show queue status to server (when joining battleground queue)
{
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED))
{
if (_queueAnnouncementTimer[bracketId] < 0)
{
_queueAnnouncementTimer[bracketId] = sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER);
}
}
else
{
if (!sBGSpam->CanAnnounce(leader, bg, q_min_level, qTotal))
{
return;
}
sWorld->SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers);
}
}
}
void BattlegroundQueue::SendJoinMessageArenaQueue(Player* leader, GroupQueueInfo* ginfo, PvPDifficultyEntry const* bracketEntry, bool isRated)
{
if (!sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
return;
if (!sScriptMgr->OnBeforeSendJoinMessageArenaQueue(this, leader, ginfo, bracketEntry, isRated))
return;
if (!isRated)
{
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(ginfo->BgTypeId);
if (!bg)
{
LOG_ERROR("bg.arena", "> Not found bg template for bgtype id {}", uint32(ginfo->BgTypeId));
return;
}
if (!bg->isArena())
{
// Skip announce for non arena
return;
}
BattlegroundBracketId bracketId = bracketEntry->GetBracketId();
auto bgName = bg->GetName();
auto arenatype = Acore::StringFormat("%uv%u", ginfo->ArenaType, ginfo->ArenaType);
uint32 playersNeed = ArenaTeam::GetReqPlayersForType(ginfo->ArenaType);
uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32)80);
uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32)80);
uint32 qPlayers = GetPlayersCountInGroupsQueue(bracketId, BG_QUEUE_NORMAL_HORDE) + GetPlayersCountInGroupsQueue(bracketId, BG_QUEUE_NORMAL_ALLIANCE);
LOG_DEBUG("bg.arena", "> Queue status for {} (skirmish {}) (Lvl: {} to {}) Queued: {} (Need at least {} more)",
bgName, arenatype, q_min_level, q_max_level, qPlayers, playersNeed - qPlayers);
if (sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY))
{
ChatHandler(leader->GetSession()).PSendSysMessage(LANG_ARENA_QUEUE_ANNOUNCE_SELF,
bgName, arenatype.c_str(), q_min_level, q_max_level, qPlayers, playersNeed - qPlayers);
}
else
{
if (!sBGSpam->CanAnnounce(leader, bg, q_min_level, qPlayers))
{
return;
}
sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, bgName.c_str(), arenatype.c_str(), q_min_level, q_max_level, qPlayers, playersNeed);
}
}
else
{
ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(ginfo->ArenaTeamId);
if (!team || !ginfo->IsRated)
{
return;
}
uint8 ArenaType = ginfo->ArenaType;
uint32 ArenaTeamRating = ginfo->ArenaTeamRating;
std::string TeamName = team->GetName();
sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating);
}
}
void BattlegroundQueue::SendExitMessageArenaQueue(GroupQueueInfo* ginfo)
{
if (!sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
return;
if (!sScriptMgr->OnBeforeSendExitMessageArenaQueue(this, ginfo))
return;
ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(ginfo->ArenaTeamId);
if (!team)
return;
if (!ginfo->IsRated)
return;
uint8 ArenaType = ginfo->ArenaType;
uint32 ArenaTeamRating = ginfo->ArenaTeamRating;
std::string TeamName = team->GetName();
if (ArenaType && ginfo->Players.empty())
{
sWorld->SendWorldTextOptional(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, ANNOUNCER_FLAG_DISABLE_ARENA_QUEUE, TeamName.c_str(), ArenaType, ArenaType, ArenaTeamRating);
}
}
void BattlegroundQueue::SetQueueAnnouncementTimer(uint32 bracketId, int32 timer, bool isCrossFactionBG /*= true*/)
{
_queueAnnouncementTimer[bracketId] = timer;
_queueAnnouncementCrossfactioned = isCrossFactionBG;
}
int32 BattlegroundQueue::GetQueueAnnouncementTimer(uint32 bracketId) const
{
return _queueAnnouncementTimer[bracketId];
}
void BattlegroundQueue::InviteGroupToBG(GroupQueueInfo* ginfo, Battleground* bg, TeamId teamId)
{
// set side if needed
if (teamId != TEAM_NEUTRAL)
ginfo->teamId = teamId;
if (ginfo->IsInvitedToBGInstanceGUID)
return;
// set invitation
ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID();
BattlegroundTypeId bgTypeId = bg->GetBgTypeID();
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(ginfo->BgTypeId, ginfo->ArenaType);
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
// set ArenaTeamId for rated matches
if (bg->isArena() && bg->isRated())
bg->SetArenaTeamIdForTeam(ginfo->teamId, ginfo->ArenaTeamId);
ginfo->RemoveInviteTime = GameTime::GetGameTimeMS().count() + INVITE_ACCEPT_WAIT_TIME;
// loop through the players
for (auto const& itr : ginfo->Players)
{
// get the player
Player* player = ObjectAccessor::FindConnectedPlayer(itr);
if (!player)
continue;
// update average wait time
bgQueue.PlayerInvitedToBGUpdateAverageWaitTime(ginfo);
// increase invited counter for each invited player
bg->IncreaseInvitedCount(ginfo->teamId);
player->SetInviteForBattlegroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID);
// create remind invite events
BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime);
bgQueue.AddEvent(inviteEvent, INVITATION_REMIND_TIME);
// create automatic remove events
BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime);
bgQueue.AddEvent(removeEvent, INVITE_ACCEPT_WAIT_TIME);
// Check queueSlot
uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId);
ASSERT(queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES);
LOG_DEBUG("bg.battleground", "Battleground: invited player {} {} to BG instance {} queueindex {} bgtype {}",
player->GetName(), player->GetGUID().ToString(), bg->GetInstanceID(), queueSlot, bgTypeId);
// send status packet
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType, TEAM_NEUTRAL, bg->isRated());
player->GetSession()->SendPacket(&data);
// pussywizard:
if (bg->isArena() && bg->isRated())
bg->ArenaLogEntries[player->GetGUID()].Fill(player->GetName(), player->GetGUID().GetCounter(), player->GetSession()->GetAccountId(), ginfo->ArenaTeamId, player->GetSession()->GetRemoteAddress());
}
}
/*********************************************************/
/*** BATTLEGROUND QUEUE EVENTS ***/
/*********************************************************/
bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
Player* player = ObjectAccessor::FindConnectedPlayer(m_PlayerGuid);
// player logged off, so he is no longer in queue
if (!player)
return true;
Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, m_BgTypeId);
// if battleground ended, do nothing
if (!bg)
return true;
// check if still in queue for this battleground
BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(m_BgTypeId, m_ArenaType);
uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId);
if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES)
{
// confirm the player is invited to this instance id (he didn't requeue in the meanwhile)
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime))
{
// send remaining time in queue
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType, TEAM_NEUTRAL, bg->isRated(), m_BgTypeId);
player->GetSession()->SendPacket(&data);
}
}
return true;
}
void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}
bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
Player* player = ObjectAccessor::FindConnectedPlayer(m_PlayerGuid);
// player logged off, so he is no longer in queue
if (!player)
return true;
Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, m_BgTypeId);
// battleground can be already deleted, bg may be nullptr!
// check if still in queue for this battleground
uint32 queueSlot = player->GetBattlegroundQueueIndex(m_BgQueueTypeId);
if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue
{
// confirm the player is invited to this instance id (he didn't requeue in the meanwhile)
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(m_BgQueueTypeId);
if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime))
{
// track if player leaves the BG by not clicking enter button
if (bg && bg->isBattleground() && (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN))
{
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS))
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
stmt->SetData(0, player->GetGUID().GetCounter());
stmt->SetData(1, BG_DESERTION_TYPE_NO_ENTER_BUTTON);
CharacterDatabase.Execute(stmt);
}
sScriptMgr->OnBattlegroundDesertion(player, BG_DESERTION_TYPE_NO_ENTER_BUTTON);
}
LOG_DEBUG("bg.battleground", "Battleground: removing player {} from bg queue for instance {} because of not pressing enter battle in time.", player->GetGUID().ToString(), m_BgInstanceGUID);
player->RemoveBattlegroundQueueId(m_BgQueueTypeId);
bgQueue.RemovePlayer(m_PlayerGuid, true);
//update queues if battleground isn't ended
if (bg && bg->isBattleground() && bg->GetStatus() != STATUS_WAIT_LEAVE)
sBattlegroundMgr->ScheduleQueueUpdate(0, 0, m_BgQueueTypeId, m_BgTypeId, bg->GetBracketId());
WorldPacket data;
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL);
player->SendDirectMessage(&data);
}
}
return true;
}
void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}