First Commit

For Azeroth!
This commit is contained in:
Yehonal
2016-06-26 10:39:44 +02:00
commit e8e94a0a66
3777 changed files with 1419268 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C)
*
* 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 "LFG.h"
#include "Language.h"
#include "ObjectMgr.h"
namespace lfg
{
/*std::string ConcatenateDungeons(LfgDungeonSet const& dungeons)
{
std::string dungeonstr = "";
if (!dungeons.empty())
{
std::ostringstream o;
LfgDungeonSet::const_iterator it = dungeons.begin();
o << (*it);
for (++it; it != dungeons.end(); ++it)
o << ", " << uint32(*it);
dungeonstr = o.str();
}
return dungeonstr;
}
std::string GetRolesString(uint8 roles)
{
std::string rolesstr = "";
if (roles & PLAYER_ROLE_TANK)
rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_TANK));
if (roles & PLAYER_ROLE_HEALER)
{
if (!rolesstr.empty())
rolesstr.append(", ");
rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_HEALER));
}
if (roles & PLAYER_ROLE_DAMAGE)
{
if (!rolesstr.empty())
rolesstr.append(", ");
rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_DAMAGE));
}
if (roles & PLAYER_ROLE_LEADER)
{
if (!rolesstr.empty())
rolesstr.append(", ");
rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_LEADER));
}
if (rolesstr.empty())
rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_NONE));
return rolesstr;
}
std::string GetStateString(LfgState state)
{
int32 entry = LANG_LFG_ERROR;
switch (state)
{
case LFG_STATE_NONE:
entry = LANG_LFG_STATE_NONE;
break;
case LFG_STATE_ROLECHECK:
entry = LANG_LFG_STATE_ROLECHECK;
break;
case LFG_STATE_QUEUED:
entry = LANG_LFG_STATE_QUEUED;
break;
case LFG_STATE_PROPOSAL:
entry = LANG_LFG_STATE_PROPOSAL;
break;
case LFG_STATE_DUNGEON:
entry = LANG_LFG_STATE_DUNGEON;
break;
case LFG_STATE_BOOT:
entry = LANG_LFG_STATE_BOOT;
break;
case LFG_STATE_FINISHED_DUNGEON:
entry = LANG_LFG_STATE_FINISHED_DUNGEON;
break;
case LFG_STATE_RAIDBROWSER:
entry = LANG_LFG_STATE_RAIDBROWSER;
break;
}
return std::string(sObjectMgr->GetTrinityStringForDBCLocale(entry));
}*/
} // namespace lfg

View File

