From 92a5c855b9ac9f7245c399a8b81b1cae3dd091fc Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 25 Feb 2022 17:12:42 -0500 Subject: [PATCH] Core/Phasing: Fixed stack overflow happening when modifying vehicle passenger phases (looping player->vehicle->passenger(player)->vehicle...) Port From (https://github.com/TrinityCore/TrinityCore/commit/6ca301e8637bc71ff6fb89180bda6a3a8f4db205) --- Source/Game/Phasing/PhasingHandler.cs | 156 ++++++++++++++++++++------ 1 file changed, 122 insertions(+), 34 deletions(-) diff --git a/Source/Game/Phasing/PhasingHandler.cs b/Source/Game/Phasing/PhasingHandler.cs index 5e5914d61..45691a907 100644 --- a/Source/Game/Phasing/PhasingHandler.cs +++ b/Source/Game/Phasing/PhasingHandler.cs @@ -1,15 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Text; +/* + * 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.Constants; +using Game.Chat; +using Game.Conditions; using Game.DataStorage; using Game.Entities; -using Framework.Constants; -using Game.Conditions; -using Game.Spells; -using System.Linq; -using Game.Networking.Packets; -using Game.Chat; using Game.Maps; +using Game.Networking.Packets; +using Game.Spells; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; namespace Game { @@ -66,10 +83,11 @@ namespace Game public static void AddPhase(WorldObject obj, uint phaseId, bool updateVisibility) { - AddPhase(obj, phaseId, obj.GetGUID(), updateVisibility); + ControlledUnitVisitor visitor = new(obj); + AddPhase(obj, phaseId, obj.GetGUID(), updateVisibility, visitor); } - - static void AddPhase(WorldObject obj, uint phaseId, ObjectGuid personalGuid, bool updateVisibility) + + static void AddPhase(WorldObject obj, uint phaseId, ObjectGuid personalGuid, bool updateVisibility, ControlledUnitVisitor visitor) { bool changed = obj.GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), null); @@ -80,9 +98,9 @@ namespace Game if (unit) { unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - AddPhase(controlled, phaseId, personalGuid, updateVisibility); + AddPhase(controlled, phaseId, personalGuid, updateVisibility, visitor); }); unit.RemoveNotOwnSingleTargetAuras(true); } @@ -91,6 +109,12 @@ namespace Game } public static void RemovePhase(WorldObject obj, uint phaseId, bool updateVisibility) + { + ControlledUnitVisitor visitor = new(obj); + RemovePhase(obj, phaseId, updateVisibility, visitor); + } + + static void RemovePhase(WorldObject obj, uint phaseId, bool updateVisibility, ControlledUnitVisitor visitor) { bool changed = obj.GetPhaseShift().RemovePhase(phaseId); @@ -98,9 +122,9 @@ namespace Game if (unit) { unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - RemovePhase(controlled, phaseId, updateVisibility); + RemovePhase(controlled, phaseId, updateVisibility, visitor); }); unit.RemoveNotOwnSingleTargetAuras(true); } @@ -109,16 +133,16 @@ namespace Game } public static void AddPhaseGroup(WorldObject obj, uint phaseGroupId, bool updateVisibility) - { - AddPhaseGroup(obj, phaseGroupId, obj.GetGUID(), updateVisibility); - } - - static void AddPhaseGroup(WorldObject obj, uint phaseGroupId, ObjectGuid personalGuid, bool updateVisibility) { var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup(phaseGroupId); if (phasesInGroup.Empty()) return; + ControlledUnitVisitor visitor = new(obj); + AddPhaseGroup(obj, phasesInGroup, obj.GetGUID(), updateVisibility, visitor); + } + static void AddPhaseGroup(WorldObject obj, List phasesInGroup, ObjectGuid personalGuid, bool updateVisibility, ControlledUnitVisitor visitor) + { bool changed = false; foreach (uint phaseId in phasesInGroup) changed = obj.GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), null) || changed; @@ -130,9 +154,9 @@ namespace Game if (unit) { unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - AddPhaseGroup(controlled, phaseGroupId, personalGuid, updateVisibility); + AddPhaseGroup(controlled, phasesInGroup, personalGuid, updateVisibility, visitor); }); unit.RemoveNotOwnSingleTargetAuras(true); } @@ -146,6 +170,12 @@ namespace Game if (phasesInGroup.Empty()) return; + ControlledUnitVisitor visitor = new(obj); + RemovePhaseGroup(obj, phasesInGroup, updateVisibility, visitor); + } + + static void RemovePhaseGroup(WorldObject obj, List phasesInGroup, bool updateVisibility, ControlledUnitVisitor visitor) + { bool changed = false; foreach (uint phaseId in phasesInGroup) changed = obj.GetPhaseShift().RemovePhase(phaseId) || changed; @@ -154,9 +184,9 @@ namespace Game if (unit) { unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - RemovePhaseGroup(controlled, phaseGroupId, updateVisibility); + RemovePhaseGroup(controlled, phasesInGroup, updateVisibility, visitor); }); unit.RemoveNotOwnSingleTargetAuras(true); } @@ -165,19 +195,25 @@ namespace Game } public static void AddVisibleMapId(WorldObject obj, uint visibleMapId) + { + ControlledUnitVisitor visitor = new(obj); + AddVisibleMapId(obj, visibleMapId, visitor); + } + + static void AddVisibleMapId(WorldObject obj, uint visibleMapId, ControlledUnitVisitor visitor) { TerrainSwapInfo terrainSwapInfo = Global.ObjectMgr.GetTerrainSwapInfo(visibleMapId); bool changed = obj.GetPhaseShift().AddVisibleMapId(visibleMapId, terrainSwapInfo); - foreach (uint uiMapPhaseId in terrainSwapInfo.UiMapPhaseIDs) - changed = obj.GetPhaseShift().AddUiMapPhaseId(uiMapPhaseId ) || changed; + foreach (uint uiMapPhaseId in terrainSwapInfo.UiMapPhaseIDs) + changed = obj.GetPhaseShift().AddUiMapPhaseId(uiMapPhaseId) || changed; Unit unit = obj.ToUnit(); if (unit) { - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - AddVisibleMapId(controlled, visibleMapId); + AddVisibleMapId(controlled, visibleMapId, visitor); }); } @@ -185,6 +221,12 @@ namespace Game } public static void RemoveVisibleMapId(WorldObject obj, uint visibleMapId) + { + ControlledUnitVisitor visitor = new(obj); + RemoveVisibleMapId(obj, visibleMapId, visitor); + } + + static void RemoveVisibleMapId(WorldObject obj, uint visibleMapId, ControlledUnitVisitor visitor) { TerrainSwapInfo terrainSwapInfo = Global.ObjectMgr.GetTerrainSwapInfo(visibleMapId); bool changed = obj.GetPhaseShift().RemoveVisibleMapId(visibleMapId); @@ -195,9 +237,9 @@ namespace Game Unit unit = obj.ToUnit(); if (unit) { - ForAllControlled(unit, controlled => + visitor.VisitControlledOf(unit, controlled => { - RemoveVisibleMapId(controlled, visibleMapId); + RemoveVisibleMapId(controlled, visibleMapId, visitor); }); } @@ -300,7 +342,8 @@ namespace Game if (changed) unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + ControlledUnitVisitor visitor = new(unit); + visitor.VisitControlledOf(unit, controlled => { InheritPhaseShift(controlled, unit); }); @@ -411,7 +454,8 @@ namespace Game if (changed) unit.OnPhaseChange(); - ForAllControlled(unit, controlled => + ControlledUnitVisitor visitor = new(unit); + visitor.VisitControlledOf(unit, controlled => { InheritPhaseShift(controlled, unit); }); @@ -493,7 +537,7 @@ namespace Game Cypher.Assert(phaseShift.HasPersonalPhase()); phaseShift.PersonalGuid = personalGuid; } - + public static void InitDbVisibleMapId(PhaseShift phaseShift, int visibleMapId) { phaseShift.VisibleMapIds.Clear(); @@ -637,4 +681,48 @@ namespace Game } } } + + class ControlledUnitVisitor + { + HashSet _visited = new(); + + public ControlledUnitVisitor(WorldObject owner) + { + _visited.Add(owner); + } + + public void VisitControlledOf(Unit unit, Action func) + { + foreach (Unit controlled in unit.m_Controlled) + { + // Player inside nested vehicle should not phase the root vehicle and its accessories (only direct root vehicle control does) + if (!controlled.IsPlayer() && controlled.GetVehicle() == null) + if (_visited.Add(controlled)) + func(controlled); + } + + foreach (ObjectGuid summonGuid in unit.m_SummonSlot) + { + if (!summonGuid.IsEmpty()) + { + Creature summon = unit.GetMap().GetCreature(summonGuid); + if (summon != null) + if (_visited.Add(summon)) + func(summon); + } + } + + Vehicle vehicle = unit.GetVehicleKit(); + if (vehicle != null) + { + foreach (var seatPair in vehicle.Seats) + { + Unit passenger = Global.ObjAccessor.GetUnit(unit, seatPair.Value.Passenger.Guid); + if (passenger != null && passenger != unit) + if (_visited.Add(passenger)) + func(passenger); + } + } + } + } }