From 3d23a9f162057cd36138f6db2341828d200b8ec2 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Tue, 2 Sep 2025 22:43:56 -0400 Subject: [PATCH] Core/Groups: Fixes groups staying linked with players. --- .../Collections/LinkedListElement.cs | 39 ++++-- Source/Framework/Dynamic/Reference.cs | 62 +++++++-- Source/Game/AI/CoreAI/PetAI.cs | 6 +- Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs | 14 +- .../Game/AI/ScriptedAI/ScriptedFollowerAI.cs | 16 +-- Source/Game/AI/SmartScripts/SmartAI.cs | 4 +- Source/Game/AI/SmartScripts/SmartScript.cs | 7 +- .../Game/Achievements/AchievementManager.cs | 11 +- Source/Game/Achievements/CriteriaHandler.cs | 25 ++-- .../Game/BattleGrounds/BattleGroundQueue.cs | 7 +- Source/Game/Chat/Commands/GroupCommands.cs | 63 ++++----- Source/Game/Chat/Commands/TeleCommands.cs | 10 +- Source/Game/DungeonFinding/LFGManager.cs | 105 +++++++-------- Source/Game/Entities/Creature/Creature.cs | 12 +- Source/Game/Entities/GameObject/GameObject.cs | 9 +- Source/Game/Entities/Object/WorldObject.cs | 5 + Source/Game/Entities/Player/KillRewarder.cs | 48 +++---- Source/Game/Entities/Player/Player.Combat.cs | 7 +- Source/Game/Entities/Player/Player.Groups.cs | 28 ++-- Source/Game/Entities/Player/Player.Quest.cs | 9 +- Source/Game/Entities/Player/Player.cs | 10 +- Source/Game/Entities/Totem.cs | 6 +- Source/Game/Entities/Unit/Unit.cs | 23 ++-- Source/Game/Groups/Group.cs | 125 +++++++----------- Source/Game/Groups/GroupInstanceReference.cs | 9 +- Source/Game/Groups/GroupReference.cs | 12 +- Source/Game/Handlers/BattleGroundHandler.cs | 38 +++--- Source/Game/Handlers/GroupHandler.cs | 10 +- Source/Game/Handlers/LFGHandler.cs | 7 +- Source/Game/Handlers/LootHandler.cs | 7 +- Source/Game/Handlers/QuestHandler.cs | 14 +- Source/Game/Loot/Loot.cs | 9 +- Source/Game/Maps/Map.cs | 23 +++- Source/Game/OutdoorPVP/OutdoorPvP.cs | 7 +- 34 files changed, 385 insertions(+), 402 deletions(-) diff --git a/Source/Framework/Collections/LinkedListElement.cs b/Source/Framework/Collections/LinkedListElement.cs index e6f4e3223..bf987d299 100644 --- a/Source/Framework/Collections/LinkedListElement.cs +++ b/Source/Framework/Collections/LinkedListElement.cs @@ -1,12 +1,28 @@ // Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. +using System.Collections.Generic; + namespace Framework.Collections { public class LinkedListElement { - internal LinkedListElement INext; - internal LinkedListElement IPrev; + internal LinkedListElement INext + { + get; + set + { + field = value; + } + } + internal LinkedListElement IPrev + { + get; + set + { + field = value; + } + } public LinkedListElement() { @@ -52,32 +68,31 @@ namespace Framework.Collections public class LinkedListHead { - LinkedListElement _iFirst = new(); - LinkedListElement _iLast = new(); + LinkedListElement iHeader = new(); uint _iSize; public LinkedListHead() { _iSize = 0; + // create empty list - - _iFirst.INext = _iLast; - _iLast.IPrev = _iFirst; + iHeader.INext = new(); + iHeader.IPrev = new(); } - public bool IsEmpty() { return (!_iFirst.INext.IsInList()); } + public bool IsEmpty() { return iHeader.INext == iHeader; } - public LinkedListElement GetFirstElement() { return (IsEmpty() ? null : _iFirst.INext); } - public LinkedListElement GetLastElement() { return (IsEmpty() ? null : _iLast.IPrev); } + public LinkedListElement GetFirstElement() { return (IsEmpty() ? null : iHeader.INext); } + public LinkedListElement GetLastElement() { return (IsEmpty() ? null : iHeader); } public void InsertFirst(LinkedListElement pElem) { - _iFirst.InsertAfter(pElem); + iHeader.InsertBefore(pElem); } public void InsertLast(LinkedListElement pElem) { - _iLast.InsertBefore(pElem); + iHeader.InsertBefore(pElem); } public uint GetSize() diff --git a/Source/Framework/Dynamic/Reference.cs b/Source/Framework/Dynamic/Reference.cs index 8e70c2768..b370190ec 100644 --- a/Source/Framework/Dynamic/Reference.cs +++ b/Source/Framework/Dynamic/Reference.cs @@ -1,11 +1,14 @@ // Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. -using Framework.Collections; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace Framework.Dynamic { - public class Reference : LinkedListElement where TO : class where FROM : class + public class Reference where TO : class where FROM : class { TO _RefTo; FROM _RefFrom; @@ -21,7 +24,8 @@ namespace Framework.Dynamic public Reference() { - _RefTo = null; _RefFrom = null; + _RefTo = null; + _RefFrom = null; } // Create new link @@ -43,7 +47,6 @@ namespace Framework.Dynamic public void Unlink() { TargetObjectDestroyLink(); - Delink(); _RefTo = null; _RefFrom = null; } @@ -53,7 +56,6 @@ namespace Framework.Dynamic public void Invalidate() // the iRefFrom MUST remain!! { SourceObjectDestroyLink(); - Delink(); _RefTo = null; } @@ -62,26 +64,60 @@ namespace Framework.Dynamic return _RefTo != null; } - public Reference Next() { return ((Reference)GetNextElement()); } - public Reference Prev() { return ((Reference)GetPrevElement()); } - public TO GetTarget() { return _RefTo; } public FROM GetSource() { return _RefFrom; } } - public class RefManager : LinkedListHead where TO : class where FROM : class + public class RefManager : IEnumerable where ReferenceType : class, new() { + LinkedList linkedList = new LinkedList(); + ~RefManager() { ClearReferences(); } - public Reference GetFirst() { return (Reference)base.GetFirstElement(); } - public Reference GetLast() { return (Reference)base.GetLastElement(); } + public void InsertFirst(ReferenceType pElem) + { + linkedList.AddFirst(pElem); + } + + public void InsertLast(ReferenceType pElem) + { + linkedList.AddLast(pElem); + } + + public ReferenceType Find(Func func) + { + return linkedList.FirstOrDefault(func); + } + + public void Remove(Func func) + { + linkedList.Remove(Find(func)); + } + + public int GetSize() + { + return linkedList.Count; + } + + public ReferenceType GetFirst() { return linkedList.First?.Value; } + public ReferenceType GetLast() { return linkedList.Last?.Value; } + + public IEnumerator GetEnumerator() + { + return linkedList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } public void ClearReferences() { - Reference refe; + ReferenceType refe; while ((refe = GetFirst()) != null) - refe.Invalidate(); + linkedList.Remove(refe); } } } diff --git a/Source/Game/AI/CoreAI/PetAI.cs b/Source/Game/AI/CoreAI/PetAI.cs index fd6da28d7..a0456524f 100644 --- a/Source/Game/AI/CoreAI/PetAI.cs +++ b/Source/Game/AI/CoreAI/PetAI.cs @@ -592,10 +592,10 @@ namespace Game.AI _allySet.Add(me.GetGUID()); if (group != null) // add group { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player target = refe.GetSource(); - if (target == null || !target.IsInMap(owner) || !group.SameSubGroup(owner.ToPlayer(), target)) + Player target = groupRef.GetSource(); + if (!target.IsInMap(owner) || !group.SameSubGroup(owner.ToPlayer(), target)) continue; if (target.GetGUID() == owner.GetGUID()) diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs index 636b43285..12f1bd677 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs @@ -83,12 +83,11 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member != null) - if (member.IsInMap(player)) - member.FailQuest(_escortQuest.Id); + if (member.IsInMap(player)) + member.FailQuest(_escortQuest.Id); } } else @@ -150,12 +149,11 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member != null) - if (me.IsWithinDistInMap(member, GetMaxPlayerDistance())) - return true; + if (me.IsWithinDistInMap(member, GetMaxPlayerDistance())) + return true; } } else if (me.IsWithinDistInMap(player, GetMaxPlayerDistance())) diff --git a/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs b/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs index 4eea4f2c3..e25ac25ff 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs @@ -52,12 +52,11 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member != null) - if (member.IsInMap(player)) - member.FailQuest(_questForFollow); + if (member.IsInMap(player)) + member.FailQuest(_questForFollow); } } else @@ -109,12 +108,9 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null && (maxRangeExceeded || questAbandoned); groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member == null) - continue; - if (maxRangeExceeded && me.IsWithinDistInMap(member, 100.0f)) maxRangeExceeded = false; @@ -253,10 +249,10 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member != null && me.IsWithinDistInMap(member, 100.0f) && member.IsAlive()) + if (me.IsWithinDistInMap(member, 100.0f) && member.IsAlive()) { Log.outDebug(LogFilter.Scripts, $"FollowerAI::GetLeaderForFollower: GetLeader changed and returned new leader. ({me.GetGUID()})"); _leaderGUID = member.GetGUID(); diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 707482caa..004860a95 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -227,7 +227,7 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player groupGuy = groupRef.GetSource(); if (!groupGuy.IsInMap(player)) @@ -333,7 +333,7 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player groupGuy = groupRef.GetSource(); if (groupGuy.IsInMap(player) && me.GetDistance(groupGuy) <= checkDist) diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 6b8af5286..aa9765b15 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -2889,12 +2889,11 @@ namespace Game.AI Group group = player.GetGroup(); if (group != null) { - for (GroupReference groupRef = group.GetFirstMember(); groupRef != null; groupRef = groupRef.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { Player member = groupRef.GetSource(); - if (member != null) - if (member.IsInMap(player)) - targets.Add(member); + if (member.IsInMap(player)) + targets.Add(member); } } // We still add the player to the list if there is no group. If we do diff --git a/Source/Game/Achievements/AchievementManager.cs b/Source/Game/Achievements/AchievementManager.cs index 0a4b9a9f6..220422c4a 100644 --- a/Source/Game/Achievements/AchievementManager.cs +++ b/Source/Game/Achievements/AchievementManager.cs @@ -671,7 +671,7 @@ namespace Game.Achievements achievementEarned.Time += receiver.GetSession().GetTimezoneOffset(); receiver.SendPacket(achievementEarned); }; - + achievementEarnedBuilder(_owner); if (!achievement.Flags.HasAnyFlag(AchievementFlags.TrackingFlag)) @@ -985,12 +985,11 @@ namespace Game.Achievements Group group = referencePlayer.GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player groupMember = refe.GetSource(); - if (groupMember != null) - if (groupMember.GetGuildId() == _owner.GetId()) - ca.CompletingPlayers.Add(groupMember.GetGUID()); + Player groupMember = groupRef.GetSource(); + if (groupMember.GetGuildId() == _owner.GetId()) + ca.CompletingPlayers.Add(groupMember.GetGUID()); } } } diff --git a/Source/Game/Achievements/CriteriaHandler.cs b/Source/Game/Achievements/CriteriaHandler.cs index 950c737c4..a71cae3fb 100644 --- a/Source/Game/Achievements/CriteriaHandler.cs +++ b/Source/Game/Achievements/CriteriaHandler.cs @@ -9,6 +9,7 @@ using Game.BattleGrounds; using Game.DataStorage; using Game.Entities; using Game.Garrisons; +using Game.Groups; using Game.Maps; using Game.Miscellaneous; using Game.Networking; @@ -1725,8 +1726,8 @@ namespace Game.Achievements var group = referencePlayer.GetGroup(); if (group != null) { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetGuildId() == referencePlayer.GetGuildId()) + foreach (GroupReference groupRefe in group.GetMembers()) + if (groupRefe.GetSource().GetGuildId() == referencePlayer.GetGuildId()) ++guildMemberCount; } @@ -2739,8 +2740,8 @@ namespace Game.Achievements var group = referencePlayer.GetGroup(); if (group != null) { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource() != referencePlayer && referencePlayer.m_playerData.VirtualPlayerRealm == itr.GetSource().m_playerData.VirtualPlayerRealm) + foreach (GroupReference groupRef in group.GetMembers()) + if (groupRef.GetSource() != referencePlayer && referencePlayer.m_playerData.VirtualPlayerRealm == groupRef.GetSource().m_playerData.VirtualPlayerRealm) ++memberCount; } @@ -2845,8 +2846,8 @@ namespace Game.Achievements if (group != null) { uint membersWithAchievement = 0; - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().HasAchieved((uint)secondaryAsset)) + foreach (GroupReference groupRef in group.GetMembers()) + if (groupRef.GetSource().HasAchieved((uint)secondaryAsset)) ++membersWithAchievement; if (membersWithAchievement > reqValue) @@ -3283,8 +3284,8 @@ namespace Game.Achievements if (group == null) return false; - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetSession().GetRecruiterId() == referencePlayer.GetSession().GetAccountId()) + foreach (GroupReference groupRef in group.GetMembers()) + if (groupRef.GetSource().GetSession().GetRecruiterId() == referencePlayer.GetSession().GetAccountId()) return true; return false; @@ -3295,8 +3296,8 @@ namespace Game.Achievements if (group == null) return false; - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (itr.GetSource().GetSession().GetAccountId() == referencePlayer.GetSession().GetRecruiterId()) + foreach (GroupReference groupRef in group.GetMembers()) + if (groupRef.GetSource().GetSession().GetAccountId() == referencePlayer.GetSession().GetRecruiterId()) return true; return false; @@ -3430,8 +3431,8 @@ namespace Game.Achievements var group = referencePlayer.GetGroup(); if (group != null) { - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (!itr.GetSource().HasAchieved(reqValue)) + foreach (GroupReference groupRef in group.GetMembers()) + if (!groupRef.GetSource().HasAchieved(reqValue)) return false; } else if (!referencePlayer.HasAchieved(reqValue)) diff --git a/Source/Game/BattleGrounds/BattleGroundQueue.cs b/Source/Game/BattleGrounds/BattleGroundQueue.cs index 9f163bad6..ef8dfe022 100644 --- a/Source/Game/BattleGrounds/BattleGroundQueue.cs +++ b/Source/Game/BattleGrounds/BattleGroundQueue.cs @@ -82,12 +82,9 @@ namespace Game.BattleGrounds //add players from group to ginfo if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member == null) - continue; // this should never happen - + Player member = groupRef.GetSource(); PlayerQueueInfo pl_info = new(); pl_info.LastOnlineTime = lastOnlineTime; pl_info.GroupInfo = ginfo; diff --git a/Source/Game/Chat/Commands/GroupCommands.cs b/Source/Game/Chat/Commands/GroupCommands.cs index 8498d7e13..377379a4c 100644 --- a/Source/Game/Chat/Commands/GroupCommands.cs +++ b/Source/Game/Chat/Commands/GroupCommands.cs @@ -106,27 +106,24 @@ namespace Game.Chat if (groupTarget == null) return false; - for (GroupReference it = groupTarget.GetFirstMember(); it != null; it = it.Next()) + foreach (GroupReference groupRef in groupTarget.GetMembers()) { - target = it.GetSource(); - if (target != null) + target = groupRef.GetSource(); + uint oldlevel = target.GetLevel(); + + if (level != oldlevel) { - uint oldlevel = target.GetLevel(); + target.SetLevel((uint)level); + target.InitTalentForLevel(); + target.SetXP(0); + } - if (level != oldlevel) - { - target.SetLevel((uint)level); - target.InitTalentForLevel(); - target.SetXP(0); - } - - if (handler.NeedReportToTarget(target)) - { - if (oldlevel < level) - target.SendSysMessage(CypherStrings.YoursLevelUp, handler.GetNameLink(), level); - else // if (oldlevel > newlevel) - target.SendSysMessage(CypherStrings.YoursLevelDown, handler.GetNameLink(), level); - } + if (handler.NeedReportToTarget(target)) + { + if (oldlevel < level) + target.SendSysMessage(CypherStrings.YoursLevelUp, handler.GetNameLink(), level); + else // if (oldlevel > newlevel) + target.SendSysMessage(CypherStrings.YoursLevelDown, handler.GetNameLink(), level); } } return true; @@ -248,12 +245,8 @@ namespace Game.Chat if (groupTarget == null) return false; - for (GroupReference it = groupTarget.GetFirstMember(); it != null; it = it.Next()) - { - Player target = it.GetSource(); - if (target != null) - target.DurabilityRepairAll(false, 0, false); - } + foreach (GroupReference groupRef in groupTarget.GetMembers()) + groupRef.GetSource().DurabilityRepairAll(false, 0, false); return true; } @@ -270,15 +263,12 @@ namespace Game.Chat if (groupTarget == null) return false; - for (GroupReference it = groupTarget.GetFirstMember(); it != null; it = it.Next()) + foreach (GroupReference groupRef in groupTarget.GetMembers()) { - Player target = it.GetSource(); - if (target != null) - { - target.ResurrectPlayer(target.GetSession().HasPermission(RBACPermissions.ResurrectWithFullHps) ? 1.0f : 0.5f); - target.SpawnCorpseBones(); - target.SaveToDB(); - } + Player target = groupRef.GetSource(); + target.ResurrectPlayer(target.GetSession().HasPermission(RBACPermissions.ResurrectWithFullHps) ? 1.0f : 0.5f); + target.SpawnCorpseBones(); + target.SaveToDB(); } return true; @@ -326,11 +316,10 @@ namespace Game.Chat } } - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - - if (player == null || player == gmPlayer || player.GetSession() == null) + Player player = groupRef.GetSource(); + if (player == gmPlayer || player.GetSession() == null) continue; // check online security @@ -364,7 +353,7 @@ namespace Game.Chat // stop flight if need if (player.IsInFlight()) - player.FinishTaxiFlight(); + player.FinishTaxiFlight(); else player.SaveRecallPosition(); // save only in non-flight case diff --git a/Source/Game/Chat/Commands/TeleCommands.cs b/Source/Game/Chat/Commands/TeleCommands.cs index 4494db743..8c705bf40 100644 --- a/Source/Game/Chat/Commands/TeleCommands.cs +++ b/Source/Game/Chat/Commands/TeleCommands.cs @@ -127,18 +127,16 @@ namespace Game.Chat string nameLink = handler.GetNameLink(target); - Group grp = target.GetGroup(); - if (grp == null) + Group group = target.GetGroup(); + if (group == null) { handler.SendSysMessage(CypherStrings.NotInGroup, nameLink); return false; } - for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - if (player == null || player.GetSession() == null) - continue; + Player player = groupRef.GetSource(); // check online security if (handler.HasLowerSecurity(player, ObjectGuid.Empty)) diff --git a/Source/Game/DungeonFinding/LFGManager.cs b/Source/Game/DungeonFinding/LFGManager.cs index 088179804..2d1afa8c5 100644 --- a/Source/Game/DungeonFinding/LFGManager.cs +++ b/Source/Game/DungeonFinding/LFGManager.cs @@ -397,27 +397,27 @@ namespace Game.DungeonFinding else { byte memberCount = 0; - for (GroupReference refe = grp.GetFirstMember(); refe != null && joinData.result == LfgJoinResult.Ok; refe = refe.Next()) + foreach (GroupReference groupRef in grp.GetMembers()) { - Player groupPlayer = refe.GetSource(); - if (groupPlayer != null) + Player groupPlayer = groupRef.GetSource(); + if (!groupPlayer.GetSession().HasPermission(RBACPermissions.JoinDungeonFinder)) + joinData.result = LfgJoinResult.NoLfgObject; + if (groupPlayer.HasAura(SharedConst.LFGSpellDungeonDeserter)) + joinData.result = LfgJoinResult.DeserterParty; + else if (!isContinue && groupPlayer.HasAura(SharedConst.LFGSpellDungeonCooldown)) + joinData.result = LfgJoinResult.RandomCooldownParty; + else if (groupPlayer.InBattleground() || groupPlayer.InArena() || groupPlayer.InBattlegroundQueue()) + joinData.result = LfgJoinResult.CantUseDungeons; + else if (groupPlayer.HasAura(9454)) // check Freeze debuff { - if (!groupPlayer.GetSession().HasPermission(RBACPermissions.JoinDungeonFinder)) - joinData.result = LfgJoinResult.NoLfgObject; - if (groupPlayer.HasAura(SharedConst.LFGSpellDungeonDeserter)) - joinData.result = LfgJoinResult.DeserterParty; - else if (!isContinue && groupPlayer.HasAura(SharedConst.LFGSpellDungeonCooldown)) - joinData.result = LfgJoinResult.RandomCooldownParty; - else if (groupPlayer.InBattleground() || groupPlayer.InArena() || groupPlayer.InBattlegroundQueue()) - joinData.result = LfgJoinResult.CantUseDungeons; - else if (groupPlayer.HasAura(9454)) // check Freeze debuff - { - joinData.result = LfgJoinResult.NoSlots; - joinData.playersMissingRequirement.Add(groupPlayer.GetName()); - } - ++memberCount; - players.Add(groupPlayer.GetGUID()); + joinData.result = LfgJoinResult.NoSlots; + joinData.playersMissingRequirement.Add(groupPlayer.GetName()); } + ++memberCount; + players.Add(groupPlayer.GetGUID()); + + if (joinData.result != LfgJoinResult.Ok) + break; } if (joinData.result == LfgJoinResult.Ok && memberCount != grp.GetMembersCount()) @@ -521,22 +521,19 @@ namespace Game.DungeonFinding SetState(gguid, LfgState.Rolecheck); // Send update to player LfgUpdateData updateData = new(LfgUpdateType.JoinQueue, dungeons); - for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in grp.GetMembers()) { - Player plrg = refe.GetSource(); - if (plrg != null) - { - ObjectGuid pguid = plrg.GetGUID(); - plrg.GetSession().SendLfgUpdateStatus(updateData, true); - SetState(pguid, LfgState.Rolecheck); - SetTicket(pguid, ticket); - if (!isContinue) - SetSelectedDungeons(pguid, dungeons); - roleCheck.roles[pguid] = 0; - if (!string.IsNullOrEmpty(debugNames)) - debugNames += ", "; - debugNames += plrg.GetName(); - } + Player plrg = groupRef.GetSource(); + ObjectGuid pguid = plrg.GetGUID(); + plrg.GetSession().SendLfgUpdateStatus(updateData, true); + SetState(pguid, LfgState.Rolecheck); + SetTicket(pguid, ticket); + if (!isContinue) + SetSelectedDungeons(pguid, dungeons); + roleCheck.roles[pguid] = 0; + if (!string.IsNullOrEmpty(debugNames)) + debugNames += ", "; + debugNames += plrg.GetName(); } // Update leader role UpdateRoleCheck(gguid, guid, roles); @@ -615,27 +612,27 @@ namespace Game.DungeonFinding UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED break; case LfgState.Proposal: + { + // Remove from Proposals + KeyValuePair it = new(); + ObjectGuid pguid = gguid == guid ? GetLeader(gguid) : guid; + foreach (var test in ProposalsStore) { - // Remove from Proposals - KeyValuePair it = new(); - ObjectGuid pguid = gguid == guid ? GetLeader(gguid) : guid; - foreach (var test in ProposalsStore) + it = test; + var itPlayer = it.Value.players.LookupByKey(pguid); + if (itPlayer != null) { - it = test; - var itPlayer = it.Value.players.LookupByKey(pguid); - if (itPlayer != null) - { - // Mark the player/leader of group who left as didn't accept the proposal - itPlayer.accept = LfgAnswer.Deny; - break; - } + // Mark the player/leader of group who left as didn't accept the proposal + itPlayer.accept = LfgAnswer.Deny; + break; } - - // Remove from queue - if proposal is found, RemoveProposal will call RemoveFromQueue - if (it.Value != null) - RemoveProposal(it, LfgUpdateType.ProposalDeclined); - break; } + + // Remove from queue - if proposal is found, RemoveProposal will call RemoveFromQueue + if (it.Value != null) + RemoveProposal(it, LfgUpdateType.ProposalDeclined); + break; + } case LfgState.None: case LfgState.Raidbrowser: break; @@ -1303,10 +1300,10 @@ namespace Game.DungeonFinding if (!fromOpcode) { // Select a player inside to be teleported to - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player plrg = refe.GetSource(); - if (plrg != null && plrg != player && plrg.GetMapId() == dungeon.map) + Player plrg = groupRef.GetSource(); + if (plrg != player && plrg.GetMapId() == dungeon.map) { mapid = plrg.GetMapId(); x = plrg.GetPositionX(); @@ -1323,7 +1320,7 @@ namespace Game.DungeonFinding player.FinishTaxiFlight(); - if (!player.TeleportTo(new Game.Entities.TeleportLocation(){ Location = new WorldLocation(mapid, x, y, z, orientation), LfgDungeonsId = dungeon.id })) + if (!player.TeleportTo(new Game.Entities.TeleportLocation() { Location = new WorldLocation(mapid, x, y, z, orientation), LfgDungeonsId = dungeon.id })) error = LfgTeleportResult.NoReturnLocation; } else diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 55a7726cf..909c4a15b 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1178,10 +1178,10 @@ namespace Game.Entities foreach (Group partyToForceIntoCombat in partiesToForceIntoCombat) { - for (GroupReference refe = partyToForceIntoCombat.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in partyToForceIntoCombat.GetMembers()) { - Player player = refe.GetSource(); - if (player == null || !player.IsInWorld || player.GetMap() != GetMap() || player.IsGameMaster()) + Player player = groupRef.GetSource(); + if (!player.IsInWorld || player.GetMap() != GetMap() || player.IsGameMaster()) continue; EngageWithTarget(player); @@ -1334,9 +1334,9 @@ namespace Game.Entities { Group group = player.GetGroup(); if (group != null) - for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - if (GetMap().IsRaid() || group.SameSubGroup(player, itr.GetSource())) - m_tapList.Add(itr.GetSource().GetGUID()); + foreach (GroupReference groupRef in group.GetMembers()) + if (GetMap().IsRaid() || group.SameSubGroup(player, groupRef.GetSource())) + m_tapList.Add(groupRef.GetSource().GetGUID()); } if (m_tapList.Count >= SharedConst.CreatureTappersSoftCap) diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 6c02c9345..5abbead8a 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -1966,12 +1966,11 @@ namespace Game.Entities Group group = player.GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member != null) - if (member.IsAtGroupRewardDistance(this)) - member.KillCreditGO(info.entry, GetGUID()); + Player member = groupRef.GetSource(); + if (member.IsAtGroupRewardDistance(this)) + member.KillCreditGO(info.entry, GetGUID()); } } else diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index ee1ae55e5..37718ac09 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -3763,6 +3763,11 @@ namespace Game.Entities public void SetLocationInstanceId(uint _instanceId) { instanceId = _instanceId; } + public override string ToString() + { + return GetName(); + } + #region Fields public TypeMask ObjectTypeMask { get; set; } protected TypeId ObjectTypeId { get; set; } diff --git a/Source/Game/Entities/Player/KillRewarder.cs b/Source/Game/Entities/Player/KillRewarder.cs index 0ad6da0d0..714e3c280 100644 --- a/Source/Game/Entities/Player/KillRewarder.cs +++ b/Source/Game/Entities/Player/KillRewarder.cs @@ -104,27 +104,24 @@ namespace Game.Entities if (group != null) { // 2. In case when player is in group, initialize variables necessary for group calculations: - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member != null) + Player member = groupRef.GetSource(); + if (killer == member || (member.IsAtGroupRewardDistance(_victim) && member.IsAlive())) { - if (killer == member || (member.IsAtGroupRewardDistance(_victim) && member.IsAlive())) - { - uint lvl = member.GetLevel(); - // 2.1. _count - number of alive group members within reward distance; - ++_count; - // 2.2. _sumLevel - sum of levels of alive group members within reward distance; - _sumLevel += lvl; - // 2.3. _maxLevel - maximum level of alive group member within reward distance; - if (_maxLevel < lvl) - _maxLevel = (byte)lvl; - // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance, - // for whom victim is not gray; - uint grayLevel = Formulas.GetGrayLevel(lvl); - if (_victim.GetLevelForTarget(member) > grayLevel && (_maxNotGrayMember == null || _maxNotGrayMember.GetLevel() < lvl)) - _maxNotGrayMember = member; - } + uint lvl = member.GetLevel(); + // 2.1. _count - number of alive group members within reward distance; + ++_count; + // 2.2. _sumLevel - sum of levels of alive group members within reward distance; + _sumLevel += lvl; + // 2.3. _maxLevel - maximum level of alive group member within reward distance; + if (_maxLevel < lvl) + _maxLevel = (byte)lvl; + // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance, + // for whom victim is not gray; + uint grayLevel = Formulas.GetGrayLevel(lvl); + if (_victim.GetLevelForTarget(member) > grayLevel && (_maxNotGrayMember == null || _maxNotGrayMember.GetLevel() < lvl)) + _maxNotGrayMember = member; } } // 2.5. _isFullXP - flag identifying that for all group members victim is not gray, @@ -254,15 +251,12 @@ namespace Game.Entities _groupRate = Formulas.XPInGroupRate(_count, isRaid); } // 3.1.3. Reward each group member (even dead or corpse) within reward distance. - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member != null) - { - // Killer may not be at reward distance, check directly - if (killer == member || member.IsAtGroupRewardDistance(_victim)) - _RewardPlayer(member, isDungeon); - } + Player member = groupRef.GetSource(); + // Killer may not be at reward distance, check directly + if (killer == member || member.IsAtGroupRewardDistance(_victim)) + _RewardPlayer(member, isDungeon); } } } diff --git a/Source/Game/Entities/Player/Player.Combat.cs b/Source/Game/Entities/Player/Player.Combat.cs index 5439fb18b..ebdf612f9 100644 --- a/Source/Game/Entities/Player/Player.Combat.cs +++ b/Source/Game/Entities/Player/Player.Combat.cs @@ -40,12 +40,9 @@ namespace Game.Entities Group group = GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - if (player == null) - continue; - + Player player = groupRef.GetSource(); if (!player.IsAtGroupRewardDistance(pRewardSource)) continue; // member (alive or dead) or his corpse at req. distance diff --git a/Source/Game/Entities/Player/Player.Groups.cs b/Source/Game/Entities/Player/Player.Groups.cs index 4941b3046..dece920f2 100644 --- a/Source/Game/Entities/Player/Player.Groups.cs +++ b/Source/Game/Entities/Player/Player.Groups.cs @@ -18,14 +18,14 @@ namespace Game.Entities List nearMembers = new(); - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player Target = refe.GetSource(); + Player target = groupRef.GetSource(); // IsHostileTo check duel and controlled by enemy - if (Target != null && Target != this && IsWithinDistInMap(Target, radius) && - !Target.HasInvisibilityAura() && !IsHostileTo(Target)) - nearMembers.Add(Target); + if (target != this && IsWithinDistInMap(target, radius) && + !target.HasInvisibilityAura() && !IsHostileTo(target)) + nearMembers.Add(target); } if (nearMembers.Empty()) @@ -37,13 +37,13 @@ namespace Game.Entities public PartyResult CanUninviteFromGroup(ObjectGuid guidMember, byte? partyIndex) { - Group grp = GetGroup(partyIndex); - if (grp == null) + Group group = GetGroup(partyIndex); + if (group == null) return PartyResult.NotInGroup; - if (grp.IsLFGGroup()) + if (group.IsLFGGroup()) { - ObjectGuid gguid = grp.GetGUID(); + ObjectGuid gguid = group.GetGUID(); if (Global.LFGMgr.GetKicksLeft(gguid) == 0) return PartyResult.PartyLfgBootLimit; @@ -51,7 +51,7 @@ namespace Game.Entities if (Global.LFGMgr.IsVoteKickActive(gguid)) return PartyResult.PartyLfgBootInProgress; - if (grp.GetMembersCount() <= SharedConst.LFGKickVotesNeeded) + if (group.GetMembersCount() <= SharedConst.LFGKickVotesNeeded) return PartyResult.PartyLfgBootTooFewPlayers; if (state == LfgState.FinishedDungeon) @@ -62,8 +62,8 @@ namespace Game.Entities return PartyResult.PartyLfgBootLootRolls; // @todo Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer. - for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.Next()) - if (refe.GetSource() != null && refe.GetSource().IsInMap(this) && refe.GetSource().IsInCombat()) + foreach (GroupReference groupRef in group.GetMembers()) + if (groupRef.GetSource().IsInMap(this) && groupRef.GetSource().IsInCombat()) return PartyResult.PartyLfgBootInCombat; /* Missing support for these types @@ -73,13 +73,13 @@ namespace Game.Entities } else { - if (!grp.IsLeader(GetGUID()) && !grp.IsAssistant(GetGUID())) + if (!group.IsLeader(GetGUID()) && !group.IsAssistant(GetGUID())) return PartyResult.NotLeader; if (InBattleground()) return PartyResult.InviteRestricted; - if (grp.IsLeader(guidMember)) + if (group.IsLeader(guidMember)) return PartyResult.NotLeader; } diff --git a/Source/Game/Entities/Player/Player.Quest.cs b/Source/Game/Entities/Player/Player.Quest.cs index 06d89452e..dd60ad1b2 100644 --- a/Source/Game/Entities/Player/Player.Quest.cs +++ b/Source/Game/Entities/Player/Player.Quest.cs @@ -2442,12 +2442,11 @@ namespace Game.Entities var group = GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - + Player player = groupRef.GetSource(); // for any leave or dead (with not released body) group member at appropriate distance - if (player != null && player.IsAtGroupRewardDistance(pEventObject) && player.GetCorpse() == null) + if (player.IsAtGroupRewardDistance(pEventObject) && player.GetCorpse() == null) player.AreaExploredOrEventHappens(questId); } } @@ -3481,7 +3480,7 @@ namespace Game.Entities { if (IsQuestRewarded(questObjective.QuestID) || IsQuestObjectiveComplete(questObjective.QuestID, questObjective.Id)) return SpawnTrackingState.Complete; - + if (GetQuestStatus(questObjective.QuestID) != QuestStatus.None && IsQuestObjectiveCompletable(questObjective.QuestID, questObjective.Id)) { var itr = m_QuestStatus.LookupByKey(questObjective.QuestID); diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 129201e8d..287ed79f1 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -147,6 +147,9 @@ namespace Game.Entities WorldMgr.DecreasePlayerCount(); + m_group.Dispose(); + m_originalGroup.Dispose(); + base.Dispose(); } @@ -7531,12 +7534,9 @@ namespace Game.Entities Group group = GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - if (player == null) - continue; - + Player player = groupRef.GetSource(); if (!player.IsAtRecruitAFriendDistance(this)) continue; // member (alive or dead) or his corpse at req. distance diff --git a/Source/Game/Entities/Totem.cs b/Source/Game/Entities/Totem.cs index 254dfc2a2..1d2babeef 100644 --- a/Source/Game/Entities/Totem.cs +++ b/Source/Game/Entities/Totem.cs @@ -123,10 +123,10 @@ namespace Game.Entities Group group = owner.GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player target = refe.GetSource(); - if (target != null && target.IsInMap(owner) && group.SameSubGroup(owner, target)) + Player target = groupRef.GetSource(); + if (target.IsInMap(owner) && group.SameSubGroup(owner, target)) target.RemoveAurasDueToSpell(GetSpell(), GetGUID()); } } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index f4a14c20e..9ae394593 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -3382,21 +3382,18 @@ namespace Game.Entities List nearMembers = new(); // reserve place for players and pets because resizing vector every unit push is unefficient (vector is reallocated then) - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player target = refe.GetSource(); - if (target != null) - { - // IsHostileTo check duel and controlled by enemy - if (target != this && IsWithinDistInMap(target, radius) && target.IsAlive() && !IsHostileTo(target)) - nearMembers.Add(target); + Player target = groupRef.GetSource(); + // IsHostileTo check duel and controlled by enemy + if (target != this && IsWithinDistInMap(target, radius) && target.IsAlive() && !IsHostileTo(target)) + nearMembers.Add(target); - // Push player's pet to vector - Unit pet = target.GetGuardianPet(); - if (pet != null) - if (pet != this && IsWithinDistInMap(pet, radius) && pet.IsAlive() && !IsHostileTo(pet)) - nearMembers.Add(pet); - } + // Push player's pet to vector + Unit pet = target.GetGuardianPet(); + if (pet != null) + if (pet != this && IsWithinDistInMap(pet, radius) && pet.IsAlive() && !IsHostileTo(pet)) + nearMembers.Add(pet); } if (nearMembers.Empty()) diff --git a/Source/Game/Groups/Group.cs b/Source/Game/Groups/Group.cs index ebc02e901..6b8d02447 100644 --- a/Source/Game/Groups/Group.cs +++ b/Source/Game/Groups/Group.cs @@ -487,27 +487,24 @@ namespace Game.Groups UpdateObject groupDataPacket; // Broadcast group members' fields to player - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - if (refe.GetSource() == player) + Player existingMember = groupRef.GetSource(); + if (existingMember == player) continue; - Player existingMember = refe.GetSource(); - if (existingMember != null) - { - if (player.HaveAtClient(existingMember)) - existingMember.BuildValuesUpdateBlockForPlayerWithFlag(groupData, UpdateFieldFlag.PartyMember, player); + if (player.HaveAtClient(existingMember)) + existingMember.BuildValuesUpdateBlockForPlayerWithFlag(groupData, UpdateFieldFlag.PartyMember, player); - if (existingMember.HaveAtClient(player)) + if (existingMember.HaveAtClient(player)) + { + UpdateData newData = new(player.GetMapId()); + UpdateObject newDataPacket; + player.BuildValuesUpdateBlockForPlayerWithFlag(newData, UpdateFieldFlag.PartyMember, existingMember); + if (newData.HasData()) { - UpdateData newData = new(player.GetMapId()); - UpdateObject newDataPacket; - player.BuildValuesUpdateBlockForPlayerWithFlag(newData, UpdateFieldFlag.PartyMember, existingMember); - if (newData.HasData()) - { - newData.BuildPacket(out newDataPacket); - existingMember.SendPacket(newDataPacket); - } + newData.BuildPacket(out newDataPacket); + existingMember.SendPacket(newDataPacket); } } } @@ -531,17 +528,14 @@ namespace Game.Groups Player player = Global.ObjAccessor.FindConnectedPlayer(guid); if (player != null) { - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player groupMember = refe.GetSource(); - if (groupMember != null) - { - if (groupMember.GetGUID() == guid) - continue; + Player groupMember = groupRef.GetSource(); + if (groupMember.GetGUID() == guid) + continue; - groupMember.RemoveAllGroupBuffsFromCaster(guid); - player.RemoveAllGroupBuffsFromCaster(groupMember.GetGUID()); - } + groupMember.RemoveAllGroupBuffsFromCaster(guid); + player.RemoveAllGroupBuffsFromCaster(groupMember.GetGUID()); } } @@ -925,37 +919,36 @@ namespace Game.Groups PartyMemberFullState packet = new(); packet.Initialize(player); - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player member = refe.GetSource(); - if (member != null && member != player && (!member.IsInMap(player) || !member.IsWithinDist(player, member.GetSightRange(), false))) + Player member = groupRef.GetSource(); + if (member != player && (!member.IsInMap(player) || !member.IsWithinDist(player, member.GetSightRange(), false))) member.SendPacket(packet); } } public void BroadcastAddonMessagePacket(ServerPacket packet, string prefix, bool ignorePlayersInBGRaid, int group = -1, ObjectGuid ignore = default) { - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player player = refe.GetSource(); - if (player == null || (!ignore.IsEmpty() && player.GetGUID() == ignore) || (ignorePlayersInBGRaid && player.GetGroup() != this)) + Player player = groupRef.GetSource(); + if ((!ignore.IsEmpty() && player.GetGUID() == ignore) || (ignorePlayersInBGRaid && player.GetGroup() != this)) continue; - if ((group == -1 || refe.GetSubGroup() == group)) - if (player.GetSession().IsAddonRegistered(prefix)) - player.SendPacket(packet); + if (player.GetSession().IsAddonRegistered(prefix) && (group == -1 || groupRef.GetSubGroup() == group)) + player.SendPacket(packet); } } public void BroadcastPacket(ServerPacket packet, bool ignorePlayersInBGRaid, int group = -1, ObjectGuid ignore = default) { - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player player = refe.GetSource(); - if (player == null || (!ignore.IsEmpty() && player.GetGUID() == ignore) || (ignorePlayersInBGRaid && player.GetGroup() != this)) + Player player = groupRef.GetSource(); + if ((!ignore.IsEmpty() && player.GetGUID() == ignore) || (ignorePlayersInBGRaid && player.GetGroup() != this)) continue; - if (player.GetSession() != null && (group == -1 || refe.GetSubGroup() == group)) + if (group == -1 || groupRef.GetSubGroup() == group) player.SendPacket(packet); } } @@ -1183,11 +1176,12 @@ namespace Game.Groups return GroupJoinBattlegroundResult.None; // ERR_GROUP_JOIN_Battleground_TOO_MANY handled on client side // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.) - Player reference = GetFirstMember().GetSource(); + var membersRefe = GetMembers().GetFirst(); // no reference found, can't join this way - if (reference == null) + if (membersRefe == null) return GroupJoinBattlegroundResult.BattlegroundJoinFailed; + Player reference = membersRefe.GetSource(); PvpDifficultyRecord bracketEntry = Global.DB2Mgr.GetBattlegroundBracketByLevel((uint)bgOrTemplate.MapIDs[0], reference.GetLevel()); if (bracketEntry == null) return GroupJoinBattlegroundResult.BattlegroundJoinFailed; @@ -1198,9 +1192,9 @@ namespace Game.Groups // check every member of the group to be able to join memberscount = 0; - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next(), ++memberscount) + foreach (GroupReference groupRef in GetMembers()) { - Player member = refe.GetSource(); + Player member = groupRef.GetSource(); // offline member? don't let join if (member == null) return GroupJoinBattlegroundResult.BattlegroundJoinFailed; @@ -1243,6 +1237,8 @@ namespace Game.Groups return GroupJoinBattlegroundResult.BattlegroundJoinFailed; if (isMercenary != (member.HasAura(BattlegroundConst.SpellMercenaryContractHorde) || member.HasAura(BattlegroundConst.SpellMercenaryContractAlliance))) return GroupJoinBattlegroundResult.BattlegroundJoinMercenary; + + memberscount++; } errorGuid = ObjectGuid.Empty; @@ -1267,12 +1263,9 @@ namespace Game.Groups DB.Characters.Execute(stmt); } - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player player = refe.GetSource(); - if (player.GetSession() == null) - continue; - + Player player = groupRef.GetSource(); player.SetDungeonDifficultyID(difficulty); player.SendDungeonDifficulty(); } @@ -1291,12 +1284,9 @@ namespace Game.Groups DB.Characters.Execute(stmt); } - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player player = refe.GetSource(); - if (player.GetSession() == null) - continue; - + Player player = groupRef.GetSource(); player.SetRaidDifficultyID(difficulty); player.SendRaidDifficulty(false); } @@ -1315,12 +1305,9 @@ namespace Game.Groups DB.Characters.Execute(stmt); } - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in GetMembers()) { - Player player = refe.GetSource(); - if (player.GetSession() == null) - continue; - + Player player = groupRef.GetSource(); player.SetLegacyRaidDifficultyID(difficulty); player.SendRaidDifficulty(true); } @@ -1348,9 +1335,9 @@ namespace Game.Groups public void ResetInstances(InstanceResetMethod method, Player notifyPlayer) { - for (GroupInstanceReference refe = m_ownedInstancesMgr.GetFirst(); refe != null; refe = refe.Next()) + foreach (GroupInstanceReference groupInstanceRef in m_ownedInstancesMgr) { - InstanceMap map = refe.GetSource(); + InstanceMap map = groupInstanceRef.GetSource(); switch (map.Reset(method)) { case InstanceResetResult.Success: @@ -1795,19 +1782,9 @@ namespace Game.Groups m_memberMgr.InsertFirst(pRef); } - void DelinkMember(ObjectGuid guid) + public void DelinkMember(ObjectGuid guid) { - GroupReference refe = m_memberMgr.GetFirst(); - while (refe != null) - { - GroupReference nextRef = refe.Next(); - if (refe.GetSource().GetGUID() == guid) - { - refe.Unlink(); - break; - } - refe = nextRef; - } + m_memberMgr.Remove(p => p.GetSource().GetGUID() == guid); } void _initRaidSubGroupsCounter() @@ -1915,7 +1892,7 @@ namespace Game.Groups public uint GetDbStoreId() { return m_dbStoreId; } public List GetMemberSlots() { return m_memberSlots; } - public GroupReference GetFirstMember() { return (GroupReference)m_memberMgr.GetFirst(); } + public GroupRefManager GetMembers() { return m_memberMgr; } public uint GetMembersCount() { return (uint)m_memberSlots.Count; } public uint GetInviteeCount() { return (uint)m_invitees.Count; } public GroupFlags GetGroupFlags() { return m_groupFlags; } @@ -1924,8 +1901,8 @@ namespace Game.Groups public void BroadcastWorker(Action worker) { - for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next()) - worker(refe.GetSource()); + foreach (GroupReference groupRef in GetMembers()) + worker(groupRef.GetSource()); } public ObjectGuid GetRecentInstanceOwner(uint mapId) diff --git a/Source/Game/Groups/GroupInstanceReference.cs b/Source/Game/Groups/GroupInstanceReference.cs index fb99fd0d0..e93db2bf0 100644 --- a/Source/Game/Groups/GroupInstanceReference.cs +++ b/Source/Game/Groups/GroupInstanceReference.cs @@ -8,9 +8,7 @@ namespace Game.Groups { public class GroupInstanceReference : Reference { - ~GroupInstanceReference() { Unlink(); } - - public new GroupInstanceReference Next() { return (GroupInstanceReference)base.Next(); } + public void Dispose() { Unlink(); } public override void TargetObjectBuildLink() { @@ -18,8 +16,5 @@ namespace Game.Groups } } - class GroupInstanceRefManager : RefManager - { - public new GroupInstanceReference GetFirst() { return (GroupInstanceReference)base.GetFirst(); } - } + class GroupInstanceRefManager : RefManager { } } diff --git a/Source/Game/Groups/GroupReference.cs b/Source/Game/Groups/GroupReference.cs index a36493e92..625d8a1ff 100644 --- a/Source/Game/Groups/GroupReference.cs +++ b/Source/Game/Groups/GroupReference.cs @@ -13,14 +13,17 @@ namespace Game.Groups iSubGroup = 0; } - ~GroupReference() { Unlink(); } + public void Dispose() { Unlink(); } public override void TargetObjectBuildLink() { GetTarget().LinkMember(this); } - public new GroupReference Next() { return (GroupReference)base.Next(); } + public override void TargetObjectDestroyLink() + { + GetTarget()?.DelinkMember(GetSource().GetGUID()); + } public byte GetSubGroup() { return iSubGroup; } @@ -29,8 +32,5 @@ namespace Game.Groups byte iSubGroup; } - public class GroupRefManager : RefManager - { - public new GroupReference GetFirst() { return (GroupReference)base.GetFirst(); } - } + public class GroupRefManager : RefManager { } } diff --git a/Source/Game/Handlers/BattleGroundHandler.cs b/Source/Game/Handlers/BattleGroundHandler.cs index 9178194a8..5a10e5835 100644 --- a/Source/Game/Handlers/BattleGroundHandler.cs +++ b/Source/Game/Handlers/BattleGroundHandler.cs @@ -86,7 +86,7 @@ namespace Game GroupJoinBattlegroundResult err = GroupJoinBattlegroundResult.None; - Group grp = _player.GetGroup(); + Group group = _player.GetGroup(); Team getQueueTeam() { @@ -105,7 +105,7 @@ namespace Game BattlefieldStatusFailed battlefieldStatusFailed; // check queue conditions - if (grp == null) + if (group == null) { if (GetPlayer().IsUsingLfg()) { @@ -177,12 +177,12 @@ namespace Game } else { - if (grp.GetLeaderGUID() != GetPlayer().GetGUID()) + if (group.GetLeaderGUID() != GetPlayer().GetGUID()) return; ObjectGuid errorGuid; - err = grp.CanJoinBattlegroundQueue(bgTemplate, bgQueueTypeId, 0, bgTemplate.GetMaxPlayersPerTeam(), false, 0, out errorGuid); - isPremade = (grp.GetMembersCount() >= bgTemplate.GetMinPlayersPerTeam()); + err = group.CanJoinBattlegroundQueue(bgTemplate, bgQueueTypeId, 0, bgTemplate.GetMaxPlayersPerTeam(), false, 0, out errorGuid); + isPremade = (group.GetMembersCount() >= bgTemplate.GetMinPlayersPerTeam()); BattlegroundQueue bgQueue = Global.BattlegroundMgr.GetBattlegroundQueue(bgQueueTypeId); GroupQueueInfo ginfo = null; @@ -191,16 +191,13 @@ namespace Game if (err == 0) { Log.outDebug(LogFilter.Battleground, "Battleground: the following players are joining as group:"); - ginfo = bgQueue.AddGroup(GetPlayer(), grp, getQueueTeam(), bracketEntry, isPremade, 0, 0); + ginfo = bgQueue.AddGroup(GetPlayer(), group, getQueueTeam(), bracketEntry, isPremade, 0, 0); avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry.GetBracketId()); } - for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member == null) - continue; // this should never happen - + Player member = groupRef.GetSource(); if (err != 0) { BattlefieldStatusFailed battlefieldStatus; @@ -510,11 +507,11 @@ namespace Game if (bracketEntry == null) return; - Group grp = GetPlayer().GetGroup(); + Group group = GetPlayer().GetGroup(); // no group found, error - if (grp == null) + if (group == null) return; - if (grp.GetLeaderGUID() != GetPlayer().GetGUID()) + if (group.GetLeaderGUID() != GetPlayer().GetGUID()) return; uint ateamId = GetPlayer().GetArenaTeamId(packet.TeamSizeIndex); @@ -525,7 +522,7 @@ namespace Game // get the team rating for queuing uint arenaRating = at.GetRating(); - uint matchmakerRating = at.GetAverageMMR(grp); + uint matchmakerRating = at.GetAverageMMR(group); // the arenateam id must match for everyone in the group if (arenaRating <= 0) @@ -537,21 +534,18 @@ namespace Game GroupQueueInfo ginfo = null; ObjectGuid errorGuid; - var err = grp.CanJoinBattlegroundQueue(bgTemplate, bgQueueTypeId, (uint)arenatype, (uint)arenatype, true, packet.TeamSizeIndex, out errorGuid); + var err = group.CanJoinBattlegroundQueue(bgTemplate, bgQueueTypeId, (uint)arenatype, (uint)arenatype, true, packet.TeamSizeIndex, out errorGuid); if (err == 0) { Log.outDebug(LogFilter.Battleground, "Battleground: arena team id {0}, leader {1} queued with matchmaker rating {2} for type {3}", GetPlayer().GetArenaTeamId(packet.TeamSizeIndex), GetPlayer().GetName(), matchmakerRating, arenatype); - ginfo = bgQueue.AddGroup(GetPlayer(), grp, _player.GetTeam(), bracketEntry, false, arenaRating, matchmakerRating, ateamId); + ginfo = bgQueue.AddGroup(GetPlayer(), group, _player.GetTeam(), bracketEntry, false, arenaRating, matchmakerRating, ateamId); avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry.GetBracketId()); } - for (GroupReference refe = grp.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member == null) - continue; - + Player member = groupRef.GetSource(); if (err != 0) { BattlefieldStatusFailed battlefieldStatus; diff --git a/Source/Game/Handlers/GroupHandler.cs b/Source/Game/Handlers/GroupHandler.cs index 9f5442f7c..f073e7937 100644 --- a/Source/Game/Handlers/GroupHandler.cs +++ b/Source/Game/Handlers/GroupHandler.cs @@ -158,7 +158,7 @@ namespace Game if (group == null) return; - if (packet.PartyIndex != 0 && group.GetGroupCategory() != (GroupCategory)packet.PartyIndex) + if (packet.PartyIndex.HasValue && group.GetGroupCategory() != (GroupCategory)packet.PartyIndex) return; if (packet.Accept) @@ -702,9 +702,9 @@ namespace Game broadcastPingUnit.SpellOverrideNameID = pingUnit.SpellOverrideNameID; broadcastPingUnit.Write(); - for (GroupReference itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = itr.GetSource(); + Player member = groupRef.GetSource(); if (_player == member || !_player.IsInMap(member)) continue; @@ -732,9 +732,9 @@ namespace Game broadcastPingWorldPoint.PingDuration = pingWorldPoint.PingDuration; broadcastPingWorldPoint.Write(); - for (GroupReference itr = group.GetFirstMember(); itr != null; itr = itr.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = itr.GetSource(); + Player member = groupRef.GetSource(); if (_player == member || !_player.IsInMap(member)) continue; diff --git a/Source/Game/Handlers/LFGHandler.cs b/Source/Game/Handlers/LFGHandler.cs index 5e90e10e7..03eae59dd 100644 --- a/Source/Game/Handlers/LFGHandler.cs +++ b/Source/Game/Handlers/LFGHandler.cs @@ -205,12 +205,9 @@ namespace Game LfgPartyInfo lfgPartyInfo = new(); // Get the Locked dungeons of the other party members - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player plrg = refe.GetSource(); - if (plrg == null) - continue; - + Player plrg = groupRef.GetSource(); ObjectGuid pguid = plrg.GetGUID(); if (pguid == guid) continue; diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index cf81ba4f7..86d473eee 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -99,12 +99,9 @@ namespace Game Group group = player.GetGroup(); List playersNear = new(); - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player member = refe.GetSource(); - if (member == null) - continue; - + Player member = groupRef.GetSource(); if (!loot.HasAllowedLooter(member.GetGUID())) continue; diff --git a/Source/Game/Handlers/QuestHandler.cs b/Source/Game/Handlers/QuestHandler.cs index 532590525..d92e1d68f 100644 --- a/Source/Game/Handlers/QuestHandler.cs +++ b/Source/Game/Handlers/QuestHandler.cs @@ -126,11 +126,10 @@ namespace Game var group = _player.GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - - if (player == null || player == _player || !player.IsInMap(_player)) // not self and in same map + Player player = groupRef.GetSource(); + if (player == _player || !player.IsInMap(_player)) // not self and in same map continue; if (player.CanTakeQuest(quest, true)) @@ -551,11 +550,10 @@ namespace Game return; } - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player receiver = refe.GetSource(); - - if (receiver == null || receiver == sender) + Player receiver = groupRef.GetSource(); + if (receiver == sender) continue; if (!receiver.GetPlayerSharingQuest().IsEmpty()) diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index 3ecb0d502..b02e82657 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -936,12 +936,11 @@ namespace Game.Loots if (loot_type == LootType.Corpse) roundRobinPlayer = lootOwner.GetGUID(); - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player player = refe.GetSource(); - if (player != null) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter - if (player.IsAtGroupRewardDistance(lootOwner)) - FillNotNormalLootFor(player); + Player player = groupRef.GetSource();// should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter + if (player.IsAtGroupRewardDistance(lootOwner)) + FillNotNormalLootFor(player); } foreach (LootItem item in items) diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index e5843f7da..927c6d8f8 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -3,7 +3,6 @@ using Framework.Constants; using Framework.Database; -using Framework.Dynamic; using Game.BattleGrounds; using Game.Collision; using Game.DataStorage; @@ -15,7 +14,6 @@ using Game.Scenarios; using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Numerics; @@ -62,7 +60,7 @@ namespace Game.Maps _worldStateValues = Global.WorldStateMgr.GetInitialWorldStatesForMap(this); } - public void Dispose() + public virtual void Dispose() { // Delete all waiting spawns // This doesn't delete from database. @@ -389,7 +387,6 @@ namespace Game.Maps } } - public void AddInfiniteAOIVignette(VignetteData vignette) { _infiniteAOIVignettes.Add(vignette); @@ -4905,10 +4902,14 @@ namespace Game.Maps } } - ~InstanceMap() + public override void Dispose() { if (i_instanceLock != null) i_instanceLock.SetInUse(false); + + i_owningGroupRef.Dispose(); + + base.Dispose(); } public override void InitVisibilityDistance() @@ -5339,6 +5340,18 @@ namespace Game.Maps InitVisibilityDistance(); } + public override void Dispose() + { + if (m_bg != null) + { + //unlink to prevent crash, always unlink all pointer reference before destruction + m_bg.SetBgMap(null); + m_bg.Dispose(); + } + + base.Dispose(); + } + public override void InitVisibilityDistance() { m_VisibleDistance = IsBattleArena() ? Global.WorldMgr.GetMaxVisibleDistanceInArenas() : Global.WorldMgr.GetMaxVisibleDistanceInBG(); diff --git a/Source/Game/OutdoorPVP/OutdoorPvP.cs b/Source/Game/OutdoorPVP/OutdoorPvP.cs index c12706095..84126057c 100644 --- a/Source/Game/OutdoorPVP/OutdoorPvP.cs +++ b/Source/Game/OutdoorPVP/OutdoorPvP.cs @@ -61,12 +61,9 @@ namespace Game.PvP Group group = killer.GetGroup(); if (group != null) { - for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next()) + foreach (GroupReference groupRef in group.GetMembers()) { - Player groupGuy = refe.GetSource(); - - if (groupGuy == null) - continue; + Player groupGuy = groupRef.GetSource(); // skip if too far away if (!groupGuy.IsAtGroupRewardDistance(killed))