@@ -0,0 +1,210 @@
/*
* Copyright (C)
*
* 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 _LFG_H
#define _LFG_H
#include "Common.h"
namespace lfg
{
enum LFGEnum
{
LFG_TANKS_NEEDED = 1,
LFG_HEALERS_NEEDED = 1,
LFG_DPS_NEEDED = 3
};
enum LfgRoles
{
PLAYER_ROLE_NONE = 0x00,
PLAYER_ROLE_LEADER = 0x01,
PLAYER_ROLE_TANK = 0x02,
PLAYER_ROLE_HEALER = 0x04,
PLAYER_ROLE_DAMAGE = 0x08
};
enum LfgUpdateType
{
LFG_UPDATETYPE_DEFAULT = 0, // Internal Use
LFG_UPDATETYPE_LEADER_UNK1 = 1, // FIXME: At group leave
LFG_UPDATETYPE_LEAVE_RAIDBROWSER = 2,
LFG_UPDATETYPE_JOIN_RAIDBROWSER = 3,
LFG_UPDATETYPE_ROLECHECK_ABORTED = 4,
LFG_UPDATETYPE_JOIN_QUEUE = 5,
LFG_UPDATETYPE_ROLECHECK_FAILED = 6,
LFG_UPDATETYPE_REMOVED_FROM_QUEUE = 7,
LFG_UPDATETYPE_PROPOSAL_FAILED = 8,
LFG_UPDATETYPE_PROPOSAL_DECLINED = 9,
LFG_UPDATETYPE_GROUP_FOUND = 10,
LFG_UPDATETYPE_ADDED_TO_QUEUE = 12,
LFG_UPDATETYPE_PROPOSAL_BEGIN = 13,
LFG_UPDATETYPE_UPDATE_STATUS = 14,
LFG_UPDATETYPE_GROUP_MEMBER_OFFLINE = 15,
LFG_UPDATETYPE_GROUP_DISBAND_UNK16 = 16, // FIXME: Sometimes at group disband
};
enum LfgState
{
LFG_STATE_NONE, // Not using LFG / LFR
LFG_STATE_ROLECHECK, // Rolecheck active
LFG_STATE_QUEUED, // Queued
LFG_STATE_PROPOSAL, // Proposal active
LFG_STATE_BOOT, // Vote kick active
LFG_STATE_DUNGEON, // In LFG Group, in a Dungeon
LFG_STATE_FINISHED_DUNGEON, // In LFG Group, in a finished Dungeon
LFG_STATE_RAIDBROWSER // Using Raid finder
};
/// Instance lock types
enum LfgLockStatusType
{
LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION = 1,
LFG_LOCKSTATUS_TOO_LOW_LEVEL = 2,
LFG_LOCKSTATUS_TOO_HIGH_LEVEL = 3,
LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE = 4,
LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE = 5,
LFG_LOCKSTATUS_RAID_LOCKED = 6,
LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL = 1001,
LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL = 1002,
LFG_LOCKSTATUS_QUEST_NOT_COMPLETED = 1022,
LFG_LOCKSTATUS_MISSING_ITEM = 1025,
LFG_LOCKSTATUS_NOT_IN_SEASON = 1031,
LFG_LOCKSTATUS_MISSING_ACHIEVEMENT = 1034
};
/// Answer state (Also used to check compatibilites)
enum LfgAnswer
{
LFG_ANSWER_PENDING = -1,
LFG_ANSWER_DENY = 0,
LFG_ANSWER_AGREE = 1
};
class Lfg5Guids;
typedef std::list<Lfg5Guids> Lfg5GuidsList;
typedef std::set<uint32> LfgDungeonSet;
typedef std::map<uint32, uint32> LfgLockMap;
typedef std::map<uint64, LfgLockMap> LfgLockPartyMap;
typedef std::set<uint64> LfgGuidSet;
typedef std::list<uint64> LfgGuidList;
typedef std::map<uint64, uint8> LfgRolesMap;
typedef std::map<uint64, uint64> LfgGroupsMap;
class Lfg5Guids
{
public:
uint64 guid[5];
LfgRolesMap* roles;
Lfg5Guids() { memset(&guid, 0, 5*8); roles = NULL; }
Lfg5Guids(uint64 g) { memset(&guid, 0, 5*8); guid[0] = g; roles = NULL; }
Lfg5Guids(Lfg5Guids const& x) { memcpy(guid, x.guid, 5*8); if (x.roles) roles = new LfgRolesMap(*(x.roles)); else roles = NULL; }
Lfg5Guids(Lfg5Guids const& x, bool copyRoles) { memcpy(guid, x.guid, 5*8); roles = NULL; }
~Lfg5Guids() { delete roles; }
void addRoles(LfgRolesMap const& r) { roles = new LfgRolesMap(r); }
void clear() { memset(&guid, 0, 5*8); }
bool empty() const { return guid[0] == 0; }
uint64 front() const { return guid[0]; }
uint8 size() const
{
if (guid[2])
{
if (guid[4]) return 5;
else if (guid[3]) return 4;
return 3;
}
else if (guid[1]) return 2;
else if (guid[0]) return 1;
return 0;
}
void insert(const uint64& g)
{
// avoid loops for performance
if (guid[0] == 0) { guid[0] = g; return; }
else if (g <= guid[0]) { if (guid[3]) guid[4] = guid[3]; if (guid[2]) guid[3] = guid[2]; if (guid[1]) guid[2] = guid[1]; guid[1] = guid[0]; guid[0] = g; return; }
if (guid[1] == 0) { guid[1] = g; return; }
else if (g <= guid[1]) { if (guid[3]) guid[4] = guid[3]; if (guid[2]) guid[3] = guid[2]; guid[2] = guid[1]; guid[1] = g; return; }
if (guid[2] == 0) { guid[2] = g; return; }
else if (g <= guid[2]) { if (guid[3]) guid[4] = guid[3]; guid[3] = guid[2]; guid[2] = g; return; }
if (guid[3] == 0) { guid[3] = g; return; }
else if (g <= guid[3]) { guid[4] = guid[3]; guid[3] = g; return; }
guid[4] = g;
}
void force_insert_front(const uint64& g)
{
if (guid[3]) guid[4] = guid[3]; if (guid[2]) guid[3] = guid[2]; if (guid[1]) guid[2] = guid[1]; guid[1] = guid[0]; guid[0] = g;
}
void remove(const uint64& g)
{
// avoid loops for performance
if (guid[0] == g) { if (guid[1]) guid[0] = guid[1]; else { guid[0] = 0; return; } if (guid[2]) guid[1] = guid[2]; else { guid[1] = 0; return; } if (guid[3]) guid[2] = guid[3]; else { guid[2] = 0; return; } if (guid[4]) guid[3] = guid[4]; else { guid[3] = 0; return; } guid[4] = 0; return; }
if (guid[1] == g) { if (guid[2]) guid[1] = guid[2]; else { guid[1] = 0; return; } if (guid[3]) guid[2] = guid[3]; else { guid[2] = 0; return; } if (guid[4]) guid[3] = guid[4]; else { guid[3] = 0; return; } guid[4] = 0; return; }
if (guid[2] == g) { if (guid[3]) guid[2] = guid[3]; else { guid[2] = 0; return; } if (guid[4]) guid[3] = guid[4]; else { guid[3] = 0; return; } guid[4] = 0; return; }
if (guid[3] == g) { if (guid[4]) guid[3] = guid[4]; else { guid[3] = 0; return; } guid[4] = 0; return; }
if (guid[4] == g) guid[4] = 0;
}
bool hasGuid(const uint64& g) const
{
return g && (guid[0] == g || guid[1] == g || guid[2] == g || guid[3] == g || guid[4] == g);
}
bool operator<(const Lfg5Guids& x) const
{
// not neat, but fast xD
if (guid[0]<=x.guid[0]) {
if (guid[0] == x.guid[0]) {
if (guid[1]<=x.guid[1]) {
if (guid[1] == x.guid[1]) {
if (guid[2]<=x.guid[2]) {
if (guid[2] == x.guid[2]) {
if (guid[3]<=x.guid[3]) {
if (guid[3] == x.guid[3]) {
if (guid[4]<=x.guid[4]) {
if (guid[4] == x.guid[4]) return false; else return true;
} else return false;
} else return true;
} else return false;
} else return true;
} else return false;
} else return true;
} else return false;
} else return true;
} else return false;
}
bool operator==(const Lfg5Guids& x) const
{
return guid[0] == x.guid[0] && guid[1] == x.guid[1] && guid[2] == x.guid[2] && guid[3] == x.guid[3] && guid[4] == x.guid[4];
}
void operator=(const Lfg5Guids& x) { memcpy(guid, x.guid, 5*8); delete roles; if (x.roles) roles = new LfgRolesMap(*(x.roles)); else roles = NULL; }
std::string toString() const // for debugging
{
std::ostringstream o;
o << GUID_LOPART(guid[0]) << "," << GUID_LOPART(guid[1]) << "," << GUID_LOPART(guid[2]) << "," << GUID_LOPART(guid[3]) << "," << GUID_LOPART(guid[4]) << ":" << (roles ? 1 : 0);
return o.str();
}
};
/*
std::string ConcatenateDungeons(LfgDungeonSet const& dungeons);
std::string GetRolesString(uint8 roles);
std::string GetStateString(LfgState state);
*/
} // namespace lfg
#endif

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C)
*
* 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 "LFG.h"
#include "LFGGroupData.h"
namespace lfg
{
LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE),
m_Leader(0), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS)
{ }
LfgGroupData::~LfgGroupData()
{ }
bool LfgGroupData::IsLfgGroup()
{
return m_OldState != LFG_STATE_NONE;
}
void LfgGroupData::SetState(LfgState state)
{
switch (state)
{
case LFG_STATE_NONE:
m_Dungeon = 0;
m_KicksLeft = LFG_GROUP_MAX_KICKS;
case LFG_STATE_FINISHED_DUNGEON:
case LFG_STATE_DUNGEON:
m_OldState = state;
// No break on purpose
default:
m_State = state;
}
}
void LfgGroupData::RestoreState()
{
m_State = m_OldState;
}
void LfgGroupData::AddPlayer(uint64 guid)
{
m_Players.insert(guid);
}
uint8 LfgGroupData::RemovePlayer(uint64 guid)
{
LfgGuidSet::iterator it = m_Players.find(guid);
if (it != m_Players.end())
m_Players.erase(it);
return uint8(m_Players.size());
}
void LfgGroupData::RemoveAllPlayers()
{
m_Players.clear();
}
void LfgGroupData::SetLeader(uint64 guid)
{
m_Leader = guid;
}
void LfgGroupData::SetDungeon(uint32 dungeon)
{
m_Dungeon = dungeon;
}
void LfgGroupData::DecreaseKicksLeft()
{
if (m_KicksLeft)
--m_KicksLeft;
}
LfgState LfgGroupData::GetState() const
{
return m_State;
}
LfgState LfgGroupData::GetOldState() const
{
return m_OldState;
}
LfgGuidSet const& LfgGroupData::GetPlayers() const
{
return m_Players;
}
uint8 LfgGroupData::GetPlayerCount() const
{
return m_Players.size();
}
uint64 LfgGroupData::GetLeader() const
{
return m_Leader;
}
uint32 LfgGroupData::GetDungeon(bool asId /* = true */) const
{
if (asId)
return (m_Dungeon & 0x00FFFFFF);
else
return m_Dungeon;
}
uint8 LfgGroupData::GetKicksLeft() const
{
return m_KicksLeft;
}
} // namespace lfg

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C)
*
* 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 _LFGGROUPDATA_H
#define _LFGGROUPDATA_H
#include "LFG.h"
namespace lfg
{
enum LfgGroupEnum
{
LFG_GROUP_MAX_KICKS = 3,
};
/**
Stores all lfg data needed about a group.
*/
class LfgGroupData
{
public:
LfgGroupData();
~LfgGroupData();
bool IsLfgGroup();
// General
void SetState(LfgState state);
void RestoreState();
void AddPlayer(uint64 guid);
uint8 RemovePlayer(uint64 guid);
void RemoveAllPlayers();
void SetLeader(uint64 guid);
// Dungeon
void SetDungeon(uint32 dungeon);
// VoteKick
void DecreaseKicksLeft();
// General
LfgState GetState() const;
LfgState GetOldState() const;
LfgGuidSet const& GetPlayers() const;
uint8 GetPlayerCount() const;
uint64 GetLeader() const;
// Dungeon
uint32 GetDungeon(bool asId = true) const;
// VoteKick
uint8 GetKicksLeft() const;
private:
// General
LfgState m_State; ///< State if group in LFG
LfgState m_OldState; ///< Old State
uint64 m_Leader; ///< Leader GUID
LfgGuidSet m_Players; ///< Players in group
// Dungeon
uint32 m_Dungeon; ///< Dungeon entry
// Vote Kick
uint8 m_KicksLeft; ///< Number of kicks left
};
} // namespace lfg
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,604 @@
/*
* Copyright (C)
*
* 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 _LFGMGR_H
#define _LFGMGR_H
#include <ace/Singleton.h>
#include "DBCStructure.h"
#include "Field.h"
#include "LFG.h"
#include "LFGQueue.h"
#include "LFGGroupData.h"
#include "LFGPlayerData.h"
class Group;
class Player;
class Quest;
namespace lfg
{
enum LfgOptions
{
LFG_OPTION_ENABLE_DUNGEON_FINDER = 0x01,
LFG_OPTION_ENABLE_RAID_BROWSER = 0x02,
};
enum LFGMgrEnum
{
LFG_TIME_ROLECHECK = 45 * IN_MILLISECONDS,
LFG_TIME_BOOT = 120,
LFG_TIME_PROPOSAL = 40,
LFG_QUEUEUPDATE_INTERVAL = 8 * IN_MILLISECONDS,
LFG_SPELL_DUNGEON_COOLDOWN = 71328,
LFG_SPELL_DUNGEON_DESERTER = 71041,
LFG_SPELL_LUCK_OF_THE_DRAW = 72221,
LFG_GROUP_KICK_VOTES_NEEDED = 3
};
enum LfgFlags
{
LFG_FLAG_UNK1 = 0x1,
LFG_FLAG_UNK2 = 0x2,
LFG_FLAG_SEASONAL = 0x4,
LFG_FLAG_UNK3 = 0x8
};
/// Determines the type of instance
enum LfgType
{
LFG_TYPE_NONE = 0,
LFG_TYPE_DUNGEON = 1,
LFG_TYPE_RAID = 2,
LFG_TYPE_HEROIC = 5,
LFG_TYPE_RANDOM = 6
};
/// Proposal states
enum LfgProposalState
{
LFG_PROPOSAL_INITIATING = 0,
LFG_PROPOSAL_FAILED = 1,
LFG_PROPOSAL_SUCCESS = 2
};
/// Teleport errors
enum LfgTeleportError
{
// 7 = "You can't do that right now" | 5 = No client reaction
LFG_TELEPORTERROR_OK = 0, // Internal use
LFG_TELEPORTERROR_PLAYER_DEAD = 1,
LFG_TELEPORTERROR_FALLING = 2,
LFG_TELEPORTERROR_IN_VEHICLE = 3,
LFG_TELEPORTERROR_FATIGUE = 4,
LFG_TELEPORTERROR_INVALID_LOCATION = 6,
LFG_TELEPORTERROR_CHARMING = 8 // FIXME - It can be 7 or 8 (Need proper data)
};
/// Queue join results
enum LfgJoinResult
{
// 3 = No client reaction | 18 = "Rolecheck failed"
LFG_JOIN_OK = 0, // Joined (no client msg)
LFG_JOIN_FAILED = 1, // RoleCheck Failed
LFG_JOIN_GROUPFULL = 2, // Your group is full
LFG_JOIN_INTERNAL_ERROR = 4, // Internal LFG Error
LFG_JOIN_NOT_MEET_REQS = 5, // You do not meet the requirements for the chosen dungeons
LFG_JOIN_PARTY_NOT_MEET_REQS = 6, // One or more party members do not meet the requirements for the chosen dungeons
LFG_JOIN_MIXED_RAID_DUNGEON = 7, // You cannot mix dungeons, raids, and random when picking dungeons
LFG_JOIN_MULTI_REALM = 8, // The dungeon you chose does not support players from multiple realms
LFG_JOIN_DISCONNECTED = 9, // One or more party members are pending invites or disconnected
LFG_JOIN_PARTY_INFO_FAILED = 10, // Could not retrieve information about some party members
LFG_JOIN_DUNGEON_INVALID = 11, // One or more dungeons was not valid
LFG_JOIN_DESERTER = 12, // You can not queue for dungeons until your deserter debuff wears off
LFG_JOIN_PARTY_DESERTER = 13, // One or more party members has a deserter debuff
LFG_JOIN_RANDOM_COOLDOWN = 14, // You can not queue for random dungeons while on random dungeon cooldown
LFG_JOIN_PARTY_RANDOM_COOLDOWN = 15, // One or more party members are on random dungeon cooldown
LFG_JOIN_TOO_MUCH_MEMBERS = 16, // You can not enter dungeons with more that 5 party members
LFG_JOIN_USING_BG_SYSTEM = 17 // You can not use the dungeon system while in BG or arenas
};
/// Role check states
enum LfgRoleCheckState
{
LFG_ROLECHECK_DEFAULT = 0, // Internal use = Not initialized.
LFG_ROLECHECK_FINISHED = 1, // Role check finished
LFG_ROLECHECK_INITIALITING = 2, // Role check begins
LFG_ROLECHECK_MISSING_ROLE = 3, // Someone didn't selected a role after 2 mins
LFG_ROLECHECK_WRONG_ROLES = 4, // Can't form a group with that role selection
LFG_ROLECHECK_ABORTED = 5, // Someone leave the group
LFG_ROLECHECK_NO_ROLE = 6 // Someone selected no role
};
enum LfgUpdateFlag // pussywizard: for raid browser
{
LFG_UPDATE_FLAG_NONE = 0x00,
LFG_UPDATE_FLAG_CHARACTERINFO = 0x01,
LFG_UPDATE_FLAG_COMMENT = 0x02,
LFG_UPDATE_FLAG_GROUPLEADER = 0x04,
LFG_UPDATE_FLAG_GROUPGUID = 0x08,
LFG_UPDATE_FLAG_ROLES = 0x10,
LFG_UPDATE_FLAG_AREA = 0x20,
LFG_UPDATE_FLAG_STATUS = 0x40,
LFG_UPDATE_FLAG_BINDED = 0x80
};
struct RBEntryInfo
{
RBEntryInfo() {}
RBEntryInfo(uint8 _roles, std::string const& _comment) : roles(_roles), comment(_comment) {}
uint8 roles;
std::string comment;
};
struct RBInternalInfo
{
uint64 guid;
std::string comment;
bool isGroupLeader;
uint64 groupGuid;
uint8 roles;
uint32 encounterMask;
uint64 instanceGuid;
// additional character info parameters:
uint8 _online;
uint8 _level;
uint8 _class;
uint8 _race;
float _avgItemLevel;
// --
uint8 _talents0;
uint8 _talents1;
uint8 _talents2;
uint32 _area;
uint32 _armor;
uint32 _spellDamage;
uint32 _spellHeal;
// --
uint32 _critRatingMelee;
uint32 _critRatingRanged;
uint32 _critRatingSpell;
float _mp5;
float _mp5combat;
// --
uint32 _attackPower;
uint32 _agility;
uint32 _health;
uint32 _mana;
uint32 _defenseSkill;
// --
uint32 _dodgeRating;
uint32 _blockRating;
uint32 _parryRating;
uint32 _hasteRating;
uint32 _expertiseRating;
RBInternalInfo() {}
RBInternalInfo(uint64 guid, std::string const& comment, bool isGroupLeader, uint64 groupGuid, uint8 roles, uint32 encounterMask, uint64 instanceGuid,
uint8 _online, uint8 _level, uint8 _class, uint8 _race, float _avgItemLevel,
uint8 (&_talents)[3], uint32 _area, uint32 _armor, uint32 _spellDamage, uint32 _spellHeal,
uint32 _critRatingMelee, uint32 _critRatingRanged, uint32 _critRatingSpell, float _mp5, float _mp5combat,
uint32 _attackPower, uint32 _agility, uint32 _health, uint32 _mana, uint32 _defenseSkill,
uint32 _dodgeRating, uint32 _blockRating, uint32 _parryRating, uint32 _hasteRating, uint32 _expertiseRating)
: guid(guid), comment(comment), isGroupLeader(isGroupLeader), groupGuid(groupGuid), roles(roles), encounterMask(encounterMask), instanceGuid(instanceGuid),
_online(_online), _level(_level), _class(_class), _race(_race), _avgItemLevel(_avgItemLevel),
_talents0(_talents[0]), _talents1(_talents[1]), _talents2(_talents[2]), _area(_area), _armor(_armor), _spellDamage(_spellDamage), _spellHeal(_spellHeal),
_critRatingMelee(_critRatingMelee), _critRatingRanged(_critRatingRanged), _critRatingSpell(_critRatingSpell), _mp5(_mp5), _mp5combat(_mp5combat),
_attackPower(_attackPower), _agility(_agility), _health(_health), _mana(_mana), _defenseSkill(_defenseSkill),
_dodgeRating(_dodgeRating), _blockRating(_blockRating), _parryRating(_parryRating), _hasteRating(_hasteRating), _expertiseRating(_expertiseRating)
{}
bool PlayerSameAs(RBInternalInfo const& i) const
{
return isGroupLeader == i.isGroupLeader && groupGuid == i.groupGuid && roles == i.roles && (isGroupLeader || (comment == i.comment && encounterMask == i.encounterMask && instanceGuid == i.instanceGuid))
&& _online == i._online && _level == i._level && _class == i._class && _race == i._race && fabs(_avgItemLevel-i._avgItemLevel) < 0.01f
&& _talents0 == i._talents0 && _talents1 == i._talents1 && _talents2 == i._talents2 && _area == i._area && _armor == i._armor && _spellDamage == i._spellDamage && _spellHeal == i._spellHeal
&& _critRatingMelee == i._critRatingMelee && _critRatingRanged == i._critRatingRanged && _critRatingSpell == i._critRatingSpell && fabs(_mp5-i._mp5) < 0.01f && fabs(_mp5combat-i._mp5combat) < 0.01f
&& _attackPower == i._attackPower && _agility == i._agility && _health == i._health && _mana == i._mana && _defenseSkill == i._defenseSkill
&& _dodgeRating == i._dodgeRating && _blockRating == i._blockRating && _parryRating == i._parryRating && _hasteRating == i._hasteRating && _expertiseRating == i._expertiseRating;
}
void CopyStats(RBInternalInfo const& i)
{
_avgItemLevel = i._avgItemLevel;
_talents0 = i._talents0; _talents1 = i._talents1; _talents2 = i._talents2; _area = i._area; _armor = i._armor; _spellDamage = i._spellDamage; _spellHeal = i._spellHeal;
_critRatingMelee = i._critRatingMelee; _critRatingRanged = i._critRatingRanged; _critRatingSpell = i._critRatingSpell; _mp5 = i._mp5; _mp5combat = i._mp5combat;
_attackPower = i._attackPower; _agility = i._agility; _health = i._health; _mana = i._mana; _defenseSkill = i._defenseSkill;
_dodgeRating = i._dodgeRating; _blockRating = i._blockRating; _parryRating = i._parryRating; _hasteRating = i._hasteRating; _expertiseRating = i._expertiseRating;
}
};
// Forward declaration (just to have all typedef together)
struct LFGDungeonData;
struct LfgReward;
struct LfgQueueInfo;
struct LfgRoleCheck;
struct LfgProposal;
struct LfgProposalPlayer;
struct LfgPlayerBoot;
typedef std::map<uint8, LFGQueue> LfgQueueContainer;
typedef std::multimap<uint32, LfgReward const*> LfgRewardContainer;
typedef std::pair<LfgRewardContainer::const_iterator, LfgRewardContainer::const_iterator> LfgRewardContainerBounds;
typedef std::map<uint8, LfgDungeonSet> LfgCachedDungeonContainer;
typedef std::map<uint64, LfgAnswer> LfgAnswerContainer;
typedef std::map<uint64, LfgRoleCheck> LfgRoleCheckContainer;
typedef std::map<uint32, LfgProposal> LfgProposalContainer;
typedef std::map<uint64, LfgProposalPlayer> LfgProposalPlayerContainer;
typedef std::map<uint64, LfgPlayerBoot> LfgPlayerBootContainer;
typedef std::map<uint64, LfgGroupData> LfgGroupDataContainer;
typedef std::map<uint64, LfgPlayerData> LfgPlayerDataContainer;
typedef UNORDERED_MAP<uint32, LFGDungeonData> LFGDungeonContainer;
// Data needed by SMSG_LFG_JOIN_RESULT
struct LfgJoinResultData
{
LfgJoinResultData(LfgJoinResult _result = LFG_JOIN_OK, LfgRoleCheckState _state = LFG_ROLECHECK_DEFAULT):
result(_result), state(_state) {}
LfgJoinResult result;
LfgRoleCheckState state;
LfgLockPartyMap lockmap;
};
// Data needed by SMSG_LFG_UPDATE_PARTY and SMSG_LFG_UPDATE_PLAYER
struct LfgUpdateData
{
LfgUpdateData(LfgUpdateType _type = LFG_UPDATETYPE_DEFAULT): updateType(_type), state(LFG_STATE_NONE), comment("") { }
LfgUpdateData(LfgUpdateType _type, LfgDungeonSet const& _dungeons, std::string const& _comment):
updateType(_type), state(LFG_STATE_NONE), dungeons(_dungeons), comment(_comment) { }
LfgUpdateData(LfgUpdateType _type, LfgState _state, LfgDungeonSet const& _dungeons, std::string const& _comment = ""):
updateType(_type), state(_state), dungeons(_dungeons), comment(_comment) { }
LfgUpdateType updateType;
LfgState state;
LfgDungeonSet dungeons;
std::string comment;
};
// Data needed by SMSG_LFG_QUEUE_STATUS
struct LfgQueueStatusData
{
LfgQueueStatusData(uint32 _dungeonId = 0, int32 _waitTime = -1, int32 _waitTimeAvg = -1, int32 _waitTimeTank = -1, int32 _waitTimeHealer = -1,
int32 _waitTimeDps = -1, uint32 _queuedTime = 0, uint8 _tanks = 0, uint8 _healers = 0, uint8 _dps = 0) :
dungeonId(_dungeonId), waitTime(_waitTime), waitTimeAvg(_waitTimeAvg), waitTimeTank(_waitTimeTank), waitTimeHealer(_waitTimeHealer),
waitTimeDps(_waitTimeDps), queuedTime(_queuedTime), tanks(_tanks), healers(_healers), dps(_dps) {}
uint32 dungeonId;
int32 waitTime;
int32 waitTimeAvg;
int32 waitTimeTank;
int32 waitTimeHealer;
int32 waitTimeDps;
uint32 queuedTime;
uint8 tanks;
uint8 healers;
uint8 dps;
};
struct LfgPlayerRewardData
{
LfgPlayerRewardData(uint32 random, uint32 current, bool _done, Quest const* _quest):
rdungeonEntry(random), sdungeonEntry(current), done(_done), quest(_quest) { }
uint32 rdungeonEntry;
uint32 sdungeonEntry;
bool done;
Quest const* quest;
};
/// Reward info
struct LfgReward
{
LfgReward(uint32 _maxLevel = 0, uint32 _firstQuest = 0, uint32 _otherQuest = 0):
maxLevel(_maxLevel), firstQuest(_firstQuest), otherQuest(_otherQuest) { }
uint32 maxLevel;
uint32 firstQuest;
uint32 otherQuest;
};
/// Stores player data related to proposal to join
struct LfgProposalPlayer
{
LfgProposalPlayer(): role(0), accept(LFG_ANSWER_PENDING), group(0) { }
uint8 role; ///< Proposed role
LfgAnswer accept; ///< Accept status (-1 not answer | 0 Not agree | 1 agree)
uint64 group; ///< Original group guid. 0 if no original group
};
/// Stores group data related to proposal to join
struct LfgProposal
{
LfgProposal(uint32 dungeon = 0): id(0), dungeonId(dungeon), state(LFG_PROPOSAL_INITIATING),
group(0), leader(0), cancelTime(0), encounters(0), isNew(true)
{ }
uint32 id; ///< Proposal Id
uint32 dungeonId; ///< Dungeon to join
LfgProposalState state; ///< State of the proposal
uint64 group; ///< Proposal group (0 if new)
uint64 leader; ///< Leader guid.
time_t cancelTime; ///< Time when we will cancel this proposal
uint32 encounters; ///< Dungeon Encounters
bool isNew; ///< Determines if it's new group or not
Lfg5Guids queues; ///< Queue Ids to remove/readd
LfgGuidList showorder; ///< Show order in update window
LfgProposalPlayerContainer players; ///< Players data
};
/// Stores all rolecheck info of a group that wants to join
struct LfgRoleCheck
{
time_t cancelTime; ///< Time when the rolecheck will fail
LfgRolesMap roles; ///< Player selected roles
LfgRoleCheckState state; ///< State of the rolecheck
LfgDungeonSet dungeons; ///< Dungeons group is applying for (expanded random dungeons)
uint32 rDungeonId; ///< Random Dungeon Id.
uint64 leader; ///< Leader of the group
};
/// Stores information of a current vote to kick someone from a group
struct LfgPlayerBoot
{
time_t cancelTime; ///< Time left to vote
bool inProgress; ///< Vote in progress
LfgAnswerContainer votes; ///< Player votes (-1 not answer | 0 Not agree | 1 agree)
uint64 victim; ///< Player guid to be kicked (can't vote)
std::string reason; ///< kick reason
};
struct LFGDungeonData
{
LFGDungeonData(): id(0), name(""), map(0), type(0), expansion(0), group(0), minlevel(0),
maxlevel(0), difficulty(REGULAR_DIFFICULTY), seasonal(false), x(0.0f), y(0.0f), z(0.0f), o(0.0f)
{ }
LFGDungeonData(LFGDungeonEntry const* dbc): id(dbc->ID), name(dbc->name[0]), map(dbc->map),
type(dbc->type), expansion(dbc->expansion), group(dbc->grouptype),
minlevel(dbc->minlevel), maxlevel(dbc->maxlevel), difficulty(Difficulty(dbc->difficulty)),
seasonal(dbc->flags & LFG_FLAG_SEASONAL), x(0.0f), y(0.0f), z(0.0f), o(0.0f)
{ }
uint32 id;
std::string name;
uint16 map;
uint8 type;
uint8 expansion;
uint8 group;
uint8 minlevel;
uint8 maxlevel;
Difficulty difficulty;
bool seasonal;
float x, y, z, o;
// Helpers
uint32 Entry() const { return id + (type << 24); }
};
class LFGMgr
{
friend class ACE_Singleton<LFGMgr, ACE_Null_Mutex>;
private:
LFGMgr();
~LFGMgr();
// pussywizard: RAIDBROWSER
typedef UNORDERED_MAP<uint32 /*playerGuidLow*/, RBEntryInfo> RBEntryInfoMap;
typedef UNORDERED_MAP<uint32 /*dungeonId*/, RBEntryInfoMap> RBStoreMap;
RBStoreMap RaidBrowserStore[2]; // for 2 factions
typedef UNORDERED_MAP<uint32 /*playerGuidLow*/, uint32 /*dungeonId*/> RBSearchersMap;
RBSearchersMap RBSearchersStore[2]; // for 2 factions
typedef UNORDERED_MAP<uint32 /*dungeonId*/, WorldPacket> RBCacheMap;
RBCacheMap RBCacheStore[2]; // for 2 factions
typedef UNORDERED_MAP<uint32 /*guidLow*/, RBInternalInfo> RBInternalInfoMap;
typedef UNORDERED_MAP<uint32 /*dungeonId*/, RBInternalInfoMap> RBInternalInfoMapMap;
RBInternalInfoMapMap RBInternalInfoStorePrev[2]; // for 2 factions
RBInternalInfoMapMap RBInternalInfoStoreCurr[2]; // for 2 factions
typedef std::set<uint32 /*dungeonId*/> RBUsedDungeonsSet; // needs to be ordered
RBUsedDungeonsSet RBUsedDungeonsStore[2]; // for 2 factions
public:
// Functions used outside lfg namespace
void Update(uint32 diff, uint8 task);
// World.cpp
/// Finish the dungeon for the given group. All check are performed using internal lfg data
void FinishDungeon(uint64 gguid, uint32 dungeonId, const Map* currMap);
/// Loads rewards for random dungeons
void LoadRewards();
/// Loads dungeons from dbc and adds teleport coords
void LoadLFGDungeons(bool reload = false);
// Multiple files
/// Check if given guid applied for random dungeon
bool selectedRandomLfgDungeon(uint64 guid);
/// Check if given guid applied for given map and difficulty. Used to know
bool inLfgDungeonMap(uint64 guid, uint32 map, Difficulty difficulty);
/// Get selected dungeons
LfgDungeonSet const& GetSelectedDungeons(uint64 guid);
/// Get current lfg state
LfgState GetState(uint64 guid);
/// Get current dungeon
uint32 GetDungeon(uint64 guid, bool asId = true);
/// Get the map id of the current dungeon
uint32 GetDungeonMapId(uint64 guid);
/// Get kicks left in current group
uint8 GetKicksLeft(uint64 gguid);
/// Load Lfg group info from DB
void _LoadFromDB(Field* fields, uint64 guid);
/// Initializes player data after loading group data from DB
void SetupGroupMember(uint64 guid, uint64 gguid);
/// Return Lfg dungeon entry for given dungeon id
uint32 GetLFGDungeonEntry(uint32 id);
// cs_lfg
/// Get current player roles
uint8 GetRoles(uint64 guid);
/// Get current player comment (used for LFR)
std::string const& GetComment(uint64 gguid);
/// Gets current lfg options
uint32 GetOptions();
/// Sets new lfg options
void SetOptions(uint32 options);
/// Checks if given lfg option is enabled
bool isOptionEnabled(uint32 option);
/// Clears queue - Only for internal testing
void Clean();
// LFGScripts
/// Get leader of the group (using internal data)
uint64 GetLeader(uint64 guid);
/// Initializes locked dungeons for given player (called at login or level change)
void InitializeLockedDungeons(Player* player, uint8 level = 0);
/// Sets player team
void SetTeam(uint64 guid, TeamId teamId);
/// Sets player group
void SetGroup(uint64 guid, uint64 group);
/// Gets player group
uint64 GetGroup(uint64 guid);
/// Sets the leader of the group
void SetLeader(uint64 gguid, uint64 leader);
/// Removes saved group data
void RemoveGroupData(uint64 guid);
/// Removes a player from a group
uint8 RemovePlayerFromGroup(uint64 gguid, uint64 guid);
/// Adds player to group
void AddPlayerToGroup(uint64 gguid, uint64 guid);
/// Xinef: Set Random Players Count
void SetRandomPlayersCount(uint64 guid, uint8 count);
/// Xinef: Get Random Players Count
uint8 GetRandomPlayersCount(uint64 guid);
// LFGHandler
/// Get locked dungeons
LfgLockMap const& GetLockedDungeons(uint64 guid);
/// Returns current lfg status
LfgUpdateData GetLfgStatus(uint64 guid);
/// Checks if Seasonal dungeon is active
bool IsSeasonActive(uint32 dungeonId);
/// Gets the random dungeon reward corresponding to given dungeon and player level
LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level);
/// Returns all random and seasonal dungeons for given level and expansion
LfgDungeonSet GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion);
/// Teleport a player to/from selected dungeon
void TeleportPlayer(Player* player, bool out, bool fromOpcode = false);
/// Inits new proposal to boot a player
void InitBoot(uint64 gguid, uint64 kicker, uint64 victim, std::string const& reason);
/// Updates player boot proposal with new player answer
void UpdateBoot(uint64 guid, bool accept);
/// Updates proposal to join dungeon with player answer
void UpdateProposal(uint32 proposalId, uint64 guid, bool accept);
/// Updates the role check with player answer
void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE);
/// Sets player lfg roles
void SetRoles(uint64 guid, uint8 roles);
/// Sets player lfr comment
void SetComment(uint64 guid, std::string const& comment);
/// Join Lfg with selected roles, dungeons and comment
void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment);
/// Leaves lfg
void LeaveLfg(uint64 guid);
/// pussywizard: cleans all queues' data
void LeaveAllLfgQueues(uint64 guid, bool allowgroup, uint64 groupguid = 0);
/// pussywizard: Raid Browser
void JoinRaidBrowser(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string comment);
void LeaveRaidBrowser(uint64 guid);
void LfrSearchAdd(Player* p, uint32 dungeonId);
void LfrSearchRemove(Player* p);
void SendRaidBrowserCachedList(Player* player, uint32 dungeonId);
void UpdateRaidBrowser(uint32 diff);
void LfrSetComment(Player* p, std::string comment);
void SendRaidBrowserJoinedPacket(Player* p, LfgDungeonSet& dungeons, std::string comment);
void RBPacketAppendGroup(const RBInternalInfo& info, ByteBuffer& buffer);
void RBPacketAppendPlayer(const RBInternalInfo& info, ByteBuffer& buffer);
void RBPacketBuildDifference(WorldPacket& differencePacket, uint32 dungeonId, uint32 deletedCounter, ByteBuffer& buffer_deleted, uint32 groupCounter, ByteBuffer& buffer_groups, uint32 playerCounter, ByteBuffer& buffer_players);
void RBPacketBuildFull(WorldPacket& fullPacket, uint32 dungeonId, RBInternalInfoMap& infoMap);
// LfgQueue
/// Get last lfg state (NONE, DUNGEON or FINISHED_DUNGEON)
LfgState GetOldState(uint64 guid);
/// Check if given group guid is lfg
bool IsLfgGroup(uint64 guid);
/// Gets the player count of given group
uint8 GetPlayerCount(uint64 guid);
/// Add a new Proposal
uint32 AddProposal(LfgProposal& proposal);
/// Checks if all players are queued
bool AllQueued(Lfg5Guids const& check);
/// Checks if given roles match, modifies given roles map with new roles
static uint8 CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true);
/// Checks if given players are ignoring each other
static bool HasIgnore(uint64 guid1, uint64 guid2);
/// Sends queue status to player
static void SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data);
private:
TeamId GetTeam(uint64 guid);
void RestoreState(uint64 guid, char const* debugMsg);
void ClearState(uint64 guid, char const* debugMsg);
void SetDungeon(uint64 guid, uint32 dungeon);
void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons);
void SetLockedDungeons(uint64 guid, LfgLockMap const& lock);
void DecreaseKicksLeft(uint64 guid);
void SetState(uint64 guid, LfgState state);
void SetCanOverrideRBState(uint64 guid, bool val);
void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap);
void _SaveToDB(uint64 guid);
LFGDungeonData const* GetLFGDungeon(uint32 id);
// Proposals
void RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type);
void MakeNewGroup(LfgProposal const& proposal);
// Generic
LFGQueue &GetQueue(uint64 guid);
LfgDungeonSet const& GetDungeonsByRandom(uint32 randomdungeon);
LfgType GetDungeonType(uint32 dungeon);
void SendLfgBootProposalUpdate(uint64 guid, LfgPlayerBoot const& boot);
void SendLfgJoinResult(uint64 guid, LfgJoinResultData const& data);
void SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles);
void SendLfgRoleCheckUpdate(uint64 guid, LfgRoleCheck const& roleCheck);
void SendLfgUpdateParty(uint64 guid, LfgUpdateData const& data);
void SendLfgUpdatePlayer(uint64 guid, LfgUpdateData const& data);
void SendLfgUpdateProposal(uint64 guid, LfgProposal const& proposal);
LfgGuidSet const& GetPlayers(uint64 guid);
// General variables
uint32 m_lfgProposalId; ///< used as internal counter for proposals
uint32 m_options; ///< Stores config options
uint32 lastProposalId; ///< pussywizard, store it here because of splitting LFGMgr update into tasks
uint32 m_raidBrowserUpdateTimer[2]; ///< pussywizard
uint32 m_raidBrowserLastUpdatedDungeonId[2]; ///< pussywizard: for 2 factions
LfgQueueContainer QueuesStore; ///< Queues
LfgCachedDungeonContainer CachedDungeonMapStore; ///< Stores all dungeons by groupType
// Reward System
LfgRewardContainer RewardMapStore; ///< Stores rewards for random dungeons
LFGDungeonContainer LfgDungeonStore;
// Rolecheck - Proposal - Vote Kicks
LfgRoleCheckContainer RoleChecksStore; ///< Current Role checks
LfgProposalContainer ProposalsStore; ///< Current Proposals
LfgPlayerBootContainer BootsStore; ///< Current player kicks
LfgPlayerDataContainer PlayersStore; ///< Player data
LfgGroupDataContainer GroupsStore; ///< Group data
};
} // namespace lfg
#define sLFGMgr ACE_Singleton<lfg::LFGMgr, ACE_Null_Mutex>::instance()
#endif

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C)
*
* 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 "LFGPlayerData.h"
#include "LFGMgr.h"
namespace lfg
{
LfgPlayerData::LfgPlayerData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), m_canOverrideRBState(false),
m_TeamId(TEAM_ALLIANCE), m_Group(0), m_Roles(0), m_Comment("")
{}
LfgPlayerData::~LfgPlayerData()
{
}
void LfgPlayerData::SetState(LfgState state)
{
if (m_State == LFG_STATE_RAIDBROWSER && state != LFG_STATE_RAIDBROWSER && !CanOverrideRBState())
return;
switch (state)
{
case LFG_STATE_NONE:
case LFG_STATE_FINISHED_DUNGEON:
m_Roles = 0;
m_SelectedDungeons.clear();
m_Comment = "";
// No break on purpose
case LFG_STATE_DUNGEON:
m_OldState = state;
// No break on purpose
default:
m_State = state;
}
}
void LfgPlayerData::RestoreState()
{
if (m_State == LFG_STATE_RAIDBROWSER && m_OldState != LFG_STATE_RAIDBROWSER && !CanOverrideRBState())
return;
if (m_OldState == LFG_STATE_NONE)
{
m_SelectedDungeons.clear();
m_Roles = 0;
}
m_State = m_OldState;
}
void LfgPlayerData::SetLockedDungeons(LfgLockMap const& lockStatus)
{
m_LockedDungeons = lockStatus;
}
void LfgPlayerData::SetTeam(TeamId teamId)
{
m_TeamId = teamId;
}
void LfgPlayerData::SetGroup(uint64 group)
{
m_Group = group;
}
void LfgPlayerData::SetRoles(uint8 roles)
{
m_Roles = roles;
}
void LfgPlayerData::SetComment(std::string const& comment)
{
m_Comment = comment;
}
void LfgPlayerData::SetSelectedDungeons(LfgDungeonSet const& dungeons)
{
m_SelectedDungeons = dungeons;
}
void LfgPlayerData::SetRandomPlayersCount(uint8 count)
{
m_randomPlayers = count;
}
uint8 LfgPlayerData::GetRandomPlayersCount() const
{
return m_randomPlayers;
}
LfgState LfgPlayerData::GetState() const
{
return m_State;
}
LfgState LfgPlayerData::GetOldState() const
{
return m_OldState;
}
const LfgLockMap& LfgPlayerData::GetLockedDungeons() const
{
return m_LockedDungeons;
}
TeamId LfgPlayerData::GetTeam() const
{
return m_TeamId;
}
uint64 LfgPlayerData::GetGroup() const
{
return m_Group;
}
uint8 LfgPlayerData::GetRoles() const
{
return m_Roles;
}
std::string const& LfgPlayerData::GetComment() const
{
return m_Comment;
}
LfgDungeonSet const& LfgPlayerData::GetSelectedDungeons() const
{
return m_SelectedDungeons;
}
} // namespace lfg

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C)
*
* 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 _LFGPLAYERDATA_H
#define _LFGPLAYERDATA_H
#include "LFG.h"
namespace lfg
{
/**
Stores all lfg data needed about the player.
*/
class LfgPlayerData
{
public:
LfgPlayerData();
~LfgPlayerData();
// General
void SetState(LfgState state);
void RestoreState();
void SetLockedDungeons(LfgLockMap const& lock);
void SetTeam(TeamId teamId);
void SetGroup(uint64 group);
void SetRandomPlayersCount(uint8 count);
// Queue
void SetRoles(uint8 roles);
void SetComment(std::string const& comment);
void SetSelectedDungeons(const LfgDungeonSet& dungeons);
// General
LfgState GetState() const;
LfgState GetOldState() const;
LfgLockMap const& GetLockedDungeons() const;
TeamId GetTeam() const;
uint64 GetGroup() const;
uint8 GetRandomPlayersCount() const;
void SetCanOverrideRBState(bool val) { m_canOverrideRBState = val; }
bool CanOverrideRBState() const { return m_canOverrideRBState; }
// Queue
uint8 GetRoles() const;
std::string const& GetComment() const;
LfgDungeonSet const& GetSelectedDungeons() const;
private:
// General
LfgState m_State; ///< State if group in LFG
LfgState m_OldState; ///< Old State - Used to restore state after failed Rolecheck/Proposal
bool m_canOverrideRBState; ///< pussywizard
// Player
LfgLockMap m_LockedDungeons; ///< Dungeons player can't do and reason
TeamId m_TeamId; ///< Player team - determines the queue to join
uint64 m_Group; ///< Original group of player when joined LFG
uint8 m_randomPlayers; ///< Xinef: Amount of random players you raid with
// Queue
uint8 m_Roles; ///< Roles the player selected when joined LFG
std::string m_Comment; ///< Player comment used when joined LFG
LfgDungeonSet m_SelectedDungeons; ///< Selected Dungeons when joined LFG
};
} // namespace lfg
#endif

