fix(Core/Movement): Fix use-after-free in WaypointMovementGenerator (#25270)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: Shauren <shauren.trinity@gmail.com> Co-authored-by: ccrs <ccrs@users.noreply.github.com>
This commit is contained in:
@@ -879,10 +879,10 @@ void MotionMaster::MoveDistract(uint32 timer)
|
||||
|
||||
void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot)
|
||||
{
|
||||
bool const delayed = (_cleanFlag & MMCF_UPDATE);
|
||||
|
||||
while (MovementGenerator* curr = Impl[slot])
|
||||
{
|
||||
bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE));
|
||||
|
||||
// clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
|
||||
Impl[slot] = nullptr;
|
||||
while (!empty() && !top())
|
||||
|
||||
@@ -157,27 +157,31 @@ void WaypointMovementGenerator<Creature>::ProcessWaypointArrival(Creature* creat
|
||||
creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.EventId, creature, nullptr);
|
||||
}
|
||||
|
||||
creature->UpdateWaypointID(waypoint.Id);
|
||||
creature->UpdateCurrentWaypointInfo(waypoint.Id, i_path->Id);
|
||||
// scripts can invalidate current path, store what we need
|
||||
uint32 const waypointId = waypoint.Id;
|
||||
uint32 const pathId = i_path->Id;
|
||||
|
||||
creature->UpdateWaypointID(waypointId);
|
||||
creature->UpdateCurrentWaypointInfo(waypointId, pathId);
|
||||
|
||||
// Inform AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
{
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, waypoint.Id);
|
||||
AI->WaypointReached(waypoint.Id, i_path->Id);
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, waypointId);
|
||||
AI->WaypointReached(waypointId, pathId);
|
||||
}
|
||||
|
||||
if (Unit* owner = creature->GetCharmerOrOwner())
|
||||
{
|
||||
if (UnitAI* AI = owner->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypoint.Id);
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TempSummon* tempSummon = creature->ToTempSummon())
|
||||
if (Unit* owner2 = tempSummon->GetSummonerUnit())
|
||||
if (UnitAI* AI = owner2->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypoint.Id);
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId);
|
||||
}
|
||||
|
||||
// Path end notifications fire after WaypointReached so that m_path_id
|
||||
@@ -187,11 +191,11 @@ void WaypointMovementGenerator<Creature>::ProcessWaypointArrival(Creature* creat
|
||||
creature->UpdateCurrentWaypointInfo(0, 0);
|
||||
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->PathEndReached(i_path->Id);
|
||||
AI->PathEndReached(pathId);
|
||||
|
||||
// Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointPathEnded(waypoint.Id, i_path->Id);
|
||||
AI->WaypointPathEnded(waypointId, pathId);
|
||||
}
|
||||
|
||||
// All hooks called and infos updated. Time to increment the waypoint node id
|
||||
@@ -385,8 +389,15 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
|
||||
WaypointNode const& passedWp = i_path->Nodes.at(i_currentNode);
|
||||
|
||||
UpdateHomePosition(creature, passedWp);
|
||||
creature->UpdateWaypointID(passedWp.Id);
|
||||
creature->UpdateCurrentWaypointInfo(passedWp.Id, i_path->Id);
|
||||
|
||||
// Save data before AI callbacks — they can invalidate the reference
|
||||
uint32 const wpId = passedWp.Id;
|
||||
uint32 const wpPathId = i_path->Id;
|
||||
uint32 const wpDelay = passedWp.Delay;
|
||||
std::optional<float> const wpOrientation = passedWp.Orientation;
|
||||
|
||||
creature->UpdateWaypointID(wpId);
|
||||
creature->UpdateCurrentWaypointInfo(wpId, wpPathId);
|
||||
|
||||
if (passedWp.EventId && urand(0, 99) < passedWp.EventChance)
|
||||
{
|
||||
@@ -396,23 +407,24 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
|
||||
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
{
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, passedWp.Id);
|
||||
AI->WaypointReached(passedWp.Id, i_path->Id);
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, wpId);
|
||||
AI->WaypointReached(wpId, wpPathId);
|
||||
}
|
||||
|
||||
// Advance node
|
||||
i_currentNode = (i_currentNode + 1) % i_path->Nodes.size();
|
||||
if (i_path && !i_path->Nodes.empty())
|
||||
i_currentNode = (i_currentNode + 1) % i_path->Nodes.size();
|
||||
|
||||
// If this waypoint has a delay, stop the spline and pause
|
||||
if (passedWp.Delay > 0)
|
||||
if (wpDelay > 0)
|
||||
{
|
||||
creature->StopMoving();
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
_waypointDelay = passedWp.Delay;
|
||||
_waypointDelay = wpDelay;
|
||||
_waypointReached = true;
|
||||
_smoothSplineLaunched = false;
|
||||
if (passedWp.Orientation.has_value())
|
||||
creature->SetFacingTo(*passedWp.Orientation);
|
||||
if (wpOrientation.has_value())
|
||||
creature->SetFacingTo(*wpOrientation);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -423,15 +435,17 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
|
||||
if (!_repeating)
|
||||
{
|
||||
// Path ended
|
||||
uint32 const endWpId = i_path->Nodes.at(i_currentNode).Id;
|
||||
uint32 const endPathId = i_path->Id;
|
||||
_done = true;
|
||||
_smoothSplineLaunched = false;
|
||||
creature->UpdateCurrentWaypointInfo(0, 0);
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->PathEndReached(i_path->Id);
|
||||
AI->PathEndReached(endPathId);
|
||||
|
||||
// Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointPathEnded(i_path->Nodes.at(i_currentNode).Id, i_path->Id);
|
||||
AI->WaypointPathEnded(endWpId, endPathId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user