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