View File

@@ -0,0 +1,591 @@
/*
* Copyright (C)
*
* 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"
#include "GroupMgr.h"
namespace lfg
{
void LFGQueue::AddToQueue(uint64 guid, bool failedProposal)
{
//sLog->outString("ADD AddToQueue: %u, failed proposal: %u", GUID_LOPART(guid), failedProposal ? 1 : 0);
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(guid);
if (itQueue == QueueDataStore.end())
{
sLog->outError("LFGQueue::AddToQueue: Queue data not found for [" UI64FMTD "]", guid);
return;
}
//sLog->outString("AddToQueue success: %u", GUID_LOPART(guid));
AddToNewQueue(guid, failedProposal);
}
void LFGQueue::RemoveFromQueue(uint64 guid, bool partial)
{
//sLog->outString("REMOVE RemoveFromQueue: %u, partial: %u", GUID_LOPART(guid), partial ? 1 : 0);
RemoveFromNewQueue(guid);
RemoveFromCompatibles(guid);
LfgQueueDataContainer::iterator itDelete = QueueDataStore.end();
for (LfgQueueDataContainer::iterator itr = QueueDataStore.begin(); itr != QueueDataStore.end(); ++itr)
{
if (itr->first != guid)
{
if (itr->second.bestCompatible.hasGuid(guid))
{
//sLog->outString("CLEAR bestCompatible: %s, because of guid: %u", itr->second.bestCompatible.toString().c_str(), GUID_LOPART(guid));
itr->second.bestCompatible.clear();
}
}
else
{
//sLog->outString("CLEAR bestCompatible SELF: %s, because of guid: %u", itr->second.bestCompatible.toString().c_str(), GUID_LOPART(guid));
//itr->second.bestCompatible.clear(); // don't clear here, because UpdateQueueTimers will try to find with every diff update
itDelete = itr;
}
}
// xinef: partial
if (!partial && itDelete != QueueDataStore.end())
{
//sLog->outString("ERASE QueueDataStore for: %u", GUID_LOPART(guid));
//sLog->outString("ERASE QueueDataStore for: %u, itDelete: %u,%u,%u", GUID_LOPART(guid), itDelete->second.dps, itDelete->second.healers, itDelete->second.tanks);
QueueDataStore.erase(itDelete);
//sLog->outString("ERASE QueueDataStore for: %u SUCCESS", GUID_LOPART(guid));
}
}
void LFGQueue::AddToNewQueue(uint64 guid, bool front)
{
if (front)
{
//sLog->outString("ADD AddToNewQueue at FRONT: %u", GUID_LOPART(guid));
restoredAfterProposal.push_back(guid);
newToQueueStore.push_front(guid);
}
else
{
//sLog->outString("ADD AddToNewQueue at the END: %u", GUID_LOPART(guid));
newToQueueStore.push_back(guid);
}
}
void LFGQueue::RemoveFromNewQueue(uint64 guid)
{
//sLog->outString("REMOVE RemoveFromNewQueue: %u", GUID_LOPART(guid));
newToQueueStore.remove(guid);
restoredAfterProposal.remove(guid);
}
void LFGQueue::AddQueueData(uint64 guid, time_t joinTime, LfgDungeonSet const& dungeons, LfgRolesMap const& rolesMap)
{
//sLog->outString("JOINED AddQueueData: %u", GUID_LOPART(guid));
QueueDataStore[guid] = LfgQueueData(joinTime, dungeons, rolesMap);
AddToQueue(guid);
}
void LFGQueue::RemoveQueueData(uint64 guid)
{
//sLog->outString("LEFT RemoveQueueData: %u", GUID_LOPART(guid));
LfgQueueDataContainer::iterator it = QueueDataStore.find(guid);
if (it != QueueDataStore.end())
QueueDataStore.erase(it);
}
void LFGQueue::UpdateWaitTimeAvg(int32 waitTime, uint32 dungeonId)
{
LfgWaitTime &wt = waitTimesAvgStore[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 = waitTimesTankStore[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 = waitTimesHealerStore[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 = waitTimesDpsStore[dungeonId];
uint32 old_number = wt.number++;
wt.time = int32((wt.time * old_number + waitTime) / wt.number);
}
void LFGQueue::RemoveFromCompatibles(uint64 guid)
{
//sLog->outString("COMPATIBLES REMOVE for: %u", GUID_LOPART(guid));
for (LfgCompatibleContainer::iterator it = CompatibleList.begin(); it != CompatibleList.end(); ++it)
if (it->hasGuid(guid))
{
//sLog->outString("Removed Compatible: %s, because of guid: %u", it->toString().c_str(), GUID_LOPART(guid));
it->clear(); // set to 0, this will be removed while iterating in FindNewGroups
}
for (LfgCompatibleContainer::iterator itr = CompatibleTempList.begin(); itr != CompatibleTempList.end(); )
{
LfgCompatibleContainer::iterator it = itr++;
if (it->hasGuid(guid))
{
//sLog->outString("Erased Temp Compatible: %s, because of guid: %u", it->toString().c_str(), GUID_LOPART(guid));
CompatibleTempList.erase(it);
}
}
}
void LFGQueue::AddToCompatibles(Lfg5Guids const& key)
{
//sLog->outString("COMPATIBLES ADD: %s", key.toString().c_str());
CompatibleTempList.push_back(key);
}
uint8 LFGQueue::FindGroups()
{
//sLog->outString("FIND GROUPS!");
uint8 newGroupsProcessed = 0;
while (!newToQueueStore.empty())
{
++newGroupsProcessed;
uint64 newGuid = newToQueueStore.front();
bool pushCompatiblesToFront = (std::find(restoredAfterProposal.begin(), restoredAfterProposal.end(), newGuid) != restoredAfterProposal.end());
//sLog->outString("newToQueueStore guid: %u, front: %u", GUID_LOPART(newGuid), pushCompatiblesToFront ? 1 : 0);
RemoveFromNewQueue(newGuid);
FindNewGroups(newGuid);
CompatibleList.splice((pushCompatiblesToFront ? CompatibleList.begin() : CompatibleList.end()), CompatibleTempList);
CompatibleTempList.clear();
return newGroupsProcessed; // pussywizard: only one per update, shouldn't be a problem
}
return newGroupsProcessed;
}
LfgCompatibility LFGQueue::FindNewGroups(const uint64& newGuid)
{
// each combination of dps+heal+tank (tank*8 + heal+4 + dps) has a value assigned 0..15
// first 16 bits of the mask are for marking if such combination was found once, second 16 bits for marking second occurence of that combination, etc
uint64 foundMask = 0;
uint32 foundCount = 0;
//sLog->outString("FIND NEW GROUPS for: %u", GUID_LOPART(newGuid));
// we have to take into account that FindNewGroups is called every X minutes if number of compatibles is low!
// build set of already present compatibles for this guid
std::set<Lfg5Guids> currentCompatibles;
for (Lfg5GuidsList::iterator it = CompatibleList.begin(); it != CompatibleList.end(); ++it)
if (it->hasGuid(newGuid))
{
// unset roles here so they are not copied, restore after insertion
LfgRolesMap* r = it->roles;
it->roles = NULL;
currentCompatibles.insert(*it);
it->roles = r;
}
LfgCompatibility selfCompatibility = LFG_COMPATIBILITY_PENDING;
if (currentCompatibles.empty())
{
selfCompatibility = CheckCompatibility(Lfg5Guids(), newGuid, foundMask, foundCount, currentCompatibles);
if (selfCompatibility != LFG_COMPATIBLES_WITH_LESS_PLAYERS) // group is already compatible (a party of 5 players)
return selfCompatibility;
}
for (Lfg5GuidsList::iterator it = CompatibleList.begin(); it != CompatibleList.end(); )
{
Lfg5GuidsList::iterator itr = it++;
if (itr->empty())
{
//sLog->outString("ERASE from CompatibleList");
CompatibleList.erase(itr);
continue;
}
LfgCompatibility compatibility = CheckCompatibility(*itr, newGuid, foundMask, foundCount, currentCompatibles);
if (compatibility == LFG_COMPATIBLES_MATCH)
return LFG_COMPATIBLES_MATCH;
if ((foundMask & 0x3FFF3FFF3FFF3FFF) == 0x3FFF3FFF3FFF3FFF) // each combination of dps+heal+tank already found 4 times
break;
}
return selfCompatibility;
}
LfgCompatibility LFGQueue::CheckCompatibility(Lfg5Guids const& checkWith, const uint64& newGuid, uint64& foundMask, uint32& foundCount, const std::set<Lfg5Guids>& currentCompatibles)
{
//sLog->outString("CHECK CheckCompatibility: %s, new guid: %u", checkWith.toString().c_str(), GUID_LOPART(newGuid));
Lfg5Guids check(checkWith, false); // here newGuid is at front
Lfg5Guids strGuids(checkWith, false); // here guids are sorted
check.force_insert_front(newGuid);
strGuids.insert(newGuid);
if (!currentCompatibles.empty() && currentCompatibles.find(strGuids) != currentCompatibles.end())
return LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS;
LfgProposal proposal;
LfgDungeonSet proposalDungeons;
LfgGroupsMap proposalGroups;
LfgRolesMap proposalRoles;
// Check if more than one LFG group and number of players joining
uint8 numPlayers = 0;
uint8 numLfgGroups = 0;
uint64 guid;
uint64 addToFoundMask = 0;
for (uint8 i=0; i<5 && (guid=check.guid[i]) != 0 && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++i)
{
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(guid);
if (itQueue == QueueDataStore.end())
{
sLog->outError("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_GUID(itQueue->first) ? itQueue->first : 0;
numPlayers += itQueue->second.roles.size();
if (sLFGMgr->IsLfgGroup(guid))
{
if (!numLfgGroups)
proposal.group = guid;
++numLfgGroups;
}
}
if (numLfgGroups > 1)
return LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS;
// Group with less that MAXGROUPSIZE members always compatible
if (check.size() == 1 && numPlayers < MAXGROUPSIZE)
{
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(check.front());
LfgRolesMap roles = itQueue->second.roles;
uint8 roleCheckResult = LFGMgr::CheckGroupRoles(roles);
strGuids.addRoles(roles);
itQueue->second.bestCompatible.clear(); // this may be left after a failed proposal (not cleared, because UpdateQueueTimers would try to generate it with every update)
//UpdateBestCompatibleInQueue(itQueue, strGuids);
AddToCompatibles(strGuids);
if (roleCheckResult && roleCheckResult <= 15)
foundMask |= ( (((uint64)1)<<(roleCheckResult-1)) | (((uint64)1)<<(16+roleCheckResult-1)) | (((uint64)1)<<(32+roleCheckResult-1)) | (((uint64)1)<<(48+roleCheckResult-1)) );
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
}
if (numPlayers > MAXGROUPSIZE)
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 (uint8 i=0; i<5 && check.guid[i]; ++i)
{
const LfgRolesMap &roles = QueueDataStore[check.guid[i]].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)
{
// pussywizard: LFG ZOMG! this means that this player was in two different LfgQueueData (in QueueDataStore), and at least one of them is a group guid, because we do checks so there aren't 2 same guids in current CHECK
//sLog->outError("LFGQueue::CheckCompatibility: ERROR! Player multiple times in queue! [" UI64FMTD "]", itRoles->first);
break;
}
else if (sLFGMgr->HasIgnore(itRoles->first, itPlayer->first))
break;
}
if (itPlayer == proposalRoles.end())
proposalRoles[itRoles->first] = itRoles->second;
else
break;
}
}
if (numPlayers != proposalRoles.size())
return LFG_INCOMPATIBLES_HAS_IGNORES;
uint8 roleCheckResult = LFGMgr::CheckGroupRoles(proposalRoles);
if (!roleCheckResult || roleCheckResult > 0xF)
return LFG_INCOMPATIBLES_NO_ROLES;
// now, every combination can occur only 4 times (explained in FindNewGroups)
if (foundMask & (((uint64)1)<<(roleCheckResult-1)))
{
if (foundMask & (((uint64)1)<<(16+roleCheckResult-1)))
{
if (foundMask & (((uint64)1)<<(32+roleCheckResult-1)))
{
if (foundMask & (((uint64)1)<<(48+roleCheckResult-1)))
{
if (foundCount >= 10) // but only after finding at least 10 compatibles (this helps when there are few groups)
return LFG_INCOMPATIBLES_NO_ROLES;
}
else
addToFoundMask |= (((uint64)1)<<(48+roleCheckResult-1));
}
else
addToFoundMask |= (((uint64)1)<<(32+roleCheckResult-1));
}
else
addToFoundMask |= (((uint64)1)<<(16+roleCheckResult-1));
}
else
addToFoundMask |= (((uint64)1)<<(roleCheckResult-1));
proposalDungeons = QueueDataStore[check.front()].dungeons;
for (uint8 i=1; i<5 && check.guid[i]; ++i)
{
LfgDungeonSet temporal;
LfgDungeonSet &dungeons = QueueDataStore[check.guid[i]].dungeons;
std::set_intersection(proposalDungeons.begin(), proposalDungeons.end(), dungeons.begin(), dungeons.end(), std::inserter(temporal, temporal.begin()));
proposalDungeons = temporal;
}
if (proposalDungeons.empty())
return LFG_INCOMPATIBLES_NO_DUNGEONS;
}
else
{
uint64 gguid = check.front();
const LfgQueueData &queue = QueueDataStore[gguid];
proposalDungeons = queue.dungeons;
proposalRoles = queue.roles;
LFGMgr::CheckGroupRoles(proposalRoles); // assing new roles
}
// Enough players?
if (numPlayers != MAXGROUPSIZE)
{
strGuids.addRoles(proposalRoles);
for (uint8 i=0; i<5 && check.guid[i]; ++i)
{
LfgQueueDataContainer::iterator itr = QueueDataStore.find(check.guid[i]);
if (!itr->second.bestCompatible.empty()) // update if groups don't have it empty (for empty it will be generated in UpdateQueueTimers)
UpdateBestCompatibleInQueue(itr, strGuids);
}
AddToCompatibles(strGuids);
foundMask |= addToFoundMask;
++foundCount;
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
}
uint64 gguid = check.front();
proposal.queues = strGuids;
proposal.isNew = numLfgGroups != 1 || sLFGMgr->GetOldState(gguid) != LFG_STATE_DUNGEON;
if (!sLFGMgr->AllQueued(check)) // can't create proposal
return LFG_COMPATIBILITY_PENDING;
// 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;
}
for (uint8 i=0; i<5 && proposal.queues.guid[i]; ++i)
RemoveFromQueue(proposal.queues.guid[i], true);
sLFGMgr->AddProposal(proposal);
return LFG_COMPATIBLES_MATCH;
}
void LFGQueue::UpdateQueueTimers(uint32 diff)
{
time_t currTime = time(NULL);
bool sendQueueStatus = false;
if (m_QueueStatusTimer > LFG_QUEUEUPDATE_INTERVAL)
{
m_QueueStatusTimer = 0;
sendQueueStatus = true;
}
else
m_QueueStatusTimer += diff;
//sLog->outString("UPDATE UpdateQueueTimers");
for (Lfg5GuidsList::iterator it = CompatibleList.begin(); it != CompatibleList.end(); )
{
Lfg5GuidsList::iterator itr = it++;
if (itr->empty())
{
//sLog->outString("UpdateQueueTimers ERASE compatible");
CompatibleList.erase(itr);
}
}
if (!sendQueueStatus)
{
for (LfgQueueDataContainer::iterator itQueue = QueueDataStore.begin(); itQueue != QueueDataStore.end(); )
{
if (currTime - itQueue->second.joinTime > 2*HOUR)
{
uint64 guid = itQueue->first;
QueueDataStore.erase(itQueue++);
sLFGMgr->LeaveAllLfgQueues(guid, true);
continue;
}
if (itQueue->second.bestCompatible.empty())
{
uint32 numOfCompatibles = FindBestCompatibleInQueue(itQueue);
if (numOfCompatibles /*must be positive, because proposals don't delete QueueQueueData*/ && currTime-itQueue->second.lastRefreshTime >= 60 && numOfCompatibles < (5-itQueue->second.bestCompatible.roles->size())*25)
{
itQueue->second.lastRefreshTime = currTime;
AddToQueue(itQueue->first, false);
}
}
++itQueue;
}
return;
}
//sLog->outTrace(LOG_FILTER_LFG, "Updating queue timers...");
for (LfgQueueDataContainer::iterator itQueue = QueueDataStore.begin(); itQueue != QueueDataStore.end(); ++itQueue)
{
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 = waitTimesTankStore[dungeonId].time;
int32 wtHealer = waitTimesHealerStore[dungeonId].time;
int32 wtDps = waitTimesDpsStore[dungeonId].time;
int32 wtAvg = waitTimesAvgStore[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;
}
if (queueinfo.bestCompatible.empty())
{
//sLog->outString("found empty bestCompatible");
FindBestCompatibleInQueue(itQueue);
}
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 QueueDataStore[guid].joinTime;
}
uint32 LFGQueue::FindBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue)
{
uint32 numOfCompatibles = 0;
for (LfgCompatibleContainer::const_iterator itr = CompatibleList.begin(); itr != CompatibleList.end(); ++itr)
if (itr->hasGuid(itrQueue->first))
{
++numOfCompatibles;
UpdateBestCompatibleInQueue(itrQueue, *itr);
}
return numOfCompatibles;
}
void LFGQueue::UpdateBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue, Lfg5Guids const& key)
{
//sLog->outString("UpdateBestCompatibleInQueue: %s", key.toString().c_str());
LfgQueueData& queueData = itrQueue->second;
uint8 storedSize = queueData.bestCompatible.size();
uint8 size = key.size();
if (size <= storedSize)
return;
queueData.bestCompatible = key;
queueData.tanks = LFG_TANKS_NEEDED;
queueData.healers = LFG_HEALERS_NEEDED;
queueData.dps = LFG_DPS_NEEDED;
for (LfgRolesMap::const_iterator it = key.roles->begin(); it != key.roles->end(); ++it)
{
uint8 role = it->second;
if (role & PLAYER_ROLE_TANK)
--queueData.tanks;
else if (role & PLAYER_ROLE_HEALER)
--queueData.healers;
else
--queueData.dps;
}
}
} // namespace lfg

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C)
*
* 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"
namespace lfg
{
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_MATCH // Must be the last one
};
/// Stores player or group queue info
struct LfgQueueData
{
LfgQueueData(): joinTime(time_t(time(NULL))), lastRefreshTime(joinTime), tanks(LFG_TANKS_NEEDED),
healers(LFG_HEALERS_NEEDED), dps(LFG_DPS_NEEDED)
{ }
LfgQueueData(time_t _joinTime, LfgDungeonSet const& _dungeons, LfgRolesMap const& _roles):
joinTime(_joinTime), lastRefreshTime(_joinTime), tanks(LFG_TANKS_NEEDED), healers(LFG_HEALERS_NEEDED),
dps(LFG_DPS_NEEDED), dungeons(_dungeons), roles(_roles)
{ }
time_t joinTime; ///< Player queue join time (to calculate wait times)
time_t lastRefreshTime; ///< pussywizard
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
Lfg5Guids bestCompatible; ///< Best compatible combination of people queued
};
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> LfgWaitTimesContainer;
typedef std::map<uint64, LfgQueueData> LfgQueueDataContainer;
typedef std::list<Lfg5Guids> LfgCompatibleContainer;
/**
Stores all data related to queue
*/
class LFGQueue
{
public:
// Add/Remove from queue
void AddToQueue(uint64 guid, bool failedProposal = false);
void RemoveFromQueue(uint64 guid, bool partial = false); // xinef: partial remove, dont delete data from list!
void AddQueueData(uint64 guid, time_t joinTime, LfgDungeonSet const& dungeons, LfgRolesMap const& 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(uint32 diff);
time_t GetJoinTime(uint64 guid);
// Find new group
uint8 FindGroups();
private:
void SetQueueUpdateData(std::string const& strGuids, LfgRolesMap const& proposalRoles);
void AddToNewQueue(uint64 guid, bool front);
void RemoveFromNewQueue(uint64 guid);
void RemoveFromCompatibles(uint64 guid);
void AddToCompatibles(Lfg5Guids const& key);
uint32 FindBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue);
void UpdateBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue, Lfg5Guids const& key);
LfgCompatibility FindNewGroups(const uint64& newGuid);
LfgCompatibility CheckCompatibility(Lfg5Guids const& checkWith, const uint64& newGuid, uint64& foundMask, uint32& foundCount, const std::set<Lfg5Guids>& currentCompatibles);
// Queue
uint32 m_QueueStatusTimer; ///< used to check interval of sending queue status
LfgQueueDataContainer QueueDataStore; ///< Queued groups
LfgCompatibleContainer CompatibleList; ///< Compatible dungeons
LfgCompatibleContainer CompatibleTempList; ///< new compatibles are added to this container while main one is being iterated
LfgWaitTimesContainer waitTimesAvgStore; ///< Average wait time to find a group queuing as multiple roles
LfgWaitTimesContainer waitTimesTankStore; ///< Average wait time to find a group queuing as tank
LfgWaitTimesContainer waitTimesHealerStore; ///< Average wait time to find a group queuing as healer
LfgWaitTimesContainer waitTimesDpsStore; ///< Average wait time to find a group queuing as dps
LfgGuidList newToQueueStore; ///< New groups to add to queue
LfgGuidList restoredAfterProposal;
};
} // namespace lfg
#endif

