/* 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 after removeplayer functions in some cases */ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id) { if(bgTypeId >= 3) sLog.outError("BattleGroundQueue::Update()dingdongzonk"); if (queue_id >= MAX_BATTLEGROUND_QUEUES) { //this is error, that caused crashes (not in , but now it shouldn't) sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash"); return; } //if no players in queue ... do nothing if (m_QueuedGroups[queue_id].empty()) return; uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId); //battleground with free slot for player should be always the last in this queue BGFreeSlotQueueType::iterator itr, next; for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { next = itr; ++next; // battleground is running, so if: if ((*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) { //we must check both teams BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr) { // did the group join for this bg type? if((*itr)->BgTypeId != bgTypeId) continue; // if so, check if fits in if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) { // if group fits in, invite it InviteGroupToBG((*itr),bg,(*itr)->Team); } } if (!bg->HasFreeSlots()) { //remove BG from BGFreeSlotQueue bg->RemoveFromBGFreeSlotQueue(); } } } // finished iterating through the bgs with free slots, maybe we need to create a new bg BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); if(!bg_template) { sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); return; } uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); uint32 discardTime = 0; // try to build the selection pools bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, discardTime); if(bAllyOK) sLog.outDebug("Battleground: ally pool successfully built"); else sLog.outDebug("Battleground: ally pool wasn't created"); bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, discardTime); if(bHordeOK) sLog.outDebug("Battleground: horde pool successfully built"); else sLog.outDebug("Battleground: horde pool wasn't created"); // if selection pools are ready, create the new bg if (bAllyOK && bHordeOK) { BattleGround * bg2 = 0; // create new battleground bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId); if(!bg2) { sLog.outError("Battleground: couldn't create bg %u",bgTypeId); return; } // start the joining of the bg bg2->SetStatus(STATUS_WAIT_JOIN); bg2->SetQueueType(queue_id); std::list<GroupQueueInfo* >::iterator itr; // Send amount of invites based on the difference between the sizes of the two faction's queues uint32 QUEUED_HORDE = m_SelectionPools[NORMAL_HORDE].SelectedGroups.size(); uint32 QUEUED_ALLIANCE = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.size(); uint16 maxbginvites = 0; if(QUEUED_ALLIANCE <= QUEUED_HORDE) maxbginvites = QUEUED_ALLIANCE; else maxbginvites = QUEUED_HORDE; // invite groups from horde selection pool uint16 invitecounter = 0; for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr) { if (invitecounter >= maxbginvites) return; InviteGroupToBG((*itr),bg2,HORDE); ++invitecounter; } // invite groups from ally selection pool invitecounter = 0; for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr) { if (invitecounter >= maxbginvites) return; InviteGroupToBG((*itr),bg2,ALLIANCE); ++invitecounter; } // start the battleground bg2->StartBattleGround(); } }
/* 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(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, ArenaType arenaType, bool isRated, uint32 arenaRating) { // std::lock_guard<std::recursive_mutex> guard(m_Lock); // 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 BGFreeSlotQueueType::iterator itr, next; for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { next = itr; ++next; // DO NOT allow queue manager to invite new player to arena if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetBracketId() == bracket_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) { BattleGround* bg = *itr; // we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid // clear selection pools m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); m_SelectionPools[TEAM_INDEX_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_INDEX_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->GroupTeam); for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_HORDE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->GroupTeam); if (!bg->HasFreeSlots()) bg->RemoveFromBGFreeSlotQueue(); } } // finished iterating through the bgs with free slots, maybe we need to create a new bg BattleGround* bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); if (!bg_template) { sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); return; } // 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 = arenaType; MinPlayersPerTeam = sBattleGroundMgr.isArenaTesting() ? 1 : arenaType; } else if (sBattleGroundMgr.isTesting()) MinPlayersPerTeam = 1; m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); m_SelectionPools[TEAM_INDEX_HORDE].Init(); if (bg_template->isBattleGround()) { // check if there is premade against premade match if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) { // create new battleground BattleGround* bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracket_id, ARENA_TYPE_NONE, false); if (!bg2) { sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); return; } // invite those selection pools for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->GroupTeam); // start bg bg2->StartBattleGround(); // clear structures m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); m_SelectionPools[TEAM_INDEX_HORDE].Init(); } } // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) if (!isRated) { // if there are enough players in pools, start new battleground or non rated arena if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) || (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) { // we successfully created a pool BattleGround* bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracket_id, arenaType, false); if (!bg2) { sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); return; } // invite those selection pools for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->GroupTeam); // 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->ArenaTeamRating; } if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) { front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front(); arenaRating = front2->ArenaTeamRating; } if (front1 && front2) { if (front1->JoinTime < front2->JoinTime) arenaRating = front1->ArenaTeamRating; } 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 uint32 discardTime = WorldTimer::getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); // we need to find 2 teams which will play next game GroupsQueueType::iterator itr_teams[PVP_TEAM_COUNT]; uint8 found = 0; uint8 team = 0; // optimalization : --- we dont need to use selection_pools - each update we select max 2 groups 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)->ArenaTeamRating >= arenaMinRating && (*itr2)->ArenaTeamRating <= arenaMaxRating) || (*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)->ArenaTeamRating >= arenaMinRating && (*itr3)->ArenaTeamRating <= arenaMaxRating) || (*itr3)->JoinTime < discardTime) && (*itr_teams[0])->ArenaTeamId != (*itr3)->ArenaTeamId) { itr_teams[found++] = itr3; break; } } } if (found == 2) { GroupQueueInfo* aTeam = *itr_teams[TEAM_INDEX_ALLIANCE]; GroupQueueInfo* hTeam = *itr_teams[TEAM_INDEX_HORDE]; BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracket_id, arenaType, true); if (!arena) { sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); return; } aTeam->OpponentsTeamRating = hTeam->ArenaTeamRating; hTeam->OpponentsTeamRating = aTeam->ArenaTeamRating; DEBUG_LOG("setting oposite teamrating for team %u to %u", aTeam->ArenaTeamId, aTeam->OpponentsTeamRating); DEBUG_LOG("setting oposite teamrating for team %u to %u", 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->GroupTeam != ALLIANCE) { m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(aTeam); m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_teams[TEAM_INDEX_ALLIANCE]); } if (hTeam->GroupTeam != HORDE) { m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(hTeam); m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_teams[TEAM_INDEX_HORDE]); } InviteGroupToBG(aTeam, arena, ALLIANCE); InviteGroupToBG(hTeam, arena, HORDE); DEBUG_LOG("Starting rated arena match!"); arena->StartBattleGround(); } } }