/* * This file is part of the TrinityCore 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 General Public License as published by the * Free Software Foundation; either version 2 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 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 . */ #include "BattlegroundQueue.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "BattlegroundMgr.h" #include "BattlegroundPackets.h" #include "Chat.h" #include "DB2Stores.h" #include "GameTime.h" #include "Group.h" #include "Language.h" #include "Log.h" #include "ObjectAccessor.h" #include "Player.h" #include "World.h" /*********************************************************/ /*** BATTLEGROUND QUEUE SYSTEM ***/ /*********************************************************/ BattlegroundQueue::BattlegroundQueue(BattlegroundQueueTypeId queueId) : m_queueId(queueId) { for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i) { for (uint32 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j) { m_SumOfWaitTimes[i][j] = 0; m_WaitTimeLastPlayer[i][j] = 0; for (uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++k) m_WaitTimes[i][j][k] = 0; } } } BattlegroundQueue::~BattlegroundQueue() { m_events.KillAllEvents(false); for (int i = 0; i < MAX_BATTLEGROUND_BRACKETS; ++i) { for (uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; ++j) { for (GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr) delete (*itr); } } } /*********************************************************/ /*** BATTLEGROUND QUEUE SELECTION POOLS ***/ /*********************************************************/ // selection pool initialization, used to clean up from prev selection void BattlegroundQueue::SelectionPool::Init() { SelectedGroups.clear(); PlayerCount = 0; } // remove group info from selection pool // returns true when we need to try to add new group to selection pool // returns false when selection pool is ok or when we kicked smaller group than we need to kick // sometimes it can be called on empty selection pool bool BattlegroundQueue::SelectionPool::KickGroup(uint32 size) { //find maxgroup or LAST group with size == size and kick it bool found = false; GroupsQueueType::iterator groupToKick = SelectedGroups.begin(); for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr) { if (abs((int32)((*itr)->Players.size() - size)) <= 1) { groupToKick = itr; found = true; } else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size()) groupToKick = itr; } //if pool is empty, do nothing if (GetPlayerCount()) { //update player count GroupQueueInfo* ginfo = (*groupToKick); SelectedGroups.erase(groupToKick); PlayerCount -= ginfo->Players.size(); //return false if we kicked smaller group or there are enough players in selection pool if (ginfo->Players.size() <= size + 1) return false; } return true; } // add group to selection pool // used when building selection pools // returns true if we can invite more players, or when we added group to selection pool // returns false when selection pool is full bool BattlegroundQueue::SelectionPool::AddGroup(GroupQueueInfo* ginfo, uint32 desiredCount) { //if group is larger than desired count - don't allow to add it to pool if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) { SelectedGroups.push_back(ginfo); // increase selected players count PlayerCount += ginfo->Players.size(); return true; } if (PlayerCount < desiredCount) return true; return false; } /*********************************************************/ /*** BATTLEGROUND QUEUES ***/ /*********************************************************/ // add group or player (grp == NULL) to bg queue with the given leader and bg specifications GroupQueueInfo* BattlegroundQueue::AddGroup(Player const* leader, Group const* group, Team team, PVPDifficultyEntry const* bracketEntry, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating) { BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); // create new ginfo GroupQueueInfo* ginfo = new GroupQueueInfo; ginfo->IsInvitedToBGInstanceGUID = 0; ginfo->JoinTime = GameTime::GetGameTimeMS(); ginfo->RemoveInviteTime = 0; ginfo->Team = team; ginfo->ArenaTeamRating = ArenaRating; ginfo->ArenaMatchmakerRating = MatchmakerRating; 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 (!m_queueId.Rated && !isPremade) index += PVP_TEAMS_COUNT; if (ginfo->Team == HORDE) index++; TC_LOG_DEBUG("bg.battleground", "Adding Group to BattlegroundQueue bgTypeId : {}, bracket_id : {}, index : {}", m_queueId.BattlemasterListId, bracketId, index); uint32 lastOnlineTime = GameTime::GetGameTimeMS(); //announce world (this don't need mutex) if (m_queueId.Rated && sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) { ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(0); if (team) sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, team->GetName().c_str(), m_queueId.TeamSize, m_queueId.TeamSize, ginfo->ArenaTeamRating); } //add players from group to ginfo if (group) { for (GroupReference const& itr : group->GetMembers()) { Player* member = itr.GetSource(); PlayerQueueInfo& pl_info = m_QueuedPlayers[member->GetGUID()]; pl_info.LastOnlineTime = lastOnlineTime; pl_info.GroupInfo = ginfo; // add the pinfo to ginfo's list ginfo->Players[member->GetGUID()] = &pl_info; } } else { PlayerQueueInfo& pl_info = m_QueuedPlayers[leader->GetGUID()]; pl_info.LastOnlineTime = lastOnlineTime; pl_info.GroupInfo = ginfo; ginfo->Players[leader->GetGUID()] = &pl_info; } //add GroupInfo to m_QueuedGroups { m_QueuedGroups[bracketId][index].push_back(ginfo); //announce to world, this code needs mutex if (!m_queueId.Rated && !isPremade && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) { if (BattlegroundTemplate const* bg = sBattlegroundMgr->GetBattlegroundTemplateByTypeId(BattlegroundTypeId(m_queueId.BattlemasterListId))) { uint32 MinPlayers = bg->GetMinPlayersPerTeam(); uint32 qHorde = 0; uint32 qAlliance = 0; uint32 q_min_level = bracketEntry->MinLevel; uint32 q_max_level = bracketEntry->MaxLevel; GroupsQueueType::const_iterator itr; for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr) if (!(*itr)->IsInvitedToBGInstanceGUID) qAlliance += (*itr)->Players.size(); for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].end(); ++itr) if (!(*itr)->IsInvitedToBGInstanceGUID) qHorde += (*itr)->Players.size(); // Show queue status to player only (when joining queue) if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) { ChatHandler chatHandler(leader->GetSession()); chatHandler.PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bg->BattlemasterEntry->Name[chatHandler.GetSessionDbcLocale()], q_min_level, q_max_level, qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); } // System message else { sWorld->SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bg->BattlemasterEntry->Name[sWorld->GetDefaultDbcLocale()], q_min_level, q_max_level, qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); } } } } return ginfo; } void BattlegroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattlegroundBracketId bracket_id) { uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, GameTime::GetGameTimeMS()); uint8 team_index = TEAM_ALLIANCE; //default set to TEAM_ALLIANCE - or non rated arenas! if (!m_queueId.TeamSize) { if (ginfo->Team == HORDE) team_index = TEAM_HORDE; } else { if (m_queueId.Rated) team_index = TEAM_HORDE; //for rated arenas use TEAM_HORDE } //store pointer to arrayindex of player that was added first uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][bracket_id]); //remove his time from sum m_SumOfWaitTimes[team_index][bracket_id] -= m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)]; //set average time to new m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)] = timeInQueue; //add new time to sum m_SumOfWaitTimes[team_index][bracket_id] += timeInQueue; //set index of last player added to next one (*lastPlayerAddedPointer)++; (*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; } uint32 BattlegroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattlegroundBracketId bracket_id) const { uint8 team_index = TEAM_ALLIANCE; //default set to TEAM_ALLIANCE - or non rated arenas! if (!m_queueId.TeamSize) { if (ginfo->Team == HORDE) team_index = TEAM_HORDE; } else { if (m_queueId.Rated) team_index = TEAM_HORDE; //for rated arenas use TEAM_HORDE } //check if there is enought values(we always add values > 0) if (m_WaitTimes[team_index][bracket_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) return (m_SumOfWaitTimes[team_index][bracket_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME); else //if there aren't enough values return 0 - not available 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) { int32 bracket_id = -1; // signed for proper for-loop finish QueuedPlayersMap::iterator itr; //remove player from map, if he's there 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(); TC_LOG_DEBUG("bg.battleground", "BattlegroundQueue: couldn't find player {} ({})", playerName, guid.ToString()); return; } GroupQueueInfo* group = itr->second.GroupInfo; GroupsQueueType::iterator group_itr; // mostly people with the highest levels are in battlegrounds, thats why // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 uint32 index = (group->Team == HORDE) ? BG_QUEUE_PREMADE_HORDE : BG_QUEUE_PREMADE_ALLIANCE; for (int32 bracket_id_tmp = MAX_BATTLEGROUND_BRACKETS - 1; bracket_id_tmp >= 0 && bracket_id == -1; --bracket_id_tmp) { //we must check premade and normal team's queue - because when players from premade are joining bg, //they leave groupinfo so we can't use its players size to find out index for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += PVP_TEAMS_COUNT) { GroupsQueueType::iterator k = m_QueuedGroups[bracket_id_tmp][j].begin(); for (; k != m_QueuedGroups[bracket_id_tmp][j].end(); ++k) { if ((*k) == group) { bracket_id = bracket_id_tmp; group_itr = k; //we must store index to be able to erase iterator index = j; break; } } } } //player can't be in queue without group, but just in case if (bracket_id == -1) { TC_LOG_ERROR("bg.battleground", "BattlegroundQueue: ERROR Cannot find groupinfo for {}", guid.ToString()); return; } TC_LOG_DEBUG("bg.battleground", "BattlegroundQueue: Removing {}, from bracket_id {}", guid.ToString(), (uint32)bracket_id); // ALL variables are correctly set // We can ignore leveling up in queue - it should not cause crash // remove player from group // if only one player there, remove group // remove player queue info from group queue info std::map::iterator pitr = group->Players.find(guid); if (pitr != group->Players.end()) group->Players.erase(pitr); // if invited to bg, and should decrease invited count, then do it if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID) if (Battleground* bg = sBattlegroundMgr->GetBattleground(group->IsInvitedToBGInstanceGUID, BattlegroundTypeId(m_queueId.BattlemasterListId))) bg->DecreaseInvitedCount(group->Team); // remove player queue info m_QueuedPlayers.erase(itr); // announce to world if arena team left queue for rated match, show only once if (m_queueId.TeamSize && m_queueId.Rated && group->Players.empty() && sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) if (ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(0)) sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, team->GetName().c_str(), m_queueId.TeamSize, m_queueId.TeamSize, group->ArenaTeamRating); // if player leaves queue and he is invited to rated arena match, then he have to lose if (group->IsInvitedToBGInstanceGUID && m_queueId.Rated && decreaseInvitedCount) { if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(0)) { TC_LOG_DEBUG("bg.battleground", "UPDATING memberLost's personal arena rating for {} by opponents rating: {}", guid.ToString(), group->OpponentsTeamRating); if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) at->MemberLost(player, group->OpponentsMatchmakerRating); else at->OfflineMemberLost(guid, group->OpponentsMatchmakerRating); at->SaveToDB(); } } // remove group queue info if needed if (group->Players.empty()) { m_QueuedGroups[bracket_id][index].erase(group_itr); delete group; return; } // if group wasn't empty, so it wasn't deleted, and player have left a rated // queue -> everyone from the group should leave too // don't remove recursively if already invited to bg! if (!group->IsInvitedToBGInstanceGUID && m_queueId.Rated) { // remove next player, this is recursive // first send removal information if (Player* plr2 = ObjectAccessor::FindConnectedPlayer(group->Players.begin()->first)) { uint32 queueSlot = plr2->GetBattlegroundQueueIndex(m_queueId); plr2->RemoveBattlegroundQueueId(m_queueId); // must be called this way, because if you move this call to // queue->removeplayer, it causes bugs WorldPackets::Battleground::BattlefieldStatusNone battlefieldStatus; BattlegroundMgr::BuildBattlegroundStatusNone(&battlefieldStatus, plr2, queueSlot, plr2->GetBattlegroundQueueJoinTime(m_queueId)); plr2->SendDirectMessage(battlefieldStatus.Write()); } // then actually delete, this may delete the group as well! RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); } } //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) { QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(pl_guid); return (qItr != m_QueuedPlayers.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == bgInstanceGuid && qItr->second.GroupInfo->RemoveInviteTime == removeTime); } bool BattlegroundQueue::GetPlayerGroupInfoData(ObjectGuid guid, GroupQueueInfo* ginfo) { QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid); if (qItr == m_QueuedPlayers.end()) return false; *ginfo = *(qItr->second.GroupInfo); return true; } uint32 BattlegroundQueue::GetPlayersInQueue(TeamId id) { return m_SelectionPools[id].GetPlayerCount(); } bool BattlegroundQueue::InviteGroupToBG(GroupQueueInfo* ginfo, Battleground* bg, Team side) { // set side if needed if (side) ginfo->Team = side; if (!ginfo->IsInvitedToBGInstanceGUID) { // not yet invited // set invitation ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); BattlegroundTypeId bgTypeId = BattlegroundTypeId(m_queueId.BattlemasterListId); BattlegroundQueueTypeId bgQueueTypeId = m_queueId; BattlegroundBracketId bracket_id = bg->GetBracketId(); ginfo->RemoveInviteTime = GameTime::GetGameTimeMS() + INVITE_ACCEPT_WAIT_TIME; // loop through the players for (std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) { // get the player Player* player = ObjectAccessor::FindConnectedPlayer(itr->first); // if offline, skip him, this should not happen - player is removed from queue when he logs out if (!player) continue; // invite the player PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id); //sBattlegroundMgr->InvitePlayer(player, bg, ginfo->Team); // set invited player counters bg->IncreaseInvitedCount(ginfo->Team); player->SetInviteForBattlegroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); // create remind invite events BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->RemoveInviteTime, m_queueId); m_events.AddEvent(inviteEvent, m_events.CalculateTime(Milliseconds(INVITATION_REMIND_TIME))); // create automatic remove events BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgQueueTypeId, ginfo->RemoveInviteTime); m_events.AddEvent(removeEvent, m_events.CalculateTime(Milliseconds(INVITE_ACCEPT_WAIT_TIME))); uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId); TC_LOG_DEBUG("bg.battleground", "Battleground: invited player {} {} to BG instance {} queueindex {} bgtype {}", player->GetName(), player->GetGUID().ToString(), bg->GetInstanceID(), queueSlot, m_queueId.BattlemasterListId); WorldPackets::Battleground::BattlefieldStatusNeedConfirmation battlefieldStatus; BattlegroundMgr::BuildBattlegroundStatusNeedConfirmation(&battlefieldStatus, bg, player, queueSlot, player->GetBattlegroundQueueJoinTime(bgQueueTypeId), INVITE_ACCEPT_WAIT_TIME, bgQueueTypeId); player->SendDirectMessage(battlefieldStatus.Write()); } return true; } return false; } /* This function is inviting players to already running battlegrounds Invitation type is based on config file large groups are disadvantageous, because they will be kicked first if invitation type = 1 */ void BattlegroundQueue::FillPlayersToBG(Battleground* bg, BattlegroundBracketId bracket_id) { int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); int32 aliFree = bg->GetFreeSlotsForTeam(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); } } } //iterator for iterating through bg queue 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 (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 // it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players bool BattlegroundQueue::CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) { //check match 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 (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() - 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 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(BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) { 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 (abs((int32)(m_SelectionPools[TEAM_HORDE].GetPlayerCount() - m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount())) > 2) return false; } //allow 1v0 if debug bg if (sBattlegroundMgr->isTesting() && (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; } // 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; Team otherTeamId = HORDE; if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) { teamIndex = TEAM_HORDE; otherTeam = TEAM_ALLIANCE; otherTeamId = 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][uint8(BG_QUEUE_NORMAL_ALLIANCE) + uint8(teamIndex)].begin(); for (; itr_team != m_QueuedGroups[bracket_id][uint8(BG_QUEUE_NORMAL_ALLIANCE) + uint8(teamIndex)].end(); ++itr_team) if (ginfo == *itr_team) break; if (itr_team == m_QueuedGroups[bracket_id][uint8(BG_QUEUE_NORMAL_ALLIANCE) + 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][uint8(BG_QUEUE_NORMAL_ALLIANCE) + 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)->Team = otherTeamId; //add team to other queue m_QueuedGroups[bracket_id][uint8(BG_QUEUE_NORMAL_ALLIANCE) + uint8(otherTeam)].push_front(*itr); //remove team from old queue GroupsQueueType::iterator itr2 = itr_team; ++itr2; for (; itr2 != m_QueuedGroups[bracket_id][uint8(BG_QUEUE_NORMAL_ALLIANCE) + uint8(teamIndex)].end(); ++itr2) { if (*itr2 == *itr) { m_QueuedGroups[bracket_id][uint8(BG_QUEUE_NORMAL_ALLIANCE) + uint8(teamIndex)].erase(itr2); break; } } } return true; } void BattlegroundQueue::UpdateEvents(uint32 diff) { m_events.Update(diff); } /* this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while (true) cycles to invite whole queue it must be called after fully adding the members of a group to ensure group joining should be called from Battleground::RemovePlayer function in some cases */ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundBracketId bracket_id, uint32 arenaRating) { //if no players in queue - do nothing if (m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty() && m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].empty() && m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].empty()) return; // 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 BattlegroundTemplate const* bg_template = sBattlegroundMgr->GetBattlegroundTemplateByTypeId(BattlegroundTypeId(m_queueId.BattlemasterListId)); if (!bg_template) { TC_LOG_ERROR("bg.battleground", "Battleground: Update: bg template not found for {}", m_queueId.BattlemasterListId); return; } // loop over queues for every map for (int32 mapId : bg_template->MapIDs) { BGFreeSlotQueueContainer& bgQueues = sBattlegroundMgr->GetBGFreeSlotQueueStore(mapId); 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->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 for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_ALLIANCE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->Team); for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_HORDE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->Team); if (!bg->HasFreeSlots()) bg->RemoveFromBGFreeSlotQueue(); } } } // finished iterating through the bgs with free slots, maybe we need to create a new bg // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); if (bg_template->IsArena()) { MaxPlayersPerTeam = m_queueId.TeamSize; MinPlayersPerTeam = sBattlegroundMgr->isArenaTesting() ? 1 : m_queueId.TeamSize; } else if (sBattlegroundMgr->isTesting()) MinPlayersPerTeam = 1; m_SelectionPools[TEAM_ALLIANCE].Init(); m_SelectionPools[TEAM_HORDE].Init(); if (!bg_template->IsArena()) { if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) { // create new battleground Battleground* bg2 = sBattlegroundMgr->CreateNewBattleground(m_queueId, bracket_id); if (!bg2) { TC_LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", m_queueId.BattlemasterListId); return; } // invite those selection pools for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->Team); bg2->StartBattleground(); //clear structures m_SelectionPools[TEAM_ALLIANCE].Init(); m_SelectionPools[TEAM_HORDE].Init(); } } // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) if (!m_queueId.Rated) { // if there are enough players in pools, start new battleground or non rated arena if (CheckNormalMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) || (bg_template->IsArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) { // we successfully created a pool Battleground* bg2 = sBattlegroundMgr->CreateNewBattleground(m_queueId, bracket_id); if (!bg2) { TC_LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", m_queueId.BattlemasterListId); return; } // invite those selection pools for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->Team); // start bg bg2->StartBattleground(); } } 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() - sBattlegroundMgr->GetRatingDiscardTimer(); // 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) { // disable this check to allow arena queue without other team if (itr3 == itr_teams[0]) continue; // skip already found team if (!(*itr3)->IsInvitedToBGInstanceGUID && (((*itr3)->ArenaMatchmakerRating >= arenaMinRating && (*itr3)->ArenaMatchmakerRating <= arenaMaxRating) || (int32)(*itr3)->JoinTime < discardTime)) { 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(m_queueId, bracket_id); if (!arena) { TC_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; TC_LOG_DEBUG("bg.battleground", "setting oposite teamrating for to {}", aTeam->OpponentsTeamRating); TC_LOG_DEBUG("bg.battleground", "setting oposite teamrating for to {}", 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->Team != 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->Team != 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(ALLIANCE, aTeam->ArenaMatchmakerRating); arena->SetArenaMatchmakerRating( HORDE, hTeam->ArenaMatchmakerRating); InviteGroupToBG(aTeam, arena, ALLIANCE); InviteGroupToBG(hTeam, arena, HORDE); TC_LOG_DEBUG("bg.battleground", "Starting rated arena match!"); arena->StartBattleground(); } } } /*********************************************************/ /*** BATTLEGROUND QUEUE EVENTS ***/ /*********************************************************/ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* player = ObjectAccessor::FindConnectedPlayer(m_PlayerGuid); // player logged off (we should do nothing, he is correctly removed from queue in another procedure) if (!player) return true; Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, m_BgTypeId); //if battleground ended and its instance deleted - do nothing if (!bg) return true; uint32 queueSlot = player->GetBattlegroundQueueIndex(m_QueueId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground { // check if player is invited to this bg BattlegroundQueue &bgQueue = sBattlegroundMgr->GetBattlegroundQueue(m_QueueId); if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) { WorldPackets::Battleground::BattlefieldStatusNeedConfirmation battlefieldStatus; BattlegroundMgr::BuildBattlegroundStatusNeedConfirmation(&battlefieldStatus, bg, player, queueSlot, player->GetBattlegroundQueueJoinTime(m_QueueId), INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, m_QueueId); player->SendDirectMessage(battlefieldStatus.Write()); } } return true; //event will be deleted } void BGQueueInviteEvent::Abort(uint64 /*e_time*/) { //do nothing } /* this event has many possibilities when it is executed: 1. player is in battleground (he clicked enter on invitation window) 2. player left battleground queue and he isn't there any more 3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0 4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet 5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer we must remove player in the 5. case even if battleground object doesn't exist! */ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* player = ObjectAccessor::FindConnectedPlayer(m_PlayerGuid); if (!player) // player logged off (we should do nothing, he is correctly removed from queue in another procedure) return true; Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, BattlegroundTypeId(m_BgQueueTypeId.BattlemasterListId)); //battleground can be deleted already when we are removing queue info //bg pointer can be NULL! so use it carefully! uint32 queueSlot = player->GetBattlegroundQueueIndex(m_BgQueueTypeId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue, or in Battleground { // check if player is in queue for this BG and if we are removing his invite event BattlegroundQueue &bgQueue = sBattlegroundMgr->GetBattlegroundQueue(m_BgQueueTypeId); if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) { TC_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, m_BgQueueTypeId, bg->GetBracketId()); WorldPackets::Battleground::BattlefieldStatusNone battlefieldStatus; BattlegroundMgr::BuildBattlegroundStatusNone(&battlefieldStatus, player, queueSlot, player->GetBattlegroundQueueJoinTime(m_BgQueueTypeId)); player->SendDirectMessage(battlefieldStatus.Write()); } } //event will be deleted return true; } void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) { //do nothing }