diff options
| -rw-r--r-- | src/server/game/BattleGrounds/BattleGroundMgr.cpp | 1073 | ||||
| -rw-r--r-- | src/server/game/BattleGrounds/BattleGroundMgr.h | 148 | ||||
| -rw-r--r-- | src/server/game/BattleGrounds/BattleGroundQueue.cpp | 1097 | ||||
| -rw-r--r-- | src/server/game/BattleGrounds/BattleGroundQueue.h | 173 |
4 files changed, 1272 insertions, 1219 deletions
diff --git a/src/server/game/BattleGrounds/BattleGroundMgr.cpp b/src/server/game/BattleGrounds/BattleGroundMgr.cpp index d3440cb0d37..f9fd23c8289 100644 --- a/src/server/game/BattleGrounds/BattleGroundMgr.cpp +++ b/src/server/game/BattleGrounds/BattleGroundMgr.cpp @@ -51,1079 +51,6 @@ #include "DisableMgr.h" /*********************************************************/ -/*** BATTLEGROUND QUEUE SYSTEM ***/ -/*********************************************************/ - -BattleGroundQueue::BattleGroundQueue() -{ - for (uint32 i = 0; i < BG_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_QueuedPlayers.clear(); - 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); - m_QueuedGroups[i][j].clear(); - } - } -} - -/*********************************************************/ -/*** 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 *leader, Group* grp, BattleGroundTypeId BgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) -{ - BattleGroundBracketId bracketId = backetEntry->GetBracketId(); - - // create new ginfo - GroupQueueInfo* ginfo = new GroupQueueInfo; - ginfo->BgTypeId = BgTypeId; - ginfo->ArenaType = ArenaType; - ginfo->ArenaTeamId = arenateamid; - ginfo->IsRated = isRated; - ginfo->IsInvitedToBGInstanceGUID = 0; - ginfo->JoinTime = getMSTime(); - ginfo->RemoveInviteTime = 0; - ginfo->Team = leader->GetTeam(); - ginfo->ArenaTeamRating = arenaRating; - ginfo->OpponentsTeamRating = 0; - - ginfo->Players.clear(); - - //compute index (if group is premade or joined a rated match) to queues - uint32 index = 0; - if (!isRated && !isPremade) - index += BG_TEAMS_COUNT; - if (ginfo->Team == HORDE) - index++; - sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, bracket_id : %u, index : %u", BgTypeId, bracketId, index); - - uint32 lastOnlineTime = getMSTime(); - - //announce world (this don't need mutex) - if (isRated && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) - { - ArenaTeam *Team = objmgr.GetArenaTeamById(arenateamid); - if (Team) - sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, Team->GetName().c_str(), ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); - } - - //add players from group to ginfo - { - //ACE_Guard<ACE_Recursive_Thread_Mutex> guard(m_Lock); - if (grp) - { - for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if (!member) - continue; // this should never happen - 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 (!isRated && !isPremade && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) - { - if (BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId)) - { - char const* bgName = bg->GetName(); - uint32 MinPlayers = bg->GetMinPlayersPerTeam(); - uint32 qHorde = 0; - uint32 qAlliance = 0; - uint32 q_min_level = backetEntry->minLevel; - uint32 q_max_level = backetEntry->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.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) - { - ChatHandler(leader).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName, 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, bgName, q_min_level, q_max_level, - qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); - } - } - } - //release mutex - } - - return ginfo; -} - -void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) -{ - uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); - uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! - if (!ginfo->ArenaType) - { - if (ginfo->Team == HORDE) - team_index = BG_TEAM_HORDE; - } - else - { - if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; //for rated arenas use BG_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) -{ - uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! - if (!ginfo->ArenaType) - { - if (ginfo->Team == HORDE) - team_index = BG_TEAM_HORDE; - } - else - { - if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; //for rated arenas use BG_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(const uint64& guid, bool decreaseInvitedCount) -{ - //Player *plr = objmgr.GetPlayer(guid); - - 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()) - { - sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); - return; - } - - GroupQueueInfo* group = itr->second.GroupInfo; - GroupsQueueType::iterator group_itr, group_itr_tmp; - // mostly people with the highest levels are in battlegrounds, thats why - // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 - // variable index removes useless searching in other team's queue - uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_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 += BG_QUEUE_NORMAL_ALLIANCE) - { - for (group_itr_tmp = m_QueuedGroups[bracket_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[bracket_id_tmp][j].end(); ++group_itr_tmp) - { - if ((*group_itr_tmp) == group) - { - bracket_id = bracket_id_tmp; - group_itr = group_itr_tmp; - //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) - { - sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); - return; - } - sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from bracket_id %u", GUID_LOPART(guid), (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<uint64, PlayerQueueInfo*>::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) - { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); - if (bg) - 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 (group->ArenaType && group->IsRated && group->Players.empty() && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) - { - ArenaTeam *Team = objmgr.GetArenaTeamById(group->ArenaTeamId); - if (Team) - sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, Team->GetName().c_str(), group->ArenaType, group->ArenaType, group->ArenaTeamRating); - } - - //if player leaves queue and he is invited to rated arena match, then he have to loose - if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) - { - ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); - if (at) - { - sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); - Player *plr = objmgr.GetPlayer(guid); - if (plr) - at->MemberLost(plr, group->OpponentsTeamRating); - else - at->OfflineMemberLost(guid, group->OpponentsTeamRating); - at->SaveToDB(); - } - } - - // remove group queue info if needed - if (group->Players.empty()) - { - m_QueuedGroups[bracket_id][index].erase(group_itr); - delete group; - } - // 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! - else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) - { - // remove next player, this is recursive - // first send removal information - if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) - { - BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); - uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); - plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to - // queue->removeplayer, it causes bugs - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); - plr2->GetSession()->SendPacket(&data); - } - // 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(const uint64& 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(const uint64& guid, GroupQueueInfo* ginfo) -{ - QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid); - if (qItr == m_QueuedPlayers.end()) - return false; - *ginfo = *(qItr->second.GroupInfo); - return true; -} - -bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) -{ - // set side if needed - if (side) - ginfo->Team = side; - - if (!ginfo->IsInvitedToBGInstanceGUID) - { - // not yet invited - // set invitation - ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); - BattleGroundTypeId bgTypeId = bg->GetTypeID(); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); - BattleGroundBracketId bracket_id = bg->GetBracketId(); - - // set ArenaTeamId for rated matches - if (bg->isArena() && bg->isRated()) - bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); - - ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; - - // loop through the players - for (std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) - { - // get the player - Player* plr = objmgr.GetPlayer(itr->first); - // if offline, skip him, this should not happen - player is removed from queue when he logs out - if (!plr) - continue; - - // invite the player - PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id); - //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); - - // set invited player counters - bg->IncreaseInvitedCount(ginfo->Team); - - plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); - - // create remind invite events - BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); - plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); - // create automatic remove events - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); - plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); - - WorldPacket data; - - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - - sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); - - // send status packet - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); - plr->GetSession()->SendPacket(&data); - } - 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); - - //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 - uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); - //index to queue which group is current - uint32 aliIndex = 0; - for (; aliIndex < aliCount && m_SelectionPools[BG_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 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); - uint32 hordeIndex = 0; - for (; hordeIndex < hordeCount && m_SelectionPools[BG_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.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) - 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[BG_TEAM_ALLIANCE].GetPlayerCount()); - int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) - { - for (; aliIndex < aliCount && m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount()) - { - if (aliFree <= diffHorde + 1) - break; - m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); - } - } - else - { - //kick horde group, add to pool new group if needed - if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) - { - for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) - ++Horde_itr; - } - if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - if (hordeFree <= diffAli + 1) - break; - m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); - } - } - //count diffs after small update - diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); - diffHorde = hordeFree - int32(m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); - //add groups/players from normal queue to size of bigger group - uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - GroupsQueueType::const_iterator itr; - for (uint32 i = 0; i < BG_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 = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); - for (uint32 i = 0; i < BG_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(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) -{ - GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; - for (uint32 i = 0; i < BG_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 = BG_TEAM_ALLIANCE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - j = BG_TEAM_HORDE; - if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 - && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_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) % BG_TEAMS_COUNT].GetPlayerCount())) - break; - } - // do not allow to start bg with more than 2 players more on 1 faction - if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) - return false; - } - //allow 1v0 if debug bg - if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) - return false; - uint32 teamIndex = BG_TEAM_ALLIANCE; - uint32 otherTeam = BG_TEAM_HORDE; - uint32 otherTeamId = HORDE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) - { - teamIndex = BG_TEAM_HORDE; - otherTeam = BG_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][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); - for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) - if (ginfo == *itr_team) - break; - if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + 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 + 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][BG_QUEUE_NORMAL_ALLIANCE + 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 + teamIndex].end(); ++itr2) - { - if (*itr2 == *itr) - { - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); - break; - } - } - } - return true; -} - -/* -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::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType, bool isRated, 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 - 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[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg, (*citr)->Team); - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg, (*citr)->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; - } - - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(),bracket_id); - if (!bracketEntry) - { - sLog.outError("Battleground: Update: bg bracket entry not found for map %u bracket id %u", bg_template->GetMapId(), bracket_id); - 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 (sBattleGroundMgr.isTesting()) - MinPlayersPerTeam = 1; - if (bg_template->isArena()) - { - if (sBattleGroundMgr.isArenaTesting()) - { - MaxPlayersPerTeam = 1; - MinPlayersPerTeam = 1; - } - else - { - //this switch can be much shorter - MaxPlayersPerTeam = arenaType; - MinPlayersPerTeam = arenaType; - /*switch(arenaType) - { - case ARENA_TYPE_2v2: - MaxPlayersPerTeam = 2; - MinPlayersPerTeam = 2; - break; - case ARENA_TYPE_3v3: - MaxPlayersPerTeam = 3; - MinPlayersPerTeam = 3; - break; - case ARENA_TYPE_5v5: - MaxPlayersPerTeam = 5; - MinPlayersPerTeam = 5; - break; - }*/ - } - } - - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_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, bracketEntry, 0, false); - if (!bg2) - { - sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); - return; - } - //invite those selection pools - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg2, (*citr)->Team); - //start bg - bg2->StartBattleGround(); - //clear structures - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_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, bracketEntry, arenaType, false); - if (!bg2) - { - sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); - return; - } - - // invite those selection pools - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_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 = NULL; - GroupQueueInfo* front2 = NULL; - 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 = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - - // we need to find 2 teams which will play next game - - GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; - - //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups - - for (uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) - { - // take the group that joined first - itr_team[i] = m_QueuedGroups[bracket_id][i].begin(); - for (; itr_team[i] != m_QueuedGroups[bracket_id][i].end(); ++(itr_team[i])) - { - // if group match conditions, then add it to pool - if (!(*itr_team[i])->IsInvitedToBGInstanceGUID - && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[i])->JoinTime < discardTime)) - { - m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); - // break for cycle to be able to start selecting another group from same faction queue - break; - } - } - } - // now we are done if we have 2 groups - ali vs horde! - // if we don't have, we must try to continue search in same queue - // tmp variables are correctly set - // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; - ++itr_team[BG_TEAM_ALLIANCE]; - for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) - { - if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime)) - { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); - break; - } - } - } - // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - { - itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; - ++itr_team[BG_TEAM_HORDE]; - for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) - { - if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime)) - { - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); - break; - } - } - } - - //if we have 2 teams, then start new arena and invite players! - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true); - if (!arena) - { - sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); - return; - } - - (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; - sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); - (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; - sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->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 ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) - { - // add to alliance queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); - // erase from horde queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); - itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); - } - if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) - { - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); - itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); - } - - InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); - InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); - - sLog.outDebug("Starting rated arena match!"); - - arena->StartBattleGround(); - } - } -} - -/*********************************************************/ -/*** BATTLEGROUND QUEUE EVENTS ***/ -/*********************************************************/ - -bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - Player* plr = objmgr.GetPlayer(m_PlayerGuid); - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) - if (!plr) - return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); - //if battleground ended and its instance deleted - do nothing - if (!bg) - return true; - - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground - { - // check if player is invited to this bg - BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) - { - WorldPacket data; - //we must send remaining time in queue - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType); - plr->GetSession()->SendPacket(&data); - } - } - 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* plr = objmgr.GetPlayer(m_PlayerGuid); - if (!plr) - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) - return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); - //battleground can be deleted already when we are removing queue info - //bg pointer can be NULL! so use it carefully! - - uint32 queueSlot = plr->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.m_BattleGroundQueues[m_BgQueueTypeId]; - if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) - { - sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); - - plr->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); - plr->GetSession()->SendPacket(&data); - } - } - - //event will be deleted - return true; -} - -void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) -{ - //do nothing -} - -/*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ diff --git a/src/server/game/BattleGrounds/BattleGroundMgr.h b/src/server/game/BattleGrounds/BattleGroundMgr.h index 9ac52c13be8..8a21461cbcb 100644 --- a/src/server/game/BattleGrounds/BattleGroundMgr.h +++ b/src/server/game/BattleGrounds/BattleGroundMgr.h @@ -22,162 +22,18 @@ #define __BATTLEGROUNDMGR_H #include "Common.h" -#include "ace/Singleton.h" - #include "DBCEnums.h" #include "BattleGround.h" +#include "BattleGroundQueue.h" +#include "ace/Singleton.h" typedef std::map<uint32, BattleGround*> BattleGroundSet; -//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears -typedef std::list<BattleGround*> BGFreeSlotQueueType; - typedef UNORDERED_MAP<uint32, BattleGroundTypeId> BattleMastersMap; #define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day -#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 #define WS_ARENA_DISTRIBUTION_TIME 20001 // Custom worldstate -struct GroupQueueInfo; // type predefinition -struct PlayerQueueInfo // stores information for players in queue -{ - uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes - GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo -}; - -struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) -{ - std::map<uint64, PlayerQueueInfo*> Players; // player queue info map - uint32 Team; // Player team (ALLIANCE/HORDE) - BattleGroundTypeId BgTypeId; // battleground type id - bool IsRated; // rated - uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG - uint32 ArenaTeamId; // team id if rated match - uint32 JoinTime; // time when group was added - uint32 RemoveInviteTime; // time when we will remove invite for players in group - uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG - uint32 ArenaTeamRating; // if rated match, inited to the rating of the team - uint32 OpponentsTeamRating; // for rated arena matches -}; - -enum BattleGroundQueueGroupTypes -{ - BG_QUEUE_PREMADE_ALLIANCE = 0, - BG_QUEUE_PREMADE_HORDE = 1, - BG_QUEUE_NORMAL_ALLIANCE = 2, - BG_QUEUE_NORMAL_HORDE = 3 -}; -#define BG_QUEUE_GROUP_TYPES_COUNT 4 - -class BattleGround; -class BattleGroundQueue -{ - public: - BattleGroundQueue(); - ~BattleGroundQueue(); - - void Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0); - - void FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id); - bool CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); - bool CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); - bool CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam); - GroupQueueInfo * AddGroup(Player* leader, Group* group, BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); - void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); - bool IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime); - bool GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo); - void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); - uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); - - typedef std::map<uint64, PlayerQueueInfo> QueuedPlayersMap; - QueuedPlayersMap m_QueuedPlayers; - - //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well - typedef std::list<GroupQueueInfo*> GroupsQueueType; - - /* - This two dimensional array is used to store All queued groups - First dimension specifies the bgTypeId - Second dimension specifies the player's group types - - BG_QUEUE_PREMADE_ALLIANCE is used for premade alliance groups and alliance rated arena teams - BG_QUEUE_PREMADE_HORDE is used for premade horde groups and horde rated arena teams - BG_QUEUE_NORMAL_ALLIANCE is used for normal (or small) alliance groups or non-rated arena matches - BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches - */ - GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_BRACKETS][BG_QUEUE_GROUP_TYPES_COUNT]; - - // class to select and invite groups to bg - class SelectionPool - { - public: - void Init(); - bool AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount); - bool KickGroup(uint32 size); - uint32 GetPlayerCount() const {return PlayerCount;} - public: - GroupsQueueType SelectedGroups; - private: - uint32 PlayerCount; - }; - - //one selection pool for horde, other one for alliance - SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; - - private: - - bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); - uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; - uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; - uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; -}; - -/* - This class is used to invite player to BG again, when minute lasts from his first invitation - it is capable to solve all possibilities -*/ -class BGQueueInviteEvent : public BasicEvent -{ - public: - BGQueueInviteEvent(const uint64& pl_guid, uint32 BgInstanceGUID, BattleGroundTypeId BgTypeId, uint8 arenaType, uint32 removeTime) : - m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID), m_BgTypeId(BgTypeId), m_ArenaType(arenaType), m_RemoveTime(removeTime) - { - }; - virtual ~BGQueueInviteEvent() {}; - - virtual bool Execute(uint64 e_time, uint32 p_time); - virtual void Abort(uint64 e_time); - private: - uint64 m_PlayerGuid; - uint32 m_BgInstanceGUID; - BattleGroundTypeId m_BgTypeId; - uint8 m_ArenaType; - uint32 m_RemoveTime; -}; - -/* - This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation - We must store removeInvite time in case player left queue and joined and is invited again - We must store bgQueueTypeId, because battleground can be deleted already, when player entered it -*/ -class BGQueueRemoveEvent : public BasicEvent -{ - public: - BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime) - : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId) - {} - - virtual ~BGQueueRemoveEvent() {} - - virtual bool Execute(uint64 e_time, uint32 p_time); - virtual void Abort(uint64 e_time); - private: - uint64 m_PlayerGuid; - uint32 m_BgInstanceGUID; - uint32 m_RemoveTime; - BattleGroundTypeId m_BgTypeId; - BattleGroundQueueTypeId m_BgQueueTypeId; -}; - class BattleGroundMgr { /// Todo: Thread safety? diff --git a/src/server/game/BattleGrounds/BattleGroundQueue.cpp b/src/server/game/BattleGrounds/BattleGroundQueue.cpp new file mode 100644 index 00000000000..401eef8a730 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundQueue.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGroundQueue.h" +#include "ArenaTeam.h" +#include "BattleGroundMgr.h" +#include "Chat.h" + +/*********************************************************/ +/*** BATTLEGROUND QUEUE SYSTEM ***/ +/*********************************************************/ + +BattleGroundQueue::BattleGroundQueue() +{ + for (uint32 i = 0; i < BG_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_QueuedPlayers.clear(); + 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); + m_QueuedGroups[i][j].clear(); + } + } +} + +/*********************************************************/ +/*** 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 *leader, Group* grp, BattleGroundTypeId BgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) +{ + BattleGroundBracketId bracketId = backetEntry->GetBracketId(); + + // create new ginfo + GroupQueueInfo* ginfo = new GroupQueueInfo; + ginfo->BgTypeId = BgTypeId; + ginfo->ArenaType = ArenaType; + ginfo->ArenaTeamId = arenateamid; + ginfo->IsRated = isRated; + ginfo->IsInvitedToBGInstanceGUID = 0; + ginfo->JoinTime = getMSTime(); + ginfo->RemoveInviteTime = 0; + ginfo->Team = leader->GetTeam(); + ginfo->ArenaTeamRating = arenaRating; + ginfo->OpponentsTeamRating = 0; + + ginfo->Players.clear(); + + //compute index (if group is premade or joined a rated match) to queues + uint32 index = 0; + if (!isRated && !isPremade) + index += BG_TEAMS_COUNT; + if (ginfo->Team == HORDE) + index++; + sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, bracket_id : %u, index : %u", BgTypeId, bracketId, index); + + uint32 lastOnlineTime = getMSTime(); + + //announce world (this don't need mutex) + if (isRated && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) + { + ArenaTeam *Team = objmgr.GetArenaTeamById(arenateamid); + if (Team) + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, Team->GetName().c_str(), ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); + } + + //add players from group to ginfo + { + //ACE_Guard<ACE_Recursive_Thread_Mutex> guard(m_Lock); + if (grp) + { + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if (!member) + continue; // this should never happen + 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 (!isRated && !isPremade && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) + { + if (BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId)) + { + char const* bgName = bg->GetName(); + uint32 MinPlayers = bg->GetMinPlayersPerTeam(); + uint32 qHorde = 0; + uint32 qAlliance = 0; + uint32 q_min_level = backetEntry->minLevel; + uint32 q_max_level = backetEntry->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.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) + { + ChatHandler(leader).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName, 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, bgName, q_min_level, q_max_level, + qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); + } + } + } + //release mutex + } + + return ginfo; +} + +void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) +{ + uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_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) +{ + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_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(const uint64& guid, bool decreaseInvitedCount) +{ + //Player *plr = objmgr.GetPlayer(guid); + + 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()) + { + sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); + return; + } + + GroupQueueInfo* group = itr->second.GroupInfo; + GroupsQueueType::iterator group_itr, group_itr_tmp; + // mostly people with the highest levels are in battlegrounds, thats why + // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 + // variable index removes useless searching in other team's queue + uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_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 += BG_QUEUE_NORMAL_ALLIANCE) + { + for (group_itr_tmp = m_QueuedGroups[bracket_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[bracket_id_tmp][j].end(); ++group_itr_tmp) + { + if ((*group_itr_tmp) == group) + { + bracket_id = bracket_id_tmp; + group_itr = group_itr_tmp; + //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) + { + sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); + return; + } + sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from bracket_id %u", GUID_LOPART(guid), (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<uint64, PlayerQueueInfo*>::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) + { + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); + if (bg) + 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 (group->ArenaType && group->IsRated && group->Players.empty() && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) + { + ArenaTeam *Team = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (Team) + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, Team->GetName().c_str(), group->ArenaType, group->ArenaType, group->ArenaTeamRating); + } + + //if player leaves queue and he is invited to rated arena match, then he have to loose + if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) + { + ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); + Player *plr = objmgr.GetPlayer(guid); + if (plr) + at->MemberLost(plr, group->OpponentsTeamRating); + else + at->OfflineMemberLost(guid, group->OpponentsTeamRating); + at->SaveToDB(); + } + } + + // remove group queue info if needed + if (group->Players.empty()) + { + m_QueuedGroups[bracket_id][index].erase(group_itr); + delete group; + } + // 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! + else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) + { + // remove next player, this is recursive + // first send removal information + if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + { + BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); + uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); + plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to + // queue->removeplayer, it causes bugs + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + plr2->GetSession()->SendPacket(&data); + } + // 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(const uint64& 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(const uint64& guid, GroupQueueInfo* ginfo) +{ + QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid); + if (qItr == m_QueuedPlayers.end()) + return false; + *ginfo = *(qItr->second.GroupInfo); + return true; +} + +bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) +{ + // set side if needed + if (side) + ginfo->Team = side; + + if (!ginfo->IsInvitedToBGInstanceGUID) + { + // not yet invited + // set invitation + ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); + BattleGroundBracketId bracket_id = bg->GetBracketId(); + + // set ArenaTeamId for rated matches + if (bg->isArena() && bg->isRated()) + bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); + + ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; + + // loop through the players + for (std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) + { + // get the player + Player* plr = objmgr.GetPlayer(itr->first); + // if offline, skip him, this should not happen - player is removed from queue when he logs out + if (!plr) + continue; + + // invite the player + PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id); + //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); + + // set invited player counters + bg->IncreaseInvitedCount(ginfo->Team); + + plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); + + // create remind invite events + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); + // create automatic remove events + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); + + WorldPacket data; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + + sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); + + // send status packet + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); + plr->GetSession()->SendPacket(&data); + } + 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); + + //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 + uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + //index to queue which group is current + uint32 aliIndex = 0; + for (; aliIndex < aliCount && m_SelectionPools[BG_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 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); + uint32 hordeIndex = 0; + for (; hordeIndex < hordeCount && m_SelectionPools[BG_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.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) + 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[BG_TEAM_ALLIANCE].GetPlayerCount()); + int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) + { + for (; aliIndex < aliCount && m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + if (aliFree <= diffHorde + 1) + break; + m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); + } + } + else + { + //kick horde group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) + { + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) + ++Horde_itr; + } + if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + if (hordeFree <= diffAli + 1) + break; + m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); + } + } + //count diffs after small update + diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); + diffHorde = hordeFree - int32(m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); + //add groups/players from normal queue to size of bigger group + uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + GroupsQueueType::const_iterator itr; + for (uint32 i = 0; i < BG_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 = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); + for (uint32 i = 0; i < BG_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(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) +{ + GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; + for (uint32 i = 0; i < BG_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 = BG_TEAM_ALLIANCE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + j = BG_TEAM_HORDE; + if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 + && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_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) % BG_TEAMS_COUNT].GetPlayerCount())) + break; + } + // do not allow to start bg with more than 2 players more on 1 faction + if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) + return false; + } + //allow 1v0 if debug bg + if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + return false; + uint32 teamIndex = BG_TEAM_ALLIANCE; + uint32 otherTeam = BG_TEAM_HORDE; + uint32 otherTeamId = HORDE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) + { + teamIndex = BG_TEAM_HORDE; + otherTeam = BG_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][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); + for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) + if (ginfo == *itr_team) + break; + if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + 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 + 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][BG_QUEUE_NORMAL_ALLIANCE + 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 + teamIndex].end(); ++itr2) + { + if (*itr2 == *itr) + { + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); + break; + } + } + } + return true; +} + +/* +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::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType, bool isRated, 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 + 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[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->Team); + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->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; + } + + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(),bracket_id); + if (!bracketEntry) + { + sLog.outError("Battleground: Update: bg bracket entry not found for map %u bracket id %u", bg_template->GetMapId(), bracket_id); + 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 (sBattleGroundMgr.isTesting()) + MinPlayersPerTeam = 1; + if (bg_template->isArena()) + { + if (sBattleGroundMgr.isArenaTesting()) + { + MaxPlayersPerTeam = 1; + MinPlayersPerTeam = 1; + } + else + { + //this switch can be much shorter + MaxPlayersPerTeam = arenaType; + MinPlayersPerTeam = arenaType; + /*switch(arenaType) + { + case ARENA_TYPE_2v2: + MaxPlayersPerTeam = 2; + MinPlayersPerTeam = 2; + break; + case ARENA_TYPE_3v3: + MaxPlayersPerTeam = 3; + MinPlayersPerTeam = 3; + break; + case ARENA_TYPE_5v5: + MaxPlayersPerTeam = 5; + MinPlayersPerTeam = 5; + break; + }*/ + } + } + + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_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, bracketEntry, 0, false); + if (!bg2) + { + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; + } + //invite those selection pools + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg2, (*citr)->Team); + //start bg + bg2->StartBattleGround(); + //clear structures + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_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, bracketEntry, arenaType, false); + if (!bg2) + { + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; + } + + // invite those selection pools + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_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 = NULL; + GroupQueueInfo* front2 = NULL; + 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 = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); + + // we need to find 2 teams which will play next game + + GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; + + //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups + + for (uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) + { + // take the group that joined first + itr_team[i] = m_QueuedGroups[bracket_id][i].begin(); + for (; itr_team[i] != m_QueuedGroups[bracket_id][i].end(); ++(itr_team[i])) + { + // if group match conditions, then add it to pool + if (!(*itr_team[i])->IsInvitedToBGInstanceGUID + && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[i])->JoinTime < discardTime)) + { + m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); + // break for cycle to be able to start selecting another group from same faction queue + break; + } + } + } + // now we are done if we have 2 groups - ali vs horde! + // if we don't have, we must try to continue search in same queue + // tmp variables are correctly set + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; + ++itr_team[BG_TEAM_ALLIANCE]; + for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) + { + if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime)) + { + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); + break; + } + } + } + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; + ++itr_team[BG_TEAM_HORDE]; + for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) + { + if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime)) + { + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); + break; + } + } + } + + //if we have 2 teams, then start new arena and invite players! + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true); + if (!arena) + { + sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); + return; + } + + (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); + (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->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 ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) + { + // add to alliance queue + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); + // erase from horde queue + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); + itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); + } + if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) + { + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); + itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); + } + + InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); + InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); + + sLog.outDebug("Starting rated arena match!"); + + arena->StartBattleGround(); + } + } +} + +/*********************************************************/ +/*** BATTLEGROUND QUEUE EVENTS ***/ +/*********************************************************/ + +bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) +{ + Player* plr = objmgr.GetPlayer(m_PlayerGuid); + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + if (!plr) + return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //if battleground ended and its instance deleted - do nothing + if (!bg) + return true; + + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground + { + // check if player is invited to this bg + BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) + { + WorldPacket data; + //we must send remaining time in queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType); + plr->GetSession()->SendPacket(&data); + } + } + 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* plr = objmgr.GetPlayer(m_PlayerGuid); + if (!plr) + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //battleground can be deleted already when we are removing queue info + //bg pointer can be NULL! so use it carefully! + + uint32 queueSlot = plr->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.m_BattleGroundQueues[m_BgQueueTypeId]; + if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) + { + sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + + plr->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); + plr->GetSession()->SendPacket(&data); + } + } + + //event will be deleted + return true; +} + +void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) +{ + //do nothing +} diff --git a/src/server/game/BattleGrounds/BattleGroundQueue.h b/src/server/game/BattleGrounds/BattleGroundQueue.h new file mode 100644 index 00000000000..a3b8ad54366 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundQueue.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDQUEUE_H +#define __BATTLEGROUNDQUEUE_H + +#include "Common.h" +#include "DBCEnums.h" +#include "BattleGround.h" + +//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears +typedef std::list<BattleGround*> BGFreeSlotQueueType; + +#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 + +struct GroupQueueInfo; // type predefinition +struct PlayerQueueInfo // stores information for players in queue +{ + uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo +}; + +struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) +{ + std::map<uint64, PlayerQueueInfo*> Players; // player queue info map + uint32 Team; // Player team (ALLIANCE/HORDE) + BattleGroundTypeId BgTypeId; // battleground type id + bool IsRated; // rated + uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG + uint32 ArenaTeamId; // team id if rated match + uint32 JoinTime; // time when group was added + uint32 RemoveInviteTime; // time when we will remove invite for players in group + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG + uint32 ArenaTeamRating; // if rated match, inited to the rating of the team + uint32 OpponentsTeamRating; // for rated arena matches +}; + +enum BattleGroundQueueGroupTypes +{ + BG_QUEUE_PREMADE_ALLIANCE = 0, + BG_QUEUE_PREMADE_HORDE = 1, + BG_QUEUE_NORMAL_ALLIANCE = 2, + BG_QUEUE_NORMAL_HORDE = 3 +}; +#define BG_QUEUE_GROUP_TYPES_COUNT 4 + +class BattleGround; +class BattleGroundQueue +{ + public: + BattleGroundQueue(); + ~BattleGroundQueue(); + + void Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0); + + void FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id); + bool CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); + bool CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); + bool CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam); + GroupQueueInfo * AddGroup(Player* leader, Group* group, BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); + void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); + bool IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime); + bool GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo); + void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); + uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); + + typedef std::map<uint64, PlayerQueueInfo> QueuedPlayersMap; + QueuedPlayersMap m_QueuedPlayers; + + //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well + typedef std::list<GroupQueueInfo*> GroupsQueueType; + + /* + This two dimensional array is used to store All queued groups + First dimension specifies the bgTypeId + Second dimension specifies the player's group types - + BG_QUEUE_PREMADE_ALLIANCE is used for premade alliance groups and alliance rated arena teams + BG_QUEUE_PREMADE_HORDE is used for premade horde groups and horde rated arena teams + BG_QUEUE_NORMAL_ALLIANCE is used for normal (or small) alliance groups or non-rated arena matches + BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches + */ + GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_BRACKETS][BG_QUEUE_GROUP_TYPES_COUNT]; + + // class to select and invite groups to bg + class SelectionPool + { + public: + void Init(); + bool AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount); + bool KickGroup(uint32 size); + uint32 GetPlayerCount() const {return PlayerCount;} + public: + GroupsQueueType SelectedGroups; + private: + uint32 PlayerCount; + }; + + //one selection pool for horde, other one for alliance + SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; + + private: + + bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); + uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; + uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; + uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; +}; + +/* + This class is used to invite player to BG again, when minute lasts from his first invitation + it is capable to solve all possibilities +*/ +class BGQueueInviteEvent : public BasicEvent +{ + public: + BGQueueInviteEvent(const uint64& pl_guid, uint32 BgInstanceGUID, BattleGroundTypeId BgTypeId, uint8 arenaType, uint32 removeTime) : + m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID), m_BgTypeId(BgTypeId), m_ArenaType(arenaType), m_RemoveTime(removeTime) + { + }; + virtual ~BGQueueInviteEvent() {}; + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + BattleGroundTypeId m_BgTypeId; + uint8 m_ArenaType; + uint32 m_RemoveTime; +}; + +/* + This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation + We must store removeInvite time in case player left queue and joined and is invited again + We must store bgQueueTypeId, because battleground can be deleted already, when player entered it +*/ +class BGQueueRemoveEvent : public BasicEvent +{ + public: + BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime) + : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId) + {} + + virtual ~BGQueueRemoveEvent() {} + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + uint32 m_RemoveTime; + BattleGroundTypeId m_BgTypeId; + BattleGroundQueueTypeId m_BgQueueTypeId; +}; + +#endif |
