Core/Dungeon Finder: Move queue related code to its own class.

- Use different queues for each team (or shared one if two-side interaction is enabled)
- Some optimizations in matching algorithm
This commit is contained in:
Spp
2012-10-19 14:00:40 +02:00
parent 479d34d2a0
commit 4e8fa520c8
8 changed files with 817 additions and 688 deletions

View File

@@ -18,19 +18,18 @@
#include "Common.h"
#include "SharedDefines.h"
#include "DBCStores.h"
#include "Containers.h"
#include "DisableMgr.h"
#include "ObjectMgr.h"
#include "SocialMgr.h"
#include "LFGMgr.h"
#include "GroupMgr.h"
#include "GameEventMgr.h"
#include "LFGScripts.h"
#include "LFGGroupData.h"
#include "LFGPlayerData.h"
#include "LFGQueue.h"
#include "Group.h"
#include "Player.h"
#include "GroupMgr.h"
#include "GameEventMgr.h"
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE)),
@@ -43,12 +42,6 @@ LFGMgr::~LFGMgr()
delete itr->second;
delete m_lfgPlayerScript;
delete m_lfgGroupScript;
for (LfgQueueInfoMap::iterator it = m_QueueInfoMap.begin(); it != m_QueueInfoMap.end(); ++it)
delete it->second;
for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end(); ++it)
delete it->second;
}
void LFGMgr::_LoadFromDB(Field* fields, uint64 guid)
@@ -385,7 +378,7 @@ void LFGMgr::Update(uint32 diff)
for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end();)
{
LfgProposalMap::iterator itRemove = it++;
if (itRemove->second->cancelTime < currTime)
if (itRemove->second.cancelTime < currTime)
RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED);
}
@@ -407,58 +400,37 @@ void LFGMgr::Update(uint32 diff)
}
}
uint32 lastProposalId = m_lfgProposalId;
// Check if a proposal can be formed with the new groups being added
for (LfgGuidListMap::iterator it = m_newToQueue.begin(); it != m_newToQueue.end(); ++it)
for (LfgQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it)
if (uint8 newProposals = it->second.FindGroups())
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Update: Found %u new groups in queue %u", newProposals, it->first);
if (lastProposalId != m_lfgProposalId)
{
uint8 queueId = it->first;
LfgGuidList& newToQueue = it->second;
LfgGuidList& currentQueue = m_currentQueue[queueId];
LfgGuidList firstNew;
while (!newToQueue.empty())
// FIXME lastProposalId ? lastProposalId +1 ?
for (LfgProposalMap::const_iterator itProposal = m_Proposals.find(m_lfgProposalId); itProposal != m_Proposals.end(); ++itProposal)
{
uint64 frontguid = newToQueue.front();
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Update: QueueId %u: checking [" UI64FMTD "] newToQueue(%u), currentQueue(%u)", queueId, frontguid, uint32(newToQueue.size()), uint32(currentQueue.size()));
firstNew.push_back(frontguid);
newToQueue.pop_front();
uint32 proposalId = itProposal->first;
LfgProposal& proposal = m_Proposals[proposalId];
LfgGuidList temporalList = currentQueue;
if (LfgProposal* pProposal = FindNewGroups(firstNew, temporalList)) // Group found!
uint64 guid = 0;
for (LfgProposalPlayerMap::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers)
{
// Remove groups in the proposal from new and current queues (not from queue map)
for (LfgGuidList::const_iterator itQueue = pProposal->queues.begin(); itQueue != pProposal->queues.end(); ++itQueue)
guid = itPlayers->first;
SetState(guid, LFG_STATE_PROPOSAL);
if (uint64 gguid = GetGroup(guid))
{
currentQueue.remove(*itQueue);
newToQueue.remove(*itQueue);
SetState(gguid, LFG_STATE_PROPOSAL);
SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
}
m_Proposals[++m_lfgProposalId] = pProposal;
uint64 guid = 0;
for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers)
{
guid = itPlayers->first;
SetState(guid, LFG_STATE_PROPOSAL);
if (Player* player = ObjectAccessor::FindPlayer(itPlayers->first))
{
Group* grp = player->GetGroup();
if (grp)
{
uint64 gguid = grp->GetGUID();
SetState(gguid, LFG_STATE_PROPOSAL);
player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
}
else
player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
player->GetSession()->SendLfgUpdateProposal(m_lfgProposalId, *pProposal);
}
}
if (pProposal->state == LFG_PROPOSAL_SUCCESS)
UpdateProposal(m_lfgProposalId, guid, true);
else
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid)));
SendLfgUpdateProposal(guid, m_lfgProposalId, proposal);
}
else
if (std::find(currentQueue.begin(), currentQueue.end(), frontguid) == currentQueue.end()) //already in queue?
currentQueue.push_back(frontguid); // Lfg group not found, add this group to the queue.
firstNew.clear();
if (proposal.state == LFG_PROPOSAL_SUCCESS)
UpdateProposal(proposalId, guid, true);
}
}
@@ -466,108 +438,14 @@ void LFGMgr::Update(uint32 diff)
if (m_QueueTimer > LFG_QUEUEUPDATE_INTERVAL)
{
m_QueueTimer = 0;
currTime = time(NULL);
for (LfgQueueInfoMap::const_iterator itQueue = m_QueueInfoMap.begin(); itQueue != m_QueueInfoMap.end(); ++itQueue)
{
LfgQueueInfo* queue = itQueue->second;
if (!queue)
{
sLog->outError(LOG_FILTER_LFG, "LFGMgr::Update: [" UI64FMTD "] queued with null queue info!", itQueue->first);
continue;
}
uint32 dungeonId = (*queue->dungeons.begin());
uint32 queuedTime = uint32(currTime - queue->joinTime);
uint8 role = PLAYER_ROLE_NONE;
for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer)
role |= itPlayer->second;
role &= ~PLAYER_ROLE_LEADER;
int32 waitTime = -1;
switch (role)
{
case PLAYER_ROLE_NONE: // Should not happen - just in case
waitTime = -1;
break;
case PLAYER_ROLE_TANK:
waitTime = m_waitTimesTank.time;
break;
case PLAYER_ROLE_HEALER:
waitTime = m_waitTimesHealer.time;
break;
case PLAYER_ROLE_DAMAGE:
waitTime = m_waitTimesDps.time;
break;
default:
waitTime = m_waitTimesAvg.time;
break;
}
LfgQueueStatusData queueData(dungeonId, waitTime, m_waitTimesAvg.time, m_waitTimesTank.time,
m_waitTimesHealer.time, m_waitTimesDps.time, queuedTime,
queue->tanks, queue->healers, queue->dps);
for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer)
SendLfgQueueStatus(itPlayer->first, queueData);
}
time_t currTime = time(NULL);
for (LfgQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it)
it->second.UpdateQueueTimers(currTime);
}
else
m_QueueTimer += diff;
}
/**
Add a guid to the queue of guids to be added to main queue. It guid its already
in queue does nothing. If this function is called guid is not in the main queue
(No need to check it here)
@param[in] guid Player or group guid to add to queue
@param[in] queueId Queue Id to add player/group to
*/
void LFGMgr::AddToQueue(uint64 guid, uint8 queueId)
{
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
queueId = 0;
LfgGuidList& list = m_newToQueue[queueId];
if (std::find(list.begin(), list.end(), guid) != list.end())
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::AddToQueue: [" UI64FMTD "] already in new queue. ignoring", guid);
else
{
list.push_back(guid);
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::AddToQueue: [" UI64FMTD "] added to m_newToQueue (size: %u)", guid, uint32(list.size()));
}
}
/**
Removes a guid from the main and new queues.
@param[in] guid Player or group guid to add to queue
@return true if guid was found in main queue.
*/
bool LFGMgr::RemoveFromQueue(uint64 guid)
{
for (LfgGuidListMap::iterator it = m_currentQueue.begin(); it != m_currentQueue.end(); ++it)
it->second.remove(guid);
for (LfgGuidListMap::iterator it = m_newToQueue.begin(); it != m_newToQueue.end(); ++it)
it->second.remove(guid);
RemoveFromCompatibles(guid);
LfgQueueInfoMap::iterator it = m_QueueInfoMap.find(guid);
if (it != m_QueueInfoMap.end())
{
delete it->second;
m_QueueInfoMap.erase(it);
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromQueue: [" UI64FMTD "] removed", guid);
return true;
}
else
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromQueue: [" UI64FMTD "] not in queue", guid);
return false;
}
}
/**
Generate the dungeon lock map for a given player
@@ -663,8 +541,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
}
// Already in queue?
LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(gguid);
if (itQueue != m_QueueInfoMap.end())
LfgState state = GetState(gguid);
if (state == LFG_STATE_QUEUED)
{
LfgDungeonSet const& playerDungeons = GetSelectedDungeons(guid);
if (playerDungeons == dungeons) // Joining the same dungeons -- Send OK
@@ -680,7 +558,10 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
return;
}
else // Remove from queue and rejoin
RemoveFromQueue(gguid);
{
LfgQueue& queue = GetQueue(gguid);
queue.RemoveFromQueue(gguid);
}
}
// Check player or group member restrictions
@@ -825,18 +706,10 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
}
else // Add player to queue
{
// Queue player
LfgQueueInfo* pqInfo = new LfgQueueInfo();
pqInfo->joinTime = time_t(time(NULL));
pqInfo->roles[player->GetGUID()] = roles;
pqInfo->dungeons = dungeons;
if (roles & PLAYER_ROLE_TANK)
--pqInfo->tanks;
else if (roles & PLAYER_ROLE_HEALER)
--pqInfo->healers;
else
--pqInfo->dps;
m_QueueInfoMap[guid] = pqInfo;
LfgRolesMap rolesMap;
rolesMap[guid] = roles;
LfgQueue& queue = GetQueue(gguid);
queue.AddQueueData(guid, time_t(time(NULL)), dungeons, rolesMap);
if (!isContinue)
{
@@ -853,7 +726,6 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
SetState(gguid, LFG_STATE_QUEUED);
SetRoles(guid, roles);
debugNames.append(player->GetName());
AddToQueue(guid, uint8(player->GetTeam()));
}
if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG))
@@ -882,7 +754,8 @@ void LFGMgr::LeaveLfg(uint64 guid)
case LFG_STATE_QUEUED:
if (gguid)
{
RemoveFromQueue(gguid);
LfgQueue& queue = GetQueue(gguid);
queue.RemoveFromQueue(gguid);
RestoreState(gguid, "Leave queue");
const LfgGuidSet& players = GetPlayers(gguid);
for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it)
@@ -894,7 +767,8 @@ void LFGMgr::LeaveLfg(uint64 guid)
}
else
{
RemoveFromQueue(guid);
LfgQueue& queue = GetQueue(guid);
queue.RemoveFromQueue(guid);
SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE));
ClearState(guid, "Leave queue");
}
@@ -910,8 +784,8 @@ void LFGMgr::LeaveLfg(uint64 guid)
uint64 pguid = gguid == guid ? GetLeader(gguid) : guid;
while (it != m_Proposals.end())
{
LfgProposalPlayerMap::iterator itPlayer = it->second->players.find(pguid);
if (itPlayer != it->second->players.end())
LfgProposalPlayerMap::iterator itPlayer = it->second.players.find(pguid);
if (itPlayer != it->second.players.end())
{
// Mark the player/leader of group who left as didn't accept the proposal
itPlayer->second.accept = LFG_ANSWER_DENY;
@@ -930,279 +804,6 @@ void LFGMgr::LeaveLfg(uint64 guid)
}
}
/**
Checks que main queue to try to form a Lfg group. Returns first match found (if any)
@param[in] check List of guids trying to match with other groups
@param[in] all List of all other guids in main queue to match against
@return Pointer to proposal, if match is found
*/
LfgProposal* LFGMgr::FindNewGroups(LfgGuidList& check, LfgGuidList& all)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FindNewGroup: (%s) - all(%s)", ConcatenateGuids(check).c_str(), ConcatenateGuids(all).c_str());
LfgProposal* pProposal = NULL;
if (check.empty() || check.size() > MAXGROUPSIZE || !CheckCompatibility(check, pProposal))
return NULL;
// Try to match with queued groups
while (!pProposal && !all.empty())
{
check.push_back(all.front());
all.pop_front();
pProposal = FindNewGroups(check, all);
check.pop_back();
}
return pProposal;
}
/**
Check compatibilities between groups
@param[in] check List of guids to check compatibilities
@param[out] pProposal Proposal found if groups are compatibles and Match
@return true if group are compatibles
*/
bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal)
{
if (pProposal) // Do not check anything if we already have a proposal
return false;
std::string strGuids = ConcatenateGuids(check);
if (check.size() > MAXGROUPSIZE || check.empty())
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s): Size wrong - Not compatibles", strGuids.c_str());
return false;
}
if (check.size() == 1 && IS_PLAYER_GUID(check.front())) // Player joining dungeon... compatible
return true;
// Previously cached?
LfgAnswer answer = GetCompatibles(strGuids);
if (answer != LFG_ANSWER_PENDING)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) compatibles (cached): %d", strGuids.c_str(), answer);
return bool(answer);
}
// Check all but new compatiblitity
if (check.size() > 2)
{
uint64 frontGuid = check.front();
check.pop_front();
// Check all-but-new compatibilities (New, A, B, C, D) --> check(A, B, C, D)
if (!CheckCompatibility(check, pProposal)) // Group not compatible
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) not compatibles (%s not compatibles)", strGuids.c_str(), ConcatenateGuids(check).c_str());
SetCompatibles(strGuids, false);
return false;
}
check.push_front(frontGuid);
// all-but-new compatibles, now check with new
}
uint8 numPlayers = 0;
uint8 numLfgGroups = 0;
uint64 groupGuid = 0;
LfgQueueInfoMap pqInfoMap;
for (LfgGuidList::const_iterator it = check.begin(); it != check.end() && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++it)
{
uint64 guid = (*it);
LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(guid);
if (itQueue == m_QueueInfoMap.end() || GetState(guid) != LFG_STATE_QUEUED)
{
sLog->outError(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: [" UI64FMTD "] is not queued but listed as queued!", (*it));
RemoveFromQueue(guid);
return false;
}
pqInfoMap[guid] = itQueue->second;
numPlayers += itQueue->second->roles.size();
if (IS_GROUP(guid))
{
if (Group* grp = sGroupMgr->GetGroupByGUID(GUID_LOPART(guid)))
if (grp->isLFGGroup())
{
if (!numLfgGroups)
groupGuid = guid;
++numLfgGroups;
}
}
}
if (check.size() == 1 && numPlayers != MAXGROUPSIZE) // Single group with less than MAXGROUPSIZE - Compatibles
return true;
// Do not match - groups already in a lfgDungeon or too much players
if (numLfgGroups > 1 || numPlayers > MAXGROUPSIZE)
{
SetCompatibles(strGuids, false);
if (numLfgGroups > 1)
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) More than one Lfggroup (%u)", strGuids.c_str(), numLfgGroups);
else
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Too much players (%u)", strGuids.c_str(), numPlayers);
return false;
}
// ----- Player checks -----
LfgRolesMap rolesMap;
uint64 leader = 0;
for (LfgQueueInfoMap::const_iterator it = pqInfoMap.begin(); it != pqInfoMap.end(); ++it)
{
for (LfgRolesMap::const_iterator itRoles = it->second->roles.begin(); itRoles != it->second->roles.end(); ++itRoles)
{
// Assign new leader
if (itRoles->second & PLAYER_ROLE_LEADER && (!leader || urand(0, 1)))
leader = itRoles->first;
rolesMap[itRoles->first] = itRoles->second;
}
}
if (rolesMap.size() != numPlayers) // Player in multiples queues!
return false;
LfgGuidSet players;
for (LfgRolesMap::const_iterator it = rolesMap.begin(); it != rolesMap.end(); ++it)
{
uint64 guid = it->first;
LfgRolesMap::const_iterator it2 = ++it;
for (; it2 != rolesMap.end(); ++it2)
if (HasIgnore(guid, it2->first))
break;
if (it2 == rolesMap.end())
players.insert(guid);
}
// if we dont have the same ammount of players then we have self ignoring candidates or different faction groups
// otherwise check if roles are compatible
if (players.size() != numPlayers || !CheckGroupRoles(rolesMap))
{
if (players.size() == numPlayers)
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Roles not compatible", strGuids.c_str());
SetCompatibles(strGuids, false);
return false;
}
// ----- Selected Dungeon checks -----
// Check if there are any compatible dungeon from the selected dungeons
LfgDungeonSet compatibleDungeons;
LfgQueueInfoMap::const_iterator itFirst = pqInfoMap.begin();
for (LfgDungeonSet::const_iterator itDungeon = itFirst->second->dungeons.begin(); itDungeon != itFirst->second->dungeons.end(); ++itDungeon)
{
LfgQueueInfoMap::const_iterator itOther = itFirst;
++itOther;
while (itOther != pqInfoMap.end() && itOther->second->dungeons.find(*itDungeon) != itOther->second->dungeons.end())
++itOther;
if (itOther == pqInfoMap.end())
compatibleDungeons.insert(*itDungeon);
}
LfgLockPartyMap lockMap;
GetCompatibleDungeons(compatibleDungeons, players, lockMap);
if (compatibleDungeons.empty())
{
SetCompatibles(strGuids, false);
return false;
}
SetCompatibles(strGuids, true);
// ----- Group is compatible, if we have MAXGROUPSIZE members then match is found
if (numPlayers != MAXGROUPSIZE)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Compatibles but not match. Players(%u)", strGuids.c_str(), numPlayers);
uint8 Tanks_Needed = LFG_TANKS_NEEDED;
uint8 Healers_Needed = LFG_HEALERS_NEEDED;
uint8 Dps_Needed = LFG_DPS_NEEDED;
for (LfgQueueInfoMap::const_iterator itQueue = pqInfoMap.begin(); itQueue != pqInfoMap.end(); ++itQueue)
{
LfgQueueInfo* queue = itQueue->second;
for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer)
{
uint8 roles = itPlayer->second;
if ((roles & PLAYER_ROLE_TANK) && Tanks_Needed > 0)
--Tanks_Needed;
else if ((roles & PLAYER_ROLE_HEALER) && Healers_Needed > 0)
--Healers_Needed;
else if ((roles & PLAYER_ROLE_DAMAGE) && Dps_Needed > 0)
--Dps_Needed;
}
}
for (LfgGuidSet::const_iterator itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers)
{
for (LfgQueueInfoMap::const_iterator itQueue = pqInfoMap.begin(); itQueue != pqInfoMap.end(); ++itQueue)
{
if (LfgQueueInfo* queue = itQueue->second)
{
LfgRolesMap::const_iterator itPlayer = queue->roles.find(*itPlayers);
if (itPlayer != queue->roles.end())
{
queue->tanks = Tanks_Needed;
queue->healers = Healers_Needed;
queue->dps = Dps_Needed;
}
}
}
}
return true;
}
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) MATCH! Group formed", strGuids.c_str());
// GROUP FORMED!
// TODO - Improve algorithm to select proper group based on Item Level
// Do not match bad tank and bad healer on same group
// Select a random dungeon from the compatible list
// TODO - Select the dungeon based on group item Level, not just random
// Create a new proposal
pProposal = new LfgProposal(Trinity::Containers::SelectRandomContainerElement(compatibleDungeons));
pProposal->cancelTime = time_t(time(NULL)) + LFG_TIME_PROPOSAL;
pProposal->state = LFG_PROPOSAL_INITIATING;
pProposal->queues = check;
pProposal->group = groupGuid;
pProposal->isNew = true;
// Assign new roles to players and assign new leader
LfgGuidSet::const_iterator itPlayers = players.begin();
if (!leader)
{
uint8 pos = uint8(urand(0, players.size() - 1));
for (uint8 i = 0; i < pos; ++i)
++itPlayers;
leader = *itPlayers;
}
pProposal->leader = leader;
uint8 numAccept = 0;
for (itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers)
{
uint64 guid = *itPlayers;
LfgProposalPlayer& player = pProposal->players[guid];
uint64 gguid = GetGroup(guid);
player.group = gguid;
if (gguid == groupGuid)
{
player.accept = LFG_ANSWER_AGREE;
++numAccept;
}
player.role = rolesMap[guid];
if (pProposal->group && pProposal->group != gguid)
pProposal->isNew = false;
}
if (numAccept == MAXGROUPSIZE)
pProposal->state = LFG_PROPOSAL_SUCCESS;
return true;
}
/**
Update the Role check info with the player selected role.
@@ -1254,15 +855,6 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /*
for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
{
uint64 pguid = it->first;
Player* plrg = ObjectAccessor::FindPlayer(pguid);
if (!plrg)
{
if (roleCheck.state == LFG_ROLECHECK_FINISHED)
SetState(pguid, LFG_STATE_QUEUED);
else if (roleCheck.state != LFG_ROLECHECK_INITIALITING)
ClearState(pguid, "Offline while Rolecheck");
continue;
}
if (!sendRoleChosen)
SendLfgRoleChosen(pguid, guid, roles);
@@ -1274,6 +866,7 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /*
continue;
case LFG_ROLECHECK_FINISHED:
SetState(pguid, LFG_STATE_QUEUED);
SetRoles(pguid, it->second);
SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid)));
break;
default:
@@ -1288,84 +881,13 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /*
if (roleCheck.state == LFG_ROLECHECK_FINISHED)
{
SetState(gguid, LFG_STATE_QUEUED);
LfgQueueInfo* pqInfo = new LfgQueueInfo();
pqInfo->joinTime = time_t(time(NULL));
pqInfo->roles = roleCheck.roles;
pqInfo->dungeons = roleCheck.dungeons;
// Set queue roles needed - As we are using check_roles will not have more that 1 tank, 1 healer, 3 dps
for (LfgRolesMap::const_iterator it = check_roles.begin(); it != check_roles.end(); ++it)
{
uint8 roles2 = it->second;
if (roles2 & PLAYER_ROLE_TANK)
--pqInfo->tanks;
else if (roles2 & PLAYER_ROLE_HEALER)
--pqInfo->healers;
else
--pqInfo->dps;
}
uint8 team = GetTeam(guid);
m_QueueInfoMap[gguid] = pqInfo;
if (GetState(gguid) != LFG_STATE_NONE)
{
LfgGuidList& currentQueue = m_currentQueue[team];
currentQueue.push_front(gguid);
}
AddToQueue(gguid, team);
LfgQueue& queue = GetQueue(gguid);
queue.AddQueueData(gguid, time_t(time(NULL)), roleCheck.dungeons, roleCheck.roles);
}
else if (roleCheck.state != LFG_ROLECHECK_INITIALITING)
{
RestoreState(gguid, "Rolecheck Failed");
m_RoleChecks.erase(itRoleCheck);
}
}
/**
Remove from cached compatible dungeons any entry that contains the given guid
@param[in] guid Guid to remove from compatible cache
*/
void LFGMgr::RemoveFromCompatibles(uint64 guid)
{
std::stringstream out;
out << guid;
std::string strGuid = out.str();
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromCompatibles: Removing [" UI64FMTD "]", guid);
for (LfgCompatibleMap::iterator itNext = m_CompatibleMap.begin(); itNext != m_CompatibleMap.end();)
{
LfgCompatibleMap::iterator it = itNext++;
if (it->first.find(strGuid) != std::string::npos) // Found, remove it
m_CompatibleMap.erase(it);
}
}
/**
Stores the compatibility of a list of guids
@param[in] key String concatenation of guids (| used as separator)
@param[in] compatibles Compatibles or not
*/
void LFGMgr::SetCompatibles(std::string key, bool compatibles)
{
m_CompatibleMap[key] = LfgAnswer(compatibles);
}
/**
Get the compatibility of a group of guids
@param[in] key String concatenation of guids (| used as separator)
@return 1 (Compatibles), 0 (Not compatibles), -1 (Not set)
*/
LfgAnswer LFGMgr::GetCompatibles(std::string key)
{
LfgAnswer answer = LFG_ANSWER_PENDING;
LfgCompatibleMap::iterator it = m_CompatibleMap.find(key);
if (it != m_CompatibleMap.end())
answer = it->second;
return answer;
m_RoleChecks.erase(itRoleCheck);
}
/**
@@ -1487,7 +1009,7 @@ void LFGMgr::MakeNewGroup(const LfgProposal& proposal)
else
players.push_back(guid);
if (GetGroup(guid) != proposal.group || GetState(proposal.group) == LFG_STATE_FINISHED_DUNGEON)
if (proposal.isNew || GetGroup(guid) != proposal.group)
playersToTeleport.push_back(guid);
}
@@ -1520,7 +1042,6 @@ void LFGMgr::MakeNewGroup(const LfgProposal& proposal)
grp->AddMember(player);
grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role);
SetState(pguid, LFG_STATE_DUNGEON);
// Add the cooldown spell if queued for a random dungeon
if (dungeon->type == LFG_TYPE_RANDOM)
@@ -1543,6 +1064,12 @@ void LFGMgr::MakeNewGroup(const LfgProposal& proposal)
grp->SendUpdate();
}
uint32 LFGMgr::AddProposal(LfgProposal const& proposal)
{
m_Proposals[++m_lfgProposalId] = proposal;
return m_lfgProposalId;
}
/**
Update Proposal info with player answer
@@ -1557,7 +1084,7 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
if (itProposal == m_Proposals.end())
return;
LfgProposal& proposal = *(itProposal->second);
LfgProposal& proposal = itProposal->second;
// Check if proposal have the current player
LfgProposalPlayerMap::iterator itProposalPlayer = proposal.players.find(guid);
@@ -1594,26 +1121,30 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
proposal.state = LFG_PROPOSAL_SUCCESS;
time_t joinTime = time_t(time(NULL));
LfgQueue& queue = GetQueue(guid);
LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND);
for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
{
uint64 pguid = it->first;
uint64 gguid = it->second.group;
uint32 dungeonId = (*GetSelectedDungeons(pguid).begin());
int32 waitTime = -1;
if (sendUpdate)
SendLfgUpdateProposal(pguid, proposalId, proposal);
if (gguid)
{
waitTime = int32((joinTime - queue.GetJoinTime(gguid)) / IN_MILLISECONDS);
SendLfgUpdateParty(pguid, updateData);
}
else
{
waitTime = int32((joinTime - queue.GetJoinTime(pguid)) / IN_MILLISECONDS);
SendLfgUpdatePlayer(pguid, updateData);
uint64 guid2 = gguid ? gguid : pguid;
LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(guid2);
if (itQueue != m_QueueInfoMap.end())
waitTime = int32(joinTime - itQueue->second->joinTime);
}
updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE;
SendLfgUpdatePlayer(pguid, updateData);
SendLfgUpdateParty(pguid, updateData);
// Update timers
uint8 role = GetRoles(pguid);
@@ -1621,16 +1152,16 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
switch (role)
{
case PLAYER_ROLE_DAMAGE:
UpdateWaitTimeDps(waitTime);
queue.UpdateWaitTimeDps(waitTime, dungeonId);
break;
case PLAYER_ROLE_HEALER:
UpdateWaitTimeHealer(waitTime);
queue.UpdateWaitTimeHealer(waitTime, dungeonId);
break;
case PLAYER_ROLE_TANK:
UpdateWaitTimeTank(waitTime);
queue.UpdateWaitTimeTank(waitTime, dungeonId);
break;
default:
UpdateWaitTimeAvg(waitTime);
queue.UpdateWaitTimeAvg(waitTime, dungeonId);
break;
}
@@ -1640,10 +1171,9 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
// Remove players/groups from Queue
for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it)
RemoveFromQueue(*it);
queue.RemoveFromQueue(*it);
MakeNewGroup(proposal);
delete itProposal->second;
m_Proposals.erase(itProposal);
}
@@ -1655,7 +1185,7 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
*/
void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type)
{
LfgProposal& proposal = *(itProposal->second);
LfgProposal& proposal = itProposal->second;
proposal.state = LFG_PROPOSAL_FAILED;
sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Proposal %u, state FAILED, UpdateType %u", itProposal->first, type);
@@ -1725,11 +1255,12 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t
}
}
LfgQueue& queue = GetQueue(proposal.players.begin()->first);
// Remove players/groups from queue
for (LfgGuidSet::const_iterator it = toRemove.begin(); it != toRemove.end(); ++it)
{
uint64 guid = *it;
RemoveFromQueue(guid);
queue.RemoveFromQueue(guid);
proposal.queues.remove(guid);
}
@@ -1737,13 +1268,9 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t
for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it)
{
uint64 guid = *it;
uint8 team = GetTeam(guid);
LfgGuidList& currentQueue = m_currentQueue[team];
currentQueue.push_front(guid); //Add GUID for high priority
AddToQueue(guid, team); //We have to add each GUID in newQueue to check for a new groups
queue.AddToQueue(guid);
}
delete itProposal->second;
m_Proposals.erase(itProposal);
}
@@ -1838,16 +1365,7 @@ void LFGMgr::UpdateBoot(uint64 guid, bool accept)
if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player
{
if (Group* group = sGroupMgr->GetGroupByGUID(GUID_LOPART(gguid)))
Player::RemoveFromGroup(group, boot.victim);
if (Player* victim = ObjectAccessor::FindPlayer(boot.victim))
{
TeleportPlayer(victim, true, false);
SetState(boot.victim, LFG_STATE_NONE);
}
if (Player* leader = ObjectAccessor::FindPlayer(sLFGMgr->GetLeader(gguid)))
leader->GetSession()->SendLfgOfferContinue(GetDungeon(gguid, false));
Player::RemoveFromGroup(group, boot.victim, GROUP_REMOVEMETHOD_KICK_LFG);
DecreaseKicksLeft(gguid);
}
m_Boots.erase(itBoot);
@@ -2338,6 +1856,32 @@ bool LFGMgr::IsLfgGroup(uint64 guid)
return guid && IS_GROUP(guid) && m_Groups[guid].IsLfgGroup();
}
LfgQueue& LFGMgr::GetQueue(uint64 guid)
{
uint8 queueId = 0;
if (IS_GROUP(guid))
{
const LfgGuidSet& players = GetPlayers(guid);
uint64 pguid = players.empty() ? 0 : (*players.begin());
if (pguid)
queueId = GetTeam(pguid);
}
else
queueId = GetTeam(guid);
return m_Queues[queueId];
}
bool LFGMgr::AllQueued(const LfgGuidList& check)
{
if (check.empty())
return false;
for (LfgGuidList::const_iterator it = check.begin(); it != check.end(); ++it)
if (GetState(*it) != LFG_STATE_QUEUED)
return false;
return true;
}
bool LFGMgr::IsSeasonActive(uint32 dungeonId)
{
switch (dungeonId)
@@ -2353,50 +1897,3 @@ bool LFGMgr::IsSeasonActive(uint32 dungeonId)
}
return false;
}
void LFGMgr::UpdateWaitTimeAvg(int32 waitTime)
{
LfgWaitTime &wt = m_waitTimesAvg;
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LFGMgr::UpdateWaitTimeTank(int32 waitTime)
{
LfgWaitTime &wt = m_waitTimesTank;
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LFGMgr::UpdateWaitTimeHealer(int32 waitTime)
{
LfgWaitTime &wt = m_waitTimesHealer;
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LFGMgr::UpdateWaitTimeDps(int32 waitTime)
{
LfgWaitTime &wt = m_waitTimesDps;
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
/**
Given a list of guids returns the concatenation using | as delimiter
@param[in] check list of guids
@returns Concatenated string
*/
std::string LFGMgr::ConcatenateGuids(LfgGuidList check)
{
if (check.empty())
return "";
std::ostringstream o;
LfgGuidList::const_iterator it = check.begin();
o << (*it);
for (++it; it != check.end(); ++it)
o << '|' << (*it);
return o.str();
}

View File

@@ -21,7 +21,7 @@
#include "Common.h"
#include <ace/Singleton.h>
#include "LFG.h"
#include "LFGQueue.h"
#include "LFGGroupData.h"
#include "LFGPlayerData.h"
@@ -125,31 +125,19 @@ struct LfgProposal;
struct LfgProposalPlayer;
struct LfgPlayerBoot;
typedef std::map<uint8, LfgGuidList> LfgGuidListMap;
typedef std::set<Player*> PlayerSet;
typedef std::list<Player*> LfgPlayerList;
typedef std::map<std::string, LfgAnswer> LfgCompatibleMap;
typedef std::map<uint64, LfgQueueInfo*> LfgQueueInfoMap;
typedef std::map<uint8, LfgQueue> LfgQueueMap;
typedef std::multimap<uint32, LfgReward const*> LfgRewardMap;
typedef std::pair<LfgRewardMap::const_iterator, LfgRewardMap::const_iterator> LfgRewardMapBounds;
typedef std::map<uint8, LfgDungeonSet> LfgCachedDungeonMap;
typedef std::map<uint64, LfgAnswer> LfgAnswerMap;
typedef std::map<uint64, LfgRoleCheck> LfgRoleCheckMap;
typedef std::map<uint32, LfgProposal*> LfgProposalMap;
typedef std::map<uint32, LfgProposal> LfgProposalMap;
typedef std::map<uint64, LfgProposalPlayer> LfgProposalPlayerMap;
typedef std::map<uint64, LfgPlayerBoot> LfgPlayerBootMap;
typedef std::map<uint64, LfgGroupData> LfgGroupDataMap;
typedef std::map<uint64, LfgPlayerData> LfgPlayerDataMap;
typedef UNORDERED_MAP<uint32, LFGDungeonEntry> LFGDungeonMap;
struct LfgWaitTime
{
LfgWaitTime(): time(-1), number(0) {}
int32 time; ///< Wait time
uint32 number; ///< Number of people used to get that wait time
};
// Data needed by SMSG_LFG_JOIN_RESULT
struct LfgJoinResultData
{
@@ -215,18 +203,6 @@ struct LfgReward
}
};
/// Stores player or group queue info
struct LfgQueueInfo
{
LfgQueueInfo(): joinTime(0), tanks(LFG_TANKS_NEEDED), healers(LFG_HEALERS_NEEDED), dps(LFG_DPS_NEEDED) {};
time_t joinTime; ///< Player queue join time (to calculate wait times)
uint8 tanks; ///< Tanks needed
uint8 healers; ///< Healers needed
uint8 dps; ///< Dps needed
LfgDungeonSet dungeons; ///< Selected Player/Group Dungeon/s
LfgRolesMap roles; ///< Selected Player Role/s
};
/// Stores player data related to proposal to join
struct LfgProposalPlayer
{
@@ -371,6 +347,7 @@ class LFGMgr
bool IsTeleported(uint64 guid);
bool AllQueued(LfgGuidList const& check);
static bool HasIgnore(uint64 guid1, uint64 guid2);
static void SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data);
@@ -395,29 +372,15 @@ class LFGMgr
void SetLockedDungeons(uint64 guid, LfgLockMap const& lock);
void DecreaseKicksLeft(uint64 guid);
// Queue
void AddToQueue(uint64 guid, uint8 queueId);
bool RemoveFromQueue(uint64 guid);
// Proposals
void RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type);
void MakeNewGroup(LfgProposal const& proposal);
// Group Matching
LfgProposal* FindNewGroups(LfgGuidList& check, LfgGuidList& all);
bool CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal);
void SetCompatibles(std::string concatenatedGuids, bool compatibles);
LfgAnswer GetCompatibles(std::string concatenatedGuids);
// Generic
LfgQueue &GetQueue(uint64 guid);
LfgDungeonSet const& GetDungeonsByRandom(uint32 randomdungeon);
LfgType GetDungeonType(uint32 dungeon);
std::string ConcatenateGuids(LfgGuidList check);
void RemoveFromCompatibles(uint64 guid);
void UpdateWaitTimeAvg(int32 waitTime);
void UpdateWaitTimeTank(int32 waitTime);
void UpdateWaitTimeHealer(int32 waitTime);
void UpdateWaitTimeDps(int32 waitTime);
void SendLfgBootProposalUpdate(uint64 guid, LfgPlayerBoot const& boot);
void SendLfgJoinResult(uint64 guid, LfgJoinResultData const& data);
void SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles);
@@ -431,6 +394,7 @@ class LFGMgr
uint32 m_lfgProposalId; ///< used as internal counter for proposals
uint32 m_options; ///< Stores config options
LfgQueueMap m_Queues; ///< Queues
LfgCachedDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType
// Reward System
LfgRewardMap m_RewardMap; ///< Stores rewards for random dungeons
@@ -443,16 +407,6 @@ class LFGMgr
LfgGroupDataMap m_Groups; ///< Group data
LfgGuidList m_teleport; ///< Players being teleported
// Queue
LfgQueueInfoMap m_QueueInfoMap; ///< Queued groups
LfgGuidListMap m_currentQueue; ///< Ordered list. Used to find groups
LfgGuidListMap m_newToQueue; ///< New groups to add to queue
LfgCompatibleMap m_CompatibleMap; ///< Compatible dungeons
LfgWaitTime m_waitTimesAvg; ///< Average wait time to find a group queuing as multiple roles
LfgWaitTime m_waitTimesTank; ///< Average wait time to find a group queuing as tank
LfgWaitTime m_waitTimesHealer; ///< Average wait time to find a group queuing as healer
LfgWaitTime m_waitTimesDps; ///< Average wait time to find a group queuing as dps
LFGPlayerScript *m_lfgPlayerScript;
LFGGroupScript *m_lfgGroupScript;
};

