From 40f55bb0f3fd089aede2d4037990977f3ffaea2e Mon Sep 17 00:00:00 2001 From: sogladev Date: Thu, 12 Mar 2026 10:59:16 +0100 Subject: [PATCH] refactor(Core/Movement): follower management of references (#25060) Co-authored-by: Shauren --- src/server/game/Entities/Unit/Unit.cpp | 10 ++---- src/server/game/Entities/Unit/Unit.h | 9 +++-- src/server/game/Movement/AbstractFollower.cpp | 10 ++++-- src/server/game/Movement/AbstractFollower.h | 11 ++---- src/server/game/Movement/FollowerRefMgr.h | 29 --------------- .../game/Movement/FollowerReference.cpp | 35 ------------------- src/server/game/Movement/FollowerReference.h | 33 ----------------- .../TargetedMovementGenerator.cpp | 24 ++++++------- .../TargetedMovementGenerator.h | 2 -- 9 files changed, 28 insertions(+), 135 deletions(-) delete mode 100644 src/server/game/Movement/FollowerRefMgr.h delete mode 100644 src/server/game/Movement/FollowerReference.cpp delete mode 100644 src/server/game/Movement/FollowerReference.h diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 9ee693b29..6030991e4 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -16,6 +16,7 @@ */ #include "Unit.h" +#include "AbstractFollower.h" #include "AreaDefines.h" #include "ArenaSpectator.h" #include "Battlefield.h" @@ -44,7 +45,6 @@ #include "MoveSpline.h" #include "MoveSplineInit.h" #include "MovementGenerator.h" -#include "AbstractFollower.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvP.h" @@ -5530,12 +5530,8 @@ void Unit::RemoveAreaAurasDueToLeaveWorld() void Unit::RemoveAllFollowers() { - while (auto* ref = m_FollowingRefMgr.getFirst()) - { - auto* source = ref->GetSource(); - ref->delink(); - source->SetTarget(nullptr); - } + while (!m_followingMe.empty()) + (*m_followingMe.begin())->SetTarget(nullptr); } void Unit::RemoveAllAuras() diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index cd0639da7..dd4d8efc6 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -20,8 +20,6 @@ #include "EnumFlag.h" #include "EventProcessor.h" -#include "FollowerRefMgr.h" -#include "FollowerReference.h" #include "HostileRefMgr.h" #include "ItemTemplate.h" #include "MotionMaster.h" @@ -74,6 +72,7 @@ class MotionTransport; class Vehicle; class TransportBase; class SpellCastTargets; +class AbstractFollower; typedef std::list UnitList; typedef std::list< std::pair > DispelChargesList; @@ -1881,9 +1880,9 @@ public: [[nodiscard]] bool IsInDisallowedMountForm() const; // Followers - void AddFollower(FollowerReference* ref) { m_FollowingRefMgr.insertFirst(ref); } + void FollowerAdded(AbstractFollower* f) { m_followingMe.insert(f); } + void FollowerRemoved(AbstractFollower* f) { m_followingMe.erase(f); } [[nodiscard]] virtual float GetFollowAngle() const { return static_cast(M_PI / 2); } - void RemoveFollower(FollowerReference* /*ref*/ ) { /* nothing to do yet */ } void RemoveAllFollowers(); // Pets, guardians, minions... @@ -2229,7 +2228,7 @@ private: // Manage all Units that are threatened by us HostileRefMgr m_HostileRefMgr; - FollowerRefMgr m_FollowingRefMgr; + std::unordered_set m_followingMe; Unit* m_comboTarget; int8 m_comboPoints; diff --git a/src/server/game/Movement/AbstractFollower.cpp b/src/server/game/Movement/AbstractFollower.cpp index a2d60f393..edc67f582 100644 --- a/src/server/game/Movement/AbstractFollower.cpp +++ b/src/server/game/Movement/AbstractFollower.cpp @@ -20,9 +20,13 @@ void AbstractFollower::SetTarget(Unit* unit) { - if (unit == GetTarget()) + if (unit == _target) return; - i_target.link(unit, this); - _target = i_target.getTarget(); + if (_target) + _target->FollowerRemoved(this); + + _target = unit; + if (_target) + _target->FollowerAdded(this); } diff --git a/src/server/game/Movement/AbstractFollower.h b/src/server/game/Movement/AbstractFollower.h index ae4d2bd73..5ef5b1e00 100644 --- a/src/server/game/Movement/AbstractFollower.h +++ b/src/server/game/Movement/AbstractFollower.h @@ -18,8 +18,6 @@ #ifndef ACORE_ABSTRACTFOLLOWER_H #define ACORE_ABSTRACTFOLLOWER_H -#include "FollowerReference.h" - class Unit; class AbstractFollower @@ -29,15 +27,10 @@ public: virtual ~AbstractFollower() { SetTarget(nullptr); } void SetTarget(Unit* unit); - Unit* GetTarget() const { return _target; } - bool IsTargetValid() const { return i_target.isValid(); } - - virtual void stopFollowing() { } + [[nodiscard]] Unit* GetTarget() const { return _target; } +private: Unit* _target = nullptr; - -protected: - FollowerReference i_target; }; #endif diff --git a/src/server/game/Movement/FollowerRefMgr.h b/src/server/game/Movement/FollowerRefMgr.h deleted file mode 100644 index acece2dbb..000000000 --- a/src/server/game/Movement/FollowerRefMgr.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * 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 2 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 . - */ - -#ifndef _FOLLOWERREFMANAGER -#define _FOLLOWERREFMANAGER - -#include "RefMgr.h" - -class Unit; -class AbstractFollower; - -class FollowerRefMgr : public RefMgr -{ -}; -#endif diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp deleted file mode 100644 index 21e565726..000000000 --- a/src/server/game/Movement/FollowerReference.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * 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 2 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 . - */ - -#include "FollowerReference.h" -#include "AbstractFollower.h" -#include "Unit.h" - -void FollowerReference::targetObjectBuildLink() -{ - getTarget()->AddFollower(this); -} - -void FollowerReference::targetObjectDestroyLink() -{ - getTarget()->RemoveFollower(this); -} - -void FollowerReference::sourceObjectDestroyLink() -{ - GetSource()->stopFollowing(); -} diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h deleted file mode 100644 index c31f42850..000000000 --- a/src/server/game/Movement/FollowerReference.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * 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 2 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 . - */ - -#ifndef _FOLLOWERREFERENCE_H -#define _FOLLOWERREFERENCE_H - -#include "Reference.h" - -class AbstractFollower; -class Unit; - -class FollowerReference : public Reference -{ -protected: - void targetObjectBuildLink() override; - void targetObjectDestroyLink() override; - void sourceObjectDestroyLink() override; -}; -#endif diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 25b7ba5e4..a5976ec2f 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -85,7 +85,7 @@ void ChaseMovementGenerator::DistanceYourself(T* owner, float distance) i_path = std::make_unique(owner); float x, y, z; - i_target->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, i_target->GetAngle(owner)); + GetTarget()->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, GetTarget()->GetAngle(owner)); if (DispatchSplineToPosition(owner, x, y, z, false, false, 0.f, false, false)) { m_currentMode = CHASE_MODE_DISTANCING; @@ -110,7 +110,7 @@ bool ChaseMovementGenerator::DispatchSplineToPosition(T* owner, float x, floa // For pets, treat incomplete paths as failures to avoid clipping through geometry // Players and Player-controlled units have more erratic movement, skip failure - if (cOwner && (cOwner->IsPet() || cOwner->IsControlledByPlayer()) && !i_target.getTarget()->IsCharmedOwnedByPlayerOrPlayer()) + if (cOwner && (cOwner->IsPet() || cOwner->IsControlledByPlayer()) && !GetTarget()->IsCharmedOwnedByPlayerOrPlayer()) if (pathType & PATHFIND_INCOMPLETE) pathFailed = true; @@ -118,7 +118,7 @@ bool ChaseMovementGenerator::DispatchSplineToPosition(T* owner, float x, floa { if (cOwner) { - cOwner->SetCannotReachTarget(i_target.getTarget()->GetGUID()); + cOwner->SetCannotReachTarget(GetTarget()->GetGUID()); if (cOwner->IsPet() || cOwner->IsControlledByPlayer()) cOwner->AttackStop(); @@ -142,7 +142,7 @@ bool ChaseMovementGenerator::DispatchSplineToPosition(T* owner, float x, floa Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->GetPath()); if (target) - init.SetFacing(i_target.getTarget()); + init.SetFacing(GetTarget()); init.SetWalk(walk); init.Launch(); @@ -152,7 +152,7 @@ bool ChaseMovementGenerator::DispatchSplineToPosition(T* owner, float x, floa template bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) { - if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget())) + if (!GetTarget() || !GetTarget()->IsInWorld() || !owner->IsInMap(GetTarget())) return false; if (!owner || !owner->IsAlive()) @@ -194,11 +194,11 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) bool forceDest = //(cOwner && (cOwner->isWorldBoss() || cOwner->IsDungeonBoss())) || // force for all bosses, even not in instances - (i_target->IsPlayer() && i_target->ToPlayer()->IsGameMaster()) || // for .npc follow + (GetTarget()->IsPlayer() && GetTarget()->ToPlayer()->IsGameMaster()) || // for .npc follow (owner->CanFly()) ; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to - Unit* target = i_target.getTarget(); + Unit* target = GetTarget(); bool mutualChase = IsMutualChase(owner, target); bool const mutualTarget = target->GetVictim() == owner; @@ -414,7 +414,7 @@ void ChaseMovementGenerator::MovementInform(T* owner) { // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle if (CreatureAI* AI = owner->ToCreature()->AI()) - AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + AI->MovementInform(CHASE_MOTION_TYPE, GetTarget()->GetGUID().GetCounter()); break; } case CHASE_MODE_DISTANCING: @@ -552,14 +552,14 @@ bool FollowMovementGenerator::PositionOkay(Unit* target, bool isPlayerPet, bo template bool FollowMovementGenerator::DoUpdate(T* owner, uint32 time_diff) { - if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget())) + if (!GetTarget() || !GetTarget()->IsInWorld() || !owner->IsInMap(GetTarget())) return false; if (!owner || !owner->IsAlive()) return false; Creature* cOwner = owner->ToCreature(); - Unit* target = i_target.getTarget(); + Unit* target = GetTarget(); // the owner might be unable to move (rooted or casting), or we have lost the target, pause movement if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || (cOwner && owner->ToCreature()->IsMovementPreventedByCasting())) @@ -580,7 +580,7 @@ bool FollowMovementGenerator::DoUpdate(T* owner, uint32 time_diff) bool forceDest = (followingMaster) || // allow pets following their master to cheat while generating paths - (i_target->IsPlayer() && i_target->ToPlayer()->IsGameMaster()) // for .npc follow + (GetTarget()->IsPlayer() && GetTarget()->ToPlayer()->IsGameMaster()) // for .npc follow ; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to bool targetIsMoving = false; @@ -725,7 +725,7 @@ void FollowMovementGenerator::MovementInform(T* owner) // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle if (CreatureAI* AI = owner->ToCreature()->AI()) - AI->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + AI->MovementInform(FOLLOW_MOTION_TYPE, GetTarget()->GetGUID().GetCounter()); } //-----------------------------------------------// diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h index c8485e130..6fe1695ad 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h @@ -25,8 +25,6 @@ #include "Timer.h" #include "Unit.h" -using TargetedMovementGeneratorBase = AbstractFollower; - enum ChaseMovementMode { CHASE_MODE_NORMAL, // chasing target