diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs index b0d0dc06e..c90240438 100644 --- a/Source/Game/Entities/Player/Player.Map.cs +++ b/Source/Game/Entities/Player/Player.Map.cs @@ -584,44 +584,27 @@ namespace Game.Entities // non-instances are always valid Map map = GetMap(); - if (!map || !map.IsDungeon()) + InstanceMap instance = map?.ToInstanceMap(); + if (instance == null) return true; + Group group = GetGroup(); // raid instances require the player to be in a raid group to be valid if (map.IsRaid() && !WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreRaid) && (map.GetEntry().Expansion() >= (Expansion)WorldConfig.GetIntValue(WorldCfg.Expansion))) - if (!GetGroup() || !GetGroup().IsRaidGroup()) + if (group == null || group.IsRaidGroup()) return false; - Group group = GetGroup(); if (group) { // check if player's group is bound to this instance - InstanceBind bind = group.GetBoundInstance(map.GetDifficultyID(), map.GetId()); - if (bind == null || bind.save == null || bind.save.GetInstanceId() != map.GetInstanceId()) + if (group != instance.GetOwningGroup()) return false; - - var players = map.GetPlayers(); - if (!players.Empty()) - foreach (var otherPlayer in players) - { - if (otherPlayer.IsGameMaster()) - continue; - if (!otherPlayer.m_InstanceValid) // ignore players that currently have a homebind timer active - continue; - if (group != otherPlayer.GetGroup()) - return false; - } } else { // instance is invalid if we are not grouped and there are other players if (map.GetPlayersCountExceptGMs() > 1) return false; - - // check if the player is bound to this instance - InstanceBind bind = GetBoundInstance(map.GetId(), map.GetDifficultyID()); - if (bind == null || bind.save == null || bind.save.GetInstanceId() != map.GetInstanceId()) - return false; } return true; diff --git a/Source/Game/Groups/Group.cs b/Source/Game/Groups/Group.cs index aef36142c..333a00bf8 100644 --- a/Source/Game/Groups/Group.cs +++ b/Source/Game/Groups/Group.cs @@ -166,7 +166,9 @@ namespace Game.Groups DB.Characters.Execute(stmt); - ConvertLeaderInstancesToGroup(leader, this, false); + InstanceMap leaderInstance = leader.GetMap().ToInstanceMap(); + if (leaderInstance != null) + leaderInstance.TrySetOwningGroup(this); Cypher.Assert(AddMember(leader)); // If the leader can't be added to a new group because it appears full, something is clearly wrong. } @@ -691,9 +693,6 @@ namespace Game.Groups } } - // Copy the permanent binds from the new leader to the group - ConvertLeaderInstancesToGroup(newLeader, this, true); - // Update the group leader stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_GROUP_LEADER); @@ -721,47 +720,6 @@ namespace Game.Groups BroadcastPacket(groupNewLeader, true); } - public static void ConvertLeaderInstancesToGroup(Player player, Group group, bool switchLeader) - { - // copy all binds to the group, when changing leader it's assumed the character - // will not have any solo binds - foreach (var difficultyDic in player.m_boundInstances.Values) - { - foreach (var pair in difficultyDic) - { - if (!switchLeader || group.GetBoundInstance(pair.Value.save.GetDifficultyID(), pair.Key) == null) - if (pair.Value.extendState != 0) // not expired - group.BindToInstance(pair.Value.save, pair.Value.perm, false); - - // permanent binds are not removed - if (switchLeader && !pair.Value.perm) - { - player.UnbindInstance(pair, difficultyDic, false); - } - } - } - - // if group leader is in a non-raid dungeon map and nobody is actually bound to this map then the group can "take over" the instance - // (example: two-player group disbanded by disconnect where the player reconnects within 60 seconds and the group is reformed) - Map playerMap = player.GetMap(); - if (playerMap) - { - if (!switchLeader && playerMap.IsNonRaidDungeon()) - { - InstanceSave save = Global.InstanceSaveMgr.GetInstanceSave(playerMap.GetInstanceId()); - if (save != null) - { - if (save.GetGroupCount() == 0 && save.GetPlayerCount() == 0) - { - Log.outDebug(LogFilter.Maps, "Group.ConvertLeaderInstancesToGroup: Group for player {0} is taking over unbound instance map {1} with Id {2}", player.GetName(), playerMap.GetId(), playerMap.GetInstanceId()); - // if nobody is saved to this, then the save wasn't permanent - group.BindToInstance(save, false, false); - } - } - } - } - } - public void Disband(bool hideDestroy = false) { Global.ScriptMgr.OnGroupDisband(this); @@ -1585,45 +1543,9 @@ namespace Game.Groups return null; } - public InstanceBind BindToInstance(InstanceSave save, bool permanent, bool load = false) + public void LinkOwnedInstance(GroupInstanceReference refe) { - if (save == null || IsBGGroup() || IsBFGroup()) - return null; - - if (!m_boundInstances.ContainsKey(save.GetDifficultyID())) - m_boundInstances[save.GetDifficultyID()] = new Dictionary(); - - if (!m_boundInstances[save.GetDifficultyID()].ContainsKey(save.GetMapId())) - m_boundInstances[save.GetDifficultyID()][save.GetMapId()] = new InstanceBind(); - - InstanceBind bind = m_boundInstances[save.GetDifficultyID()][save.GetMapId()]; - if (!load && (bind.save == null || permanent != bind.perm || save != bind.save)) - { - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_GROUP_INSTANCE); - - stmt.AddValue(0, m_dbStoreId); - stmt.AddValue(1, save.GetInstanceId()); - stmt.AddValue(2, permanent); - - DB.Characters.Execute(stmt); - } - - if (bind.save != save) - { - if (bind.save != null) - bind.save.RemoveGroup(this); - save.AddGroup(this); - } - - bind.save = save; - bind.perm = permanent; - if (!load) - Log.outDebug(LogFilter.Maps, "Group.BindToInstance: Group ({0}, storage id: {1}) is now bound to map {2}, instance {3}, difficulty {4}", - GetGUID().ToString(), m_dbStoreId, save.GetMapId(), save.GetInstanceId(), save.GetDifficultyID()); - - m_boundInstances[save.GetDifficultyID()][save.GetMapId()] = bind; - - return bind; + m_ownedInstancesMgr.InsertLast(refe); } public void UnbindInstance(uint mapid, Difficulty difficulty, bool unload = false) @@ -2199,6 +2121,7 @@ namespace Game.Groups ObjectGuid m_masterLooterGuid; Dictionary> m_boundInstances = new(); Dictionary> m_recentInstances = new(); + GroupInstanceRefManager m_ownedInstancesMgr = new(); byte[] m_subGroupsCounts; ObjectGuid m_guid; uint m_dbStoreId; diff --git a/Source/Game/Groups/GroupInstanceReference.cs b/Source/Game/Groups/GroupInstanceReference.cs new file mode 100644 index 000000000..eafdbca21 --- /dev/null +++ b/Source/Game/Groups/GroupInstanceReference.cs @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012-2020 CypherCore + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 . + */ + +using Framework.Dynamic; +using Game.Maps; + +namespace Game.Groups +{ + public class GroupInstanceReference : Reference + { + ~GroupInstanceReference() { Unlink(); } + + public new GroupInstanceReference Next() { return (GroupInstanceReference)base.Next(); } + + public override void TargetObjectBuildLink() + { + GetTarget().LinkOwnedInstance(this); + } + } + + class GroupInstanceRefManager : RefManager + { + public new GroupInstanceReference GetFirst() { return (GroupInstanceReference)base.GetFirst(); } + } +} diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 1c5f48648..7851c956c 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -4914,7 +4914,7 @@ namespace Game.Maps if (i_instanceLock != null) { MapDb2Entries entries = new(GetEntry(), GetMapDifficulty()); - if (entries.IsInstanceIdBound() || IsRaid() || entries.MapDifficulty.IsRestoringDungeonState() || !i_owningGroupRef.isValid()) + if (entries.IsInstanceIdBound() || IsRaid() || entries.MapDifficulty.IsRestoringDungeonState() || !i_owningGroupRef.IsValid()) { InstanceLockData lockData = i_instanceLock.GetInstanceInitializationData(); i_data.SetCompletedEncountersMask(lockData.CompletedEncountersMask); @@ -4931,6 +4931,14 @@ namespace Game.Maps i_data.Create(); } + public Group GetOwningGroup() { return i_owningGroupRef.GetTarget(); } + + public void TrySetOwningGroup(Group group) + { + if (!i_owningGroupRef.IsValid()) + i_owningGroupRef.Link(group, this); + } + public bool Reset(InstanceResetMethod method) { // note: since the map may not be loaded when the instance needs to be reset @@ -5149,6 +5157,7 @@ namespace Game.Maps uint i_script_id; InstanceScenario i_scenario; InstanceLock i_instanceLock; + GroupInstanceReference i_owningGroupRef = new(); bool m_resetAfterUnload; bool m_unloadWhenEmpty; } diff --git a/Source/Game/Maps/MapManager.cs b/Source/Game/Maps/MapManager.cs index 525d68cb3..a887b7b29 100644 --- a/Source/Game/Maps/MapManager.cs +++ b/Source/Game/Maps/MapManager.cs @@ -91,6 +91,8 @@ namespace Game.Entities map.LoadRespawnTimes(); map.LoadCorpseData(); + if (group != null) + map.TrySetOwningGroup(group); map.CreateInstanceData(); map.SetInstanceScenario(Global.ScenarioMgr.CreateInstanceScenario(map, team));