View File

@@ -31,6 +31,9 @@ void LfgPlayerData::SetState(LfgState state)
{
case LFG_STATE_NONE:
case LFG_STATE_FINISHED_DUNGEON:
m_Roles = 0;
m_SelectedDungeons.clear();
// No break on purpose
case LFG_STATE_DUNGEON:
m_OldState = state;
// No break on purpose

View File

@@ -0,0 +1,539 @@
/*
* Copyright (C) 2008-2012 TrinityCore <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, see <http://www.gnu.org/licenses/>.
*/
#include "ObjectDefines.h"
#include "Containers.h"
#include "DBCStructure.h"
#include "DBCStores.h"
#include "Group.h"
#include "LFGQueue.h"
#include "LFGMgr.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "World.h"
/**
Given a list of guids returns the concatenation using | as delimiter
@param[in] check list of guids
@returns Concatenated string
*/
std::string ConcatenateGuids(LfgGuidList const& check)
{
if (check.empty())
return "";
// need the guids in order to avoid duplicates
LfgGuidSet guids(check.begin(), check.end());
std::ostringstream o;
LfgGuidSet::const_iterator it = guids.begin();
o << (*it);
for (++it; it != guids.end(); ++it)
o << '|' << (*it);
return o.str();
}
char const * GetCompatibleString(LfgCompatibility compatibles)
{
switch (compatibles)
{
case LFG_COMPATIBILITY_PENDING:
return "Pending";
case LFG_COMPATIBLES_BAD_STATES:
return "Compatibles (Bad States)";
case LFG_COMPATIBLES_MATCH:
return "Match";
case LFG_COMPATIBLES_WITH_LESS_PLAYERS:
return "Compatibles (Not enough players)";
case LFG_INCOMPATIBLES_HAS_IGNORES:
return "Has ignores";
case LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS:
return "Multiple Lfg Groups";
case LFG_INCOMPATIBLES_NO_DUNGEONS:
return "Incompatible dungeons";
case LFG_INCOMPATIBLES_NO_ROLES:
return "Incompatible roles";
case LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS:
return "Too much players";
case LFG_INCOMPATIBLES_WRONG_GROUP_SIZE:
return "Wrong group size";
default:
return "Unknown";
}
}
void LfgQueue::AddToQueue(uint64 guid)
{
LfgQueueDataMap::iterator itQueue = m_QueueDataMap.find(guid);
if (itQueue == m_QueueDataMap.end())
{
sLog->outError(LOG_FILTER_LFG, "LfgQueue::AddToQueue: Queue data not found for [" UI64FMTD "]", guid);
return;
}
AddToNewQueue(guid);
}
void LfgQueue::RemoveFromQueue(uint64 guid)
{
RemoveFromNewQueue(guid);
RemoveFromCurrentQueue(guid);
RemoveFromCompatibles(guid);
RemoveQueueData(guid);
}
void LfgQueue::AddToNewQueue(uint64 guid)
{
m_newToQueue.push_back(guid);
}
void LfgQueue::RemoveFromNewQueue(uint64 guid)
{
m_newToQueue.remove(guid);
}
void LfgQueue::AddToCurrentQueue(uint64 guid)
{
m_currentQueue.push_back(guid);
}
void LfgQueue::RemoveFromCurrentQueue(uint64 guid)
{
m_currentQueue.remove(guid);
}
void LfgQueue::AddQueueData(uint64 guid, time_t joinTime, const LfgDungeonSet &dungeons, const LfgRolesMap &rolesMap)
{
m_QueueDataMap[guid] = LfgQueueData(joinTime, dungeons, rolesMap);
AddToQueue(guid);
}
void LfgQueue::RemoveQueueData(uint64 guid)
{
LfgQueueDataMap::iterator it = m_QueueDataMap.find(guid);
if (it != m_QueueDataMap.end())
m_QueueDataMap.erase(it);
}
void LfgQueue::UpdateWaitTimeAvg(int32 waitTime, uint32 dungeonId)
{
LfgWaitTime &wt = m_waitTimesAvg[dungeonId];
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LfgQueue::UpdateWaitTimeTank(int32 waitTime, uint32 dungeonId)
{
LfgWaitTime &wt = m_waitTimesTank[dungeonId];
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LfgQueue::UpdateWaitTimeHealer(int32 waitTime, uint32 dungeonId)
{
LfgWaitTime &wt = m_waitTimesHealer[dungeonId];
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LfgQueue::UpdateWaitTimeDps(int32 waitTime, uint32 dungeonId)
{
LfgWaitTime &wt = m_waitTimesDps[dungeonId];
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
/**
Remove from cached compatible dungeons any entry that contains the given guid
@param[in] guid Guid to remove from compatible cache
*/
void LfgQueue::RemoveFromCompatibles(uint64 guid)
{
std::stringstream out;
out << guid;
std::string strGuid = out.str();
sLog->outDebug(LOG_FILTER_LFG, "LfgQueue::RemoveFromCompatibles: Removing [" UI64FMTD "]", guid);
for (LfgCompatibleMap::iterator itNext = m_CompatibleMap.begin(); itNext != m_CompatibleMap.end();)
{
LfgCompatibleMap::iterator it = itNext++;
if (it->first.find(strGuid) != std::string::npos) // Found, remove it
m_CompatibleMap.erase(it);
}
}
/**
Stores the compatibility of a list of guids
@param[in] key String concatenation of guids (| used as separator)
@param[in] compatibles type of compatibility
*/
void LfgQueue::SetCompatibles(const std::string &key, LfgCompatibility compatibles)
{
m_CompatibleMap[key] = compatibles;
}
/**
Get the compatibility of a group of guids
@param[in] key String concatenation of guids (| used as separator)
@return LfgCompatibility type of compatibility
*/
LfgCompatibility LfgQueue::GetCompatibles(std::string const& key)
{
LfgCompatibleMap::iterator it = m_CompatibleMap.find(key);
if (it != m_CompatibleMap.end())
return it->second;
return LFG_COMPATIBILITY_PENDING;
}
uint8 LfgQueue::FindGroups()
{
uint8 proposals = 0;
LfgGuidList firstNew;
while (!m_newToQueue.empty())
{
uint64 frontguid = m_newToQueue.front();
sLog->outDebug(LOG_FILTER_LFG, "LfgQueue::FindGroups: checking [" UI64FMTD "] newToQueue(%u), currentQueue(%u)", frontguid, uint32(m_newToQueue.size()), uint32(m_currentQueue.size()));
firstNew.clear();
firstNew.push_back(frontguid);
RemoveFromNewQueue(frontguid);
LfgGuidList temporalList = m_currentQueue;
LfgCompatibility compatibles = FindNewGroups(firstNew, temporalList);
if (compatibles == LFG_COMPATIBLES_MATCH)
++proposals;
else
AddToCurrentQueue(frontguid); // Lfg group not found, add this group to the queue.
}
return proposals;
}
/**
Checks que main queue to try to form a Lfg group. Returns first match found (if any)
@param[in] check List of guids trying to match with other groups
@param[in] all List of all other guids in main queue to match against
@return LfgCompatibility type of compatibility between groups
*/
LfgCompatibility LfgQueue::FindNewGroups(LfgGuidList& check, LfgGuidList& all)
{
std::string strGuids = ConcatenateGuids(check);
LfgCompatibility compatibles = GetCompatibles(strGuids);
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::FindNewGroup: (%s): %s - all(%s)", strGuids.c_str(), GetCompatibleString(compatibles), ConcatenateGuids(all).c_str());
if (compatibles == LFG_COMPATIBILITY_PENDING || compatibles == LFG_COMPATIBLES_BAD_STATES) // Not previously cached, calculate
compatibles = CheckCompatibility(check);
if (compatibles != LFG_COMPATIBLES_WITH_LESS_PLAYERS)
return compatibles;
// Try to match with queued groups
while (!all.empty())
{
check.push_back(all.front());
all.pop_front();
LfgCompatibility subcompatibility = FindNewGroups(check, all);
if (subcompatibility == LFG_COMPATIBLES_MATCH)
return LFG_COMPATIBLES_MATCH;
check.pop_back();
}
return compatibles;
}
/**
Check compatibilities between groups. If group is Matched proposal will be created
@param[in] check List of guids to check compatibilities
@return LfgCompatibility type of compatibility
*/
LfgCompatibility LfgQueue::CheckCompatibility(LfgGuidList check)
{
std::string strGuids = ConcatenateGuids(check);
LfgProposal proposal;
LfgDungeonSet proposalDungeons;
LfgGroupsMap proposalGroups;
LfgRolesMap proposalRoles;
// Check for correct size
if (check.size() > MAXGROUPSIZE || check.empty())
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s): Size wrong - Not compatibles", strGuids.c_str());
return LFG_INCOMPATIBLES_WRONG_GROUP_SIZE;
}
// Player joining alone always compatible
if (check.size() == 1 && IS_PLAYER_GUID(check.front()))
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
// Check all-but-new compatiblitity
if (check.size() > 2)
{
uint64 frontGuid = check.front();
check.pop_front();
// Check all-but-new compatibilities (New, A, B, C, D) --> check(A, B, C, D)
LfgCompatibility child_compatibles = CheckCompatibility(check);
if (child_compatibles < LFG_COMPATIBLES_WITH_LESS_PLAYERS) // Group not compatible
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) child %s not compatibles", strGuids.c_str(), ConcatenateGuids(check).c_str());
SetCompatibles(strGuids, child_compatibles);
return child_compatibles;
}
check.push_front(frontGuid);
}
// Check if more than one LFG group and number of players joining
uint8 numPlayers = 0;
uint8 numLfgGroups = 0;
for (LfgGuidList::const_iterator it = check.begin(); it != check.end() && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++it)
{
uint64 guid = (*it);
LfgQueueDataMap::iterator itQueue = m_QueueDataMap.find(guid);
if (itQueue == m_QueueDataMap.end())
{
sLog->outError(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: [" UI64FMTD "] is not queued but listed as queued!", guid);
RemoveFromQueue(guid);
return LFG_COMPATIBILITY_PENDING;
}
// Store group so we don't need to call Mgr to get it later (if it's player group will be 0 otherwise would have joined as group)
for (LfgRolesMap::const_iterator it2 = itQueue->second.roles.begin(); it2 != itQueue->second.roles.end(); ++it2)
proposalGroups[it2->first] = IS_GROUP(itQueue->first) ? itQueue->first : 0;
numPlayers += itQueue->second.roles.size();
if (sLFGMgr->IsLfgGroup(guid))
{
if (!numLfgGroups)
proposal.group = guid;
++numLfgGroups;
}
}
// Group with less that MAXGROUPSIZE members always compatible
if (check.size() == 1 && numPlayers != MAXGROUPSIZE)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) sigle group. Compatibles", strGuids.c_str());
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
}
if (numLfgGroups > 1)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) More than one Lfggroup (%u)", strGuids.c_str(), numLfgGroups);
SetCompatibles(strGuids, LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS);
return LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS;
}
if (numPlayers > MAXGROUPSIZE)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Too much players (%u)", strGuids.c_str(), numPlayers);
SetCompatibles(strGuids, LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS);
return LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS;
}
// If it's single group no need to check for duplicate players, ignores, bad roles or bad dungeons as it's been checked before joining
if (check.size() > 1)
{
for (LfgGuidList::const_iterator it = check.begin(); it != check.end(); ++it)
{
const LfgRolesMap &roles = m_QueueDataMap[(*it)].roles;
for (LfgRolesMap::const_iterator itRoles = roles.begin(); itRoles != roles.end(); ++itRoles)
{
LfgRolesMap::const_iterator itPlayer;
for (itPlayer = proposalRoles.begin(); itPlayer != proposalRoles.end(); ++itPlayer)
{
if (itRoles->first == itPlayer->first)
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: ERROR! Player multiple times in queue! [" UI64FMTD "]", itRoles->first);
else if (sLFGMgr->HasIgnore(itRoles->first, itPlayer->first))
break;
}
if (itPlayer == proposalRoles.end())
proposalRoles[itRoles->first] = itRoles->second;
}
}
if (uint8 playersize = numPlayers - proposalRoles.size())
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) not compatible, %u players are ignoring each other", strGuids.c_str(), playersize);
SetCompatibles(strGuids, LFG_INCOMPATIBLES_HAS_IGNORES);
return LFG_INCOMPATIBLES_HAS_IGNORES;
}
LfgRolesMap debugRoles = proposalRoles; // DEBUG
if (!LFGMgr::CheckGroupRoles(proposalRoles))
{
std::ostringstream o;
for (LfgRolesMap::const_iterator it = debugRoles.begin(); it != debugRoles.end(); ++it)
o << ", " << it->first << ": " << sLFGMgr->GetRolesString(it->second);
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Roles not compatible%s", strGuids.c_str(), o.str().c_str());
SetCompatibles(strGuids, LFG_INCOMPATIBLES_NO_ROLES);
return LFG_INCOMPATIBLES_NO_ROLES;
}
LfgGuidList::iterator itguid = check.begin();
proposalDungeons = m_QueueDataMap[*itguid].dungeons;
std::ostringstream o;
o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(proposalDungeons) << ")";
for (++itguid; itguid != check.end(); ++itguid)
{
LfgDungeonSet temporal;
LfgDungeonSet &dungeons = m_QueueDataMap[*itguid].dungeons;
o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(dungeons) << ")";
std::set_intersection(proposalDungeons.begin(), proposalDungeons.end(), dungeons.begin(), dungeons.end(), std::inserter(temporal, temporal.begin()));
proposalDungeons = temporal;
}
if (proposalDungeons.empty())
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) No compatible dungeons%s", strGuids.c_str(), o.str().c_str());
SetCompatibles(strGuids, LFG_INCOMPATIBLES_NO_DUNGEONS);
return LFG_INCOMPATIBLES_NO_DUNGEONS;
}
}
else
{
uint64 gguid = *check.begin();
const LfgQueueData &queue = m_QueueDataMap[gguid];
proposalDungeons = queue.dungeons;
proposalRoles = queue.roles;
LFGMgr::CheckGroupRoles(proposalRoles); // assing new roles
}
// Enough players?
if (numPlayers != MAXGROUPSIZE)
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Compatibles but not enough players(%u)", strGuids.c_str(), numPlayers);
SetCompatibles(strGuids, LFG_COMPATIBLES_WITH_LESS_PLAYERS);
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
}
proposal.queues = check;
if (check.size() == 1)
{
for (LfgGroupsMap::const_iterator it = proposalGroups.begin(); it != proposalGroups.end(); ++it)
if (proposal.group && it->second != proposal.group)
proposal.isNew = false;
}
if (!sLFGMgr->AllQueued(check))
{
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Group MATCH but can't create proposal!", strGuids.c_str());
SetCompatibles(strGuids, LFG_COMPATIBLES_BAD_STATES);
return LFG_COMPATIBLES_BAD_STATES;
}
// Create a new proposal
proposal.cancelTime = time(NULL) + LFG_TIME_PROPOSAL;
proposal.state = LFG_PROPOSAL_INITIATING;
proposal.leader = 0;
proposal.dungeonId = Trinity::Containers::SelectRandomContainerElement(proposalDungeons);
bool leader = false;
for (LfgRolesMap::const_iterator itRoles = proposalRoles.begin(); itRoles != proposalRoles.end(); ++itRoles)
{
// Assing new leader
if (itRoles->second & PLAYER_ROLE_LEADER)
{
if (!leader || !proposal.leader || urand(0, 1))
proposal.leader = itRoles->first;
leader = true;
}
else if (!leader && (!proposal.leader || urand(0, 1)))
proposal.leader = itRoles->first;
// Assing player data and roles
LfgProposalPlayer &data = proposal.players[itRoles->first];
data.role = itRoles->second;
data.group = proposalGroups.find(itRoles->first)->second;
if (!proposal.isNew && data.group && data.group == proposal.group) // Player from existing group, autoaccept
data.accept = LFG_ANSWER_AGREE;
}
// Mark proposal members as not queued (but not remove queue data)
for (LfgGuidList::const_iterator itQueue = proposal.queues.begin(); itQueue != proposal.queues.end(); ++itQueue)
{
uint64 guid = (*itQueue);
RemoveFromNewQueue(guid);
RemoveFromCurrentQueue(guid);
}
sLFGMgr->AddProposal(proposal);
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) MATCH! Group formed", strGuids.c_str());
SetCompatibles(strGuids, LFG_COMPATIBLES_MATCH);
return LFG_COMPATIBLES_MATCH;
}
void LfgQueue::UpdateQueueTimers(time_t currTime)
{
for (LfgQueueDataMap::const_iterator itQueue = m_QueueDataMap.begin(); itQueue != m_QueueDataMap.end(); ++itQueue)
{
const LfgQueueData &queueinfo = itQueue->second;
uint32 dungeonId = (*queueinfo.dungeons.begin());
uint32 queuedTime = uint32(currTime - queueinfo.joinTime);
uint8 role = PLAYER_ROLE_NONE;
int32 waitTime = -1;
int32 wtTank = m_waitTimesTank[dungeonId].time;
int32 wtHealer = m_waitTimesHealer[dungeonId].time;
int32 wtDps = m_waitTimesDps[dungeonId].time;
int32 wtAvg = m_waitTimesAvg[dungeonId].time;
for (LfgRolesMap::const_iterator itPlayer = queueinfo.roles.begin(); itPlayer != queueinfo.roles.end(); ++itPlayer)
role |= itPlayer->second;
role &= ~PLAYER_ROLE_LEADER;
switch (role)
{
case PLAYER_ROLE_NONE: // Should not happen - just in case
waitTime = -1;
break;
case PLAYER_ROLE_TANK:
waitTime = wtTank;
break;
case PLAYER_ROLE_HEALER:
waitTime = wtHealer;
break;
case PLAYER_ROLE_DAMAGE:
waitTime = wtDps;
break;
default:
waitTime = wtAvg;
break;
}
LfgQueueStatusData queueData(dungeonId, waitTime, wtAvg, wtTank, wtHealer, wtDps, queuedTime, queueinfo.tanks, queueinfo.healers, queueinfo.dps);
for (LfgRolesMap::const_iterator itPlayer = queueinfo.roles.begin(); itPlayer != queueinfo.roles.end(); ++itPlayer)
{
uint64 pguid = itPlayer->first;
LFGMgr::SendLfgQueueStatus(pguid, queueData);
}
}
}
time_t LfgQueue::GetJoinTime(uint64 guid)
{
return m_QueueDataMap[guid].joinTime;
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2008-2012 TrinityCore <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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _LFGQUEUE_H
#define _LFGQUEUE_H
#include "LFG.h"
enum LfgCompatibility
{
LFG_COMPATIBILITY_PENDING,
LFG_INCOMPATIBLES_WRONG_GROUP_SIZE,
LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS,
LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS,
LFG_INCOMPATIBLES_HAS_IGNORES,
LFG_INCOMPATIBLES_NO_ROLES,
LFG_INCOMPATIBLES_NO_DUNGEONS,
LFG_COMPATIBLES_WITH_LESS_PLAYERS, // Values under this = not compatible (do not modify order)
LFG_COMPATIBLES_BAD_STATES,
LFG_COMPATIBLES_MATCH // Must be the last one
};
/// Stores player or group queue info
struct LfgQueueData
{
LfgQueueData(): joinTime(time_t(time(NULL))), tanks(LFG_TANKS_NEEDED),
healers(LFG_HEALERS_NEEDED), dps(LFG_DPS_NEEDED)
{ }
LfgQueueData(time_t _joinTime, const LfgDungeonSet &_dungeons, const LfgRolesMap &_roles)
{
joinTime = _joinTime;
dungeons = _dungeons;
roles = _roles;
tanks = LFG_TANKS_NEEDED;
healers = LFG_HEALERS_NEEDED;
dps = LFG_DPS_NEEDED;
for (LfgRolesMap::const_iterator it = roles.begin(); it != roles.end(); ++it)
{
uint8 role = it->second;
if (role & PLAYER_ROLE_TANK)
--tanks;
else if (role & PLAYER_ROLE_HEALER)
--healers;
else
--dps;
}
}
time_t joinTime; ///< Player queue join time (to calculate wait times)
uint8 tanks; ///< Tanks needed
uint8 healers; ///< Healers needed
uint8 dps; ///< Dps needed
LfgDungeonSet dungeons; ///< Selected Player/Group Dungeon/s
LfgRolesMap roles; ///< Selected Player Role/s
};
struct LfgWaitTime
{
LfgWaitTime(): time(-1), number(0) {}
int32 time; ///< Wait time
uint32 number; ///< Number of people used to get that wait time
};
typedef std::map<uint32, LfgWaitTime> LfgWaitTimesMap;
typedef std::map<std::string, LfgCompatibility> LfgCompatibleMap;
typedef std::map<uint64, LfgQueueData> LfgQueueDataMap;
/**
Stores all data related to queue
*/
class LfgQueue
{
public:
// Add/Remove from queue
void AddToQueue(uint64 guid);
void RemoveFromQueue(uint64 guid);
void AddQueueData(uint64 guid, time_t joinTime, const LfgDungeonSet &dungeons, const LfgRolesMap &rolesMap);
void RemoveQueueData(uint64 guid);
// Update Timers (when proposal success)
void UpdateWaitTimeAvg(int32 waitTime, uint32 dungeonId);
void UpdateWaitTimeTank(int32 waitTime, uint32 dungeonId);
void UpdateWaitTimeHealer(int32 waitTime, uint32 dungeonId);
void UpdateWaitTimeDps(int32 waitTime, uint32 dungeonId);
// Update Queue timers
void UpdateQueueTimers(time_t currTime);
time_t GetJoinTime(uint64 guid);
// Find new group
uint8 FindGroups();
// Just for debugging purposes
LfgCompatibleMap const& GetCompatibleMap();
std::string DumpQueueInfo() const;
std::string DumpCompatibleInfo() const;
private:
void AddToNewQueue(uint64 guid);
void AddToCurrentQueue(uint64 guid);
void RemoveFromNewQueue(uint64 guid);
void RemoveFromCurrentQueue(uint64 guid);
void SetCompatibles(std::string const& key, LfgCompatibility compatibles);
LfgCompatibility GetCompatibles(std::string const& key);
void RemoveFromCompatibles(uint64 guid);
LfgCompatibility FindNewGroups(LfgGuidList& check, LfgGuidList& all);
LfgCompatibility CheckCompatibility(LfgGuidList check);
// Queue
LfgQueueDataMap m_QueueDataMap; ///< Queued groups
LfgCompatibleMap m_CompatibleMap; ///< Compatible dungeons
LfgWaitTimesMap m_waitTimesAvg; ///< Average wait time to find a group queuing as multiple roles
LfgWaitTimesMap m_waitTimesTank; ///< Average wait time to find a group queuing as tank
LfgWaitTimesMap m_waitTimesHealer; ///< Average wait time to find a group queuing as healer
LfgWaitTimesMap m_waitTimesDps; ///< Average wait time to find a group queuing as dps
LfgGuidList m_currentQueue; ///< Ordered list. Used to find groups
LfgGuidList m_newToQueue; ///< New groups to add to queue
};
#endif

View File

@@ -109,13 +109,6 @@ void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod meth
uint64 gguid = group->GetGUID();
sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnRemoveMember [" UI64FMTD "]: remove [" UI64FMTD "] Method: %d Kicker: [" UI64FMTD "] Reason: %s", gguid, guid, method, kicker, (reason ? reason : ""));
if (method == GROUP_REMOVEMETHOD_DEFAULT)
return;
LfgState state = sLFGMgr->GetState(gguid);
if (state == LFG_STATE_QUEUED)
sLFGMgr->LeaveLfg(gguid); // TODO - Remove the one leaving and rejoin group
bool isLFG = group->isLFGGroup();
if (isLFG && method == GROUP_REMOVEMETHOD_KICK) // Player have been kicked
@@ -128,15 +121,26 @@ void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod meth
return;
}
sLFGMgr->ClearState(guid, "OnRemoveMember");
LfgState state = sLFGMgr->GetState(gguid);
// If group is being formed after proposal success do nothing more
if (state == LFG_STATE_PROPOSAL && method == GROUP_REMOVEMETHOD_DEFAULT)
{
// LfgData: Remove player from group
sLFGMgr->SetGroup(guid, 0);
sLFGMgr->RemovePlayerFromGroup(gguid, guid);
return;
}
sLFGMgr->LeaveLfg(guid);
sLFGMgr->SetState(guid, LFG_STATE_NONE);
sLFGMgr->SetGroup(guid, 0);
sLFGMgr->RemovePlayerFromGroup(gguid, guid);
uint8 players = sLFGMgr->RemovePlayerFromGroup(gguid, guid);
if (Player* player = ObjectAccessor::FindPlayer(guid))
{
if (method == GROUP_REMOVEMETHOD_LEAVE && state == LFG_STATE_DUNGEON &&
sLFGMgr->GetDungeon(gguid, false))
players >= LFG_GROUP_KICK_VOTES_NEEDED)
player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, true);
//else if (state == LFG_STATE_BOOT)
// Update internal kick cooldown of kicked
@@ -165,24 +169,16 @@ void LFGGroupScript::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 o
sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnChangeLeader [" UI64FMTD "]: old [" UI64FMTD "] new [" UI64FMTD "]", gguid, newLeaderGuid, oldLeaderGuid);
sLFGMgr->SetLeader(gguid, newLeaderGuid);
Player* player = ObjectAccessor::FindPlayer(newLeaderGuid);
LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_LEADER_UNK1);
if (player)
player->GetSession()->SendLfgUpdateParty(updateData);
player = ObjectAccessor::FindPlayer(oldLeaderGuid);
if (player)
{
updateData.updateType = LFG_UPDATETYPE_GROUP_DISBAND_UNK16;
player->GetSession()->SendLfgUpdateParty(updateData);
}
}
void LFGGroupScript::OnInviteMember(Group* group, uint64 guid)
{
uint64 gguid = group->GetGUID();
sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, group->GetLeaderGUID());
sLFGMgr->LeaveLfg(gguid);
uint64 leader = group->GetLeaderGUID();
sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, leader);
// No gguid == new group being formed
// No leader == after group creation first invite is new leader
// leader and no gguid == first invite after leader is added to new group (this is the real invite)
if (leader && !gguid)
sLFGMgr->LeaveLfg(leader);
}

View File

@@ -523,7 +523,7 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod &method /*= GROUP_REMOV
WorldPacket data;
if (method == GROUP_REMOVEMETHOD_KICK)
if (method == GROUP_REMOVEMETHOD_KICK || method == GROUP_REMOVEMETHOD_KICK_LFG)
{
data.Initialize(SMSG_GROUP_UNINVITE, 0);
player->GetSession()->SendPacket(&data);

View File

@@ -3399,9 +3399,10 @@ enum XPColorChar
enum RemoveMethod
{
GROUP_REMOVEMETHOD_DEFAULT = 0,
GROUP_REMOVEMETHOD_KICK = 1,
GROUP_REMOVEMETHOD_LEAVE = 2
GROUP_REMOVEMETHOD_DEFAULT = 0,
GROUP_REMOVEMETHOD_KICK = 1,
GROUP_REMOVEMETHOD_LEAVE = 2,
GROUP_REMOVEMETHOD_KICK_LFG = 3
};
enum ActivateTaxiReply