From ca276292dcce939d65649d434a08796269789901 Mon Sep 17 00:00:00 2001 From: kaelima Date: Wed, 17 Oct 2012 16:56:35 +0200 Subject: [PATCH 1/4] Core/BG: - Force removal of stealth and invisible auras when using gameobjects with type GAMEOBJECT_TYPE_FLAGSTAND and GAMEOBJECT_TYPE_FLAGDROP - Do not allow players to use unfriendly battleground objects (prevents flag cap with Nitro Boosts active for example) --- src/server/game/Entities/GameObject/GameObject.cpp | 12 ++++++++++-- src/server/game/Entities/Player/Player.cpp | 10 +++++++--- src/server/game/Entities/Player/Player.h | 2 +- src/server/game/Spells/Spell.cpp | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index da6e4ef8407..59f68a0494f 100755 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1522,14 +1522,18 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); - if (player->CanUseBattlegroundObject()) + if (player->CanUseBattlegroundObject(this)) { // in battleground check Battleground* bg = player->GetBattleground(); if (!bg) return; + if (player->GetVehicle()) return; + + player->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); + player->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY); // BG flag click // AB: // 15001 @@ -1562,14 +1566,18 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); - if (player->CanUseBattlegroundObject()) + if (player->CanUseBattlegroundObject(this)) { // in battleground check Battleground* bg = player->GetBattleground(); if (!bg) return; + if (player->GetVehicle()) return; + + player->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); + player->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY); // BG flag dropped // WS: // 179785 - Silverwing Flag diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 501379228e2..161fdc2be1d 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -23488,10 +23488,14 @@ WorldObject* Player::GetViewpoint() const return NULL; } -bool Player::CanUseBattlegroundObject() +bool Player::CanUseBattlegroundObject(GameObject* gameobject) { - // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction) - // maybe gameobject code should handle that ForceReaction usage + FactionTemplateEntry const* playerFaction = getFactionTemplateEntry(); + FactionTemplateEntry const* faction = sFactionTemplateStore.LookupEntry(gameobject->GetUInt32Value(GAMEOBJECT_FACTION)); + + if (playerFaction && faction && !playerFaction->IsFriendlyTo(*faction)) + return false; + // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet // Note: Mount, stealth and invisibility will be removed when used return (!isTotalImmune() && // Damage immune diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 5670ca05a85..c417c73cdbc 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2237,7 +2237,7 @@ class Player : public Unit, public GridObject bool GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const; bool isTotalImmunity(); - bool CanUseBattlegroundObject(); + bool CanUseBattlegroundObject(GameObject* gameobject); bool isTotalImmune(); bool CanCaptureTowerPoint(); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 85a1d0d3c7f..295c8216b7f 100755 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5210,7 +5210,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->Id != 1842 || (m_targets.GetGOTarget() && m_targets.GetGOTarget()->GetGOInfo()->type != GAMEOBJECT_TYPE_TRAP)) if (m_caster->ToPlayer()->InBattleground() && // In Battleground players can use only flags and banners - !m_caster->ToPlayer()->CanUseBattlegroundObject()) + !m_caster->ToPlayer()->CanUseBattlegroundObject(m_targets.GetGOTarget())) return SPELL_FAILED_TRY_AGAIN; // get the lock entry From d0334bf94839301cda93f95913db8ea3f27c19cb Mon Sep 17 00:00:00 2001 From: Spp Date: Wed, 17 Oct 2012 14:19:59 +0200 Subject: [PATCH 2/4] Core/Dungeon Finder: Some optimizations. - Store teleport coordinates to avoid recalculations each time someone has to be teleported - Correct teleport error msg when player is charming or using vehicle - Internal changes in storage types - Proper code for missing achievement lock type - Better debug msgs - Trying to get rid of "Player*" and "Group*" inside LFGMgr as much as possible (Step 1) --- src/server/game/DataStores/DBCStores.cpp | 2 +- src/server/game/DataStores/DBCStores.h | 2 +- src/server/game/DataStores/DBCStructure.h | 4 +- src/server/game/DataStores/DBCfmt.h | 198 ++-- src/server/game/DungeonFinding/LFG.h | 33 +- .../game/DungeonFinding/LFGGroupData.cpp | 19 +- src/server/game/DungeonFinding/LFGGroupData.h | 5 +- src/server/game/DungeonFinding/LFGMgr.cpp | 901 ++++++++++-------- src/server/game/DungeonFinding/LFGMgr.h | 187 ++-- src/server/game/DungeonFinding/LFGScripts.cpp | 20 +- src/server/game/Entities/Player/Player.cpp | 22 +- src/server/game/Entities/Player/Player.h | 1 + src/server/game/Globals/ObjectMgr.cpp | 3 +- src/server/game/Groups/Group.cpp | 2 +- src/server/game/Handlers/LFGHandler.cpp | 221 ++--- src/server/game/Maps/Map.cpp | 4 +- src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.h | 13 +- src/server/game/Spells/SpellScript.h | 2 +- src/server/game/World/World.cpp | 5 +- .../scripts/Northrend/Nexus/Oculus/oculus.cpp | 2 +- src/server/scripts/Spells/spell_generic.cpp | 4 +- src/server/scripts/World/npcs_special.cpp | 2 +- 23 files changed, 940 insertions(+), 714 deletions(-) diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 850c58bdf93..16595eb16dd 100755 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -118,7 +118,7 @@ DBCStorage sItemRandomPropertiesStore(ItemRandomProp DBCStorage sItemRandomSuffixStore(ItemRandomSuffixfmt); DBCStorage sItemSetStore(ItemSetEntryfmt); -DBCStorage sLFGDungeonStore(LFGDungeonEntryfmt); +DBCStorage sLFGDungeonStore(LFGDungeonEntryfmt); DBCStorage sLiquidTypeStore(LiquidTypefmt); DBCStorage sLockStore(LockEntryfmt); diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index cd30ed587f8..f6fabb5a73d 100755 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -120,7 +120,7 @@ extern DBCStorage sItemLimitCategoryStore; extern DBCStorage sItemRandomPropertiesStore; extern DBCStorage sItemRandomSuffixStore; extern DBCStorage sItemSetStore; -extern DBCStorage sLFGDungeonStore; +extern DBCStorage sLFGDungeonStore; extern DBCStorage sLiquidTypeStore; extern DBCStorage sLockStore; extern DBCStorage sMailTemplateStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 538a53344e8..2fc61e69fe2 100755 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1193,10 +1193,10 @@ struct ItemSetEntry uint32 required_skill_value; // 52 m_requiredSkillRank }; -struct LFGDungeonEntry +struct LFGDungeonEntryDbc { uint32 ID; // 0 - //char* name[16]; // 1-17 Name lang + char* name[16]; // 1-17 Name lang uint32 minlevel; // 18 uint32 maxlevel; // 19 uint32 reclevel; // 20 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 39f9009419e..0c2cc565b22 100755 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -19,111 +19,111 @@ #ifndef TRINITY_DBCSFRM_H #define TRINITY_DBCSFRM_H -const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; +char const Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; const std::string CustomAchievementfmt="pppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapapaaaaaaaaaaaaaaaaaapp"; const std::string CustomAchievementIndex = "ID"; -const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix"; -const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxiiiiixxx"; -const char AreaGroupEntryfmt[]="niiiiiii"; -const char AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; -const char AreaTriggerEntryfmt[]="niffffffff"; -const char AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx"; -const char BankBagSlotPricesEntryfmt[]="ni"; -const char BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; -const char BattlemasterListEntryfmt[]="niiiiiiiiixssssssssssssssssxiixx"; -const char CharStartOutfitEntryfmt[]="diiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char CharTitlesEntryfmt[]="nxssssssssssssssssxxxxxxxxxxxxxxxxxxi"; -const char ChatChannelsEntryfmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; +char const AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix"; +char const AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxiiiiixxx"; +char const AreaGroupEntryfmt[]="niiiiiii"; +char const AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; +char const AreaTriggerEntryfmt[]="niffffffff"; +char const AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx"; +char const BankBagSlotPricesEntryfmt[]="ni"; +char const BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; +char const BattlemasterListEntryfmt[]="niiiiiiiiixssssssssssssssssxiixx"; +char const CharStartOutfitEntryfmt[]="diiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const CharTitlesEntryfmt[]="nxssssssssssssssssxxxxxxxxxxxxxxxxxxi"; +char const ChatChannelsEntryfmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; // ChatChannelsEntryfmt, index not used (more compact store) -const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; -const char ChrRacesEntryfmt[]="nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; -const char CinematicSequencesEntryfmt[]="nxxxxxxxxx"; -const char CreatureDisplayInfofmt[]="nixxfxxxxxxxxxxx"; -const char CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx"; -const char CreatureModelDatafmt[]="nxxxfxxxxxxxxxxffxxxxxxxxxxx"; -const char CreatureSpellDatafmt[]="niiiixxxx"; -const char CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx"; -const char CurrencyTypesfmt[]="xnxi"; -const char DestructibleModelDatafmt[]="nxxixxxixxxixxxixxx"; -const char DungeonEncounterfmt[]="niixissssssssssssssssxx"; -const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; -const char DurabilityQualityfmt[]="nf"; -const char EmotesEntryfmt[]="nxxiiix"; -const char EmotesTextEntryfmt[]="nxixxxxxxxxxxxxxxxx"; -const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; -const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; -const char GameObjectDisplayInfofmt[]="nsxxxxxxxxxxffffffx"; -const char GemPropertiesEntryfmt[]="nixxi"; -const char GlyphPropertiesfmt[]="niii"; -const char GlyphSlotfmt[]="nii"; -const char GtBarberShopCostBasefmt[]="f"; -const char GtCombatRatingsfmt[]="f"; -const char GtChanceToMeleeCritBasefmt[]="f"; -const char GtChanceToMeleeCritfmt[]="f"; -const char GtChanceToSpellCritBasefmt[]="f"; -const char GtChanceToSpellCritfmt[]="f"; -const char GtOCTClassCombatRatingScalarfmt[]="df"; -const char GtOCTRegenHPfmt[]="f"; -//const char GtOCTRegenMPfmt[]="f"; -const char GtRegenHPPerSptfmt[]="f"; -const char GtRegenMPPerSptfmt[]="f"; -const char Holidaysfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix"; -const char Itemfmt[]="niiiiiii"; -const char ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; -//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; -//const char ItemCondExtCostsEntryfmt[]="xiii"; -const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix"; -const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; -const char ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx"; -const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx"; -const char ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; -const char LFGDungeonEntryfmt[]="nxxxxxxxxxxxxxxxxxiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; -const char LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; -const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; -const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix"; -const char MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix"; -const char MovieEntryfmt[]="nxx"; -const char OverrideSpellDatafmt[]="niiiiiiiiiix"; -const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; -const char QuestXPfmt[]="niiiiiiiiii"; -const char QuestFactionRewardfmt[]="niiiiiiiiii"; -const char PvPDifficultyfmt[]="diiiii"; -const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; -const char ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii"; -const char ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiiiiiiii"; -const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; -const char SkillLineAbilityfmt[]="niiiixxiiiiixx"; -const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char SpellCastTimefmt[]="nixx"; -const char SpellDifficultyfmt[]="niiii"; +char const ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; +char const ChrRacesEntryfmt[]="nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +char const CinematicSequencesEntryfmt[]="nxxxxxxxxx"; +char const CreatureDisplayInfofmt[]="nixxfxxxxxxxxxxx"; +char const CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx"; +char const CreatureModelDatafmt[]="nxxxfxxxxxxxxxxffxxxxxxxxxxx"; +char const CreatureSpellDatafmt[]="niiiixxxx"; +char const CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx"; +char const CurrencyTypesfmt[]="xnxi"; +char const DestructibleModelDatafmt[]="nxxixxxixxxixxxixxx"; +char const DungeonEncounterfmt[]="niixissssssssssssssssxx"; +char const DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; +char const DurabilityQualityfmt[]="nf"; +char const EmotesEntryfmt[]="nxxiiix"; +char const EmotesTextEntryfmt[]="nxixxxxxxxxxxxxxxxx"; +char const FactionEntryfmt[]="niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; +char const FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; +char const GameObjectDisplayInfofmt[]="nsxxxxxxxxxxffffffx"; +char const GemPropertiesEntryfmt[]="nixxi"; +char const GlyphPropertiesfmt[]="niii"; +char const GlyphSlotfmt[]="nii"; +char const GtBarberShopCostBasefmt[]="f"; +char const GtCombatRatingsfmt[]="f"; +char const GtChanceToMeleeCritBasefmt[]="f"; +char const GtChanceToMeleeCritfmt[]="f"; +char const GtChanceToSpellCritBasefmt[]="f"; +char const GtChanceToSpellCritfmt[]="f"; +char const GtOCTClassCombatRatingScalarfmt[]="df"; +char const GtOCTRegenHPfmt[]="f"; +//char const GtOCTRegenMPfmt[]="f"; +char const GtRegenHPPerSptfmt[]="f"; +char const GtRegenMPPerSptfmt[]="f"; +char const Holidaysfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix"; +char const Itemfmt[]="niiiiiii"; +char const ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; +//char const ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; +//char const ItemCondExtCostsEntryfmt[]="xiii"; +char const ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix"; +char const ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +char const ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx"; +char const ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx"; +char const ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; +char const LFGDungeonEntryfmt[]="nssssssssssssssssiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; +char const LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; +char const MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; +char const MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix"; +char const MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix"; +char const MovieEntryfmt[]="nxx"; +char const OverrideSpellDatafmt[]="niiiiiiiiiix"; +char const QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; +char const QuestXPfmt[]="niiiiiiiiii"; +char const QuestFactionRewardfmt[]="niiiiiiiiii"; +char const PvPDifficultyfmt[]="diiiii"; +char const RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; +char const ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii"; +char const ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiiiiiiii"; +char const SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; +char const SkillLineAbilityfmt[]="niiiixxiiiiixx"; +char const SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const SpellCastTimefmt[]="nixx"; +char const SpellDifficultyfmt[]="niiii"; const std::string CustomSpellDifficultyfmt="ppppp"; const std::string CustomSpellDifficultyIndex="id"; -const char SpellDurationfmt[]="niii"; -const char SpellEntryfmt[]="niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx"; +char const SpellDurationfmt[]="niii"; +char const SpellEntryfmt[]="niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx"; const std::string CustomSpellEntryfmt="papppppppppppapapaaaaaaaaaaapaaapapppppppaaaaapaapaaaaaaaaaaaaaaaaaappppppppppppppppppppppppppppppppppppaaaaaapppppppppaaapppppppppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaappppppppapppaaaaappaaaaaaa"; const std::string CustomSpellEntryIndex = "Id"; -const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; -const char SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii"; -const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; -const char SpellRadiusfmt[]="nfxf"; -const char SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char SpellRuneCostfmt[]="niiii"; -const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii"; -const char StableSlotPricesfmt[] = "ni"; -const char SummonPropertiesfmt[] = "niiiii"; -const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx"; -const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix"; -const char TaxiNodesEntryfmt[]="nifffssssssssssssssssxii"; -const char TaxiPathEntryfmt[]="niii"; -const char TaxiPathNodeEntryfmt[]="diiifffiiii"; -const char TeamContributionPointsfmt[]="df"; -const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; -const char VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx"; -const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx"; -const char WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx"; -const char WorldMapAreaEntryfmt[]="xinxffffixx"; -const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx"; -const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; +char const SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; +char const SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii"; +char const SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; +char const SpellRadiusfmt[]="nfxf"; +char const SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const SpellRuneCostfmt[]="niiii"; +char const SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii"; +char const StableSlotPricesfmt[] = "ni"; +char const SummonPropertiesfmt[] = "niiiii"; +char const TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx"; +char const TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix"; +char const TaxiNodesEntryfmt[]="nifffssssssssssssssssxii"; +char const TaxiPathEntryfmt[]="niii"; +char const TaxiPathNodeEntryfmt[]="diiifffiiii"; +char const TeamContributionPointsfmt[]="df"; +char const TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +char const VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx"; +char const VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx"; +char const WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx"; +char const WorldMapAreaEntryfmt[]="xinxffffixx"; +char const WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx"; +char const WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; #endif diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h index 28498749af8..c00cc830ce8 100755 --- a/src/server/game/DungeonFinding/LFG.h +++ b/src/server/game/DungeonFinding/LFG.h @@ -20,13 +20,20 @@ #include "Common.h" +enum LFGEnum +{ + LFG_TANKS_NEEDED = 1, + LFG_HEALERS_NEEDED = 1, + LFG_DPS_NEEDED = 3 +}; + enum LfgRoles { - ROLE_NONE = 0x00, - ROLE_LEADER = 0x01, - ROLE_TANK = 0x02, - ROLE_HEALER = 0x04, - ROLE_DAMAGE = 0x08 + PLAYER_ROLE_NONE = 0x00, + PLAYER_ROLE_LEADER = 0x01, + PLAYER_ROLE_TANK = 0x02, + PLAYER_ROLE_HEALER = 0x04, + PLAYER_ROLE_DAMAGE = 0x08 }; enum LfgUpdateType @@ -73,7 +80,16 @@ enum LfgLockStatusType 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_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 }; /// Dungeon and reason why player can't join @@ -84,7 +100,10 @@ struct LfgLockStatus }; typedef std::set LfgDungeonSet; -typedef std::map LfgLockMap; +typedef std::map LfgLockMap; typedef std::map LfgLockPartyMap; +typedef std::set LfgGuidSet; +typedef std::list LfgGuidList; +typedef std::map LfgRolesMap; #endif diff --git a/src/server/game/DungeonFinding/LFGGroupData.cpp b/src/server/game/DungeonFinding/LFGGroupData.cpp index 607389c5dbf..cbcb1d130bb 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.cpp +++ b/src/server/game/DungeonFinding/LFGGroupData.cpp @@ -18,14 +18,16 @@ #include "LFG.h" #include "LFGGroupData.h" -LfgGroupData::LfgGroupData(): -m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), m_Dungeon(0), -m_VotesNeeded(LFG_GROUP_KICK_VOTES_NEEDED), m_KicksLeft(LFG_GROUP_MAX_KICKS) -{ -} +LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), + 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) @@ -36,7 +38,7 @@ void LfgGroupData::SetState(LfgState state) case LFG_STATE_DUNGEON: case LFG_STATE_FINISHED_DUNGEON: m_OldState = state; - // No break on purpose + // No break on purpose default: m_State = state; } @@ -71,11 +73,6 @@ uint32 LfgGroupData::GetDungeon(bool asId /* = true */) const return m_Dungeon; } -uint8 LfgGroupData::GetVotesNeeded() const -{ - return m_VotesNeeded; -} - uint8 LfgGroupData::GetKicksLeft() const { return m_KicksLeft; diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h index 74570817698..359f7be7eee 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.h +++ b/src/server/game/DungeonFinding/LFGGroupData.h @@ -23,7 +23,6 @@ enum LfgGroupEnum { LFG_GROUP_MAX_KICKS = 3, - LFG_GROUP_KICK_VOTES_NEEDED = 3 }; /** @@ -35,13 +34,13 @@ class LfgGroupData LfgGroupData(); ~LfgGroupData(); + bool IsLfgGroup(); // General void SetState(LfgState state); void RestoreState(); // Dungeon void SetDungeon(uint32 dungeon); // VoteKick - void SetVotesNeeded(uint8 votes); void DecreaseKicksLeft(); // General @@ -49,7 +48,6 @@ class LfgGroupData // Dungeon uint32 GetDungeon(bool asId = true) const; // VoteKick - uint8 GetVotesNeeded() const; uint8 GetKicksLeft() const; private: @@ -59,7 +57,6 @@ class LfgGroupData // Dungeon uint32 m_Dungeon; ///< Dungeon entry // Vote Kick - uint8 m_VotesNeeded; ///< Votes need to kick success uint8 m_KicksLeft; ///< Number of kicks left }; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 2b425dffff0..1b4a2fcdda3 100755 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -32,46 +32,25 @@ #include "Group.h" #include "Player.h" -LFGMgr::LFGMgr(): m_update(true), m_QueueTimer(0), m_lfgProposalId(1), -m_WaitTimeAvg(-1), m_WaitTimeTank(-1), m_WaitTimeHealer(-1), m_WaitTimeDps(-1), -m_NumWaitTimeAvg(0), m_NumWaitTimeTank(0), m_NumWaitTimeHealer(0), m_NumWaitTimeDps(0) -{ - m_update = sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE); - if (m_update) - { - new LFGPlayerScript(); - new LFGGroupScript(); - - // Initialize dungeon cache - for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (dungeon && dungeon->type != LFG_TYPE_ZONE) - { - if (dungeon->type != LFG_TYPE_RANDOM) - m_CachedDungeonMap[dungeon->grouptype].insert(dungeon->ID); - m_CachedDungeonMap[0].insert(dungeon->ID); - } - } - } -} +LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1), + m_options(sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE)), + m_WaitTimeAvg(-1), m_WaitTimeTank(-1), m_WaitTimeHealer(-1), m_WaitTimeDps(-1), + m_NumWaitTimeAvg(0), m_NumWaitTimeTank(0), m_NumWaitTimeHealer(0), m_NumWaitTimeDps(0), + m_lfgPlayerScript(new LFGPlayerScript()), m_lfgGroupScript(new LFGGroupScript()) +{ } LFGMgr::~LFGMgr() { for (LfgRewardMap::iterator itr = m_RewardMap.begin(); itr != m_RewardMap.end(); ++itr) 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; - - for (LfgPlayerBootMap::iterator it = m_Boots.begin(); it != m_Boots.end(); ++it) - delete it->second; - - for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end(); ++it) - delete it->second; } void LFGMgr::_LoadFromDB(Field* fields, uint64 guid) @@ -122,6 +101,86 @@ void LFGMgr::_SaveToDB(uint64 guid, uint32 db_guid) CharacterDatabase.Execute(stmt); } +std::string LFGMgr::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 LFGMgr::GetRolesString(uint8 roles) +{ + std::string rolesstr = ""; + + if (roles & PLAYER_ROLE_TANK) + rolesstr.append("Tank"); + + if (roles & PLAYER_ROLE_HEALER) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append("Healer"); + } + + if (roles & PLAYER_ROLE_DAMAGE) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append("Dps"); + } + + if (roles & PLAYER_ROLE_LEADER) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append("Leader"); + } + + if (rolesstr.empty()) + rolesstr.append("None"); + + return rolesstr; +} + +char const * LFGMgr::GetStateString(LfgState state) +{ + switch (state) + { + case LFG_STATE_NONE: + return "None"; + break; + case LFG_STATE_ROLECHECK: + return "Rolecheck"; + break; + case LFG_STATE_QUEUED: + return "Queued"; + break; + case LFG_STATE_PROPOSAL: + return "Proposal"; + break; + case LFG_STATE_DUNGEON: + return "In Dungeon"; + break; + case LFG_STATE_BOOT: + return "Boot Player"; + break; + case LFG_STATE_FINISHED_DUNGEON: + return "Finished"; + break; + case LFG_STATE_RAIDBROWSER: + return "RaidBrowser"; + } + return "Error"; +} + /// Load rewards for completing dungeons void LFGMgr::LoadRewards() { @@ -155,7 +214,7 @@ void LFGMgr::LoadRewards() uint32 otherMoneyVar = fields[6].GetUInt32(); uint32 otherXPVar = fields[7].GetUInt32(); - if (!sLFGDungeonStore.LookupEntry(dungeonId)) + if (!GetLFGDungeon(dungeonId)) { sLog->outError(LOG_FILTER_SQL, "Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId); continue; @@ -186,16 +245,50 @@ void LFGMgr::LoadRewards() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg dungeon rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void LFGMgr::LoadEntrancePositions() +LFGDungeonEntry const* LFGMgr::GetLFGDungeon(uint32 id) +{ + LFGDungeonMap::const_iterator itr = m_LfgDungeonMap.find(id); + if (itr != m_LfgDungeonMap.end()) + return &(itr->second); + + return NULL; +} + +LFGDungeonMap & LFGMgr::GetLFGDungeonMap() +{ + return m_LfgDungeonMap; +} + +void LFGMgr::LoadLFGDungeons(bool reload /* = false */) { uint32 oldMSTime = getMSTime(); - m_entrancePositions.clear(); + m_LfgDungeonMap.clear(); + + // Initialize Dungeon map with data from dbcs + for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) + { + LFGDungeonEntryDbc const* dungeon = sLFGDungeonStore.LookupEntry(i); + if (!dungeon) + continue; + + switch (dungeon->type) + { + case LFG_TYPE_DUNGEON: + case LFG_TYPE_HEROIC: + case LFG_TYPE_RAID: + case LFG_TYPE_RANDOM: + m_LfgDungeonMap[dungeon->ID] = LFGDungeonEntry(dungeon); + break; + } + } + + // Fill teleport locations from DB QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances"); if (!result) { - sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); + sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); return; } @@ -205,49 +298,85 @@ void LFGMgr::LoadEntrancePositions() { Field* fields = result->Fetch(); uint32 dungeonId = fields[0].GetUInt32(); - Position pos; - pos.m_positionX = fields[1].GetFloat(); - pos.m_positionY = fields[2].GetFloat(); - pos.m_positionZ = fields[3].GetFloat(); - pos.m_orientation = fields[4].GetFloat(); - m_entrancePositions[dungeonId] = pos; + LFGDungeonMap::iterator dungeonItr = m_LfgDungeonMap.find(dungeonId); + if (dungeonItr == m_LfgDungeonMap.end()) + { + sLog->outError(LOG_FILTER_SQL, "table `lfg_entrances` contains coordinates for wrong dungeon %u", dungeonId); + continue; + } + + LFGDungeonEntry& data = dungeonItr->second; + data.x = fields[1].GetFloat(); + data.y = fields[2].GetFloat(); + data.z = fields[3].GetFloat(); + data.o = fields[4].GetFloat(); + ++count; } while (result->NextRow()); sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg entrance positions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + // Fill all other teleport coords from areatriggers + for (LFGDungeonMap::iterator itr = m_LfgDungeonMap.begin(); itr != m_LfgDungeonMap.end(); ++itr) + { + LFGDungeonEntry& dungeon = itr->second; + // No teleport coords in database, load from areatriggers + if (dungeon.x == 0.0f && dungeon.y == 0.0f && dungeon.z == 0.0f) + { + AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon.map); + if (!at) + { + sLog->outError(LOG_FILTER_LFG, "LFGMgr::LoadLFGDungeons: Failed to load dungeon %s, cant find areatrigger for map %u", dungeon.name.c_str(), dungeon.map); + continue; + } + + dungeon.map = at->target_mapId; + dungeon.x = at->target_X; + dungeon.y = at->target_Y; + dungeon.z = at->target_Z; + dungeon.o = at->target_Orientation; + } + + if (dungeon.type != LFG_TYPE_RANDOM) + m_CachedDungeonMap[dungeon.group].insert(dungeon.id); + m_CachedDungeonMap[0].insert(dungeon.id); + } + + if (reload) + { + m_CachedDungeonMap.clear(); + // Recalculate locked dungeons + for (LfgPlayerDataMap::const_iterator it = m_Players.begin(); it != m_Players.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayer(it->first)) + InitializeLockedDungeons(player); + } } void LFGMgr::Update(uint32 diff) { - if (!m_update) + if (!m_options) return; - m_update = false; time_t currTime = time(NULL); // Remove obsolete role checks for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end();) { LfgRoleCheckMap::iterator itRoleCheck = it++; - LfgRoleCheck* roleCheck = itRoleCheck->second; - if (currTime < roleCheck->cancelTime) + LfgRoleCheck& roleCheck = itRoleCheck->second; + if (currTime < roleCheck.cancelTime) continue; - roleCheck->state = LFG_ROLECHECK_MISSING_ROLE; + roleCheck.state = LFG_ROLECHECK_MISSING_ROLE; - for (LfgRolesMap::const_iterator itRoles = roleCheck->roles.begin(); itRoles != roleCheck->roles.end(); ++itRoles) + for (LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); itRoles != roleCheck.roles.end(); ++itRoles) { uint64 guid = itRoles->first; - ClearState(guid); - if (Player* player = ObjectAccessor::FindPlayer(guid)) - { - player->GetSession()->SendLfgRoleCheckUpdate(roleCheck); - - if (itRoles->first == roleCheck->leader) - player->GetSession()->SendLfgJoinResult(LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE)); - } + ClearState(guid, "Remove Obsolete RoleCheck"); + SendLfgRoleCheckUpdate(guid, roleCheck); + if (guid == roleCheck.leader) + SendLfgJoinResult(guid, LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE)); } - delete roleCheck; m_RoleChecks.erase(itRoleCheck); } @@ -263,15 +392,16 @@ void LFGMgr::Update(uint32 diff) for (LfgPlayerBootMap::iterator it = m_Boots.begin(); it != m_Boots.end();) { LfgPlayerBootMap::iterator itBoot = it++; - LfgPlayerBoot* pBoot = itBoot->second; - if (pBoot->cancelTime < currTime) + LfgPlayerBoot& boot = itBoot->second; + if (boot.cancelTime < currTime) { - pBoot->inProgress = false; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) - if (Player* plrg = ObjectAccessor::FindPlayer(itVotes->first)) - if (plrg->GetGUID() != pBoot->victim) - plrg->GetSession()->SendLfgBootPlayer(pBoot); - delete pBoot; + boot.inProgress = false; + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + uint64 pguid = itVotes->first; + if (pguid != boot.victim) + SendLfgBootProposalUpdate(pguid, boot); + } m_Boots.erase(itBoot); } } @@ -317,7 +447,7 @@ void LFGMgr::Update(uint32 diff) } else player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); - player->GetSession()->SendLfgUpdateProposal(m_lfgProposalId, pProposal); + player->GetSession()->SendLfgUpdateProposal(m_lfgProposalId, *pProposal); } } @@ -346,24 +476,24 @@ void LFGMgr::Update(uint32 diff) } uint32 dungeonId = (*queue->dungeons.begin()); uint32 queuedTime = uint32(currTime - queue->joinTime); - uint8 role = ROLE_NONE; + uint8 role = PLAYER_ROLE_NONE; for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) role |= itPlayer->second; - role &= ~ROLE_LEADER; + role &= ~PLAYER_ROLE_LEADER; int32 waitTime = -1; switch (role) { - case ROLE_NONE: // Should not happen - just in case + case PLAYER_ROLE_NONE: // Should not happen - just in case waitTime = -1; break; - case ROLE_TANK: + case PLAYER_ROLE_TANK: waitTime = m_WaitTimeTank; break; - case ROLE_HEALER: + case PLAYER_ROLE_HEALER: waitTime = m_WaitTimeHealer; break; - case ROLE_DAMAGE: + case PLAYER_ROLE_DAMAGE: waitTime = m_WaitTimeDps; break; default: @@ -371,14 +501,13 @@ void LFGMgr::Update(uint32 diff) break; } + LfgQueueStatusData queueData(dungeonId, waitTime, m_WaitTimeAvg, m_WaitTimeTank, m_WaitTimeHealer, m_WaitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) - if (Player* player = ObjectAccessor::FindPlayer(itPlayer->first)) - player->GetSession()->SendLfgQueueStatus(dungeonId, waitTime, m_WaitTimeAvg, m_WaitTimeTank, m_WaitTimeHealer, m_WaitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); + SendLfgQueueStatus(itPlayer->first, queueData); } } else m_QueueTimer += diff; - m_update = true; } /** @@ -441,68 +570,61 @@ bool LFGMgr::RemoveFromQueue(uint64 guid) @param[in] player Player we need to initialize the lock status map */ -void LFGMgr::InitializeLockedDungeons(Player* player) +void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */) { uint64 guid = player->GetGUID(); - uint8 level = player->getLevel(); + if (!level) + level = player->getLevel(); uint8 expansion = player->GetSession()->Expansion(); - LfgDungeonSet dungeons = GetDungeonsByRandom(0); + LfgDungeonSet const& dungeons = GetDungeonsByRandom(0); LfgLockMap lock; for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it); + LFGDungeonEntry const* dungeon = GetLFGDungeon(*it); if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore continue; - AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)); - - LfgLockStatusType locktype = LFG_LOCKSTATUS_OK; + uint32 lockData = 0; if (dungeon->expansion > expansion) - locktype = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; + lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player)) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; + lockData = LFG_LOCKSTATUS_RAID_LOCKED; else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && player->GetBoundInstance(dungeon->map, Difficulty(dungeon->difficulty))) - { - //if (!player->GetGroup() || !player->GetGroup()->isLFGGroup() || GetDungeon(player->GetGroup()->GetGUID(), true) != dungeon->ID || GetState(player->GetGroup()->GetGUID()) != LFG_STATE_DUNGEON) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; - } + lockData = LFG_LOCKSTATUS_RAID_LOCKED; else if (dungeon->minlevel > level) - locktype = LFG_LOCKSTATUS_TOO_LOW_LEVEL; + lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL; else if (dungeon->maxlevel < level) - locktype = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; - else if (dungeon->flags & LFG_FLAG_SEASONAL) - { - if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) - if (!IsHolidayActive(holiday)) - locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; - } - else if (locktype == LFG_LOCKSTATUS_OK && ar) + lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; + else if (dungeon->seasonal && !IsSeasonActive(dungeon->id)) + lockData = LFG_LOCKSTATUS_NOT_IN_SEASON; + else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty))) { if (ar->achievement && !player->HasAchieved(ar->achievement)) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; // FIXME: Check the correct lock value + lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT; else if (player->GetTeam() == ALLIANCE && ar->quest_A && !player->GetQuestRewardStatus(ar->quest_A)) - locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (player->GetTeam() == HORDE && ar->quest_H && !player->GetQuestRewardStatus(ar->quest_H)) - locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (ar->item) { if (!player->HasItemCount(ar->item, 1) && (!ar->item2 || !player->HasItemCount(ar->item2, 1))) - locktype = LFG_LOCKSTATUS_MISSING_ITEM; + lockData = LFG_LOCKSTATUS_MISSING_ITEM; } else if (ar->item2 && !player->HasItemCount(ar->item2, 1)) - locktype = LFG_LOCKSTATUS_MISSING_ITEM; + lockData = LFG_LOCKSTATUS_MISSING_ITEM; } + /* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED) - locktype = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; - locktype = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; - locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; - locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; + lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; */ - if (locktype != LFG_LOCKSTATUS_OK) - lock[dungeon->Entry()] = locktype; + if (lockData) + lock[dungeon->Entry()] = lockData; } SetLockedDungeons(guid, lock); } @@ -517,10 +639,10 @@ void LFGMgr::InitializeLockedDungeons(Player* player) @param[in] dungeons Dungeons the player/group is applying for @param[in] comment Player selected comment */ -void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDungeons, const std::string& comment) +void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const std::string& comment) { - if (!player || !player->GetSession() || selectedDungeons.empty()) - return; + if (!player || !player->GetSession() || dungeons.empty()) + return; Group* grp = player->GetGroup(); uint64 guid = player->GetGUID(); @@ -529,7 +651,6 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung PlayerSet players; uint32 rDungeonId = 0; bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; - LfgDungeonSet dungeons = selectedDungeons; // Do not allow to change dungeon in the middle of a current dungeon if (isContinue) @@ -542,7 +663,7 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(gguid); if (itQueue != m_QueueInfoMap.end()) { - LfgDungeonSet playerDungeons = GetSelectedDungeons(guid); + LfgDungeonSet const& playerDungeons = GetSelectedDungeons(guid); if (playerDungeons == dungeons) // Joining the same dungeons -- Send OK { LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, comment); @@ -652,25 +773,24 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung return; } - // FIXME - Raid browser not supported yet + SetComment(guid, comment); + if (isRaid) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] trying to join raid browser and it's disabled.", guid); return; } - SetComment(guid, comment); - + std::string debugNames = ""; if (grp) // Begin rolecheck { // Create new rolecheck - LfgRoleCheck* roleCheck = new LfgRoleCheck(); - roleCheck->cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; - roleCheck->state = LFG_ROLECHECK_INITIALITING; - roleCheck->leader = guid; - roleCheck->dungeons = dungeons; - roleCheck->rDungeonId = rDungeonId; - m_RoleChecks[gguid] = roleCheck; + LfgRoleCheck& roleCheck = m_RoleChecks[gguid]; + roleCheck.cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; + roleCheck.state = LFG_ROLECHECK_INITIALITING; + roleCheck.leader = guid; + roleCheck.dungeons = dungeons; + roleCheck.rDungeonId = rDungeonId; if (rDungeonId) { @@ -690,7 +810,10 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung SetState(pguid, LFG_STATE_ROLECHECK); if (!isContinue) SetSelectedDungeons(pguid, dungeons); - roleCheck->roles[pguid] = 0; + roleCheck.roles[pguid] = 0; + if (!debugNames.empty()) + debugNames.append(", "); + debugNames.append(plrg->GetName()); } } // Update leader role @@ -703,19 +826,14 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung pqInfo->joinTime = time_t(time(NULL)); pqInfo->roles[player->GetGUID()] = roles; pqInfo->dungeons = dungeons; - if (roles & ROLE_TANK) + if (roles & PLAYER_ROLE_TANK) --pqInfo->tanks; - else if (roles & ROLE_HEALER) + else if (roles & PLAYER_ROLE_HEALER) --pqInfo->healers; else --pqInfo->dps; m_QueueInfoMap[guid] = pqInfo; - // Send update to player - player->GetSession()->SendLfgJoinResult(joinData); - player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_PROPOSAL, dungeons, comment)); - SetState(gguid, LFG_STATE_QUEUED); - SetRoles(guid, roles); if (!isContinue) { if (rDungeonId) @@ -725,9 +843,22 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung } SetSelectedDungeons(guid, dungeons); } + // Send update to player + player->GetSession()->SendLfgJoinResult(joinData); + player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_PROPOSAL, dungeons, comment)); + SetState(gguid, LFG_STATE_QUEUED); + SetRoles(guid, roles); + debugNames.append(player->GetName()); AddToQueue(guid, uint8(player->GetTeam())); } - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] joined with %u members. dungeons: %u", guid, grp ? grp->GetMembersCount() : 1, uint8(dungeons.size())); + + if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG)) + { + std::ostringstream o; + o << "LFGMgr::Join: [" << guid << "] joined (" << (grp ? "group" : "player") << ") Members: " << debugNames.c_str() + << ". Dungeons (" << uint32(dungeons.size()) << "): " << ConcatenateDungeons(dungeons); + sLog->outDebug(LOG_FILTER_LFG, "%s", o.str().c_str()); + } } /** @@ -737,7 +868,7 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung @param[in] player Player trying to leave (can be NULL) @param[in] grp Group trying to leave (default NULL) */ -void LFGMgr::Leave(Player* player, Group* grp /* = NULL*/) +void LFGMgr::LeaveLfg(Player* player, Group* grp /* = NULL*/) { if (!player && !grp) return; @@ -754,19 +885,19 @@ void LFGMgr::Leave(Player* player, Group* grp /* = NULL*/) LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); if (grp) { - RestoreState(guid); + RestoreState(guid, "Leave queue"); for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) if (Player* plrg = itr->getSource()) { plrg->GetSession()->SendLfgUpdateParty(updateData); uint64 pguid = plrg->GetGUID(); - ClearState(pguid); + ClearState(pguid, "Leave queue"); } } else { player->GetSession()->SendLfgUpdatePlayer(updateData); - ClearState(guid); + ClearState(guid, "Leave queue"); } } break; @@ -941,7 +1072,7 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) for (LfgRolesMap::const_iterator itRoles = it->second->roles.begin(); itRoles != it->second->roles.end(); ++itRoles) { // Assign new leader - if (itRoles->second & ROLE_LEADER && (!leader || urand(0, 1))) + if (itRoles->second & PLAYER_ROLE_LEADER && (!leader || urand(0, 1))) leader = itRoles->first; rolesMap[itRoles->first] = itRoles->second; @@ -961,8 +1092,7 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) { for (PlayerSet::const_iterator itPlayer = players.begin(); itPlayer != players.end() && player; ++itPlayer) { - // Do not form a group with ignoring candidates - if (player->GetSocial()->HasIgnore((*itPlayer)->GetGUIDLow()) || (*itPlayer)->GetSocial()->HasIgnore(player->GetGUIDLow())) + if (HasIgnore((*itPlayer)->GetGUID(), player->GetGUID())) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Players [" UI64FMTD "] and [" UI64FMTD "] ignoring", strGuids.c_str(), (*itPlayer)->GetGUID(), player->GetGUID()); player = NULL; @@ -1021,11 +1151,11 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) { uint8 roles = itPlayer->second; - if ((roles & ROLE_TANK) && Tanks_Needed > 0) + if ((roles & PLAYER_ROLE_TANK) && Tanks_Needed > 0) --Tanks_Needed; - else if ((roles & ROLE_HEALER) && Healers_Needed > 0) + else if ((roles & PLAYER_ROLE_HEALER) && Healers_Needed > 0) --Healers_Needed; - else if ((roles & ROLE_DAMAGE) && Dps_Needed > 0) + else if ((roles & PLAYER_ROLE_DAMAGE) && Dps_Needed > 0) --Dps_Needed; } } @@ -1106,7 +1236,7 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) @param[in] guid Player guid (0 = rolecheck failed) @param[in] roles Player selected roles */ -void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = ROLE_NONE */) +void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = PLAYER_ROLE_NONE */) { if (!gguid) return; @@ -1116,87 +1246,88 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* if (itRoleCheck == m_RoleChecks.end()) return; - LfgRoleCheck* roleCheck = itRoleCheck->second; - bool sendRoleChosen = roleCheck->state != LFG_ROLECHECK_DEFAULT && guid; + LfgRoleCheck& roleCheck = itRoleCheck->second; + bool sendRoleChosen = roleCheck.state != LFG_ROLECHECK_DEFAULT && guid; if (!guid) - roleCheck->state = LFG_ROLECHECK_ABORTED; - else if (roles < ROLE_TANK) // Player selected no role. - roleCheck->state = LFG_ROLECHECK_NO_ROLE; + roleCheck.state = LFG_ROLECHECK_ABORTED; + else if (roles < PLAYER_ROLE_TANK) // Player selected no role. + roleCheck.state = LFG_ROLECHECK_NO_ROLE; else { - roleCheck->roles[guid] = roles; + roleCheck.roles[guid] = roles; // Check if all players have selected a role - LfgRolesMap::const_iterator itRoles = roleCheck->roles.begin(); - while (itRoles != roleCheck->roles.end() && itRoles->second != ROLE_NONE) + LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); + while (itRoles != roleCheck.roles.end() && itRoles->second != PLAYER_ROLE_NONE) ++itRoles; - if (itRoles == roleCheck->roles.end()) + if (itRoles == roleCheck.roles.end()) { // use temporal var to check roles, CheckGroupRoles modifies the roles - check_roles = roleCheck->roles; - roleCheck->state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES; + check_roles = roleCheck.roles; + roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES; } } uint8 team = 0; LfgDungeonSet dungeons; - if (roleCheck->rDungeonId) - dungeons.insert(roleCheck->rDungeonId); + if (roleCheck.rDungeonId) + dungeons.insert(roleCheck.rDungeonId); else - dungeons = roleCheck->dungeons; + dungeons = roleCheck.dungeons; - LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck->state); - for (LfgRolesMap::const_iterator it = roleCheck->roles.begin(); it != roleCheck->roles.end(); ++it) + LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck.state); + 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) + if (roleCheck.state == LFG_ROLECHECK_FINISHED) SetState(pguid, LFG_STATE_QUEUED); - else if (roleCheck->state != LFG_ROLECHECK_INITIALITING) - ClearState(pguid); + else if (roleCheck.state != LFG_ROLECHECK_INITIALITING) + ClearState(pguid, "Offline while Rolecheck"); continue; } team = uint8(plrg->GetTeam()); if (!sendRoleChosen) - plrg->GetSession()->SendLfgRoleChosen(guid, roles); - plrg->GetSession()->SendLfgRoleCheckUpdate(roleCheck); - switch (roleCheck->state) + SendLfgRoleChosen(pguid, guid, roles); + + SendLfgRoleCheckUpdate(pguid, roleCheck); + switch (roleCheck.state) { case LFG_ROLECHECK_INITIALITING: continue; case LFG_ROLECHECK_FINISHED: SetState(pguid, LFG_STATE_QUEUED); - plrg->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid))); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid))); break; default: - if (roleCheck->leader == pguid) - plrg->GetSession()->SendLfgJoinResult(joinData); - plrg->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED)); - ClearState(pguid); + if (roleCheck.leader == pguid) + SendLfgJoinResult(pguid, joinData); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED)); + ClearState(pguid, "Role check Failed"); break; } } - if (roleCheck->state == LFG_ROLECHECK_FINISHED) + 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; + 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 & ROLE_TANK) + if (roles2 & PLAYER_ROLE_TANK) --pqInfo->tanks; - else if (roles2 & ROLE_HEALER) + else if (roles2 & PLAYER_ROLE_HEALER) --pqInfo->healers; else --pqInfo->dps; @@ -1210,12 +1341,9 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* } AddToQueue(gguid, team); } - - if (roleCheck->state != LFG_ROLECHECK_INITIALITING) + else if (roleCheck.state != LFG_ROLECHECK_INITIALITING) { - if (roleCheck->state != LFG_ROLECHECK_FINISHED) - RestoreState(gguid); - delete roleCheck; + RestoreState(gguid, "Rolecheck Failed"); m_RoleChecks.erase(itRoleCheck); } } @@ -1280,7 +1408,7 @@ void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, const PlayerSet& pla for (PlayerSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) { uint64 guid = (*it)->GetGUID(); - LfgLockMap cachedLockMap = GetLockedDungeons(guid); + LfgLockMap const& cachedLockMap = GetLockedDungeons(guid); for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) { uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids @@ -1314,21 +1442,21 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true if (removeLeaderFlag) for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) - it->second &= ~ROLE_LEADER; + it->second &= ~PLAYER_ROLE_LEADER; for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) { - if (it->second == ROLE_NONE) + if (it->second == PLAYER_ROLE_NONE) return false; - if (it->second & ROLE_TANK) + if (it->second & PLAYER_ROLE_TANK) { - if (it->second != ROLE_TANK) + if (it->second != PLAYER_ROLE_TANK) { - it->second -= ROLE_TANK; + it->second -= PLAYER_ROLE_TANK; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_TANK; + it->second += PLAYER_ROLE_TANK; } else if (tank == LFG_TANKS_NEEDED) return false; @@ -1336,14 +1464,14 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true tank++; } - if (it->second & ROLE_HEALER) + if (it->second & PLAYER_ROLE_HEALER) { - if (it->second != ROLE_HEALER) + if (it->second != PLAYER_ROLE_HEALER) { - it->second -= ROLE_HEALER; + it->second -= PLAYER_ROLE_HEALER; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_HEALER; + it->second += PLAYER_ROLE_HEALER; } else if (healer == LFG_HEALERS_NEEDED) return false; @@ -1351,14 +1479,14 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true healer++; } - if (it->second & ROLE_DAMAGE) + if (it->second & PLAYER_ROLE_DAMAGE) { - if (it->second != ROLE_DAMAGE) + if (it->second != PLAYER_ROLE_DAMAGE) { - it->second -= ROLE_DAMAGE; + it->second -= PLAYER_ROLE_DAMAGE; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_DAMAGE; + it->second += PLAYER_ROLE_DAMAGE; } else if (damage == LFG_DPS_NEEDED) return false; @@ -1426,7 +1554,7 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) if (!allAnswered) { for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - (*it)->GetSession()->SendLfgUpdateProposal(proposalId, pProposal); + (*it)->GetSession()->SendLfgUpdateProposal(proposalId, *pProposal); } else { @@ -1454,7 +1582,7 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) } // Set the dungeon difficulty - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(pProposal->dungeonId); + LFGDungeonEntry const* dungeon = GetLFGDungeon(pProposal->dungeonId); ASSERT(dungeon); // Create a new group (if needed) @@ -1466,7 +1594,7 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) uint64 pguid = player->GetGUID(); Group* group = player->GetGroup(); if (sendUpdate) - player->GetSession()->SendLfgUpdateProposal(proposalId, pProposal); + player->GetSession()->SendLfgUpdateProposal(proposalId, *pProposal); if (group) { @@ -1491,22 +1619,22 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) // Update timers uint8 role = GetRoles(pguid); - role &= ~ROLE_LEADER; + role &= ~PLAYER_ROLE_LEADER; switch (role) { - case ROLE_DAMAGE: + case PLAYER_ROLE_DAMAGE: { uint32 old_number = m_NumWaitTimeDps++; m_WaitTimeDps = int32((m_WaitTimeDps * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeDps); break; } - case ROLE_HEALER: + case PLAYER_ROLE_HEALER: { uint32 old_number = m_NumWaitTimeHealer++; m_WaitTimeHealer = int32((m_WaitTimeHealer * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeHealer); break; } - case ROLE_TANK: + case PLAYER_ROLE_TANK: { uint32 old_number = m_NumWaitTimeTank++; m_WaitTimeTank = int32((m_WaitTimeTank * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeTank); @@ -1559,19 +1687,19 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) */ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type) { - LfgProposal* pProposal = itProposal->second; - pProposal->state = LFG_PROPOSAL_FAILED; + 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); // Mark all people that didn't answered as no accept if (type == LFG_UPDATETYPE_PROPOSAL_FAILED) - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) if (it->second->accept == LFG_ANSWER_PENDING) it->second->accept = LFG_ANSWER_DENY; // Mark players/groups to be removed LfgGuidSet toRemove; - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { if (it->second->accept == LFG_ANSWER_AGREE) continue; @@ -1587,14 +1715,14 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t uint8 team = 0; // Notify players - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { Player* player = ObjectAccessor::FindPlayer(it->first); if (!player) continue; team = uint8(player->GetTeam()); - player->GetSession()->SendLfgUpdateProposal(itProposal->first, pProposal); + player->GetSession()->SendLfgUpdateProposal(itProposal->first, proposal); Group* grp = player->GetGroup(); uint64 guid = player->GetGUID(); @@ -1613,10 +1741,10 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE; sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] in same group that someone that didn't accept. Removing from queue and compatible cache", guid); } - ClearState(guid); + ClearState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept"); if (grp) { - RestoreState(gguid); + RestoreState(gguid, "Proposal Fail (someone in group didn't accepted)"); player->GetSession()->SendLfgUpdateParty(updateData); } else @@ -1641,11 +1769,11 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t { uint64 guid = *it; RemoveFromQueue(guid); - pProposal->queues.remove(guid); + proposal.queues.remove(guid); } // Readd to queue - for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) + for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it) { uint64 guid = *it; LfgGuidList& currentQueue = m_currentQueue[team]; @@ -1653,7 +1781,7 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t AddToQueue(guid, team); //We have to add each GUID in newQueue to check for a new groups } - delete pProposal; + delete &proposal; m_Proposals.erase(itProposal); } @@ -1665,41 +1793,39 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t @param[in] victim Victim guid @param[in] reason Kick reason */ -void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string reason) +void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string const& reason) { if (!grp) return; - uint64 gguid = grp->GetGUID(); + uint32 gguid = grp->GetLowGUID(); SetState(gguid, LFG_STATE_BOOT); - LfgPlayerBoot* pBoot = new LfgPlayerBoot(); - pBoot->inProgress = true; - pBoot->cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; - pBoot->reason = reason; - pBoot->victim = victim; - pBoot->votedNeeded = GetVotesNeeded(gguid); + LfgPlayerBoot& boot = m_Boots[gguid]; + boot.inProgress = true; + boot.cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; + boot.reason = reason; + boot.victim = victim; + + LfgGuidSet players; + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + if (Player* player = itr->getSource()) + players.insert(player->GetGUID()); // Set votes - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr) { - if (Player* plrg = itr->getSource()) - { - uint64 guid = plrg->GetGUID(); - SetState(guid, LFG_STATE_BOOT); - if (guid == victim) - pBoot->votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO - else if (guid == kicker) - pBoot->votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES - else - { - pBoot->votes[guid] = LFG_ANSWER_PENDING; // Other members need to vote - plrg->GetSession()->SendLfgBootPlayer(pBoot); - } - } + uint64 guid = (*itr); + SetState(guid, LFG_STATE_BOOT); + boot.votes[guid] = LFG_ANSWER_PENDING; } - m_Boots[grp->GetLowGUID()] = pBoot; + boot.votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO + boot.votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES + + // Notify players + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + SendLfgBootProposalUpdate(*it, boot); } /** @@ -1714,25 +1840,23 @@ void LFGMgr::UpdateBoot(Player* player, bool accept) if (!grp) return; - uint32 bootId = grp->GetLowGUID(); + uint32 gguid = grp->GetLowGUID(); uint64 guid = player->GetGUID(); - LfgPlayerBootMap::iterator itBoot = m_Boots.find(bootId); + LfgPlayerBootMap::iterator itBoot = m_Boots.find(gguid); if (itBoot == m_Boots.end()) return; - LfgPlayerBoot* pBoot = itBoot->second; - if (!pBoot) + LfgPlayerBoot& boot = itBoot->second; + + if (boot.votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice return; - if (pBoot->votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice - return; - - pBoot->votes[guid] = LfgAnswer(accept); + boot.votes[guid] = LfgAnswer(accept); uint8 votesNum = 0; uint8 agreeNum = 0; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) { if (itVotes->second != LFG_ANSWER_PENDING) { @@ -1742,39 +1866,36 @@ void LFGMgr::UpdateBoot(Player* player, bool accept) } } - if (agreeNum == pBoot->votedNeeded || // Vote passed - votesNum == pBoot->votes.size() || // All voted but not passed - (pBoot->votes.size() - votesNum + agreeNum) < pBoot->votedNeeded) // Vote didnt passed - { - // Send update info to all players - pBoot->inProgress = false; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) - { - uint64 pguid = itVotes->first; - if (pguid != pBoot->victim) - { - SetState(pguid, LFG_STATE_DUNGEON); - if (Player* plrg = ObjectAccessor::FindPlayer(pguid)) - plrg->GetSession()->SendLfgBootPlayer(pBoot); - } - } + // if we don't have enough votes (agree or deny) do nothing + if (agreeNum < LFG_GROUP_KICK_VOTES_NEEDED && (votesNum - agreeNum) < LFG_GROUP_KICK_VOTES_NEEDED) + return; - uint64 gguid = grp->GetGUID(); - SetState(gguid, LFG_STATE_DUNGEON); - if (agreeNum == pBoot->votedNeeded) // Vote passed - Kick player + // Send update info to all players + boot.inProgress = false; + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + uint64 pguid = itVotes->first; + if (pguid != boot.victim) { - Player::RemoveFromGroup(grp, pBoot->victim); - if (Player* victim = ObjectAccessor::FindPlayer(pBoot->victim)) - { - TeleportPlayer(victim, true, false); - SetState(pBoot->victim, LFG_STATE_NONE); - } - OfferContinue(grp); - DecreaseKicksLeft(gguid); + SetState(pguid, LFG_STATE_DUNGEON); + SendLfgBootProposalUpdate(pguid, boot); } - delete pBoot; - m_Boots.erase(itBoot); } + + gguid = grp->GetGUID(); + SetState(gguid, LFG_STATE_DUNGEON); + if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player + { + Player::RemoveFromGroup(grp, boot.victim); + if (Player* victim = ObjectAccessor::FindPlayer(boot.victim)) + { + TeleportPlayer(victim, true, false); + SetState(boot.victim, LFG_STATE_NONE); + } + OfferContinue(grp); + DecreaseKicksLeft(gguid); + } + m_Boots.erase(itBoot); } /** @@ -1788,8 +1909,20 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::TeleportPlayer: [" UI64FMTD "] is being teleported %s", player->GetGUID(), out ? "out" : "in"); - LfgTeleportError error = LFG_TELEPORTERROR_OK; Group* grp = player->GetGroup(); + uint64 gguid = grp->GetGUID(); + LFGDungeonEntry const* dungeon = GetLFGDungeon(GetDungeon(gguid)); + if (!dungeon || (out && player->GetMapId() != uint32(dungeon->map))) + return; + + if (out) + { + player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW); + player->TeleportToBGEntryPoint(); + return; + } + + LfgTeleportError error = LFG_TELEPORTERROR_OK; if (!grp || !grp->isLFGGroup()) // should never happen, but just in case... error = LFG_TELEPORTERROR_INVALID_LOCATION; @@ -1799,33 +1932,21 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* error = LFG_TELEPORTERROR_FALLING; else if (player->IsMirrorTimerActive(FATIGUE_TIMER)) error = LFG_TELEPORTERROR_FATIGUE; + else if (player->GetVehicle()) + error = LFG_TELEPORTERROR_IN_VEHICLE; + else if (player->GetCharmGUID()) + error = LFG_TELEPORTERROR_CHARMING; else { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(GetDungeon(grp->GetGUID())); - - if (out) - { - // Player needs to be inside the LFG dungeon to be able to teleport out - if (dungeon && player->GetMapId() == uint32(dungeon->map)) - { - player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW); - player->TeleportToBGEntryPoint(); - } - else - player->GetSession()->SendLfgTeleportError(LFG_TELEPORTERROR_DONT_REPORT); // Not sure which error message to send - - return; - } - if (!dungeon) error = LFG_TELEPORTERROR_INVALID_LOCATION; else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance { - uint32 mapid = 0; - float x = 0; - float y = 0; - float z = 0; - float orientation = 0; + uint32 mapid = dungeon->map; + float x = dungeon->x; + float y = dungeon->y; + float z = dungeon->z; + float orientation = dungeon->o; if (!fromOpcode) { @@ -1844,32 +1965,6 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* } } - if (!mapid) - { - LfgEntrancePositionMap::const_iterator itr = m_entrancePositions.find(dungeon->ID); - if (itr != m_entrancePositions.end()) - { - mapid = dungeon->map; - x = itr->second.GetPositionX(); - y = itr->second.GetPositionY(); - z = itr->second.GetPositionZ(); - orientation = itr->second.GetOrientation(); - } - else if (AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon->map)) - { - mapid = at->target_mapId; - x = at->target_X; - y = at->target_Y; - z = at->target_Z; - orientation = at->target_Orientation; - } - else - { - sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "]: No areatrigger found for map: %u difficulty: %u", player->GetGUID(), dungeon->map, dungeon->difficulty); - error = LFG_TELEPORTERROR_INVALID_LOCATION; - } - } - if (error == LFG_TELEPORTERROR_OK) { if (!player->GetMap()->IsDungeon()) @@ -1881,10 +1976,7 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* player->CleanupAfterTaxiFlight(); } - if (player->TeleportTo(mapid, x, y, z, orientation)) - // FIXME - HACK - this should be done by teleport, when teleporting far - player->RemoveAurasByType(SPELL_AURA_MOUNTED); - else + if (!player->TeleportTo(mapid, x, y, z, orientation)) { error = LFG_TELEPORTERROR_INVALID_LOCATION; sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "] to map %u: ", player->GetGUID(), mapid); @@ -1932,12 +2024,12 @@ void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player* player) // Clear player related lfg stuff uint32 rDungeonId = (*GetSelectedDungeons(guid).begin()); - ClearState(guid); + ClearState(guid, "Dungeon Finished"); SetState(guid, LFG_STATE_FINISHED_DUNGEON); // Give rewards only if its a random or seasonal dungeon - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(rDungeonId); - if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !(dungeon->flags & LFG_FLAG_SEASONAL))) + LFGDungeonEntry const* dungeon = GetLFGDungeon(rDungeonId); + if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !dungeon->seasonal)) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RewardDungeonDoneFor: [" UI64FMTD "] dungeon %u is not random nor seasonal", guid, rDungeonId); return; @@ -1986,9 +2078,9 @@ void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player* player) */ const LfgDungeonSet& LFGMgr::GetDungeonsByRandom(uint32 randomdungeon) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(randomdungeon); - uint32 groupType = dungeon ? dungeon->grouptype : 0; - return m_CachedDungeonMap[groupType]; + LFGDungeonEntry const* dungeon = GetLFGDungeon(randomdungeon); + uint32 group = dungeon ? dungeon->group : 0; + return m_CachedDungeonMap[group]; } /** @@ -2021,7 +2113,7 @@ LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level) */ LfgType LFGMgr::GetDungeonType(uint32 dungeonId) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId); + LFGDungeonEntry const* dungeon = GetLFGDungeon(dungeonId); if (!dungeon) return LFG_TYPE_NONE; @@ -2047,31 +2139,6 @@ std::string LFGMgr::ConcatenateGuids(LfgGuidList check) return o.str(); } -HolidayIds LFGMgr::GetDungeonSeason(uint32 dungeonId) -{ - HolidayIds holiday = HOLIDAY_NONE; - - switch (dungeonId) - { - case 285: - holiday = HOLIDAY_HALLOWS_END; - break; - case 286: - holiday = HOLIDAY_FIRE_FESTIVAL; - break; - case 287: - holiday = HOLIDAY_BREWFEST; - break; - case 288: - holiday = HOLIDAY_LOVE_IS_IN_THE_AIR; - break; - default: - break; - } - - return holiday; -} - LfgState LFGMgr::GetState(uint64 guid) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "]", guid); @@ -2123,35 +2190,45 @@ const LfgLockMap& LFGMgr::GetLockedDungeons(uint64 guid) uint8 LFGMgr::GetKicksLeft(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "]", guid); - return m_Groups[guid].GetKicksLeft(); + uint8 kicks = m_Groups[guid].GetKicksLeft(); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "] = %u", guid, kicks); + return kicks; } -uint8 LFGMgr::GetVotesNeeded(uint64 guid) +void LFGMgr::RestoreState(uint64 guid, char const *debugMsg) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetVotesNeeded: [" UI64FMTD "]", guid); - return m_Groups[guid].GetVotesNeeded(); + LfgGroupData& data = m_Groups[guid]; + char const * const ps = GetStateString(data.GetState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s), State: %s", guid, debugMsg, ps); + data.RestoreState(); } -void LFGMgr::RestoreState(uint64 guid) +void LFGMgr::ClearState(uint64 guid, char const *debugMsg) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: [" UI64FMTD "]", guid); - m_Groups[guid].RestoreState(); -} - -void LFGMgr::ClearState(uint64 guid) -{ - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: [" UI64FMTD "]", guid); - m_Players[guid].ClearState(); + LfgPlayerData& data = m_Players[guid]; + char const * const ps = GetStateString(data.GetState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: Player: [" UI64FMTD "] (%s) State: %s", guid, debugMsg, ps); + data.ClearState(); } void LFGMgr::SetState(uint64 guid, LfgState state) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: [" UI64FMTD "] state %u", guid, state); if (IS_GROUP(guid)) - m_Groups[guid].SetState(state); + { + LfgGroupData& data = m_Groups[guid]; + char const * const ns = GetStateString(state); + char const * const ps = GetStateString(data.GetState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s", guid, ns, ps); + data.SetState(state); + } else - m_Players[guid].SetState(state); + { + LfgPlayerData& data = m_Players[guid]; + char const * const ns = GetStateString(state); + char const * const ps = GetStateString(data.GetState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s", guid, ns, ps); + data.SetState(state); + } } void LFGMgr::SetDungeon(uint64 guid, uint32 dungeon) @@ -2205,3 +2282,81 @@ void LFGMgr::RemoveGroupData(uint64 guid) if (it != m_Groups.end()) m_Groups.erase(it); } + +bool LFGMgr::HasIgnore(uint64 guid1, uint64 guid2) +{ + Player* plr1 = ObjectAccessor::FindPlayer(guid1); + Player* plr2 = ObjectAccessor::FindPlayer(guid2); + uint32 low1 = GUID_LOPART(guid1); + uint32 low2 = GUID_LOPART(guid2); + return plr1 && plr2 && (plr1->GetSocial()->HasIgnore(low2) || plr2->GetSocial()->HasIgnore(low1)); +} + +void LFGMgr::SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgRoleChosen(pguid, roles); +} + +void LFGMgr::SendLfgRoleCheckUpdate(uint64 guid, const LfgRoleCheck& roleCheck) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgRoleCheckUpdate(roleCheck); +} + +void LFGMgr::SendLfgUpdatePlayer(uint64 guid, const LfgUpdateData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdatePlayer(data); +} + +void LFGMgr::SendLfgUpdateParty(uint64 guid, const LfgUpdateData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdateParty(data); +} + +void LFGMgr::SendLfgJoinResult(uint64 guid, const LfgJoinResultData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgJoinResult(data); +} + +void LFGMgr::SendLfgBootProposalUpdate(uint64 guid, const LfgPlayerBoot& boot) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgBootProposalUpdate(boot); +} + +void LFGMgr::SendLfgUpdateProposal(uint64 guid, uint32 proposalId, const LfgProposal& proposal) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdateProposal(proposalId, proposal); +} + +void LFGMgr::SendLfgQueueStatus(uint64 guid, const LfgQueueStatusData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgQueueStatus(data); +} + +bool LFGMgr::IsLfgGroup(uint64 guid) +{ + return guid && IS_GROUP(guid) && m_Groups[guid].IsLfgGroup(); +} + +bool LFGMgr::IsSeasonActive(uint32 dungeonId) +{ + switch (dungeonId) + { + case 285: // The Headless Horseman + return IsHolidayActive(HOLIDAY_HALLOWS_END); + case 286: // The Frost Lord Ahune + return IsHolidayActive(HOLIDAY_FIRE_FESTIVAL); + case 287: // Coren Direbrew + return IsHolidayActive(HOLIDAY_BREWFEST); + case 288: // The Crown Chemical Co. + return IsHolidayActive(HOLIDAY_LOVE_IS_IN_THE_AIR); + } + return false; +} diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 9937759741b..d4d35974604 100755 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -21,24 +21,24 @@ #include "Common.h" #include #include "LFG.h" +#include "LFGGroupData.h" +#include "LFGPlayerData.h" -class LfgGroupData; -class LfgPlayerData; +class LFGPlayerScript; +class LFGGroupScript; class Group; class Player; -enum LFGenum +enum LFGMgrEnum { - LFG_TIME_ROLECHECK = 40*IN_MILLISECONDS, - LFG_TIME_BOOT = 2*MINUTE, - LFG_TIME_PROPOSAL = 2*MINUTE, - LFG_TANKS_NEEDED = 1, - LFG_HEALERS_NEEDED = 1, - LFG_DPS_NEEDED = 3, - LFG_QUEUEUPDATE_INTERVAL = 15*IN_MILLISECONDS, + LFG_TIME_ROLECHECK = 40 * IN_MILLISECONDS, + LFG_TIME_BOOT = 120, + LFG_TIME_PROPOSAL = 120, + LFG_QUEUEUPDATE_INTERVAL = 15 * IN_MILLISECONDS, LFG_SPELL_DUNGEON_COOLDOWN = 71328, LFG_SPELL_DUNGEON_DESERTER = 71041, - LFG_SPELL_LUCK_OF_THE_DRAW = 72221 + LFG_SPELL_LUCK_OF_THE_DRAW = 72221, + LFG_GROUP_KICK_VOTES_NEEDED = 3 }; enum LfgFlags @@ -52,11 +52,9 @@ enum LfgFlags /// Determines the type of instance enum LfgType { - LFG_TYPE_NONE = 0, // Internal use only + LFG_TYPE_NONE = 0, LFG_TYPE_DUNGEON = 1, LFG_TYPE_RAID = 2, - LFG_TYPE_QUEST = 3, - LFG_TYPE_ZONE = 4, LFG_TYPE_HEROIC = 5, LFG_TYPE_RANDOM = 6 }; @@ -72,13 +70,14 @@ enum LfgProposalState /// Teleport errors enum LfgTeleportError { - // 3, 7, 8 = "You can't do that right now" | 5 = No client reaction + // 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_DONT_REPORT = 3, + LFG_TELEPORTERROR_IN_VEHICLE = 3, LFG_TELEPORTERROR_FATIGUE = 4, - LFG_TELEPORTERROR_INVALID_LOCATION = 6 + LFG_TELEPORTERROR_INVALID_LOCATION = 6, + LFG_TELEPORTERROR_CHARMING = 8 // FIXME - It can be 7 or 8 (Need proper data) }; /// Queue join results @@ -116,42 +115,31 @@ enum LfgRoleCheckState LFG_ROLECHECK_NO_ROLE = 6 // Someone selected no role }; -/// Answer state (Also used to check compatibilites) -enum LfgAnswer -{ - LFG_ANSWER_PENDING = -1, - LFG_ANSWER_DENY = 0, - LFG_ANSWER_AGREE = 1 -}; - // Forward declaration (just to have all typedef together) +struct LFGDungeonEntry; struct LfgReward; -struct LfgLockStatus; struct LfgQueueInfo; struct LfgRoleCheck; struct LfgProposal; struct LfgProposalPlayer; struct LfgPlayerBoot; -typedef std::set LfgGuidSet; -typedef std::list LfgGuidList; typedef std::map LfgGuidListMap; typedef std::set PlayerSet; typedef std::list LfgPlayerList; typedef std::multimap LfgRewardMap; typedef std::pair LfgRewardMapBounds; typedef std::map LfgCompatibleMap; -typedef std::map LfgDungeonMap; -typedef std::map LfgRolesMap; +typedef std::map LfgCachedDungeonMap; typedef std::map LfgAnswerMap; -typedef std::map LfgRoleCheckMap; +typedef std::map LfgRoleCheckMap; typedef std::map LfgQueueInfoMap; typedef std::map LfgProposalMap; typedef std::map LfgProposalPlayerMap; -typedef std::map LfgPlayerBootMap; +typedef std::map LfgPlayerBootMap; typedef std::map LfgGroupDataMap; typedef std::map LfgPlayerDataMap; -typedef std::map LfgEntrancePositionMap; +typedef UNORDERED_MAP LFGDungeonMap; // Data needed by SMSG_LFG_JOIN_RESULT struct LfgJoinResultData @@ -167,7 +155,7 @@ struct LfgJoinResultData struct LfgUpdateData { LfgUpdateData(LfgUpdateType _type = LFG_UPDATETYPE_DEFAULT): updateType(_type), comment("") {} - LfgUpdateData(LfgUpdateType _type, const LfgDungeonSet& _dungeons, std::string _comment): + LfgUpdateData(LfgUpdateType _type, LfgDungeonSet const& _dungeons, std::string _comment): updateType(_type), dungeons(_dungeons), comment(_comment) {} LfgUpdateType updateType; @@ -175,6 +163,26 @@ struct LfgUpdateData 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; +}; + /// Reward info struct LfgReward { @@ -236,7 +244,6 @@ struct LfgProposal time_t cancelTime; ///< Time when we will cancel this proposal LfgGuidList queues; ///< Queue Ids to remove/readd LfgProposalPlayerMap players; ///< Players data - }; /// Stores all rolecheck info of a group that wants to join @@ -261,6 +268,33 @@ struct LfgPlayerBoot std::string reason; ///< kick reason }; +struct LFGDungeonEntry +{ + LFGDungeonEntry(): 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) + { } + LFGDungeonEntry(LFGDungeonEntryDbc 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; @@ -274,57 +308,70 @@ class LFGMgr // Reward void LoadRewards(); - void RewardDungeonDoneFor(const uint32 dungeonId, Player* player); + void RewardDungeonDoneFor(uint32 const dungeonId, Player* player); LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level); // Queue - void Join(Player* player, uint8 roles, const LfgDungeonSet& dungeons, const std::string& comment); - void Leave(Player* player, Group* grp = NULL); + void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment); + void LeaveLfg(Player* player, Group* grp = NULL); // Role Check - void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = ROLE_NONE); + void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE); // Proposals void UpdateProposal(uint32 proposalId, uint64 guid, bool accept); // Teleportation - void LoadEntrancePositions(); void TeleportPlayer(Player* player, bool out, bool fromOpcode = false); // Vote kick - void InitBoot(Group* grp, uint64 kguid, uint64 vguid, std::string reason); + void InitBoot(Group* grp, uint64 kguid, uint64 vguid, std::string const& reason); void UpdateBoot(Player* player, bool accept); void OfferContinue(Group* grp); - HolidayIds GetDungeonSeason(uint32 dungeonId); + void InitializeLockedDungeons(Player* player, uint8 level = 0); - void InitializeLockedDungeons(Player* player); + void SetRoles(uint64 guid, uint8 roles); + void SetComment(uint64 guid, std::string const& comment); + void SetState(uint64 guid, LfgState state); + void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons); void _LoadFromDB(Field* fields, uint64 guid); void _SaveToDB(uint64 guid, uint32 db_guid); - void SetComment(uint64 guid, const std::string& comment); - const LfgLockMap& GetLockedDungeons(uint64 guid); - LfgState GetState(uint64 guid); - const LfgDungeonSet& GetSelectedDungeons(uint64 guid); - uint32 GetDungeon(uint64 guid, bool asId = true); - void SetState(uint64 guid, LfgState state); - void ClearState(uint64 guid); void RemovePlayerData(uint64 guid); void RemoveGroupData(uint64 guid); - uint8 GetKicksLeft(uint64 gguid); - uint8 GetVotesNeeded(uint64 gguid); - bool IsTeleported(uint64 pguid); - void SetRoles(uint64 guid, uint8 roles); - void SetSelectedDungeons(uint64 guid, const LfgDungeonSet& dungeons); + LfgLockMap const& GetLockedDungeons(uint64 guid); + LfgDungeonSet const& GetSelectedDungeons(uint64 guid); + uint32 GetDungeon(uint64 guid, bool asId = true); + LfgState GetState(uint64 guid); + uint8 GetKicksLeft(uint64 gguid); + bool IsLfgGroup(uint64 guid); + uint8 GetRoles(uint64 guid); + std::string const& GetComment(uint64 gguid); + bool IsTeleported(uint64 guid); + + static bool HasIgnore(uint64 guid1, uint64 guid2); + static void SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data); + + bool IsSeasonActive(uint32 dungeonId); + + static std::string ConcatenateDungeons(LfgDungeonSet const& dungeons); + static std::string GetRolesString(uint8 roles); + static char const * GetStateString(LfgState state); + + void LoadLFGDungeons(bool reload = false); + LFGDungeonEntry const* GetLFGDungeon(uint32 id); + LFGDungeonMap& GetLFGDungeonMap(); + + void ClearState(uint64 guid, char const *debugMsg); private: - uint8 GetRoles(uint64 guid); - const std::string& GetComment(uint64 gguid); - void RestoreState(uint64 guid); + void RestoreState(uint64 guid, char const *debugMsg); + void SetDungeon(uint64 guid, uint32 dungeon); - void SetLockedDungeons(uint64 guid, const LfgLockMap& lock); + void SetLockedDungeons(uint64 guid, LfgLockMap const& lock); void DecreaseKicksLeft(uint64 guid); // Queue @@ -338,20 +385,29 @@ class LFGMgr LfgProposal* FindNewGroups(LfgGuidList& check, LfgGuidList& all); bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); bool CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal); - void GetCompatibleDungeons(LfgDungeonSet& dungeons, const PlayerSet& players, LfgLockPartyMap& lockMap); + void GetCompatibleDungeons(LfgDungeonSet& dungeons, PlayerSet const& players, LfgLockPartyMap& lockMap); void SetCompatibles(std::string concatenatedGuids, bool compatibles); LfgAnswer GetCompatibles(std::string concatenatedGuids); void RemoveFromCompatibles(uint64 guid); // Generic - const LfgDungeonSet& GetDungeonsByRandom(uint32 randomdungeon); + LfgDungeonSet const& GetDungeonsByRandom(uint32 randomdungeon); LfgType GetDungeonType(uint32 dungeon); std::string ConcatenateGuids(LfgGuidList check); + 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, uint32 proposalId, LfgProposal const& proposal); + // General variables - bool m_update; ///< Doing an update? uint32 m_QueueTimer; ///< used to check interval of update uint32 m_lfgProposalId; ///< used as internal counter for proposals + uint32 m_options; ///< Stores config options + int32 m_WaitTimeAvg; ///< Average wait time to find a group queuing as multiple roles int32 m_WaitTimeTank; ///< Average wait time to find a group queuing as tank int32 m_WaitTimeHealer; ///< Average wait time to find a group queuing as healer @@ -360,10 +416,10 @@ class LFGMgr uint32 m_NumWaitTimeTank; ///< Num of players used to calc tank wait time uint32 m_NumWaitTimeHealer; ///< Num of players used to calc healers wait time uint32 m_NumWaitTimeDps; ///< Num of players used to calc dps wait time - LfgDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType - LfgEntrancePositionMap m_entrancePositions; ///< Stores special entrance positions + LfgCachedDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType // Reward System LfgRewardMap m_RewardMap; ///< Stores rewards for random dungeons + LFGDungeonMap m_LfgDungeonMap; // Queue LfgQueueInfoMap m_QueueInfoMap; ///< Queued groups LfgGuidListMap m_currentQueue; ///< Ordered list. Used to find groups @@ -376,6 +432,9 @@ class LFGMgr LfgPlayerBootMap m_Boots; ///< Current player kicks LfgPlayerDataMap m_Players; ///< Player data LfgGroupDataMap m_Groups; ///< Group data + + LFGPlayerScript *m_lfgPlayerScript; + LFGGroupScript *m_lfgGroupScript; }; #define sLFGMgr ACE_Singleton::instance() diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index 36f04b3020b..26686dbaa33 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -39,12 +39,12 @@ void LFGPlayerScript::OnLevelChanged(Player* player, uint8 /*oldLevel*/) void LFGPlayerScript::OnLogout(Player* player) { - sLFGMgr->Leave(player); + uint64 guid = player->GetGUID(); + sLFGMgr->LeaveLfg(player); LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); player->GetSession()->SendLfgUpdateParty(updateData); player->GetSession()->SendLfgUpdatePlayer(updateData); - player->GetSession()->SendLfgUpdateSearch(false); - uint64 guid = player->GetGUID(); + player->GetSession()->SendLfgLfrList(false); // TODO - Do not remove, add timer before deleting sLFGMgr->RemovePlayerData(guid); } @@ -85,11 +85,11 @@ void LFGGroupScript::OnAddMember(Group* group, uint64 guid) // TODO - if group is queued and new player is added convert to rolecheck without notify the current players queued if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) - sLFGMgr->Leave(NULL, group); + sLFGMgr->LeaveLfg(NULL, group); if (sLFGMgr->GetState(guid) == LFG_STATE_QUEUED) if (Player* player = ObjectAccessor::FindPlayer(guid)) - sLFGMgr->Leave(player); + sLFGMgr->LeaveLfg(player); } void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod method, uint64 kicker, char const* reason) @@ -102,7 +102,7 @@ void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod meth if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) { // TODO - Do not remove, just remove the one leaving and rejoin queue with all other data - sLFGMgr->Leave(NULL, group); + sLFGMgr->LeaveLfg(NULL, group); } if (!group->isLFGGroup()) @@ -119,16 +119,14 @@ void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod meth } uint32 state = sLFGMgr->GetState(gguid); - sLFGMgr->ClearState(guid); + sLFGMgr->ClearState(guid, "OnRemoveMember"); sLFGMgr->SetState(guid, LFG_STATE_NONE); if (Player* player = ObjectAccessor::FindPlayer(guid)) { if (method == GROUP_REMOVEMETHOD_LEAVE && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON && sLFGMgr->GetDungeon(gguid, false)) player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, true); - /* - else if (group->isLfgKickActive()) + //else if (state == LFG_STATE_BOOT) // Update internal kick cooldown of kicked - */ LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_LEADER); player->GetSession()->SendLfgUpdateParty(updateData); @@ -176,5 +174,5 @@ void LFGGroupScript::OnInviteMember(Group* group, uint64 guid) return; sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, group->GetLeaderGUID()); - sLFGMgr->Leave(NULL, group); + sLFGMgr->LeaveLfg(NULL, group); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 161fdc2be1d..93a0580a866 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -11901,7 +11901,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje bool lootedObjectInDungeon = false; Map const* map = lootedObject->GetMap(); if (uint32 dungeonId = sLFGMgr->GetDungeon(GetGroup()->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) + if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == uint32(map->GetDifficulty())) lootedObjectInDungeon = true; @@ -23261,7 +23261,7 @@ PartyResult Player::CanUninviteFromGroup() const if (state == LFG_STATE_BOOT) return ERR_PARTY_LFG_BOOT_IN_PROGRESS; - if (grp->GetMembersCount() <= sLFGMgr->GetVotesNeeded(gguid)) + if (grp->GetMembersCount() <= LFG_GROUP_KICK_VOTES_NEEDED) return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS; if (state == LFG_STATE_FINISHED_DUNGEON) @@ -23294,8 +23294,22 @@ PartyResult Player::CanUninviteFromGroup() const bool Player::isUsingLfg() { - uint64 guid = GetGUID(); - return sLFGMgr->GetState(guid) != LFG_STATE_NONE; + return sLFGMgr->GetState(GetGUID()) != LFG_STATE_NONE; +} + +bool Player::inRandomLfgDungeon() +{ + if (isUsingLfg()) + { + const LfgDungeonSet& dungeons = sLFGMgr->GetSelectedDungeons(GetGUID()); + if (!dungeons.empty()) + { + LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(*dungeons.begin()); + if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal)) + return true; + } + } + return false; } void Player::SetBattlegroundOrBattlefieldRaid(Group* group, int8 subgroup) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index c417c73cdbc..9d80a17bc5e 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2356,6 +2356,7 @@ class Player : public Unit, public GridObject void RemoveAtLoginFlag(AtLoginFlags flags, bool persist = false); bool isUsingLfg(); + bool inRandomLfgDungeon(); typedef std::set DFQuestsDoneList; DFQuestsDoneList m_DFQuests; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b884a369eeb..17982a58061 100755 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -45,6 +45,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "PoolMgr.h" +#include "LFGMgr.h" ScriptMapMap sQuestEndScripts; ScriptMapMap sQuestStartScripts; @@ -5028,7 +5029,7 @@ void ObjectMgr::LoadInstanceEncounters() continue; } - if (lastEncounterDungeon && !sLFGDungeonStore.LookupEntry(lastEncounterDungeon)) + if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeon(lastEncounterDungeon)) { sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName[0], lastEncounterDungeon); continue; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 19ea3ff8174..f06225988d6 100755 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -600,7 +600,7 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod &method /*= GROUP_REMOV if (isLFGGroup() && GetMembersCount() == 1) { Player* Leader = ObjectAccessor::FindPlayer(GetLeaderGUID()); - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(sLFGMgr->GetDungeon(GetGUID())); + LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(sLFGMgr->GetDungeon(GetGUID())); if ((Leader && dungeon && Leader->isAlive() && Leader->GetMapId() != uint32(dungeon->map)) || !dungeon) { Disband(); diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp index f5d5a0b67e2..59631618757 100755 --- a/src/server/game/Handlers/LFGHandler.cpp +++ b/src/server/game/Handlers/LFGHandler.cpp @@ -23,7 +23,6 @@ #include "LFGMgr.h" #include "ObjectMgr.h" #include "GroupMgr.h" -#include "GameEventMgr.h" #include "InstanceScript.h" void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock) @@ -82,18 +81,19 @@ void WorldSession::HandleLfgJoinOpcode(WorldPacket& recv_data) std::string comment; recv_data >> comment; sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] roles: %u, Dungeons: %u, Comment: %s", GetPlayer()->GetGUID(), roles, uint8(newDungeons.size()), comment.c_str()); - sLFGMgr->Join(GetPlayer(), uint8(roles), newDungeons, comment); + sLFGMgr->JoinLfg(GetPlayer(), uint8(roles), newDungeons, comment); } void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recv_data*/) { Group* grp = GetPlayer()->GetGroup(); + uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", GetPlayer()->GetGUID(), grp ? 1 : 0); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", guid, grp ? 1 : 0); // Check cheating - only leader can leave the queue if (!grp || grp->GetLeaderGUID() == GetPlayer()->GetGUID()) - sLFGMgr->Leave(GetPlayer(), grp); + sLFGMgr->LeaveLfg(GetPlayer(), grp); } void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recv_data) @@ -128,7 +128,7 @@ void WorldSession::HandleLfgSetCommentOpcode(WorldPacket& recv_data) std::string comment; recv_data >> comment; uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SET_LFG_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str()); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str()); sLFGMgr->SetComment(guid, comment); } @@ -138,7 +138,8 @@ void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recv_data) bool agree; // Agree to kick player recv_data >> agree; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", GetPlayer()->GetGUID(), agree ? 1 : 0); + uint64 guid = GetPlayer()->GetGUID(); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", guid, agree ? 1 : 0); sLFGMgr->UpdateBoot(GetPlayer(), agree); } @@ -154,32 +155,24 @@ void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data) void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data*/) { uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); // Get Random dungeons that can be done at a certain level and expansion LfgDungeonSet randomDungeons; uint8 level = GetPlayer()->getLevel(); uint8 expansion = GetPlayer()->GetSession()->Expansion(); - for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (dungeon && dungeon->expansion <= expansion && dungeon->minlevel <= level && level <= dungeon->maxlevel) - { - if (dungeon->flags & LFG_FLAG_SEASONAL) - { - if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) - if (!IsHolidayActive(holiday)) - continue; - } - else if (dungeon->type != LFG_TYPE_RANDOM) - continue; - randomDungeons.insert(dungeon->Entry()); - } + LFGDungeonMap& LfgDungeons = sLFGMgr->GetLFGDungeonMap(); + for (LFGDungeonMap::const_iterator itr = LfgDungeons.begin(); itr != LfgDungeons.end(); ++itr) + { + LFGDungeonEntry const& dungeon = itr->second; + if ((dungeon.type == LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id))) + && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel) + randomDungeons.insert(dungeon.Entry()); } // Get player locked Dungeons - LfgLockMap lock = sLFGMgr->GetLockedDungeons(guid); + LfgLockMap const& lock = sLFGMgr->GetLockedDungeons(guid); uint32 rsize = uint32(randomDungeons.size()); uint32 lsize = uint32(lock.size()); @@ -191,40 +184,37 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data { data << uint32(*it); // Dungeon Entry (id + type) LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level); - Quest const* qRew = NULL; + Quest const* quest = NULL; uint8 done = 0; if (reward) { - qRew = sObjectMgr->GetQuestTemplate(reward->reward[0].questId); - if (qRew) + quest = sObjectMgr->GetQuestTemplate(reward->reward[0].questId); + if (quest) { - done = !GetPlayer()->CanRewardQuest(qRew, false); + done = !GetPlayer()->CanRewardQuest(quest, false); if (done) - qRew = sObjectMgr->GetQuestTemplate(reward->reward[1].questId); + quest = sObjectMgr->GetQuestTemplate(reward->reward[1].questId); } } - if (qRew) + + if (quest) { data << uint8(done); - data << uint32(qRew->GetRewOrReqMoney()); - data << uint32(qRew->XPValue(GetPlayer())); + data << uint32(quest->GetRewOrReqMoney()); + data << uint32(quest->XPValue(GetPlayer())); data << uint32(reward->reward[done].variableMoney); data << uint32(reward->reward[done].variableXP); - data << uint8(qRew->GetRewItemsCount()); - if (qRew->GetRewItemsCount()) + data << uint8(quest->GetRewItemsCount()); + if (quest->GetRewItemsCount()) { - ItemTemplate const* iProto = NULL; for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - { - if (!qRew->RewardItemId[i]) - continue; - - iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]); - - data << uint32(qRew->RewardItemId[i]); - data << uint32(iProto ? iProto->DisplayInfoID : 0); - data << uint32(qRew->RewardItemIdCount[i]); - } + if (uint32 itemId = quest->RewardItemId[i]) + { + ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); + data << uint32(itemId); + data << uint32(item ? item->DisplayInfoID : 0); + data << uint32(quest->RewardItemIdCount[i]); + } } } else @@ -244,7 +234,7 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data*/) { uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); Group* grp = GetPlayer()->GetGroup(); if (!grp) @@ -275,11 +265,11 @@ void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data SendPacket(&data); } -void WorldSession::HandleLfrSearchOpcode(WorldPacket& recv_data) +void WorldSession::HandleLfrJoinOpcode(WorldPacket& recv_data) { uint32 entry; // Raid id to search recv_data >> entry; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LFR_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry); //SendLfrUpdateListOpcode(entry); } @@ -287,7 +277,7 @@ void WorldSession::HandleLfrLeaveOpcode(WorldPacket& recv_data) { uint32 dungeonId; // Raid id queue to leave recv_data >> dungeonId; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LFR_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId); //sLFGMgr->LeaveLfr(GetPlayer(), dungeonId); } @@ -397,45 +387,44 @@ void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles) SendPacket(&data); } -void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck) +void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck) { - ASSERT(pRoleCheck); LfgDungeonSet dungeons; - if (pRoleCheck->rDungeonId) - dungeons.insert(pRoleCheck->rDungeonId); + if (roleCheck.rDungeonId) + dungeons.insert(roleCheck.rDungeonId); else - dungeons = pRoleCheck->dungeons; + dungeons = roleCheck.dungeons; sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHECK_UPDATE [" UI64FMTD "]", GetPlayer()->GetGUID()); - WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1)); + WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + roleCheck.roles.size() * (8 + 1 + 4 + 1)); - data << uint32(pRoleCheck->state); // Check result - data << uint8(pRoleCheck->state == LFG_ROLECHECK_INITIALITING); + data << uint32(roleCheck.state); // Check result + data << uint8(roleCheck.state == LFG_ROLECHECK_INITIALITING); data << uint8(dungeons.size()); // Number of dungeons if (!dungeons.empty()) { for (LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it); + LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(*it); data << uint32(dungeon ? dungeon->Entry() : 0); // Dungeon } } - data << uint8(pRoleCheck->roles.size()); // Players in group - if (!pRoleCheck->roles.empty()) + data << uint8(roleCheck.roles.size()); // Players in group + if (!roleCheck.roles.empty()) { // Leader info MUST be sent 1st :S - uint64 guid = pRoleCheck->leader; - uint8 roles = pRoleCheck->roles.find(guid)->second; + uint64 guid = roleCheck.leader; + uint8 roles = roleCheck.roles.find(guid)->second; data << uint64(guid); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles Player* player = ObjectAccessor::FindPlayer(guid); data << uint8(player ? player->getLevel() : 0); // Level - for (LfgRolesMap::const_iterator it = pRoleCheck->roles.begin(); it != pRoleCheck->roles.end(); ++it) + for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it) { - if (it->first == pRoleCheck->leader) + if (it->first == roleCheck.leader) continue; guid = it->first; @@ -465,30 +454,31 @@ void WorldSession::SendLfgJoinResult(const LfgJoinResultData& joinData) SendPacket(&data); } -void WorldSession::SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps) +void WorldSession::SendLfgQueueStatus(const LfgQueueStatusData& queueData) { - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", GetPlayer()->GetGUID(), dungeon, waitTime, avgWaitTime, waitTimeTanks, waitTimeHealer, waitTimeDps, queuedTime, tanks, healers, dps); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", + GetPlayer()->GetGUID(), queueData.dungeonId, queueData.waitTime, queueData.waitTimeAvg, queueData.waitTimeTank, queueData.waitTimeHealer, queueData.waitTimeDps, queueData.queuedTime, queueData.tanks, queueData.healers, queueData.dps); WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4); - data << uint32(dungeon); // Dungeon - data << int32(avgWaitTime); // Average Wait time - data << int32(waitTime); // Wait Time - data << int32(waitTimeTanks); // Wait Tanks - data << int32(waitTimeHealer); // Wait Healers - data << int32(waitTimeDps); // Wait Dps - data << uint8(tanks); // Tanks needed - data << uint8(healers); // Healers needed - data << uint8(dps); // Dps needed - data << uint32(queuedTime); // Player wait time in queue + data << uint32(queueData.dungeonId); // Dungeon + data << int32(queueData.waitTimeAvg); // Average Wait time + data << int32(queueData.waitTime); // Wait Time + data << int32(queueData.waitTimeTank); // Wait Tanks + data << int32(queueData.waitTimeHealer); // Wait Healers + data << int32(queueData.waitTimeDps); // Wait Dps + data << uint8(queueData.tanks); // Tanks needed + data << uint8(queueData.healers); // Healers needed + data << uint8(queueData.dps); // Dps needed + data << uint32(queueData.queuedTime); // Player wait time in queue SendPacket(&data); } -void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* qRew) +void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* quest) { - if (!rdungeonEntry || !sdungeonEntry || !qRew) + if (!rdungeonEntry || !sdungeonEntry || !quest) return; - uint8 itemNum = uint8(qRew ? qRew->GetRewItemsCount() : 0); + uint8 itemNum = uint8(quest ? quest->GetRewItemsCount() : 0); sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_REWARD [" UI64FMTD "] rdungeonEntry: %u - sdungeonEntry: %u - done: %u", GetPlayer()->GetGUID(), rdungeonEntry, sdungeonEntry, done); WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4)); @@ -496,37 +486,33 @@ void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntr data << uint32(sdungeonEntry); // Dungeon Finished data << uint8(done); data << uint32(1); - data << uint32(qRew->GetRewOrReqMoney()); - data << uint32(qRew->XPValue(GetPlayer())); + data << uint32(quest->GetRewOrReqMoney()); + data << uint32(quest->XPValue(GetPlayer())); data << uint32(reward->reward[done].variableMoney); data << uint32(reward->reward[done].variableXP); data << uint8(itemNum); if (itemNum) { - ItemTemplate const* iProto = NULL; for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - { - if (!qRew->RewardItemId[i]) - continue; - - iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]); - - data << uint32(qRew->RewardItemId[i]); - data << uint32(iProto ? iProto->DisplayInfoID : 0); - data << uint32(qRew->RewardItemIdCount[i]); - } + if (uint32 itemId = quest->RewardItemId[i]) + { + ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); + data << uint32(itemId); + data << uint32(item ? item->DisplayInfoID : 0); + data << uint32(quest->RewardItemIdCount[i]); + } } SendPacket(&data); } -void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot) +void WorldSession::SendLfgBootProposalUpdate(const LfgPlayerBoot& boot) { uint64 guid = GetPlayer()->GetGUID(); - LfgAnswer playerVote = pBoot->votes.find(guid)->second; + LfgAnswer playerVote = boot.votes.find(guid)->second; uint8 votesNum = 0; uint8 agreeNum = 0; - uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000); - for (LfgAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it) + uint32 secsleft = uint8((boot.cancelTime - time(NULL)) / 1000); + for (LfgAnswerMap::const_iterator it = boot.votes.begin(); it != boot.votes.end(); ++it) { if (it->second != LFG_ANSWER_PENDING) { @@ -536,34 +522,31 @@ void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot) } } sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PROPOSAL_UPDATE [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s", - guid, uint8(pBoot->inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), pBoot->victim, votesNum, agreeNum, secsleft, pBoot->votedNeeded, pBoot->reason.c_str()); - WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length()); - data << uint8(pBoot->inProgress); // Vote in progress + guid, uint8(boot.inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), boot.victim, votesNum, agreeNum, secsleft, boot.votedNeeded, boot.reason.c_str()); + WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length()); + data << uint8(boot.inProgress); // Vote in progress data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree - data << uint64(pBoot->victim); // Victim GUID + data << uint64(boot.victim); // Victim GUID data << uint32(votesNum); // Total Votes data << uint32(agreeNum); // Agree Count data << uint32(secsleft); // Time Left - data << uint32(pBoot->votedNeeded); // Needed Votes - data << pBoot->reason.c_str(); // Kick reason + data << uint32(boot.votedNeeded); // Needed Votes + data << boot.reason.c_str(); // Kick reason SendPacket(&data); } -void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* pProp) +void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal& proposal) { - if (!pProp) - return; - uint64 guid = GetPlayer()->GetGUID(); - LfgProposalPlayerMap::const_iterator itPlayer = pProp->players.find(guid); - if (itPlayer == pProp->players.end()) // Player MUST be in the proposal + LfgProposalPlayerMap::const_iterator itPlayer = proposal.players.find(guid); + if (itPlayer == proposal.players.end()) // Player MUST be in the proposal return; LfgProposalPlayer* ppPlayer = itPlayer->second; uint32 pLowGroupGuid = ppPlayer->groupLowGuid; - uint32 dLowGuid = pProp->groupLowGuid; - uint32 dungeonId = pProp->dungeonId; + uint32 dLowGuid = proposal.groupLowGuid; + uint32 dungeonId = proposal.dungeonId; bool isSameDungeon = false; bool isContinue = false; Group* grp = dLowGuid ? sGroupMgr->GetGroupByGUID(dLowGuid) : NULL; @@ -575,8 +558,8 @@ void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* p isSameDungeon = GetPlayer()->GetGroup() == grp && isContinue; } - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), pProp->state); - WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProp->players.size() * (4 + 1 + 1 + 1 + 1 +1)); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), proposal.state); + WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + proposal.players.size() * (4 + 1 + 1 + 1 + 1 +1)); if (!isContinue) // Only show proposal dungeon if it's continue { @@ -585,7 +568,7 @@ void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* p dungeonId = (*playerDungeons.begin()); } - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) + if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) { dungeonId = dungeon->Entry(); @@ -606,13 +589,13 @@ void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* p } data << uint32(dungeonId); // Dungeon - data << uint8(pProp->state); // Result state + data << uint8(proposal.state); // Result state data << uint32(proposalId); // Internal Proposal ID data << uint32(completedEncounters); // Bosses killed data << uint8(isSameDungeon); // Silent (show client window) - data << uint8(pProp->players.size()); // Group size + data << uint8(proposal.players.size()); // Group size - for (itPlayer = pProp->players.begin(); itPlayer != pProp->players.end(); ++itPlayer) + for (itPlayer = proposal.players.begin(); itPlayer != proposal.players.end(); ++itPlayer) { ppPlayer = itPlayer->second; data << uint32(ppPlayer->role); // Role @@ -633,9 +616,9 @@ void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* p SendPacket(&data); } -void WorldSession::SendLfgUpdateSearch(bool update) +void WorldSession::SendLfgLfrList(bool update) { - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_SEARCH [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_LFR_LIST [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0); WorldPacket data(SMSG_LFG_UPDATE_SEARCH, 1); data << uint8(update); // In Lfg Queue? SendPacket(&data); @@ -667,8 +650,8 @@ void WorldSession::SendLfgTeleportError(uint8 err) /* void WorldSession::SendLfrUpdateListOpcode(uint32 dungeonEntry) { - sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_UPDATE_LFG_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry); - WorldPacket data(SMSG_UPDATE_LFG_LIST); + sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_LFG_UPDATE_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry); + WorldPacket data(SMSG_LFG_UPDATE_LIST); SendPacket(&data); } */ diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index bc524c720f7..c3c6befe2f9 100755 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2468,8 +2468,8 @@ bool InstanceMap::AddPlayerToMap(Player* player) if (group && group->isLFGGroup()) if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) - if (LFGDungeonEntry const* randomDungeon = sLFGDungeonStore.LookupEntry(*(sLFGMgr->GetSelectedDungeons(player->GetGUID()).begin()))) + if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) + if (LFGDungeonEntry const* randomDungeon = sLFGMgr->GetLFGDungeon(*(sLFGMgr->GetSelectedDungeons(player->GetGUID()).begin()))) if (uint32(dungeon->map) == GetId() && dungeon->difficulty == uint32(GetDifficulty()) && randomDungeon->type == uint32(LFG_TYPE_RANDOM)) player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true); } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index a229d3ed536..af2ff2c6d86 100755 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -888,7 +888,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x35C*/ { "CMSG_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgJoinOpcode }, /*0x35D*/ { "CMSG_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgLeaveOpcode }, - /*0x35E*/ { "CMSG_SEARCH_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrSearchOpcode }, + /*0x35E*/ { "CMSG_SEARCH_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrJoinOpcode }, /*0x35F*/ { "CMSG_SEARCH_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrLeaveOpcode }, /*0x360*/ { "SMSG_UPDATE_LFG_LIST", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x361*/ { "SMSG_LFG_PROPOSAL_UPDATE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index bc9e6ac4298..3ff45bff3b1 100755 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -54,6 +54,7 @@ struct LfgJoinResultData; struct LfgLockStatus; struct LfgPlayerBoot; struct LfgProposal; +struct LfgQueueStatusData; struct LfgReward; struct LfgRoleCheck; struct LfgUpdateData; @@ -815,19 +816,19 @@ class WorldSession void HandleLfgProposalResultOpcode(WorldPacket& recv_data); void HandleLfgSetBootVoteOpcode(WorldPacket& recv_data); void HandleLfgTeleportOpcode(WorldPacket& recv_data); - void HandleLfrSearchOpcode(WorldPacket& recv_data); + void HandleLfrJoinOpcode(WorldPacket& recv_data); void HandleLfrLeaveOpcode(WorldPacket& recv_data); void SendLfgUpdatePlayer(const LfgUpdateData& updateData); void SendLfgUpdateParty(const LfgUpdateData& updateData); void SendLfgRoleChosen(uint64 guid, uint8 roles); - void SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck); - void SendLfgUpdateSearch(bool update); + void SendLfgRoleCheckUpdate(const LfgRoleCheck& pRoleCheck); + void SendLfgLfrList(bool update); void SendLfgJoinResult(const LfgJoinResultData& joinData); - void SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps); + void SendLfgQueueStatus(const LfgQueueStatusData& queueData); void SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest *qRew); - void SendLfgBootPlayer(const LfgPlayerBoot* pBoot); - void SendLfgUpdateProposal(uint32 proposalId, const LfgProposal *pProp); + void SendLfgBootProposalUpdate(const LfgPlayerBoot& boot); + void SendLfgUpdateProposal(uint32 proposalId, const LfgProposal& proposal); void SendLfgDisabled(); void SendLfgOfferContinue(uint32 dungeonEntry); void SendLfgTeleportError(uint8 err); diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index c8126f8aaf3..b1a2097a8f9 100755 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -586,7 +586,7 @@ class AuraScript : public _SpellScript uint8 _currentScriptState; bool _defaultActionPrevented; ScriptStateStore(uint8 currentScriptState, AuraApplication const* auraApplication, bool defaultActionPrevented) - : _currentScriptState(currentScriptState), _auraApplication(auraApplication), _defaultActionPrevented(defaultActionPrevented) + : _auraApplication(auraApplication), _currentScriptState(currentScriptState), _defaultActionPrevented(defaultActionPrevented) {} }; typedef std::stack ScriptStateStack; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 06e8d3eccbe..35f991cd368 100755 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1461,14 +1461,15 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Vehicle Accessories..."); sObjectMgr->LoadVehicleAccessories(); // must be after LoadCreatureTemplates() and LoadNPCSpellClickSpells() + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG entrance positions..."); + sLFGMgr->LoadLFGDungeons(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Dungeon boss data..."); sObjectMgr->LoadInstanceEncounters(); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG rewards..."); sLFGMgr->LoadRewards(); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG entrance positions..."); - sLFGMgr->LoadEntrancePositions(); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading SpellArea Data..."); // must be after quest load sSpellMgr->LoadSpellAreas(); diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 39fff139b52..28595571a2f 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -232,7 +232,7 @@ public: { npc_verdisa_beglaristrasz_eternosAI(Creature* creature) : ScriptedAI(creature) { } - void MovementInform(uint32 type, uint32 id) + void MovementInform(uint32 /*type*/, uint32 id) { // When Belgaristraz finish his moving say grateful text if (me->GetEntry() == NPC_BELGARISTRASZ) diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 5cc31ad54e8..547cb3dba51 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -1581,12 +1581,12 @@ class spell_gen_luck_of_the_draw : public SpellScriptLoader } - LFGDungeonEntry const* randomDungeon = sLFGDungeonStore.LookupEntry(*itr); + LFGDungeonEntry const* randomDungeon = sLFGMgr->GetLFGDungeon(*itr); if (Group* group = owner->GetGroup()) if (Map const* map = owner->GetMap()) if (group->isLFGGroup()) if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) + if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == uint32(map->GetDifficulty())) if (randomDungeon && randomDungeon->type == LFG_TYPE_RANDOM) return; // in correct dungeon diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index b8df77d0346..b291b9751b4 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -2115,7 +2115,7 @@ class npc_shadowfiend : public CreatureScript { npc_shadowfiendAI(Creature* creature) : PetAI(creature) {} - void JustDied(Unit* killer) + void JustDied(Unit* /*killer*/) { if (me->isSummon()) if (Unit* owner = me->ToTempSummon()->GetSummoner()) From 479d34d2a0e54c13160d8f52c37dbf691d14d933 Mon Sep 17 00:00:00 2001 From: Spp Date: Thu, 18 Oct 2012 14:31:28 +0200 Subject: [PATCH 3/4] Core/Dungeon Finder: Minor optimizations - Internal changes in store types - Remove of (Player *) and (Group *) From LFGMgr.cpp (step 2) --- .../2012_10_18_00_world_trinity_string.sql | 16 + src/server/game/DungeonFinding/LFG.h | 13 +- .../game/DungeonFinding/LFGGroupData.cpp | 42 +- src/server/game/DungeonFinding/LFGGroupData.h | 14 + src/server/game/DungeonFinding/LFGMgr.cpp | 714 +++++++++--------- src/server/game/DungeonFinding/LFGMgr.h | 90 ++- .../game/DungeonFinding/LFGPlayerData.cpp | 40 +- .../game/DungeonFinding/LFGPlayerData.h | 22 +- src/server/game/DungeonFinding/LFGScripts.cpp | 92 ++- src/server/game/Handlers/LFGHandler.cpp | 103 +-- src/server/game/Miscellaneous/Language.h | 17 +- 11 files changed, 652 insertions(+), 511 deletions(-) create mode 100644 sql/updates/world/2012_10_18_00_world_trinity_string.sql diff --git a/sql/updates/world/2012_10_18_00_world_trinity_string.sql b/sql/updates/world/2012_10_18_00_world_trinity_string.sql new file mode 100644 index 00000000000..8d62174d868 --- /dev/null +++ b/sql/updates/world/2012_10_18_00_world_trinity_string.sql @@ -0,0 +1,16 @@ +REPLACE INTO `trinity_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) +VALUES + (11015, 'Error', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11014, 'None', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11013, 'Leader', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11012, 'Dps', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11011, 'Healer', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11010, 'Tank', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11009, 'Raid browser', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11008, 'Finished dungeon', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11007, 'In dungeon', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11006, 'Vote kick', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11005, 'Proposal', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11004, 'Queued', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11003, 'Role check', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (11002, 'None', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h index c00cc830ce8..abdb0ffa511 100755 --- a/src/server/game/DungeonFinding/LFG.h +++ b/src/server/game/DungeonFinding/LFG.h @@ -39,7 +39,7 @@ enum LfgRoles enum LfgUpdateType { LFG_UPDATETYPE_DEFAULT = 0, // Internal Use - LFG_UPDATETYPE_LEADER = 1, + LFG_UPDATETYPE_LEADER_UNK1 = 1, // FIXME: At group leave LFG_UPDATETYPE_ROLECHECK_ABORTED = 4, LFG_UPDATETYPE_JOIN_PROPOSAL = 5, LFG_UPDATETYPE_ROLECHECK_FAILED = 6, @@ -51,7 +51,7 @@ enum LfgUpdateType LFG_UPDATETYPE_PROPOSAL_BEGIN = 13, LFG_UPDATETYPE_CLEAR_LOCK_LIST = 14, LFG_UPDATETYPE_GROUP_MEMBER_OFFLINE = 15, - LFG_UPDATETYPE_GROUP_DISBAND = 16 + LFG_UPDATETYPE_GROUP_DISBAND_UNK16 = 16, // FIXME: Sometimes at group disband }; enum LfgState @@ -69,7 +69,6 @@ enum LfgState /// Instance lock types enum LfgLockStatusType { - LFG_LOCKSTATUS_OK = 0, // Internal use only LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION = 1, LFG_LOCKSTATUS_TOO_LOW_LEVEL = 2, LFG_LOCKSTATUS_TOO_HIGH_LEVEL = 3, @@ -92,18 +91,12 @@ enum LfgAnswer LFG_ANSWER_AGREE = 1 }; -/// Dungeon and reason why player can't join -struct LfgLockStatus -{ - uint32 dungeon; ///< Dungeon Id - LfgLockStatusType lockstatus; ///< Lock type -}; - typedef std::set LfgDungeonSet; typedef std::map LfgLockMap; typedef std::map LfgLockPartyMap; typedef std::set LfgGuidSet; typedef std::list LfgGuidList; typedef std::map LfgRolesMap; +typedef std::map LfgGroupsMap; #endif diff --git a/src/server/game/DungeonFinding/LFGGroupData.cpp b/src/server/game/DungeonFinding/LFGGroupData.cpp index cbcb1d130bb..712ae5132b0 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.cpp +++ b/src/server/game/DungeonFinding/LFGGroupData.cpp @@ -19,7 +19,7 @@ #include "LFGGroupData.h" LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), - m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS) + m_Leader(0), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS) { } LfgGroupData::~LfgGroupData() @@ -34,9 +34,9 @@ void LfgGroupData::SetState(LfgState state) { switch (state) { + case LFG_STATE_FINISHED_DUNGEON: case LFG_STATE_NONE: case LFG_STATE_DUNGEON: - case LFG_STATE_FINISHED_DUNGEON: m_OldState = state; // No break on purpose default: @@ -49,6 +49,29 @@ 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; @@ -65,6 +88,21 @@ LfgState LfgGroupData::GetState() const return m_State; } +LfgState LfgGroupData::GetOldState() const +{ + return m_OldState; +} + +const LfgGuidSet &LfgGroupData::GetPlayers() const +{ + return m_Players; +} + +uint64 LfgGroupData::GetLeader() const +{ + return m_Leader; +} + uint32 LfgGroupData::GetDungeon(bool asId /* = true */) const { if (asId) diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h index 359f7be7eee..43cd64f97c3 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.h +++ b/src/server/game/DungeonFinding/LFGGroupData.h @@ -35,18 +35,30 @@ class 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; + uint64 GetLeader() const; + // Dungeon uint32 GetDungeon(bool asId = true) const; + // VoteKick uint8 GetKicksLeft() const; @@ -54,6 +66,8 @@ class LfgGroupData // 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 diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 1b4a2fcdda3..af12ce93c65 100755 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -34,8 +34,6 @@ LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1), m_options(sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE)), - m_WaitTimeAvg(-1), m_WaitTimeTank(-1), m_WaitTimeHealer(-1), m_WaitTimeDps(-1), - m_NumWaitTimeAvg(0), m_NumWaitTimeTank(0), m_NumWaitTimeHealer(0), m_NumWaitTimeDps(0), m_lfgPlayerScript(new LFGPlayerScript()), m_lfgGroupScript(new LFGGroupScript()) { } @@ -121,64 +119,67 @@ std::string LFGMgr::GetRolesString(uint8 roles) std::string rolesstr = ""; if (roles & PLAYER_ROLE_TANK) - rolesstr.append("Tank"); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_TANK)); if (roles & PLAYER_ROLE_HEALER) { if (!rolesstr.empty()) rolesstr.append(", "); - rolesstr.append("Healer"); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_HEALER)); } if (roles & PLAYER_ROLE_DAMAGE) { if (!rolesstr.empty()) rolesstr.append(", "); - rolesstr.append("Dps"); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_DAMAGE)); } if (roles & PLAYER_ROLE_LEADER) { if (!rolesstr.empty()) rolesstr.append(", "); - rolesstr.append("Leader"); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_LEADER)); } if (rolesstr.empty()) - rolesstr.append("None"); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_NONE)); return rolesstr; } char const * LFGMgr::GetStateString(LfgState state) { + int32 entry = LANG_LFG_ERROR; switch (state) { case LFG_STATE_NONE: - return "None"; + entry = LANG_LFG_STATE_NONE; break; case LFG_STATE_ROLECHECK: - return "Rolecheck"; + entry = LANG_LFG_STATE_ROLECHECK; break; case LFG_STATE_QUEUED: - return "Queued"; + entry = LANG_LFG_STATE_QUEUED; break; case LFG_STATE_PROPOSAL: - return "Proposal"; + entry = LANG_LFG_STATE_PROPOSAL; break; case LFG_STATE_DUNGEON: - return "In Dungeon"; + entry = LANG_LFG_STATE_DUNGEON; break; case LFG_STATE_BOOT: - return "Boot Player"; + entry = LANG_LFG_STATE_BOOT; break; case LFG_STATE_FINISHED_DUNGEON: - return "Finished"; + entry = LANG_LFG_STATE_FINISHED_DUNGEON; break; case LFG_STATE_RAIDBROWSER: - return "RaidBrowser"; + entry = LANG_LFG_STATE_RAIDBROWSER; + break; } - return "Error"; + char const * const str = sObjectMgr->GetTrinityStringForDBCLocale(entry); + return str; } /// Load rewards for completing dungeons @@ -488,20 +489,22 @@ void LFGMgr::Update(uint32 diff) waitTime = -1; break; case PLAYER_ROLE_TANK: - waitTime = m_WaitTimeTank; + waitTime = m_waitTimesTank.time; break; case PLAYER_ROLE_HEALER: - waitTime = m_WaitTimeHealer; + waitTime = m_waitTimesHealer.time; break; case PLAYER_ROLE_DAMAGE: - waitTime = m_WaitTimeDps; + waitTime = m_waitTimesDps.time; break; default: - waitTime = m_WaitTimeAvg; + waitTime = m_waitTimesAvg.time; break; } - LfgQueueStatusData queueData(dungeonId, waitTime, m_WaitTimeAvg, m_WaitTimeTank, m_WaitTimeHealer, m_WaitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); + 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); } @@ -648,7 +651,7 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const uint64 guid = player->GetGUID(); uint64 gguid = grp ? grp->GetGUID() : guid; LfgJoinResultData joinData; - PlayerSet players; + LfgGuidSet players; uint32 rDungeonId = 0; bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; @@ -707,15 +710,16 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue()) joinData.result = LFG_JOIN_USING_BG_SYSTEM; ++memberCount; - players.insert(plrg); + players.insert(plrg->GetGUID()); } } - if (memberCount != grp->GetMembersCount()) + + if (joinData.result == LFG_JOIN_OK && memberCount != grp->GetMembersCount()) joinData.result = LFG_JOIN_DISCONNECTED; } } else - players.insert(player); + players.insert(player->GetGUID()); // Check if all dungeons are valid bool isRaid = false; @@ -865,57 +869,52 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const Leaves Dungeon System. Player/Group is removed from queue, rolechecks, proposals or votekicks. Player or group needs to be not NULL and using Dungeon System - @param[in] player Player trying to leave (can be NULL) - @param[in] grp Group trying to leave (default NULL) + @param[in] guid Player or group guid */ -void LFGMgr::LeaveLfg(Player* player, Group* grp /* = NULL*/) +void LFGMgr::LeaveLfg(uint64 guid) { - if (!player && !grp) - return; - - uint64 guid = grp ? grp->GetGUID() : player->GetGUID(); LfgState state = GetState(guid); + uint64 gguid = IS_GROUP(guid) ? guid : GetGroup(guid); sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Leave: [" UI64FMTD "]", guid); switch (state) { case LFG_STATE_QUEUED: + if (gguid) + { + RemoveFromQueue(gguid); + RestoreState(gguid, "Leave queue"); + const LfgGuidSet& players = GetPlayers(gguid); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 guid = (*it); + ClearState(guid, "Leave queue"); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + } + } + else { RemoveFromQueue(guid); - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); - if (grp) - { - RestoreState(guid, "Leave queue"); - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - if (Player* plrg = itr->getSource()) - { - plrg->GetSession()->SendLfgUpdateParty(updateData); - uint64 pguid = plrg->GetGUID(); - ClearState(pguid, "Leave queue"); - } - } - else - { - player->GetSession()->SendLfgUpdatePlayer(updateData); - ClearState(guid, "Leave queue"); - } + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + ClearState(guid, "Leave queue"); } break; case LFG_STATE_ROLECHECK: - if (grp) - UpdateRoleCheck(guid); // No player to update role = LFG_ROLECHECK_ABORTED + if (gguid) + UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED break; case LFG_STATE_PROPOSAL: { // Remove from Proposals LfgProposalMap::iterator it = m_Proposals.begin(); + uint64 pguid = gguid == guid ? GetLeader(gguid) : guid; while (it != m_Proposals.end()) { - LfgProposalPlayerMap::iterator itPlayer = it->second->players.find(player ? player->GetGUID() : grp->GetLeaderGUID()); + 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; + itPlayer->second.accept = LFG_ANSWER_DENY; break; } ++it; @@ -931,21 +930,6 @@ void LFGMgr::LeaveLfg(Player* player, Group* grp /* = NULL*/) } } -/** - Sends the leader of a group the offer to continue popup - - @param[in] grp Group to send offer to -*/ -void LFGMgr::OfferContinue(Group* grp) -{ - if (grp) - { - uint64 gguid = grp->GetGUID(); - if (Player* leader = ObjectAccessor::FindPlayer(grp->GetLeaderGUID())) - leader->GetSession()->SendLfgOfferContinue(GetDungeon(gguid, false)); - } -} - /** Checks que main queue to try to form a Lfg group. Returns first match found (if any) @@ -1022,7 +1006,7 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) uint8 numPlayers = 0; uint8 numLfgGroups = 0; - uint32 groupLowGuid = 0; + uint64 groupGuid = 0; LfgQueueInfoMap pqInfoMap; for (LfgGuidList::const_iterator it = check.begin(); it != check.end() && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++it) { @@ -1039,12 +1023,11 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) if (IS_GROUP(guid)) { - uint32 lowGuid = GUID_LOPART(guid); - if (Group* grp = sGroupMgr->GetGroupByGUID(lowGuid)) + if (Group* grp = sGroupMgr->GetGroupByGUID(GUID_LOPART(guid))) if (grp->isLFGGroup()) { if (!numLfgGroups) - groupLowGuid = lowGuid; + groupGuid = guid; ++numLfgGroups; } } @@ -1082,25 +1065,16 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) if (rolesMap.size() != numPlayers) // Player in multiples queues! return false; - PlayerSet players; + LfgGuidSet players; for (LfgRolesMap::const_iterator it = rolesMap.begin(); it != rolesMap.end(); ++it) { - Player* player = ObjectAccessor::FindPlayer(it->first); - if (!player) - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Warning! [" UI64FMTD "] offline! Marking as not compatibles!", strGuids.c_str(), it->first); - else - { - for (PlayerSet::const_iterator itPlayer = players.begin(); itPlayer != players.end() && player; ++itPlayer) - { - if (HasIgnore((*itPlayer)->GetGUID(), player->GetGUID())) - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Players [" UI64FMTD "] and [" UI64FMTD "] ignoring", strGuids.c_str(), (*itPlayer)->GetGUID(), player->GetGUID()); - player = NULL; - } - } - if (player) - players.insert(player); - } + 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 @@ -1159,17 +1133,15 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) --Dps_Needed; } } - for (PlayerSet::const_iterator itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) + + for (LfgGuidSet::const_iterator itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) { for (LfgQueueInfoMap::const_iterator itQueue = pqInfoMap.begin(); itQueue != pqInfoMap.end(); ++itQueue) { - LfgQueueInfo* queue = itQueue->second; - if (!queue) - continue; - - for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) + if (LfgQueueInfo* queue = itQueue->second) { - if (*itPlayers == ObjectAccessor::FindPlayer(itPlayer->first)) + LfgRolesMap::const_iterator itPlayer = queue->roles.find(*itPlayers); + if (itPlayer != queue->roles.end()) { queue->tanks = Tanks_Needed; queue->healers = Healers_Needed; @@ -1178,6 +1150,7 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) } } } + return true; } sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) MATCH! Group formed", strGuids.c_str()); @@ -1193,36 +1166,37 @@ bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) pProposal->cancelTime = time_t(time(NULL)) + LFG_TIME_PROPOSAL; pProposal->state = LFG_PROPOSAL_INITIATING; pProposal->queues = check; - pProposal->groupLowGuid = groupLowGuid; + pProposal->group = groupGuid; + pProposal->isNew = true; // Assign new roles to players and assign new leader - PlayerSet::const_iterator itPlayers = players.begin(); + 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)->GetGUID(); + leader = *itPlayers; } pProposal->leader = leader; uint8 numAccept = 0; for (itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) { - uint64 guid = (*itPlayers)->GetGUID(); - LfgProposalPlayer* ppPlayer = new LfgProposalPlayer(); - if (Group* grp = (*itPlayers)->GetGroup()) + uint64 guid = *itPlayers; + LfgProposalPlayer& player = pProposal->players[guid]; + uint64 gguid = GetGroup(guid); + player.group = gguid; + if (gguid == groupGuid) { - ppPlayer->groupLowGuid = grp->GetLowGUID(); - if (grp->isLFGGroup()) // Player from existing group, autoaccept - { - ppPlayer->accept = LFG_ANSWER_AGREE; - ++numAccept; - } + player.accept = LFG_ANSWER_AGREE; + ++numAccept; } - ppPlayer->role = rolesMap[guid]; - pProposal->players[guid] = ppPlayer; + player.role = rolesMap[guid]; + if (pProposal->group && pProposal->group != gguid) + pProposal->isNew = false; } + if (numAccept == MAXGROUPSIZE) pProposal->state = LFG_PROPOSAL_SUCCESS; @@ -1270,7 +1244,6 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* } } - uint8 team = 0; LfgDungeonSet dungeons; if (roleCheck.rDungeonId) dungeons.insert(roleCheck.rDungeonId); @@ -1291,7 +1264,6 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* continue; } - team = uint8(plrg->GetTeam()); if (!sendRoleChosen) SendLfgRoleChosen(pguid, guid, roles); @@ -1333,6 +1305,7 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* --pqInfo->dps; } + uint8 team = GetTeam(guid); m_QueueInfoMap[gguid] = pqInfo; if (GetState(gguid) != LFG_STATE_NONE) { @@ -1402,12 +1375,12 @@ LfgAnswer LFGMgr::GetCompatibles(std::string key) @param[in] players Set of players to check their dungeon restrictions @param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty) */ -void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, const PlayerSet& players, LfgLockPartyMap& lockMap) +void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, const LfgGuidSet& players, LfgLockPartyMap& lockMap) { lockMap.clear(); - for (PlayerSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) { - uint64 guid = (*it)->GetGUID(); + uint64 guid = (*it); LfgLockMap const& cachedLockMap = GetLockedDungeons(guid); for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) { @@ -1497,6 +1470,79 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true return (tank + healer + damage) == uint8(groles.size()); } +/** + Makes a new group given a proposal + @param[in] proposal Proposal to get info from +*/ +void LFGMgr::MakeNewGroup(const LfgProposal& proposal) +{ + LfgGuidList players; + LfgGuidList playersToTeleport; + + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 guid = it->first; + if (guid == proposal.leader) + players.push_front(guid); + else + players.push_back(guid); + + if (GetGroup(guid) != proposal.group || GetState(proposal.group) == LFG_STATE_FINISHED_DUNGEON) + playersToTeleport.push_back(guid); + } + + // Set the dungeon difficulty + LFGDungeonEntry const* dungeon = GetLFGDungeon(proposal.dungeonId); + ASSERT(dungeon); + + Group* grp = proposal.group ? sGroupMgr->GetGroupByGUID(GUID_LOPART(proposal.group)) : NULL; + for (LfgGuidList::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 pguid = (*it); + Player* player = ObjectAccessor::FindPlayer(pguid); + if (!player) + continue; + + Group* group = player->GetGroup(); + if (group && group != grp) + player->RemoveFromGroup(); + + if (!grp) + { + grp = new Group(); + grp->ConvertToLFG(); + grp->Create(player); + uint64 gguid = grp->GetGUID(); + SetState(gguid, LFG_STATE_PROPOSAL); + sGroupMgr->AddGroup(grp); + } + else if (group != grp) + 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) + player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false); + } + + grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); + uint64 gguid = grp->GetGUID(); + SetDungeon(gguid, dungeon->Entry()); + SetState(gguid, LFG_STATE_DUNGEON); + + _SaveToDB(gguid, grp->GetDbStoreId()); + + // Teleport Player + for (LfgGuidList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayer(*it)) + TeleportPlayer(player, false); + + // Update group info + grp->SendUpdate(); +} + /** Update Proposal info with player answer @@ -1510,15 +1556,17 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) LfgProposalMap::iterator itProposal = m_Proposals.find(proposalId); if (itProposal == m_Proposals.end()) return; - LfgProposal* pProposal = itProposal->second; + + LfgProposal& proposal = *(itProposal->second); // Check if proposal have the current player - LfgProposalPlayerMap::iterator itProposalPlayer = pProposal->players.find(guid); - if (itProposalPlayer == pProposal->players.end()) + LfgProposalPlayerMap::iterator itProposalPlayer = proposal.players.find(guid); + if (itProposalPlayer == proposal.players.end()) return; - LfgProposalPlayer* ppPlayer = itProposalPlayer->second; - ppPlayer->accept = LfgAnswer(accept); + LfgProposalPlayer& player = itProposalPlayer->second; + player.accept = LfgAnswer(accept); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Player [" UI64FMTD "] of proposal %u selected: %u", guid, proposalId, accept); if (!accept) { @@ -1526,157 +1574,77 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) return; } - LfgPlayerList players; - LfgPlayerList playersToTeleport; - // check if all have answered and reorder players (leader first) bool allAnswered = true; - for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers) - { - if (Player* player = ObjectAccessor::FindPlayer(itPlayers->first)) - { - if (itPlayers->first == pProposal->leader) - players.push_front(player); - else - players.push_back(player); - - // Only teleport new players - Group* grp = player->GetGroup(); - uint64 gguid = grp ? grp->GetGUID() : 0; - if (!gguid || !grp->isLFGGroup() || GetState(gguid) == LFG_STATE_FINISHED_DUNGEON) - playersToTeleport.push_back(player); - } - - if (itPlayers->second->accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0) + for (LfgProposalPlayerMap::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers) + if (itPlayers->second.accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0) allAnswered = false; - } if (!allAnswered) { - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - (*it)->GetSession()->SendLfgUpdateProposal(proposalId, *pProposal); + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 guid = it->first; + SendLfgUpdateProposal(guid, proposalId, proposal); + } + return; } - else + + bool sendUpdate = proposal.state != LFG_PROPOSAL_SUCCESS; + proposal.state = LFG_PROPOSAL_SUCCESS; + time_t joinTime = time_t(time(NULL)); + + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND); + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - bool sendUpdate = pProposal->state != LFG_PROPOSAL_SUCCESS; - pProposal->state = LFG_PROPOSAL_SUCCESS; - time_t joinTime = time_t(time(NULL)); - std::map waitTimesMap; - // Save wait times before redoing groups - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - { - LfgProposalPlayer* player = pProposal->players[(*it)->GetGUID()]; - uint32 lowgroupguid = (*it)->GetGroup() ? (*it)->GetGroup()->GetLowGUID() : 0; - if (player->groupLowGuid != lowgroupguid) - sLog->outError(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: [" UI64FMTD "] group mismatch: actual (%u) - queued (%u)", (*it)->GetGUID(), lowgroupguid, player->groupLowGuid); + uint64 pguid = it->first; + uint64 gguid = it->second.group; + int32 waitTime = -1; + if (sendUpdate) + SendLfgUpdateProposal(pguid, proposalId, proposal); - uint64 guid2 = player->groupLowGuid ? MAKE_NEW_GUID(player->groupLowGuid, 0, HIGHGUID_GROUP) : (*it)->GetGUID(); - LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(guid2); - if (itQueue == m_QueueInfoMap.end()) - { - sLog->outError(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Queue info for guid [" UI64FMTD "] not found!", guid); - waitTimesMap[(*it)->GetGUID()] = -1; - } - else - waitTimesMap[(*it)->GetGUID()] = int32(joinTime - itQueue->second->joinTime); + if (gguid) + { + SendLfgUpdateParty(pguid, updateData); + } + else + 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); + + // Update timers + uint8 role = GetRoles(pguid); + role &= ~PLAYER_ROLE_LEADER; + switch (role) + { + case PLAYER_ROLE_DAMAGE: + UpdateWaitTimeDps(waitTime); + break; + case PLAYER_ROLE_HEALER: + UpdateWaitTimeHealer(waitTime); + break; + case PLAYER_ROLE_TANK: + UpdateWaitTimeTank(waitTime); + break; + default: + UpdateWaitTimeAvg(waitTime); + break; } - // Set the dungeon difficulty - LFGDungeonEntry const* dungeon = GetLFGDungeon(pProposal->dungeonId); - ASSERT(dungeon); - - // Create a new group (if needed) - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND); - Group* grp = pProposal->groupLowGuid ? sGroupMgr->GetGroupByGUID(pProposal->groupLowGuid) : NULL; - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - { - Player* player = (*it); - uint64 pguid = player->GetGUID(); - Group* group = player->GetGroup(); - if (sendUpdate) - player->GetSession()->SendLfgUpdateProposal(proposalId, *pProposal); - - if (group) - { - player->GetSession()->SendLfgUpdateParty(updateData); - if (group != grp) - player->RemoveFromGroup(); - } - else - player->GetSession()->SendLfgUpdatePlayer(updateData); - - if (!grp) - { - grp = new Group(); - grp->Create(player); - grp->ConvertToLFG(); - uint64 gguid = grp->GetGUID(); - SetState(gguid, LFG_STATE_PROPOSAL); - sGroupMgr->AddGroup(grp); - } - else if (group != grp) - grp->AddMember(player); - - // Update timers - uint8 role = GetRoles(pguid); - role &= ~PLAYER_ROLE_LEADER; - switch (role) - { - case PLAYER_ROLE_DAMAGE: - { - uint32 old_number = m_NumWaitTimeDps++; - m_WaitTimeDps = int32((m_WaitTimeDps * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeDps); - break; - } - case PLAYER_ROLE_HEALER: - { - uint32 old_number = m_NumWaitTimeHealer++; - m_WaitTimeHealer = int32((m_WaitTimeHealer * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeHealer); - break; - } - case PLAYER_ROLE_TANK: - { - uint32 old_number = m_NumWaitTimeTank++; - m_WaitTimeTank = int32((m_WaitTimeTank * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeTank); - break; - } - default: - { - uint32 old_number = m_NumWaitTimeAvg++; - m_WaitTimeAvg = int32((m_WaitTimeAvg * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeAvg); - break; - } - } - - m_teleport.push_back(pguid); - grp->SetLfgRoles(pguid, pProposal->players[pguid]->role); - SetState(pguid, LFG_STATE_DUNGEON); - - // Add the cooldown spell if queued for a random dungeon - if (dungeon->type == LFG_TYPE_RANDOM) - player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false); - } - - grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); - uint64 gguid = grp->GetGUID(); - SetDungeon(gguid, dungeon->Entry()); - SetState(gguid, LFG_STATE_DUNGEON); - _SaveToDB(gguid, grp->GetDbStoreId()); - - // Remove players/groups from Queue - for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) - RemoveFromQueue(*it); - - // Teleport Player - for (LfgPlayerList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it) - TeleportPlayer(*it, false); - - // Update group info - grp->SendUpdate(); - - delete pProposal; - m_Proposals.erase(itProposal); + m_teleport.push_back(pguid); + SetState(pguid, LFG_STATE_DUNGEON); } + + // Remove players/groups from Queue + for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it) + RemoveFromQueue(*it); + + MakeNewGroup(proposal); + delete itProposal->second; + m_Proposals.erase(itProposal); } /** @@ -1693,45 +1661,38 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Proposal %u, state FAILED, UpdateType %u", itProposal->first, type); // Mark all people that didn't answered as no accept if (type == LFG_UPDATETYPE_PROPOSAL_FAILED) - for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) - if (it->second->accept == LFG_ANSWER_PENDING) - it->second->accept = LFG_ANSWER_DENY; + for (LfgProposalPlayerMap::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + if (it->second.accept == LFG_ANSWER_PENDING) + it->second.accept = LFG_ANSWER_DENY; // Mark players/groups to be removed LfgGuidSet toRemove; - for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + for (LfgProposalPlayerMap::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - if (it->second->accept == LFG_ANSWER_AGREE) + if (it->second.accept == LFG_ANSWER_AGREE) continue; - uint64 guid = it->second->groupLowGuid ? MAKE_NEW_GUID(it->second->groupLowGuid, 0, HIGHGUID_GROUP) : it->first; + uint64 guid = it->second.group ? it->second.group : it->first; // Player didn't accept or still pending when no secs left - if (it->second->accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED) + if (it->second.accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED) { - it->second->accept = LFG_ANSWER_DENY; + it->second.accept = LFG_ANSWER_DENY; toRemove.insert(guid); } } - uint8 team = 0; // Notify players for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - Player* player = ObjectAccessor::FindPlayer(it->first); - if (!player) - continue; + uint64 guid = it->first; + uint64 gguid = it->second.group ? it->second.group : guid; - team = uint8(player->GetTeam()); - player->GetSession()->SendLfgUpdateProposal(itProposal->first, proposal); - - Group* grp = player->GetGroup(); - uint64 guid = player->GetGUID(); - uint64 gguid = it->second->groupLowGuid ? MAKE_NEW_GUID(it->second->groupLowGuid, 0, HIGHGUID_GROUP) : guid; + SendLfgUpdateProposal(guid, itProposal->first, proposal); if (toRemove.find(gguid) != toRemove.end()) // Didn't accept or in same group that someone that didn't accept { LfgUpdateData updateData; - if (it->second->accept == LFG_ANSWER_DENY) + if (it->second.accept == LFG_ANSWER_DENY) { updateData.updateType = type; sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] didn't accept. Removing from queue and compatible cache", guid); @@ -1742,25 +1703,25 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] in same group that someone that didn't accept. Removing from queue and compatible cache", guid); } ClearState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept"); - if (grp) + if (gguid != guid) { RestoreState(gguid, "Proposal Fail (someone in group didn't accepted)"); - player->GetSession()->SendLfgUpdateParty(updateData); + SendLfgUpdateParty(guid, updateData); } else - player->GetSession()->SendLfgUpdatePlayer(updateData); + SendLfgUpdatePlayer(guid, updateData); } else { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Readding [" UI64FMTD "] to queue.", guid); SetState(guid, LFG_STATE_QUEUED); - if (grp) + if (gguid != guid) { SetState(gguid, LFG_STATE_QUEUED); - player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); } else - player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); } } @@ -1776,29 +1737,26 @@ 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 } - delete &proposal; + delete itProposal->second; m_Proposals.erase(itProposal); } /** Initialize a boot kick vote - @param[in] grp Group the vote kicks belongs to + @param[in] gguid Group the vote kicks belongs to @param[in] kicker Kicker guid @param[in] victim Victim guid @param[in] reason Kick reason */ -void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string const& reason) +void LFGMgr::InitBoot(uint64 gguid, uint64 kicker, uint64 victim, std::string const& reason) { - if (!grp) - return; - - uint32 gguid = grp->GetLowGUID(); SetState(gguid, LFG_STATE_BOOT); LfgPlayerBoot& boot = m_Boots[gguid]; @@ -1807,10 +1765,7 @@ void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string cons boot.reason = reason; boot.victim = victim; - LfgGuidSet players; - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - if (Player* player = itr->getSource()) - players.insert(player->GetGUID()); + LfgGuidSet const& players = GetPlayers(gguid); // Set votes for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr) @@ -1831,18 +1786,15 @@ void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string cons /** Update Boot info with player answer - @param[in] player Player who has answered + @param[in] guid Player who has answered @param[in] accept player answer */ -void LFGMgr::UpdateBoot(Player* player, bool accept) +void LFGMgr::UpdateBoot(uint64 guid, bool accept) { - Group* grp = player ? player->GetGroup() : NULL; - if (!grp) + uint64 gguid = GetGroup(guid); + if (!gguid) return; - uint32 gguid = grp->GetLowGUID(); - uint64 guid = player->GetGUID(); - LfgPlayerBootMap::iterator itBoot = m_Boots.find(gguid); if (itBoot == m_Boots.end()) return; @@ -1882,17 +1834,20 @@ void LFGMgr::UpdateBoot(Player* player, bool accept) } } - gguid = grp->GetGUID(); SetState(gguid, LFG_STATE_DUNGEON); if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player { - Player::RemoveFromGroup(grp, boot.victim); + 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); } - OfferContinue(grp); + + if (Player* leader = ObjectAccessor::FindPlayer(sLFGMgr->GetLeader(gguid))) + leader->GetSession()->SendLfgOfferContinue(GetDungeon(gguid, false)); DecreaseKicksLeft(gguid); } m_Boots.erase(itBoot); @@ -2120,49 +2075,35 @@ LfgType LFGMgr::GetDungeonType(uint32 dungeonId) return LfgType(dungeon->type); } -/** - 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(); -} - LfgState LFGMgr::GetState(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "]", guid); + LfgState state; if (IS_GROUP(guid)) - return m_Groups[guid].GetState(); + state = m_Groups[guid].GetState(); else - return m_Players[guid].GetState(); + state = m_Players[guid].GetState(); + + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "] = %u", guid, state); + return state; } -uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true*/) +uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true */) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u", guid, asId); - return m_Groups[guid].GetDungeon(asId); + uint32 dungeon = m_Groups[guid].GetDungeon(asId); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u = %u", guid, asId, dungeon); + return dungeon; } uint8 LFGMgr::GetRoles(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "]", guid); - return m_Players[guid].GetRoles(); + uint8 roles = m_Players[guid].GetRoles(); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "] = %u", guid, roles); + return roles; } const std::string& LFGMgr::GetComment(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "] = %s", guid, m_Players[guid].GetComment().c_str()); return m_Players[guid].GetComment(); } @@ -2199,7 +2140,8 @@ void LFGMgr::RestoreState(uint64 guid, char const *debugMsg) { LfgGroupData& data = m_Groups[guid]; char const * const ps = GetStateString(data.GetState()); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s), State: %s", guid, debugMsg, ps); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s), State: %s, oldState: %s", guid, debugMsg, ps, os); data.RestoreState(); } @@ -2207,7 +2149,8 @@ void LFGMgr::ClearState(uint64 guid, char const *debugMsg) { LfgPlayerData& data = m_Players[guid]; char const * const ps = GetStateString(data.GetState()); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: Player: [" UI64FMTD "] (%s) State: %s", guid, debugMsg, ps); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: Player: [" UI64FMTD "] (%s) State: %s, oldState: %s", guid, debugMsg, ps, os); data.ClearState(); } @@ -2218,7 +2161,8 @@ void LFGMgr::SetState(uint64 guid, LfgState state) LfgGroupData& data = m_Groups[guid]; char const * const ns = GetStateString(state); char const * const ps = GetStateString(data.GetState()); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s", guid, ns, ps); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); data.SetState(state); } else @@ -2226,7 +2170,8 @@ void LFGMgr::SetState(uint64 guid, LfgState state) LfgPlayerData& data = m_Players[guid]; char const * const ns = GetStateString(state); char const * const ps = GetStateString(data.GetState()); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s", guid, ns, ps); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); data.SetState(state); } } @@ -2283,6 +2228,54 @@ void LFGMgr::RemoveGroupData(uint64 guid) m_Groups.erase(it); } +uint8 LFGMgr::GetTeam(uint64 guid) +{ + return m_Players[guid].GetTeam(); +} + +uint8 LFGMgr::RemovePlayerFromGroup(uint64 gguid, uint64 guid) +{ + return m_Groups[gguid].RemovePlayer(guid); +} + +void LFGMgr::AddPlayerToGroup(uint64 gguid, uint64 guid) +{ + m_Groups[gguid].AddPlayer(guid); +} + +void LFGMgr::SetLeader(uint64 gguid, uint64 leader) +{ + m_Groups[gguid].SetLeader(leader); +} + +void LFGMgr::SetTeam(uint64 guid, uint8 team) +{ + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + team = 0; + + m_Players[guid].SetTeam(team); +} + +uint64 LFGMgr::GetGroup(uint64 guid) +{ + return m_Players[guid].GetGroup(); +} + +void LFGMgr::SetGroup(uint64 guid, uint64 group) +{ + m_Players[guid].SetGroup(group); +} + +const LfgGuidSet& LFGMgr::GetPlayers(uint64 guid) +{ + return m_Groups[guid].GetPlayers(); +} + +uint64 LFGMgr::GetLeader(uint64 guid) +{ + return m_Groups[guid].GetLeader(); +} + bool LFGMgr::HasIgnore(uint64 guid1, uint64 guid2) { Player* plr1 = ObjectAccessor::FindPlayer(guid1); @@ -2360,3 +2353,50 @@ 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(); +} \ No newline at end of file diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index d4d35974604..756f7094be6 100755 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -21,6 +21,7 @@ #include "Common.h" #include #include "LFG.h" + #include "LFGGroupData.h" #include "LFGPlayerData.h" @@ -127,20 +128,28 @@ struct LfgPlayerBoot; typedef std::map LfgGuidListMap; typedef std::set PlayerSet; typedef std::list LfgPlayerList; +typedef std::map LfgCompatibleMap; +typedef std::map LfgQueueInfoMap; + typedef std::multimap LfgRewardMap; typedef std::pair LfgRewardMapBounds; -typedef std::map LfgCompatibleMap; typedef std::map LfgCachedDungeonMap; typedef std::map LfgAnswerMap; typedef std::map LfgRoleCheckMap; -typedef std::map LfgQueueInfoMap; typedef std::map LfgProposalMap; -typedef std::map LfgProposalPlayerMap; +typedef std::map LfgProposalPlayerMap; typedef std::map LfgPlayerBootMap; typedef std::map LfgGroupDataMap; typedef std::map LfgPlayerDataMap; typedef UNORDERED_MAP 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 { @@ -221,27 +230,26 @@ struct LfgQueueInfo /// Stores player data related to proposal to join struct LfgProposalPlayer { - LfgProposalPlayer(): role(0), accept(LFG_ANSWER_PENDING), groupLowGuid(0) {}; + 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) - uint32 groupLowGuid; ///< Original group guid (Low guid) 0 if no original group + uint64 group; ///< Original group guid. 0 if no original group }; /// Stores group data related to proposal to join struct LfgProposal { - LfgProposal(uint32 dungeon = 0): dungeonId(dungeon), state(LFG_PROPOSAL_INITIATING), groupLowGuid(0), leader(0), cancelTime(0) {} + LfgProposal(uint32 dungeon = 0): dungeonId(dungeon), state(LFG_PROPOSAL_INITIATING), + group(0), leader(0), cancelTime(0), encounters(0), isNew(true) + { } - ~LfgProposal() - { - for (LfgProposalPlayerMap::iterator it = players.begin(); it != players.end(); ++it) - delete it->second; - }; uint32 dungeonId; ///< Dungeon to join LfgProposalState state; ///< State of the proposal - uint32 groupLowGuid; ///< Proposal group (0 if new) + 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 LfgGuidList queues; ///< Queue Ids to remove/readd LfgProposalPlayerMap players; ///< Players data }; @@ -264,7 +272,6 @@ struct LfgPlayerBoot bool inProgress; ///< Vote in progress LfgAnswerMap votes; ///< Player votes (-1 not answer | 0 Not agree | 1 agree) uint64 victim; ///< Player guid to be kicked (can't vote) - uint8 votedNeeded; ///< Votes needed to kick the player std::string reason; ///< kick reason }; @@ -313,26 +320,33 @@ class LFGMgr // Queue void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment); - void LeaveLfg(Player* player, Group* grp = NULL); + void LeaveLfg(uint64 guid); // Role Check void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE); + // Group Matching + static bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); + void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap); + // Proposals + uint32 AddProposal(LfgProposal const& proposal); void UpdateProposal(uint32 proposalId, uint64 guid, bool accept); // Teleportation void TeleportPlayer(Player* player, bool out, bool fromOpcode = false); // Vote kick - void InitBoot(Group* grp, uint64 kguid, uint64 vguid, std::string const& reason); - void UpdateBoot(Player* player, bool accept); - void OfferContinue(Group* grp); + void InitBoot(uint64 gguid, uint64 kguid, uint64 vguid, std::string const& reason); + void UpdateBoot(uint64 guid, bool accept); void InitializeLockedDungeons(Player* player, uint8 level = 0); void SetRoles(uint64 guid, uint8 roles); void SetComment(uint64 guid, std::string const& comment); + void SetTeam(uint64 guid, uint8 team); + void SetGroup(uint64 guid, uint64 group); + void SetLeader(uint64 gguid, uint64 leader); void SetState(uint64 guid, LfgState state); void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons); @@ -341,15 +355,20 @@ class LFGMgr void RemovePlayerData(uint64 guid); void RemoveGroupData(uint64 guid); + uint8 RemovePlayerFromGroup(uint64 gguid, uint64 guid); + void AddPlayerToGroup(uint64 gguid, uint64 guid); LfgLockMap const& GetLockedDungeons(uint64 guid); LfgDungeonSet const& GetSelectedDungeons(uint64 guid); uint32 GetDungeon(uint64 guid, bool asId = true); LfgState GetState(uint64 guid); uint8 GetKicksLeft(uint64 gguid); + uint64 GetLeader(uint64 guid); bool IsLfgGroup(uint64 guid); uint8 GetRoles(uint64 guid); std::string const& GetComment(uint64 gguid); + LfgGuidSet const& GetPlayers(uint64 guid); + bool IsTeleported(uint64 guid); static bool HasIgnore(uint64 guid1, uint64 guid2); @@ -368,6 +387,8 @@ class LFGMgr void ClearState(uint64 guid, char const *debugMsg); private: + uint8 GetTeam(uint64 guid); + uint64 GetGroup(uint64 guid); void RestoreState(uint64 guid, char const *debugMsg); void SetDungeon(uint64 guid, uint32 dungeon); @@ -380,21 +401,23 @@ class LFGMgr // Proposals void RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type); + void MakeNewGroup(LfgProposal const& proposal); // Group Matching LfgProposal* FindNewGroups(LfgGuidList& check, LfgGuidList& all); - bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); bool CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal); - void GetCompatibleDungeons(LfgDungeonSet& dungeons, PlayerSet const& players, LfgLockPartyMap& lockMap); void SetCompatibles(std::string concatenatedGuids, bool compatibles); LfgAnswer GetCompatibles(std::string concatenatedGuids); - void RemoveFromCompatibles(uint64 guid); // Generic 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); @@ -408,30 +431,27 @@ class LFGMgr uint32 m_lfgProposalId; ///< used as internal counter for proposals uint32 m_options; ///< Stores config options - int32 m_WaitTimeAvg; ///< Average wait time to find a group queuing as multiple roles - int32 m_WaitTimeTank; ///< Average wait time to find a group queuing as tank - int32 m_WaitTimeHealer; ///< Average wait time to find a group queuing as healer - int32 m_WaitTimeDps; ///< Average wait time to find a group queuing as dps - uint32 m_NumWaitTimeAvg; ///< Num of players used to calc avs wait time - uint32 m_NumWaitTimeTank; ///< Num of players used to calc tank wait time - uint32 m_NumWaitTimeHealer; ///< Num of players used to calc healers wait time - uint32 m_NumWaitTimeDps; ///< Num of players used to calc dps wait time LfgCachedDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType // Reward System LfgRewardMap m_RewardMap; ///< Stores rewards for random dungeons LFGDungeonMap m_LfgDungeonMap; - // 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 - LfgGuidList m_teleport; ///< Players being teleported // Rolecheck - Proposal - Vote Kicks LfgRoleCheckMap m_RoleChecks; ///< Current Role checks LfgProposalMap m_Proposals; ///< Current Proposals LfgPlayerBootMap m_Boots; ///< Current player kicks LfgPlayerDataMap m_Players; ///< Player data 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; diff --git a/src/server/game/DungeonFinding/LFGPlayerData.cpp b/src/server/game/DungeonFinding/LFGPlayerData.cpp index 4a6e86ab966..6f030831342 100644 --- a/src/server/game/DungeonFinding/LFGPlayerData.cpp +++ b/src/server/game/DungeonFinding/LFGPlayerData.cpp @@ -17,8 +17,8 @@ #include "LFGPlayerData.h" -LfgPlayerData::LfgPlayerData(): -m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), m_Roles(0), m_Comment("") +LfgPlayerData::LfgPlayerData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), + m_Team(0), m_Group(0), m_Roles(0), m_Comment("") {} LfgPlayerData::~LfgPlayerData() @@ -30,10 +30,10 @@ void LfgPlayerData::SetState(LfgState state) switch (state) { case LFG_STATE_NONE: - case LFG_STATE_DUNGEON: case LFG_STATE_FINISHED_DUNGEON: + case LFG_STATE_DUNGEON: m_OldState = state; - // No break on purpose + // No break on purpose default: m_State = state; } @@ -51,6 +51,16 @@ void LfgPlayerData::SetLockedDungeons(const LfgLockMap& lockStatus) m_LockedDungeons = lockStatus; } +void LfgPlayerData::SetTeam(uint8 team) +{ + m_Team = team; +} + +void LfgPlayerData::SetGroup(uint64 group) +{ + m_Group = group; +} + void LfgPlayerData::SetRoles(uint8 roles) { m_Roles = roles; @@ -66,21 +76,31 @@ void LfgPlayerData::SetSelectedDungeons(const LfgDungeonSet& dungeons) m_SelectedDungeons = dungeons; } -void LfgPlayerData::ClearSelectedDungeons() -{ - m_SelectedDungeons.clear(); -} - LfgState LfgPlayerData::GetState() const { return m_State; } -const LfgLockMap & LfgPlayerData::GetLockedDungeons() const +LfgState LfgPlayerData::GetOldState() const +{ + return m_OldState; +} + +const LfgLockMap& LfgPlayerData::GetLockedDungeons() const { return m_LockedDungeons; } +uint8 LfgPlayerData::GetTeam() const +{ + return m_Team; +} + +uint64 LfgPlayerData::GetGroup() const +{ + return m_Group; +} + uint8 LfgPlayerData::GetRoles() const { return m_Roles; diff --git a/src/server/game/DungeonFinding/LFGPlayerData.h b/src/server/game/DungeonFinding/LFGPlayerData.h index e8bae1c5a0f..0682ad66698 100644 --- a/src/server/game/DungeonFinding/LFGPlayerData.h +++ b/src/server/game/DungeonFinding/LFGPlayerData.h @@ -18,7 +18,6 @@ #ifndef _LFGPLAYERDATA_H #define _LFGPLAYERDATA_H -#include "Common.h" #include "LFG.h" /** @@ -33,20 +32,26 @@ class LfgPlayerData // General void SetState(LfgState state); void ClearState(); - void SetLockedDungeons(const LfgLockMap& lock); + void SetLockedDungeons(LfgLockMap const& lock); + void SetTeam(uint8 team); + void SetGroup(uint64 group); + // Queue void SetRoles(uint8 roles); - void SetComment(const std::string& comment); + void SetComment(std::string const& comment); void SetSelectedDungeons(const LfgDungeonSet& dungeons); - void ClearSelectedDungeons(); // General LfgState GetState() const; - const LfgLockMap& GetLockedDungeons() const; + LfgState GetOldState() const; + LfgLockMap const& GetLockedDungeons() const; + uint8 GetTeam() const; + uint64 GetGroup() const; + // Queue uint8 GetRoles() const; - const std::string& GetComment() const; - const LfgDungeonSet& GetSelectedDungeons() const; + std::string const& GetComment() const; + LfgDungeonSet const& GetSelectedDungeons() const; private: // General @@ -54,6 +59,9 @@ class LfgPlayerData LfgState m_OldState; ///< Old State // Player LfgLockMap m_LockedDungeons; ///< Dungeons player can't do and reason + uint8 m_Team; ///< Player team - determines the queue to join + uint64 m_Group; ///< Original group of player when joined LFG + // Queue uint8 m_Roles; ///< Roles the player selected when joined LFG std::string m_Comment; ///< Player comment used when joined LFG diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index 26686dbaa33..76cc169f477 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -40,7 +40,7 @@ void LFGPlayerScript::OnLevelChanged(Player* player, uint8 /*oldLevel*/) void LFGPlayerScript::OnLogout(Player* player) { uint64 guid = player->GetGUID(); - sLFGMgr->LeaveLfg(player); + sLFGMgr->LeaveLfg(guid); LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); player->GetSession()->SendLfgUpdateParty(updateData); player->GetSession()->SendLfgUpdatePlayer(updateData); @@ -52,6 +52,7 @@ void LFGPlayerScript::OnLogout(Player* player) void LFGPlayerScript::OnLogin(Player* player) { sLFGMgr->InitializeLockedDungeons(player); + sLFGMgr->SetTeam(player->GetGUID(), player->GetTeam()); // TODO - Restore LfgPlayerData and send proper status to player if it was in a group } @@ -69,73 +70,85 @@ LFGGroupScript::LFGGroupScript() : GroupScript("LFGGroupScript") void LFGGroupScript::OnAddMember(Group* group, uint64 guid) { uint64 gguid = group->GetGUID(); - if (!gguid) - return; + uint64 leader = group->GetLeaderGUID(); - sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "]", gguid, guid); - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_CLEAR_LOCK_LIST); - for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + if (leader == guid) { - if (Player* plrg = itr->getSource()) + 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); + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_CLEAR_LOCK_LIST); + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { - plrg->GetSession()->SendLfgUpdatePlayer(updateData); - plrg->GetSession()->SendLfgUpdateParty(updateData); + if (Player* plrg = itr->getSource()) + { + plrg->GetSession()->SendLfgUpdatePlayer(updateData); + plrg->GetSession()->SendLfgUpdateParty(updateData); + } } + + if (state == LFG_STATE_QUEUED) + sLFGMgr->LeaveLfg(guid); + + // TODO - if group is queued and new player is added convert to rolecheck without notify the current players queued + if (gstate == LFG_STATE_QUEUED) + sLFGMgr->LeaveLfg(gguid); } - // TODO - if group is queued and new player is added convert to rolecheck without notify the current players queued - if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) - sLFGMgr->LeaveLfg(NULL, group); - - if (sLFGMgr->GetState(guid) == LFG_STATE_QUEUED) - if (Player* player = ObjectAccessor::FindPlayer(guid)) - sLFGMgr->LeaveLfg(player); + sLFGMgr->SetGroup(guid, gguid); + sLFGMgr->AddPlayerToGroup(gguid, guid); } void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod method, uint64 kicker, char const* reason) { uint64 gguid = group->GetGUID(); - if (!gguid || method == GROUP_REMOVEMETHOD_DEFAULT) - return; - sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnRemoveMember [" UI64FMTD "]: remove [" UI64FMTD "] Method: %d Kicker: [" UI64FMTD "] Reason: %s", gguid, guid, method, kicker, (reason ? reason : "")); - if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) - { - // TODO - Do not remove, just remove the one leaving and rejoin queue with all other data - sLFGMgr->LeaveLfg(NULL, group); - } - if (!group->isLFGGroup()) + if (method == GROUP_REMOVEMETHOD_DEFAULT) return; - if (method == GROUP_REMOVEMETHOD_KICK) // Player have been kicked + 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 { // TODO - Update internal kick cooldown of kicker std::string str_reason = ""; if (reason) str_reason = std::string(reason); - sLFGMgr->InitBoot(group, kicker, guid, str_reason); + sLFGMgr->InitBoot(gguid, kicker, guid, str_reason); return; } - uint32 state = sLFGMgr->GetState(gguid); sLFGMgr->ClearState(guid, "OnRemoveMember"); sLFGMgr->SetState(guid, LFG_STATE_NONE); + sLFGMgr->SetGroup(guid, 0); + sLFGMgr->RemovePlayerFromGroup(gguid, guid); + if (Player* player = ObjectAccessor::FindPlayer(guid)) { - if (method == GROUP_REMOVEMETHOD_LEAVE && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON && sLFGMgr->GetDungeon(gguid, false)) + if (method == GROUP_REMOVEMETHOD_LEAVE && state == LFG_STATE_DUNGEON && + sLFGMgr->GetDungeon(gguid, false)) player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, true); //else if (state == LFG_STATE_BOOT) // Update internal kick cooldown of kicked - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_LEADER); - player->GetSession()->SendLfgUpdateParty(updateData); - if (player->GetMap()->IsDungeon()) // Teleport player out the dungeon + player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_LEADER_UNK1)); + if (isLFG && player->GetMap()->IsDungeon()) // Teleport player out the dungeon sLFGMgr->TeleportPlayer(player, true); } - if (state != LFG_STATE_FINISHED_DUNGEON)// Need more players to finish the dungeon - sLFGMgr->OfferContinue(group); + if (isLFG && state != LFG_STATE_FINISHED_DUNGEON) // Need more players to finish the dungeon + if (Player* leader = ObjectAccessor::FindPlayer(sLFGMgr->GetLeader(gguid))) + leader->GetSession()->SendLfgOfferContinue(sLFGMgr->GetDungeon(gguid, false)); } void LFGGroupScript::OnDisband(Group* group) @@ -149,20 +162,19 @@ void LFGGroupScript::OnDisband(Group* group) void LFGGroupScript::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid) { uint64 gguid = group->GetGUID(); - if (!gguid) - return; 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); + 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; + updateData.updateType = LFG_UPDATETYPE_GROUP_DISBAND_UNK16; player->GetSession()->SendLfgUpdateParty(updateData); } } @@ -170,9 +182,7 @@ void LFGGroupScript::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 o void LFGGroupScript::OnInviteMember(Group* group, uint64 guid) { uint64 gguid = group->GetGUID(); - if (!gguid) - return; sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, group->GetLeaderGUID()); - sLFGMgr->LeaveLfg(NULL, group); + sLFGMgr->LeaveLfg(gguid); } diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp index 59631618757..4f208d33bde 100755 --- a/src/server/game/Handlers/LFGHandler.cpp +++ b/src/server/game/Handlers/LFGHandler.cpp @@ -23,7 +23,6 @@ #include "LFGMgr.h" #include "ObjectMgr.h" #include "GroupMgr.h" -#include "InstanceScript.h" void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock) { @@ -88,12 +87,13 @@ void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recv_data*/) { Group* grp = GetPlayer()->GetGroup(); uint64 guid = GetPlayer()->GetGUID(); + uint64 gguid = grp ? grp->GetGUID() : guid; sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", guid, grp ? 1 : 0); // Check cheating - only leader can leave the queue if (!grp || grp->GetLeaderGUID() == GetPlayer()->GetGUID()) - sLFGMgr->LeaveLfg(GetPlayer(), grp); + sLFGMgr->LeaveLfg(gguid); } void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recv_data) @@ -140,7 +140,7 @@ void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recv_data) uint64 guid = GetPlayer()->GetGUID(); sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", guid, agree ? 1 : 0); - sLFGMgr->UpdateBoot(GetPlayer(), agree); + sLFGMgr->UpdateBoot(guid, agree); } void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data) @@ -285,6 +285,8 @@ void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData) { bool queued = false; bool extrainfo = false; + uint64 guid = GetPlayer()->GetGUID(); + uint8 size = uint8(updateData.dungeons.size()); switch (updateData.updateType) { @@ -301,9 +303,6 @@ void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData) break; } - uint64 guid = GetPlayer()->GetGUID(); - uint8 size = uint8(updateData.dungeons.size()); - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PLAYER [" UI64FMTD "] updatetype: %u", guid, updateData.updateType); WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + size * 4 + updateData.comment.length())); data << uint8(updateData.updateType); // Lfg Update type @@ -328,6 +327,8 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData) bool join = false; bool extrainfo = false; bool queued = false; + uint64 guid = GetPlayer()->GetGUID(); + uint8 size = uint8(updateData.dungeons.size()); switch (updateData.updateType) { @@ -351,9 +352,6 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData) break; } - uint64 guid = GetPlayer()->GetGUID(); - uint8 size = uint8(updateData.dungeons.size()); - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PARTY [" UI64FMTD "] updatetype: %u", guid, updateData.updateType); WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + size * 4 + updateData.comment.length())); data << uint8(updateData.updateType); // Lfg Update type @@ -522,7 +520,7 @@ void WorldSession::SendLfgBootProposalUpdate(const LfgPlayerBoot& boot) } } sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PROPOSAL_UPDATE [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s", - guid, uint8(boot.inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), boot.victim, votesNum, agreeNum, secsleft, boot.votedNeeded, boot.reason.c_str()); + guid, uint8(boot.inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), boot.victim, votesNum, agreeNum, secsleft, LFG_GROUP_KICK_VOTES_NEEDED, boot.reason.c_str()); WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length()); data << uint8(boot.inProgress); // Vote in progress data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote @@ -531,87 +529,56 @@ void WorldSession::SendLfgBootProposalUpdate(const LfgPlayerBoot& boot) data << uint32(votesNum); // Total Votes data << uint32(agreeNum); // Agree Count data << uint32(secsleft); // Time Left - data << uint32(boot.votedNeeded); // Needed Votes + data << uint32(LFG_GROUP_KICK_VOTES_NEEDED); // Needed Votes data << boot.reason.c_str(); // Kick reason SendPacket(&data); } -void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal& proposal) +void WorldSession::SendLfgUpdateProposal(uint32 proposalId, LfgProposal const& proposal) { uint64 guid = GetPlayer()->GetGUID(); - LfgProposalPlayerMap::const_iterator itPlayer = proposal.players.find(guid); - if (itPlayer == proposal.players.end()) // Player MUST be in the proposal - return; + uint64 gguid = proposal.players.find(guid)->second.group; + bool silent = !proposal.isNew && gguid == proposal.group; + uint32 dungeonEntry = proposal.dungeonId; - LfgProposalPlayer* ppPlayer = itPlayer->second; - uint32 pLowGroupGuid = ppPlayer->groupLowGuid; - uint32 dLowGuid = proposal.groupLowGuid; - uint32 dungeonId = proposal.dungeonId; - bool isSameDungeon = false; - bool isContinue = false; - Group* grp = dLowGuid ? sGroupMgr->GetGroupByGUID(dLowGuid) : NULL; - uint32 completedEncounters = 0; - if (grp) - { - uint64 gguid = grp->GetGUID(); - isContinue = grp->isLFGGroup() && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; - isSameDungeon = GetPlayer()->GetGroup() == grp && isContinue; - } - - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), proposal.state); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", guid, proposal.state); WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + proposal.players.size() * (4 + 1 + 1 + 1 + 1 +1)); - if (!isContinue) // Only show proposal dungeon if it's continue + // show random dungeon if player selected random dungeon and it's not lfg group + if (!silent) { - LfgDungeonSet playerDungeons = sLFGMgr->GetSelectedDungeons(guid); - if (playerDungeons.size() == 1) - dungeonId = (*playerDungeons.begin()); + LfgDungeonSet const& playerDungeons = sLFGMgr->GetSelectedDungeons(guid); + if (playerDungeons.find(proposal.dungeonId) == playerDungeons.end()) + dungeonEntry = (*playerDungeons.begin()); } - if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) - { - dungeonId = dungeon->Entry(); + if (LFGDungeonEntry const* dungeon = sLFGMgr->GetLFGDungeon(dungeonEntry)) + dungeonEntry = dungeon->Entry(); - // Select a player inside to be get completed encounters from - if (grp) - { - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* groupMember = itr->getSource(); - if (groupMember && groupMember->GetMapId() == uint32(dungeon->map)) - { - if (InstanceScript* instance = groupMember->GetInstanceScript()) - completedEncounters = instance->GetCompletedEncounterMask(); - break; - } - } - } - } - - data << uint32(dungeonId); // Dungeon - data << uint8(proposal.state); // Result state - data << uint32(proposalId); // Internal Proposal ID - data << uint32(completedEncounters); // Bosses killed - data << uint8(isSameDungeon); // Silent (show client window) + data << uint32(dungeonEntry); // Dungeon + data << uint8(proposal.state); // Proposal state + data << uint32(proposalId); // Proposal ID + data << uint32(proposal.encounters); // encounters done + data << uint8(silent); // Show proposal window data << uint8(proposal.players.size()); // Group size - for (itPlayer = proposal.players.begin(); itPlayer != proposal.players.end(); ++itPlayer) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - ppPlayer = itPlayer->second; - data << uint32(ppPlayer->role); // Role - data << uint8(itPlayer->first == guid); // Self player - if (!ppPlayer->groupLowGuid) // Player not it a group + LfgProposalPlayer const& player = it->second; + data << uint32(player.role); // Role + data << uint8(it->first == guid); // Self player + if (!player.group) // Player not it a group { data << uint8(0); // Not in dungeon data << uint8(0); // Not same group } else { - data << uint8(ppPlayer->groupLowGuid == dLowGuid); // In dungeon (silent) - data << uint8(ppPlayer->groupLowGuid == pLowGroupGuid); // Same Group than player + data << uint8(player.group == proposal.group); // In dungeon (silent) + data << uint8(player.group == gguid); // Same Group than player } - data << uint8(ppPlayer->accept != LFG_ANSWER_PENDING); // Answered - data << uint8(ppPlayer->accept == LFG_ANSWER_AGREE); // Accepted + data << uint8(player.accept != LFG_ANSWER_PENDING); // Answered + data << uint8(player.accept == LFG_ANSWER_AGREE); // Accepted } SendPacket(&data); } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 5893bbd6564..ee3be6f2b2d 100755 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1076,7 +1076,22 @@ enum TrinityStrings // Use for custom patches 11000-11999 LANG_AUTO_BROADCAST = 11000, - LANG_INVALID_REALMID = 11001 + LANG_INVALID_REALMID = 11001, + + LANG_LFG_STATE_NONE = 11002, + LANG_LFG_STATE_ROLECHECK = 11003, + LANG_LFG_STATE_QUEUED = 11004, + LANG_LFG_STATE_PROPOSAL = 11005, + LANG_LFG_STATE_BOOT = 11006, + LANG_LFG_STATE_DUNGEON = 11007, + LANG_LFG_STATE_FINISHED_DUNGEON = 11008, + LANG_LFG_STATE_RAIDBROWSER = 11009, + LANG_LFG_ROLE_TANK = 11010, + LANG_LFG_ROLE_HEALER = 11011, + LANG_LFG_ROLE_DAMAGE = 11012, + LANG_LFG_ROLE_LEADER = 11013, + LANG_LFG_ROLE_NONE = 11014, + LANG_LFG_ERROR = 11015, // NOT RESERVED IDS 12000-1999999999 // `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID) From 4e8fa520c8aca831580fba9bf762486b9a9d7ed0 Mon Sep 17 00:00:00 2001 From: Spp Date: Fri, 19 Oct 2012 14:00:40 +0200 Subject: [PATCH 4/4] 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 --- src/server/game/DungeonFinding/LFGMgr.cpp | 709 +++--------------- src/server/game/DungeonFinding/LFGMgr.h | 60 +- .../game/DungeonFinding/LFGPlayerData.cpp | 3 + src/server/game/DungeonFinding/LFGQueue.cpp | 539 +++++++++++++ src/server/game/DungeonFinding/LFGQueue.h | 139 ++++ src/server/game/DungeonFinding/LFGScripts.cpp | 46 +- src/server/game/Groups/Group.cpp | 2 +- src/server/game/Miscellaneous/SharedDefines.h | 7 +- 8 files changed, 817 insertions(+), 688 deletions(-) create mode 100644 src/server/game/DungeonFinding/LFGQueue.cpp create mode 100644 src/server/game/DungeonFinding/LFGQueue.h diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index af12ce93c65..16fda8952ac 100755 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -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(); -} \ No newline at end of file diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 756f7094be6..1f9c9cf3b2c 100755 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -21,7 +21,7 @@ #include "Common.h" #include #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 LfgGuidListMap; -typedef std::set PlayerSet; -typedef std::list LfgPlayerList; -typedef std::map LfgCompatibleMap; -typedef std::map LfgQueueInfoMap; - +typedef std::map LfgQueueMap; typedef std::multimap LfgRewardMap; typedef std::pair LfgRewardMapBounds; typedef std::map LfgCachedDungeonMap; typedef std::map LfgAnswerMap; typedef std::map LfgRoleCheckMap; -typedef std::map LfgProposalMap; +typedef std::map LfgProposalMap; typedef std::map LfgProposalPlayerMap; typedef std::map LfgPlayerBootMap; typedef std::map LfgGroupDataMap; typedef std::map LfgPlayerDataMap; typedef UNORDERED_MAP 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; }; diff --git a/src/server/game/DungeonFinding/LFGPlayerData.cpp b/src/server/game/DungeonFinding/LFGPlayerData.cpp index 6f030831342..e5645f0f0aa 100644 --- a/src/server/game/DungeonFinding/LFGPlayerData.cpp +++ b/src/server/game/DungeonFinding/LFGPlayerData.cpp @@ -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 diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp new file mode 100644 index 00000000000..c1d89a16300 --- /dev/null +++ b/src/server/game/DungeonFinding/LFGQueue.cpp @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2008-2012 TrinityCore + * + * 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 . + */ + +#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; +} \ No newline at end of file diff --git a/src/server/game/DungeonFinding/LFGQueue.h b/src/server/game/DungeonFinding/LFGQueue.h new file mode 100644 index 00000000000..f08199d725a --- /dev/null +++ b/src/server/game/DungeonFinding/LFGQueue.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008-2012 TrinityCore + * + * 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 . + */ + +#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 LfgWaitTimesMap; +typedef std::map LfgCompatibleMap; +typedef std::map 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 diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index 76cc169f477..10ce42da610 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -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); } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index f06225988d6..f4106730e49 100755 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -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); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 4f6ad917423..422385f406f 100755 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -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