View File

@@ -0,0 +1,290 @@
/*
* Copyright (C)
*
* 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/>.
*/
/*
* Interaction between core and LFGScripts
*/
#include "Common.h"
#include "SharedDefines.h"
#include "Player.h"
#include "Group.h"
#include "LFGScripts.h"
#include "LFGMgr.h"
#include "ScriptMgr.h"
#include "ObjectAccessor.h"
#include "WorldSession.h"
namespace lfg
{
LFGPlayerScript::LFGPlayerScript() : PlayerScript("LFGPlayerScript")
{
}
void LFGPlayerScript::OnLevelChanged(Player* player, uint8 /*oldLevel*/)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
sLFGMgr->InitializeLockedDungeons(player);
}
void LFGPlayerScript::OnLogout(Player* player)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
if (!player->GetGroup() || !player->GetGroup()->isLFGGroup())
{
player->GetSession()->SendLfgLfrList(false);
sLFGMgr->LeaveLfg(player->GetGUID());
sLFGMgr->LeaveAllLfgQueues(player->GetGUID(), true, player->GetGroup() ? player->GetGroup()->GetGUID() : 0);
// pussywizard: after all necessary actions handle raid browser
// pussywizard: already done above
//if (sLFGMgr->GetState(player->GetGUID()) == LFG_STATE_RAIDBROWSER)
// sLFGMgr->LeaveLfg(player->GetGUID());
}
sLFGMgr->LfrSearchRemove(player);
}
void LFGPlayerScript::OnLogin(Player* player)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
// Temporal: Trying to determine when group data and LFG data gets desynched
uint64 guid = player->GetGUID();
uint64 gguid = sLFGMgr->GetGroup(guid);
if (Group const* group = player->GetGroup())
{
uint64 gguid2 = group->GetGUID();
if (gguid != gguid2)
{
//sLog->outError("%s on group %u but LFG has group %u saved... Fixing.",
// player->GetSession()->GetPlayerInfo().c_str(), GUID_LOPART(gguid2), GUID_LOPART(gguid));
sLFGMgr->SetupGroupMember(guid, group->GetGUID());
}
}
sLFGMgr->InitializeLockedDungeons(player);
sLFGMgr->SetTeam(player->GetGUID(), player->GetTeamId());
// TODO - Restore LfgPlayerData and send proper status to player if it was in a group
}
void LFGPlayerScript::OnBindToInstance(Player* player, Difficulty difficulty, uint32 mapId, bool /*permanent*/)
{
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
if (mapEntry->IsDungeon() && difficulty > DUNGEON_DIFFICULTY_NORMAL)
sLFGMgr->InitializeLockedDungeons(player);
}
void LFGPlayerScript::OnMapChanged(Player* player)
{
Map const* map = player->GetMap();
if (sLFGMgr->inLfgDungeonMap(player->GetGUID(), map->GetId(), map->GetDifficulty()))
{
Group* group = player->GetGroup();
// This function is also called when players log in
// if for some reason the LFG system recognises the player as being in a LFG dungeon,
// but the player was loaded without a valid group, we'll teleport to homebind to prevent
// crashes or other undefined behaviour
if (!group)
{
sLFGMgr->LeaveLfg(player->GetGUID());
sLFGMgr->LeaveAllLfgQueues(player->GetGUID(), true);
player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW);
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, 0.0f);
;//sLog->outError(LOG_FILTER_LFG, "LFGPlayerScript::OnMapChanged, Player %s (%u) is in LFG dungeon map but does not have a valid group! "
// "Teleporting to homebind.", player->GetName().c_str(), player->GetGUIDLow());
return;
}
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
if (Player* member = itr->GetSource())
player->GetSession()->SendNameQueryOpcode(member->GetGUID());
if (group->IsLfgWithBuff())
player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true);
}
else
{
player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW);
// Xinef: Destroy group if only one player is left
if (Group* group = player->GetGroup())
if (group->GetMembersCount() <= 1u)
group->Disband();
}
}
LFGGroupScript::LFGGroupScript() : GroupScript("LFGGroupScript")
{
}
void LFGGroupScript::OnAddMember(Group* group, uint64 guid)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
uint64 gguid = group->GetGUID();
uint64 leader = group->GetLeaderGUID();
if (leader == guid)
{
;//sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "] leader " UI64FMTD "]", gguid, guid, leader);
sLFGMgr->SetLeader(gguid, guid);
}
else
{
LfgState gstate = sLFGMgr->GetState(gguid);
LfgState state = sLFGMgr->GetState(guid);
;//sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "] leader " UI64FMTD "] gstate: %u, state: %u", gguid, guid, leader, gstate, state);
if (state == LFG_STATE_QUEUED)
sLFGMgr->LeaveLfg(guid);
if (gstate == LFG_STATE_QUEUED)
sLFGMgr->LeaveLfg(gguid);
}
if (!group->isLFGGroup())
{
sLFGMgr->LeaveAllLfgQueues(leader, true, gguid); // pussywizard: invited, queued, party formed, neither party nor new member are queued, but leader is in queue solo!
sLFGMgr->LeaveAllLfgQueues(guid, false);
}
sLFGMgr->SetGroup(guid, gguid);
sLFGMgr->AddPlayerToGroup(gguid, guid);
// pussywizard: after all necessary actions handle raid browser
if (sLFGMgr->GetState(guid) == LFG_STATE_RAIDBROWSER)
sLFGMgr->LeaveLfg(guid);
}
void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod method, uint64 kicker, char const* reason)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
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 : ""));
bool isLFG = group->isLFGGroup();
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->LeaveAllLfgQueues(guid, true, gguid);
sLFGMgr->SetGroup(guid, 0);
uint8 players = sLFGMgr->RemovePlayerFromGroup(gguid, guid);
// pussywizard: after all necessary actions handle raid browser
// pussywizard: already done above
//if (sLFGMgr->GetState(guid) == LFG_STATE_RAIDBROWSER)
// sLFGMgr->LeaveLfg(guid);
// Xinef: only LFG groups can go below
if (!isLFG)
return;
if (Player* player = ObjectAccessor::FindPlayerInOrOutOfWorld(guid))
{
// xinef: fixed dungeon deserter
if (method != GROUP_REMOVEMETHOD_KICK_LFG && state != LFG_STATE_FINISHED_DUNGEON &&
player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN) && players >= LFG_GROUP_KICK_VOTES_NEEDED)
{
player->AddAura(LFG_SPELL_DUNGEON_DESERTER, player);
}
//else if (state == LFG_STATE_BOOT)
// Update internal kick cooldown of kicked
player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_LEADER_UNK1));
if (player->GetMap()->IsDungeon()) // Teleport player out the dungeon
{
// Xinef: no longer valid sLFGMgr->TeleportPlayer(player, true);
if (!player->IsBeingTeleportedFar() && player->GetMapId() == sLFGMgr->GetDungeonMapId(gguid))
player->TeleportToEntryPoint();
}
}
if (state != LFG_STATE_FINISHED_DUNGEON) // Need more players to finish the dungeon
if (Player* leader = ObjectAccessor::FindPlayerInOrOutOfWorld(sLFGMgr->GetLeader(gguid)))
leader->GetSession()->SendLfgOfferContinue(sLFGMgr->GetDungeon(gguid, false));
}
void LFGGroupScript::OnDisband(Group* group)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
uint64 gguid = group->GetGUID();
;//sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnDisband [" UI64FMTD "]", gguid);
// pussywizard: after all necessary actions handle raid browser
if (sLFGMgr->GetState(group->GetLeaderGUID()) == LFG_STATE_RAIDBROWSER)
sLFGMgr->LeaveLfg(group->GetLeaderGUID());
sLFGMgr->RemoveGroupData(gguid);
}
void LFGGroupScript::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
uint64 gguid = group->GetGUID();
;//sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnChangeLeader [" UI64FMTD "]: old [" UI64FMTD "] new [" UI64FMTD "]", gguid, newLeaderGuid, oldLeaderGuid);
sLFGMgr->SetLeader(gguid, newLeaderGuid);
// pussywizard: after all necessary actions handle raid browser
if (sLFGMgr->GetState(oldLeaderGuid) == LFG_STATE_RAIDBROWSER)
sLFGMgr->LeaveLfg(oldLeaderGuid);
}
void LFGGroupScript::OnInviteMember(Group* group, uint64 guid)
{
if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER))
return;
uint64 gguid = group->GetGUID();
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);
sLFGMgr->LeaveAllLfgQueues(leader, true);
}
}
} // namespace lfg

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C)
*
* 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/>.
*/
/*
* Interaction between core and LFGScripts
*/
#include "Common.h"
#include "SharedDefines.h"
#include "ScriptMgr.h"
class Player;
class Group;
namespace lfg
{
class LFGPlayerScript : public PlayerScript
{
public:
LFGPlayerScript();
// Player Hooks
void OnLevelChanged(Player* player, uint8 oldLevel);
void OnLogout(Player* player);
void OnLogin(Player* player);
void OnBindToInstance(Player* player, Difficulty difficulty, uint32 mapId, bool permanent);
void OnMapChanged(Player* player);
};
class LFGGroupScript : public GroupScript
{
public:
LFGGroupScript();
// Group Hooks
void OnAddMember(Group* group, uint64 guid);
void OnRemoveMember(Group* group, uint64 guid, RemoveMethod method, uint64 kicker, char const* reason);
void OnDisband(Group* group);
void OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid);
void OnInviteMember(Group* group, uint64 guid);
};
} // namespace lfg