From b7e9688e963d45f316ac48ee88f6f2d5195a1ec5 Mon Sep 17 00:00:00 2001 From: Laizerox Date: Wed, 12 Feb 2025 14:34:00 +0100 Subject: [PATCH] Map: Optimize low priority objects to not update every time --- src/framework/Utilities/EventProcessor.h | 1 + src/game/Chat/Level3.cpp | 1 + src/game/Entities/Creature.cpp | 8 ++++ src/game/Entities/Creature.h | 2 + src/game/Entities/Object.cpp | 17 +++++++- src/game/Entities/Object.h | 11 +++++ src/game/Entities/Unit.cpp | 51 +++++++++++++++++++++- src/game/Entities/Unit.h | 5 +++ src/game/Maps/Map.cpp | 27 ++++++++---- src/game/Maps/Map.h | 2 + src/game/MotionGenerators/MotionMaster.cpp | 27 ++++++++++++ src/game/MotionGenerators/MotionMaster.h | 1 + src/game/Spells/Spell.cpp | 4 ++ src/game/Spells/SpellAuras.cpp | 10 +++++ src/game/Spells/SpellAuras.h | 2 + src/shared/Util/Timer.h | 11 ++--- 16 files changed, 163 insertions(+), 17 deletions(-) diff --git a/src/framework/Utilities/EventProcessor.h b/src/framework/Utilities/EventProcessor.h index 3f280081904..4078e6dceba 100644 --- a/src/framework/Utilities/EventProcessor.h +++ b/src/framework/Utilities/EventProcessor.h @@ -71,6 +71,7 @@ class EventProcessor void ModifyEventTime(BasicEvent* event, uint64 msTime); uint64 CalculateTime(uint64 t_offset) const; EventList& GetEvents() { return m_events; } + bool IsEmpty() const { return m_events.empty(); } protected: diff --git a/src/game/Chat/Level3.cpp b/src/game/Chat/Level3.cpp index 4d77c3040c9..a156b32baa5 100644 --- a/src/game/Chat/Level3.cpp +++ b/src/game/Chat/Level3.cpp @@ -4132,6 +4132,7 @@ bool ChatHandler::HandleNpcInfoCommand(char* /*args*/) PSendSysMessage("Spell Lists %s ID %u", spellList.Disabled ? "disabled" : "enabled", spellList.Id); PSendSysMessage("Combat Timer: %u Leashing disabled: %s", target->GetCombatManager().GetCombatTimer(), target->GetCombatManager().IsLeashingDisabled() ? "true" : "false"); + PSendSysMessage("Accumulated diff: %u NextUpdateTime: %u", target->GetAccumulatedUpdateDiff(), target->GetNextUpdateTime()); PSendSysMessage("Combat Script: %s", target->AI()->GetCombatScriptStatus() ? "true" : "false"); PSendSysMessage("Movementflags: %u", target->m_movementInfo.moveFlags); diff --git a/src/game/Entities/Creature.cpp b/src/game/Entities/Creature.cpp index e3a1b00eb55..3c69f4ce49c 100644 --- a/src/game/Entities/Creature.cpp +++ b/src/game/Entities/Creature.cpp @@ -3128,6 +3128,14 @@ void Creature::ResetSpellHitCounter() m_hitBySpells.clear(); } +uint32 Creature::GetNextUpdateTime() +{ + if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && m_deathState != ALIVE) + return 500; + + return WorldObject::GetNextUpdateTime(); +} + void Creature::Heartbeat() { Unit::Heartbeat(); diff --git a/src/game/Entities/Creature.h b/src/game/Entities/Creature.h index 96ab01580b9..92dbc5ce2ec 100644 --- a/src/game/Entities/Creature.h +++ b/src/game/Entities/Creature.h @@ -883,6 +883,8 @@ class Creature : public Unit void UnregisterHitBySpell(uint32 spellId); void ResetSpellHitCounter(); + uint32 GetNextUpdateTime() override; + HighGuid GetParentHigh() const override { return HIGHGUID_UNIT; } void Heartbeat() override; diff --git a/src/game/Entities/Object.cpp b/src/game/Entities/Object.cpp index f98d5360f62..7b6d1b63dd7 100644 --- a/src/game/Entities/Object.cpp +++ b/src/game/Entities/Object.cpp @@ -1370,7 +1370,7 @@ bool WorldObject::HasStringId(uint32 stringId) const WorldObject::WorldObject() : m_transport(nullptr), m_transportInfo(nullptr), m_isOnEventNotified(false), - m_visibilityData(this), m_currMap(nullptr), + m_visibilityData(this), m_nextUpdateTime(0), m_accumulatedUpdateDiff(0), m_currMap(nullptr), m_mapId(0), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_isActiveObject(false), m_debugFlags(0), m_destLocCounter(0), m_castCounter(0) { @@ -3334,6 +3334,21 @@ int32 WorldObject::CalculateSpellEffectValue(Unit const* target, SpellEntry cons return value; } +uint32 WorldObject::ShouldPerformObjectUpdate(uint32 const diff) +{ + // For objects that don't have next update time return diff immediately + if (!m_nextUpdateTime) + return diff; + + m_accumulatedUpdateDiff += diff; + + // Once accumulated time reaches and goes over update time lets use it + if (m_accumulatedUpdateDiff >= GetNextUpdateTime()) + return m_accumulatedUpdateDiff; + + return 0; +} + float Position::GetAngle(const float x, const float y) const { float dx = x - GetPositionX(); diff --git a/src/game/Entities/Object.h b/src/game/Entities/Object.h index 57081b0e4cf..f1da63c3b5e 100644 --- a/src/game/Entities/Object.h +++ b/src/game/Entities/Object.h @@ -1222,6 +1222,14 @@ class WorldObject : public Object VisibilityData const& GetVisibilityData() const { return m_visibilityData; } VisibilityData& GetVisibilityData() { return m_visibilityData; } + virtual uint32 GetNextUpdateTime() { return m_nextUpdateTime; } + virtual void SetNextUpdateTime(uint32 time) { m_nextUpdateTime = time; } + virtual void UpdateNextUpdateTime() {} + uint32 GetAccumulatedUpdateDiff() { return m_accumulatedUpdateDiff; } + void ResetAccumulatedUpdateDiff() { m_accumulatedUpdateDiff = 0; } + + virtual uint32 ShouldPerformObjectUpdate(uint32 const diff); + bool HaveDebugFlag(CMDebugFlags flag) const { return (uint64(m_debugFlags) & flag) != 0; } void SetDebugFlag(CMDebugFlags flag) { m_debugFlags |= uint64(flag); } void ClearDebugFlag(CMDebugFlags flag) { m_debugFlags &= ~(uint64(flag)); } @@ -1305,6 +1313,9 @@ class WorldObject : public Object VisibilityData m_visibilityData; + uint32 m_nextUpdateTime; + uint32 m_accumulatedUpdateDiff; + ShortTimeTracker m_heartBeatTimer; private: Map* m_currMap; // current object's Map location diff --git a/src/game/Entities/Unit.cpp b/src/game/Entities/Unit.cpp index ad04d613008..3a05c9212a3 100644 --- a/src/game/Entities/Unit.cpp +++ b/src/game/Entities/Unit.cpp @@ -267,7 +267,8 @@ Unit::Unit() : m_ignoreRangedTargets(false), m_auraUpdateMask(0), m_combatManager(this), - m_isMountOverriden(false), m_overridenMountId(0) + m_isMountOverriden(false), m_overridenMountId(0), + m_hasPeriodicAura(false) { m_objectType |= TYPEMASK_UNIT; m_objectTypeId = TYPEID_UNIT; @@ -5483,6 +5484,11 @@ bool Unit::AddSpellAuraHolder(SpellAuraHolder* holder) if (!holder->IsDeleted()) { holder->HandleSpellSpecificBoosts(true); + m_hasPeriodicAura = m_hasPeriodicAura || holder->HasPeriodicAura(); + if (m_hasPeriodicAura) + SetNextUpdateTime(1); + else + SetNextUpdateTime(0); SpellProcEventEntry const* procEntry = sSpellMgr.GetSpellProcEvent(aurSpellInfo->Id); if (aurSpellInfo->procFlags & PROC_FLAG_HEARTBEAT || (procEntry && procEntry->procFlags & PROC_FLAG_HEARTBEAT)) ++m_hasHeartbeatProcCounter; @@ -6107,6 +6113,9 @@ void Unit::RemoveSpellAuraHolder(SpellAuraHolder* holder, AuraRemoveMode mode) if (itr->second == holder) { m_spellAuraHolders.erase(itr); + m_hasPeriodicAura = HasPeriodicAura(); + if (!m_hasPeriodicAura) + SetNextUpdateTime(0); break; } } @@ -6418,6 +6427,19 @@ bool Unit::HasAuraTypeWithCaster(AuraType auratype, ObjectGuid caster) const return false; } +bool Unit::HasPeriodicAura() const +{ + for (auto holder : m_spellAuraHolders) + { + for (auto aura : holder.second->m_auras) + { + if (aura && aura->IsPeriodic()) + return true; + } + } + return false; +} + bool Unit::HasMechanicMaskOrDispelMaskAura(uint32 dispelMask, uint32 mechanicMask, Unit const* caster) const { Unit::SpellAuraHolderMap const& Auras = GetSpellAuraHolderMap(); @@ -13616,6 +13638,33 @@ uint32 Unit::GetModifierXpBasedOnDamageReceived(uint32 xp) return xp; } +void Unit::UpdateNextUpdateTime() +{ + // If we already have next update time don't reset it (movement mutation should do it) + if (m_nextUpdateTime) + return; + + if (!m_events.IsEmpty() || m_hasPeriodicAura) + SetNextUpdateTime(1); + // If motion type is idle and there is no nextUpdateTime force it + else if (GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) + SetNextUpdateTime(urand(500, 1000)); + // If motion type is random and there is no nextUpdateTime force it + else if (GetMotionMaster()->GetCurrentMovementGeneratorType() == RANDOM_MOTION_TYPE) + SetNextUpdateTime(urand(250, 500)); +} + +uint32 Unit::ShouldPerformObjectUpdate(uint32 const diff) +{ + if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + return diff; + + if (IsInCombat()) + return diff + m_accumulatedUpdateDiff; + + return WorldObject::ShouldPerformObjectUpdate(diff); +} + void Unit::OverrideMountDisplayId(uint32 newDisplayId) { if (newDisplayId) diff --git a/src/game/Entities/Unit.h b/src/game/Entities/Unit.h index 31206e6034e..d8fff858bdc 100644 --- a/src/game/Entities/Unit.h +++ b/src/game/Entities/Unit.h @@ -1756,6 +1756,7 @@ class Unit : public WorldObject } bool HasAuraOfDifficulty(uint32 spellId) const; bool HasAuraTypeWithCaster(AuraType auratype, ObjectGuid caster) const; + bool HasPeriodicAura() const; bool HasMechanicMaskOrDispelMaskAura(uint32 dispelMask, uint32 mechanicMask, Unit const* caster) const; bool HasNegativeAuraWithInterruptFlag(SpellAuraInterruptFlags flag) const; template @@ -2623,6 +2624,9 @@ class Unit : public WorldObject uint32 GetDamageDoneByOthers() { return m_damageByOthers; } uint32 GetModifierXpBasedOnDamageReceived(uint32 xp); + + void UpdateNextUpdateTime() override; + uint32 ShouldPerformObjectUpdate(uint32 const diff) override; void OverrideMountDisplayId(uint32 newDisplayId); @@ -2823,6 +2827,7 @@ class Unit : public WorldObject Position m_last_notified_position; BasicEvent* m_AINotifyEvent; ShortTimeTracker m_movesplineTimer; + bool m_hasPeriodicAura; Diminishing m_Diminishing; diff --git a/src/game/Maps/Map.cpp b/src/game/Maps/Map.cpp index 62a0c301d10..93833d21cb3 100644 --- a/src/game/Maps/Map.cpp +++ b/src/game/Maps/Map.cpp @@ -784,8 +784,6 @@ void Map::Update(const uint32& t_diff) localtime_r(&m_curTime, &m_curTimeTm); #endif - uint64 count = 0; - m_dyn_tree.update(t_diff); GetMessager().Execute(this); @@ -1099,12 +1097,7 @@ void Map::Update(const uint32& t_diff) } } - // update all objects - for (auto wObj : objToUpdate) - { - wObj->Update(t_diff); - ++count; - } + uint64 count = PerformObjectUpdate(t_diff, objToUpdate); #ifdef BUILD_METRICS meas.add_field("count", std::to_string(static_cast(count))); @@ -1147,6 +1140,24 @@ void Map::Update(const uint32& t_diff) sLog.outError("Map Id %u took %u miliseconds", GetId(), ms_int.count()); } +uint64 Map::PerformObjectUpdate(uint32 t_diff, WorldObjectUnSet& objToUpdate) +{ + uint64 count = 0; + // update all objects + for (WorldObject* object : objToUpdate) + { + if (uint32 accumulatedDiff = object->ShouldPerformObjectUpdate(t_diff)) + { + object->Update(accumulatedDiff); + object->ResetAccumulatedUpdateDiff(); + object->UpdateNextUpdateTime(); + + ++count; + } + } + return count; +} + void Map::Remove(Player* player, bool remove) { if (i_data) diff --git a/src/game/Maps/Map.h b/src/game/Maps/Map.h index 42a0acaf32e..433125f90d4 100644 --- a/src/game/Maps/Map.h +++ b/src/game/Maps/Map.h @@ -158,6 +158,8 @@ class Map : public GridRefManager void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor &gridVisitor, TypeContainerVisitor &worldVisitor); virtual void Update(const uint32&); + uint64 PerformObjectUpdate(uint32 t_diff, WorldObjectUnSet& objToUpdate); + void MessageBroadcast(Player const*, WorldPacket const&, bool to_self); void MessageBroadcast(WorldObject const*, WorldPacket const&); void MessageDistBroadcast(Player const*, WorldPacket const&, float dist, bool to_self, bool own_team_only = false); diff --git a/src/game/MotionGenerators/MotionMaster.cpp b/src/game/MotionGenerators/MotionMaster.cpp index a6013324b98..cb9364f1523 100644 --- a/src/game/MotionGenerators/MotionMaster.cpp +++ b/src/game/MotionGenerators/MotionMaster.cpp @@ -79,6 +79,8 @@ void MotionMaster::Initialize() auto creature = static_cast(m_owner); m_currentPathId = m_defaultPathId; MovementGenerator* movement = FactorySelector::selectMovementGenerator(creature); + // Initialize update timers + InitializeObjectUpdateTimer(movement ? movement->GetMovementGeneratorType() : IDLE_MOTION_TYPE); push(movement == nullptr ? &si_idleMovement : movement); top()->Initialize(*m_owner); @@ -800,3 +802,28 @@ bool MotionMaster::GetDestination(float& x, float& y, float& z) const z = dest.z; return true; } + +void MotionMaster::InitializeObjectUpdateTimer(const MovementGeneratorType type) +{ + if (m_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + { + if (m_owner->GetNextUpdateTime()) + m_owner->SetNextUpdateTime(0); + + return; + } + + switch (type) + { + case IDLE_MOTION_TYPE: + m_owner->SetNextUpdateTime(urand(500, 1000)); + return; + case RANDOM_MOTION_TYPE: + m_owner->SetNextUpdateTime(urand(250, 500)); + return; + default: + if (m_owner->GetNextUpdateTime()) + m_owner->SetNextUpdateTime(0); + return; + } +} diff --git a/src/game/MotionGenerators/MotionMaster.h b/src/game/MotionGenerators/MotionMaster.h index 98ed9af28f6..86a46010c1c 100644 --- a/src/game/MotionGenerators/MotionMaster.h +++ b/src/game/MotionGenerators/MotionMaster.h @@ -185,6 +185,7 @@ class MotionMaster : private std::stack private: void Mutate(MovementGenerator* m); // use Move* functions instead + void InitializeObjectUpdateTimer(const MovementGeneratorType type); void DirectClean(bool reset, bool all); void DelayedClean(bool reset, bool all); diff --git a/src/game/Spells/Spell.cpp b/src/game/Spells/Spell.cpp index 88b286e5f76..145d73dd2df 100644 --- a/src/game/Spells/Spell.cpp +++ b/src/game/Spells/Spell.cpp @@ -3318,6 +3318,7 @@ SpellCastResult Spell::SpellStart(SpellCastTargets const* targets, Aura* trigger // create and add update event for this spell m_spellEvent = new SpellEvent(this); m_trueCaster->m_events.AddEvent(m_spellEvent, m_trueCaster->m_events.CalculateTime(1)); + m_trueCaster->SetNextUpdateTime(1); if (m_trueCaster->IsUnit()) // gameobjects dont have a sense of already casting a spell { @@ -5024,7 +5025,10 @@ void Spell::SendChannelStart(uint32 duration) } if (target) + { + target->SetNextUpdateTime(1); m_caster->SetChannelObject(target); + } m_caster->SetUInt32Value(UNIT_FIELD_CHANNEL_SPELL, m_spellInfo->Id); m_caster->addUnitState(UNIT_STAT_CHANNELING); diff --git a/src/game/Spells/SpellAuras.cpp b/src/game/Spells/SpellAuras.cpp index de7174c631d..79691e42d45 100755 --- a/src/game/Spells/SpellAuras.cpp +++ b/src/game/Spells/SpellAuras.cpp @@ -51,6 +51,7 @@ #include "Entities/TemporarySpawn.h" #include "Maps/InstanceData.h" #include "AI/ScriptDevAI/include/sc_grid_searchers.h" +#include "SpellAuras.h" #define NULL_AURA_SLOT 0xFF @@ -11014,6 +11015,15 @@ bool SpellAuraHolder::IsDispellableByMask(uint32 dispelMask, Unit const* caster, return false; } +bool SpellAuraHolder::HasPeriodicAura() const +{ + for (Aura* aura : m_auras) + if (aura && aura->IsPeriodic()) + return true; + + return false; +} + bool SpellAuraHolder::IsPersistent() const { for (auto aur : m_auras) diff --git a/src/game/Spells/SpellAuras.h b/src/game/Spells/SpellAuras.h index 111adad4fad..968c4d617d9 100644 --- a/src/game/Spells/SpellAuras.h +++ b/src/game/Spells/SpellAuras.h @@ -202,6 +202,8 @@ class SpellAuraHolder bool HasMechanicMask(uint32 mechanicMask) const; bool IsDispellableByMask(uint32 dispelMask, Unit const* caster, SpellEntry const* spellInfo) const; + bool HasPeriodicAura() const; + void SetCreationDelayFlag(); bool IsProcReady(TimePoint const& now) const; diff --git a/src/shared/Util/Timer.h b/src/shared/Util/Timer.h index 39e3fb44be4..7a7d4f7d0a6 100644 --- a/src/shared/Util/Timer.h +++ b/src/shared/Util/Timer.h @@ -148,17 +148,14 @@ struct ShortTimeTracker ShortTimeTracker(uint32 expiry = 0) : i_expiryTime(expiry) {} void Update(uint32 diff) { - if (i_expiryTime <= diff) - i_expiryTime = 0; - else - i_expiryTime -= diff; + i_expiryTime -= diff; } bool Passed() const { return (i_expiryTime <= 0); } - void Reset(uint32 interval) { i_expiryTime = interval; } - uint32 GetExpiry() const { return i_expiryTime; } + void Reset(int32 interval) { i_expiryTime = interval; } + int32 GetExpiry() const { return i_expiryTime; } private: - uint32 i_expiryTime; + int32 i_expiryTime; }; #endif