aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/BattleGrounds/BattleGroundMgr.cpp1073
-rw-r--r--src/server/game/BattleGrounds/BattleGroundMgr.h148
-rw-r--r--src/server/game/BattleGrounds/BattleGroundQueue.cpp1097
-rw-r--r--src/server/game/BattleGrounds/BattleGroundQueue.h173
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