Core/Phasing: Fixed stack overflow happening when modifying vehicle passenger phases (looping player->vehicle->passenger(player)->vehicle...)

Closes #27599
This commit is contained in:
Shauren
2022-01-12 15:09:36 +01:00
parent 6f8d6f5c9b
commit 6ca301e863
2 changed files with 92 additions and 39 deletions

View File

@@ -30,6 +30,8 @@
#include "Player.h"
#include "SpellAuraEffects.h"
#include "Vehicle.h"
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
#include <sstream>
namespace
@@ -49,33 +51,49 @@ inline PhaseFlags GetPhaseFlags(uint32 phaseId)
return PhaseFlags::None;
}
}
template<typename Func>
inline void ForAllControlled(Unit* unit, Func&& func)
class PhasingHandler::ControlledUnitVisitor
{
for (Unit* controlled : unit->m_Controlled)
if (controlled->GetTypeId() != TYPEID_PLAYER
&& !controlled->GetVehicle()) // Player inside nested vehicle should not phase the root vehicle and its accessories (only direct root vehicle control does)
func(controlled);
public:
explicit ControlledUnitVisitor(WorldObject* owner)
{
_visited.insert(owner);
}
for (ObjectGuid summonGuid : unit->m_SummonSlot)
if (!summonGuid.IsEmpty())
if (Creature* summon = unit->GetMap()->GetCreature(summonGuid))
func(summon);
template<typename Func>
inline void VisitControlledOf(Unit* unit, Func&& func)
{
for (Unit* controlled : unit->m_Controlled)
if (controlled->GetTypeId() != TYPEID_PLAYER
&& !controlled->GetVehicle()) // Player inside nested vehicle should not phase the root vehicle and its accessories (only direct root vehicle control does)
if (_visited.insert(controlled).second)
func(controlled);
if (Vehicle const* vehicle = unit->GetVehicleKit())
for (auto seat = vehicle->Seats.begin(); seat != vehicle->Seats.end(); ++seat)
if (Unit* passenger = ObjectAccessor::GetUnit(*unit, seat->second.Passenger.Guid))
func(passenger);
}
}
for (ObjectGuid summonGuid : unit->m_SummonSlot)
if (!summonGuid.IsEmpty())
if (Creature* summon = unit->GetMap()->GetCreature(summonGuid))
if (_visited.insert(summon).second)
func(summon);
if (Vehicle const* vehicle = unit->GetVehicleKit())
for (auto seat = vehicle->Seats.begin(); seat != vehicle->Seats.end(); ++seat)
if (Unit* passenger = ObjectAccessor::GetUnit(*unit, seat->second.Passenger.Guid); passenger && passenger != unit)
if (_visited.insert(passenger).second)
func(passenger);
}
private:
boost::container::flat_set<WorldObject*, std::less<WorldObject*>, boost::container::small_vector<WorldObject*, 8>> _visited;
};
void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, bool updateVisibility)
{
AddPhase(object, phaseId, object->GetGUID(), updateVisibility);
ControlledUnitVisitor visitor(object);
AddPhase(object, phaseId, object->GetGUID(), updateVisibility, visitor);
}
void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid const& personalGuid, bool updateVisibility)
void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid const& personalGuid, bool updateVisibility, ControlledUnitVisitor& visitor)
{
bool changed = object->GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr);
@@ -85,9 +103,9 @@ void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid co
if (Unit* unit = object->ToUnit())
{
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
AddPhase(controlled, phaseId, personalGuid, updateVisibility);
AddPhase(controlled, phaseId, personalGuid, updateVisibility, visitor);
});
unit->RemoveNotOwnSingleTargetAuras(true);
}
@@ -96,15 +114,21 @@ void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid co
}
void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updateVisibility)
{
ControlledUnitVisitor visitor(object);
RemovePhase(object, phaseId, updateVisibility, visitor);
}
void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updateVisibility, ControlledUnitVisitor& visitor)
{
bool changed = object->GetPhaseShift().RemovePhase(phaseId).Erased;
if (Unit* unit = object->ToUnit())
{
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
RemovePhase(controlled, phaseId, updateVisibility);
RemovePhase(controlled, phaseId, updateVisibility, visitor);
});
unit->RemoveNotOwnSingleTargetAuras(true);
}
@@ -113,16 +137,17 @@ void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updat
}
void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, bool updateVisibility)
{
AddPhaseGroup(object, phaseGroupId, object->GetGUID(), updateVisibility);
}
void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, ObjectGuid const& personalGuid, bool updateVisibility)
{
std::vector<uint32> const* phasesInGroup = sDB2Manager.GetPhasesForGroup(phaseGroupId);
if (!phasesInGroup)
return;
ControlledUnitVisitor visitor(object);
AddPhaseGroup(object, phasesInGroup, object->GetGUID(), updateVisibility, visitor);
}
void PhasingHandler::AddPhaseGroup(WorldObject* object, std::vector<uint32> const* phasesInGroup, ObjectGuid const& personalGuid, bool updateVisibility, ControlledUnitVisitor& visitor)
{
bool changed = false;
for (uint32 phaseId : *phasesInGroup)
changed = object->GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), nullptr) || changed;
@@ -133,9 +158,9 @@ void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, Obj
if (Unit* unit = object->ToUnit())
{
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
AddPhaseGroup(controlled, phaseGroupId, personalGuid, updateVisibility);
AddPhaseGroup(controlled, phasesInGroup, personalGuid, updateVisibility, visitor);
});
unit->RemoveNotOwnSingleTargetAuras(true);
}
@@ -149,6 +174,12 @@ void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId,
if (!phasesInGroup)
return;
ControlledUnitVisitor visitor(object);
RemovePhaseGroup(object, phasesInGroup, updateVisibility, visitor);
}
void PhasingHandler::RemovePhaseGroup(WorldObject* object, std::vector<uint32> const* phasesInGroup, bool updateVisibility, ControlledUnitVisitor& visitor)
{
bool changed = false;
for (uint32 phaseId : *phasesInGroup)
changed = object->GetPhaseShift().RemovePhase(phaseId).Erased || changed;
@@ -156,9 +187,9 @@ void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId,
if (Unit* unit = object->ToUnit())
{
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
RemovePhaseGroup(controlled, phaseGroupId, updateVisibility);
RemovePhaseGroup(controlled, phasesInGroup, updateVisibility, visitor);
});
unit->RemoveNotOwnSingleTargetAuras(true);
}
@@ -167,6 +198,12 @@ void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId,
}
void PhasingHandler::AddVisibleMapId(WorldObject* object, uint32 visibleMapId)
{
ControlledUnitVisitor visitor(object);
AddVisibleMapId(object, visibleMapId, visitor);
}
void PhasingHandler::AddVisibleMapId(WorldObject* object, uint32 visibleMapId, ControlledUnitVisitor& visitor)
{
TerrainSwapInfo const* terrainSwapInfo = sObjectMgr->GetTerrainSwapInfo(visibleMapId);
bool changed = object->GetPhaseShift().AddVisibleMapId(visibleMapId, terrainSwapInfo);
@@ -176,9 +213,9 @@ void PhasingHandler::AddVisibleMapId(WorldObject* object, uint32 visibleMapId)
if (Unit* unit = object->ToUnit())
{
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
AddVisibleMapId(controlled, visibleMapId);
AddVisibleMapId(controlled, visibleMapId, visitor);
});
}
@@ -186,6 +223,12 @@ void PhasingHandler::AddVisibleMapId(WorldObject* object, uint32 visibleMapId)
}
void PhasingHandler::RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId)
{
ControlledUnitVisitor visitor(object);
RemoveVisibleMapId(object, visibleMapId, visitor);
}
void PhasingHandler::RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId, ControlledUnitVisitor& visitor)
{
TerrainSwapInfo const* terrainSwapInfo = sObjectMgr->GetTerrainSwapInfo(visibleMapId);
bool changed = object->GetPhaseShift().RemoveVisibleMapId(visibleMapId).Erased;
@@ -195,9 +238,9 @@ void PhasingHandler::RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId
if (Unit* unit = object->ToUnit())
{
ForAllControlled(unit, [&](Unit* controlled)
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
RemoveVisibleMapId(controlled, visibleMapId);
RemoveVisibleMapId(controlled, visibleMapId, visitor);
});
}
@@ -299,7 +342,8 @@ void PhasingHandler::OnAreaChange(WorldObject* object)
if (changed)
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
ControlledUnitVisitor visitor(unit);
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
InheritPhaseShift(controlled, unit);
});
@@ -418,7 +462,8 @@ void PhasingHandler::OnConditionChange(WorldObject* object)
if (changed)
unit->OnPhaseChange();
ForAllControlled(unit, [&](Unit* controlled)
ControlledUnitVisitor visitor(unit);
visitor.VisitControlledOf(unit, [&](Unit* controlled)
{
InheritPhaseShift(controlled, unit);
});

View File

@@ -20,6 +20,7 @@
#include "Define.h"
#include <string>
#include <vector>
class ChatHandler;
class Map;
@@ -74,8 +75,15 @@ public:
static bool IsPersonalPhase(uint32 phaseId);
private:
static void AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid const& personalGuid, bool updateVisibility);
static void AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, ObjectGuid const& personalGuid, bool updateVisibility);
class ControlledUnitVisitor;
friend ControlledUnitVisitor;
static void AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid const& personalGuid, bool updateVisibility, ControlledUnitVisitor& visitor);
static void RemovePhase(WorldObject* object, uint32 phaseId, bool updateVisibility, ControlledUnitVisitor& visitor);
static void AddPhaseGroup(WorldObject* object, std::vector<uint32> const* phasesInGroup, ObjectGuid const& personalGuid, bool updateVisibility, ControlledUnitVisitor& visitor);
static void RemovePhaseGroup(WorldObject* object, std::vector<uint32> const* phasesInGroup, bool updateVisibility, ControlledUnitVisitor& visitor);
static void AddVisibleMapId(WorldObject* object, uint32 visibleMapId, ControlledUnitVisitor& visitor);
static void RemoveVisibleMapId(WorldObject* object, uint32 visibleMapId, ControlledUnitVisitor& visitor);
static void UpdateVisibilityIfNeeded(WorldObject* object, bool updateVisibility, bool changed);
};