diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index ecccd2cef..cab3f8bfd 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1419,18 +1419,6 @@ CheckGameObjectLoS = 1 PreloadAllNonInstancedMapGrids = 0 -# -# SetAllCreaturesWithWaypointMovementActive -# Description: Set all creatures with waypoint movement active. This means that they will start -# movement once they are loaded (which happens on grid load) and keep moving even -# when no player is near. This will increase CPU usage significantly and can be -# used with enabled "PreloadAllNonInstancedMapGrids" to start waypoint movement on -# server startup. -# Default: 0 - (Disabled) -# 1 - (Enabled) - -SetAllCreaturesWithWaypointMovementActive = 0 - # # DontCacheRandomMovementPaths # Description: Random movement paths (calculated using MoveMaps) can be cached to save cpu time, diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7b2ef3e94..cabf69286 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2772,11 +2772,7 @@ bool Creature::LoadCreaturesAddon(bool reload) //Load Path if (cainfo->path_id != 0) - { - if (sWorld->getBoolConfig(CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE)) - setActive(true); m_path_id = cainfo->path_id; - } if (!cainfo->auras.empty()) { @@ -3896,3 +3892,27 @@ std::string Creature::GetDebugInfo() const << " WaypointPath: " << GetWaypointPath() << " SpawnId: " << GetSpawnId(); return sstr.str(); } + +// Note: This is called in a tight (heavy) loop, is it critical that all checks are FAST and are hopefully only simple conditionals. +bool Creature::IsUpdateNeeded() +{ + if (WorldObject::IsUpdateNeeded()) + return true; + + if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId())) + return true; + + if (IsInCombat()) + return true; + + if (IsVisibilityOverridden()) + return true; + + if (GetMotionMaster()->HasMovementGeneratorType(WAYPOINT_MOTION_TYPE)) + return true; + + if (HasUnitState(UNIT_STATE_EVADE)) + return true; + + return false; +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index c66b63e8f..49a7854cc 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -39,7 +39,7 @@ class CreatureGroup; #define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY -class Creature : public Unit, public GridObject, public MovableMapObject +class Creature : public Unit, public GridObject, public MovableMapObject, public UpdatableMapObject { public: explicit Creature(bool isWorldObject = false); @@ -436,6 +436,8 @@ public: std::string GetDebugInfo() const override; + bool IsUpdateNeeded() override; + protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr); bool InitEntry(uint32 entry, const CreatureData* data = nullptr); diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index f7cef0326..dc7e4f129 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -31,7 +31,7 @@ enum DynamicObjectType DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2, }; -class DynamicObject : public WorldObject, public GridObject, public MovableMapObject +class DynamicObject : public WorldObject, public GridObject, public MovableMapObject, public UpdatableMapObject { public: DynamicObject(bool isWorldObject); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 298edcde8..70544335d 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -3076,3 +3076,21 @@ std::string GameObject::GetDebugInfo() const << "SpawnId: " << GetSpawnId() << " GoState: " << std::to_string(GetGoState()) << " ScriptId: " << GetScriptId() << " AIName: " << GetAIName(); return sstr.str(); } + +// Note: This is called in a tight (heavy) loop, is it critical that all checks are FAST and are hopefully only simple conditionals. +bool GameObject::IsUpdateNeeded() +{ + if (WorldObject::IsUpdateNeeded()) + return true; + + if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId())) + return true; + + if (IsVisibilityOverridden()) + return true; + + if (IsTransport()) + return true; + + return false; +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 9c7f50bf4..37605de21 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -116,7 +116,7 @@ enum LootState // 5 sec for bobber catch #define FISHING_BOBBER_READY_TIME 5 -class GameObject : public WorldObject, public GridObject, public MovableMapObject +class GameObject : public WorldObject, public GridObject, public MovableMapObject, public UpdatableMapObject { public: explicit GameObject(); @@ -362,6 +362,8 @@ public: void SaveStateToDB(); std::string GetDebugInfo() const override; + + bool IsUpdateNeeded() override; protected: bool AIM_Initialize(); GameObjectModel* CreateModel(); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 82a691b34..947ac8ee7 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1188,6 +1188,7 @@ void WorldObject::AddToWorld() { Object::AddToWorld(); GetMap()->GetZoneAndAreaId(GetPhaseMask(), _zoneId, _areaId, GetPositionX(), GetPositionY(), GetPositionZ()); + GetMap()->AddObjectToPendingUpdateList(this); } void WorldObject::RemoveFromWorld() @@ -3220,3 +3221,27 @@ void WorldObject::RemoveAllowedLooter(ObjectGuid guid) { _allowedLooters.erase(guid); } + +bool WorldObject::IsUpdateNeeded() +{ + if (isActiveObject()) + return true; + + return false; +} + +bool WorldObject::CanBeAddedToMapUpdateList() +{ + switch (GetTypeId()) + { + case TYPEID_UNIT: + return IsCreature(); + case TYPEID_DYNAMICOBJECT: + case TYPEID_GAMEOBJECT: + return true; + default: + return false; + } + + return false; +} diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 1e5d157ed..82647d60d 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -405,14 +405,58 @@ class MovableMapObject protected: MovableMapObject() = default; -private: [[nodiscard]] Cell const& GetCurrentCell() const { return _currentCell; } + +private: void SetCurrentCell(Cell const& cell) { _currentCell = cell; } Cell _currentCell; MapObjectCellMoveState _moveState{MAP_OBJECT_CELL_MOVE_NONE}; }; +class UpdatableMapObject +{ + friend class Map; + +public: + enum UpdateState : uint8 + { + NotUpdating, + PendingAdd, + Updating + }; + +protected: + UpdatableMapObject() : _mapUpdateListOffset(0), _mapUpdateState(NotUpdating) { } + +private: + void SetMapUpdateListOffset(std::size_t const offset) + { + ASSERT(_mapUpdateState == Updating, "Attempted to set update list offset when object is not in map update list"); + _mapUpdateListOffset = offset; + } + + size_t GetMapUpdateListOffset() const + { + ASSERT(_mapUpdateState == Updating, "Attempted to get update list offset when object is not in map update list"); + return _mapUpdateListOffset; + } + + void SetUpdateState(UpdateState state) + { + _mapUpdateState = state; + } + + UpdateState GetUpdateState() const + { + return _mapUpdateState; + } + +private: + std::size_t _mapUpdateListOffset; + UpdateState _mapUpdateState; +}; + class WorldObject : public Object, public WorldLocation { protected: @@ -639,6 +683,9 @@ public: [[nodiscard]] GuidUnorderedSet const& GetAllowedLooters() const; void RemoveAllowedLooter(ObjectGuid guid); + virtual bool IsUpdateNeeded(); + bool CanBeAddedToMapUpdateList(); + std::string GetDebugInfo() const override; // Event handler diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index a3354ef64..9c2cde694 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1683,6 +1683,8 @@ template void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::vector& visibleNow) { + GetMap()->AddObjectToPendingUpdateList(target); + if (HaveAtClient(target)) { if (!CanSeeOrDetect(target, false, true)) diff --git a/src/server/game/Grids/GridObjectLoader.cpp b/src/server/game/Grids/GridObjectLoader.cpp index 09550ab90..10172a4a1 100644 --- a/src/server/game/Grids/GridObjectLoader.cpp +++ b/src/server/game/Grids/GridObjectLoader.cpp @@ -116,6 +116,9 @@ void GridObjectUnloader::Visit(GridRefMgr& m) //Example: Flame Leviathan Turret 33139 is summoned when a creature is deleted //TODO: Check if that script has the correct logic. Do we really need to summons something before deleting? obj->CleanupsBeforeDelete(); + + obj->GetMap()->RemoveObjectFromMapUpdateList(obj); + ///- object will get delinked from the manager when deleted delete obj; } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 586e1336b..67259d8ab 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -354,19 +354,6 @@ void MessageDistDelivererToHostile::Visit(DynamicObjectMapType& m) } } -template -void ObjectUpdater::Visit(GridRefMgr& m) -{ - T* obj; - for (typename GridRefMgr::iterator iter = m.begin(); iter != m.end(); ) - { - obj = iter->GetSource(); - ++iter; - if (obj->IsInWorld() && (i_largeOnly == obj->IsVisibilityOverridden())) - obj->Update(i_timeDiff); - } -} - bool AnyDeadUnitObjectInRangeCheck::operator()(Player* u) { return !u->IsAlive() && !u->HasGhostAura() && i_searchObj->IsWithinDistInMap(u, i_range); @@ -396,7 +383,3 @@ bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Creature* u) { return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u); } - -template void ObjectUpdater::Visit(CreatureMapType&); -template void ObjectUpdater::Visit(GameObjectMapType&); -template void ObjectUpdater::Visit(DynamicObjectMapType&); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 6e084b733..4594c7979 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -154,16 +154,6 @@ namespace Acore } }; - struct ObjectUpdater - { - uint32 i_timeDiff; - bool i_largeOnly; - explicit ObjectUpdater(const uint32 diff, bool largeOnly) : i_timeDiff(diff), i_largeOnly(largeOnly) {} - template void Visit(GridRefMgr& m); - void Visit(PlayerMapType&) {} - void Visit(CorpseMapType&) {} - }; - // SEARCHERS & LIST SEARCHERS & WORKERS // WorldObject searchers & workers diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index b82e217ea..cf1e22543 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -80,6 +80,7 @@ Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : m_parentMap = (_parent ? _parent : this); _zonePlayerCountMap.clear(); + _updatableObjectListRecheckTimer.SetInterval(UPDATABLE_OBJECT_LIST_RECHECK_TIMER); //lets initialize visibility distance for map Map::InitVisibilityDistance(); @@ -492,45 +493,7 @@ bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) return true; } -void Map::VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor) -{ - // check for valid position - if (!player->IsPositionValid()) - return; - - // check normal grid activation range of the player - VisitNearbyCellsOf(player, gridVisitor, worldVisitor, largeGridVisitor, largeWorldVisitor); - - // check maximum visibility distance for large creatures - CellArea area = Cell::CalculateCellArea(player->GetPositionX(), player->GetPositionY(), MAX_VISIBILITY_DISTANCE); - - for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x) - { - for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y) - { - // marked cells are those that have been visited - // don't visit the same cell twice - uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; - if (isCellMarkedLarge(cell_id)) - continue; - - markCellLarge(cell_id); - CellCoord pair(x, y); - Cell cell(pair); - - Visit(cell, largeGridVisitor); - Visit(cell, largeWorldVisitor); - } - } -} - -void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor) +void Map::MarkNearbyCellsOf(WorldObject* obj) { // Check for valid position if (!obj->IsPositionValid()) @@ -538,30 +501,13 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitorGetPositionX(), obj->GetPositionY(), obj->GetGridActivationRange()); - for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x) { for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y) { // marked cells are those that have been visited - // don't visit the same cell twice uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; - if (isCellMarked(cell_id)) - continue; - markCell(cell_id); - CellCoord pair(x, y); - Cell cell(pair); - - Visit(cell, gridVisitor); - Visit(cell, worldVisitor); - - if (!isCellMarkedLarge(cell_id)) - { - markCellLarge(cell_id); - Visit(cell, largeGridVisitor); - Visit(cell, largeWorldVisitor); - } } } } @@ -613,39 +559,10 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) return; } - /// update active cells around players and active objects + _updatableObjectListRecheckTimer.Update(t_diff); resetMarkedCells(); - resetMarkedCellsLarge(); - // Prepare object updaters - Acore::ObjectUpdater updater(t_diff, false); - - // For creature - TypeContainerVisitor grid_object_update(updater); - - // For pets - TypeContainerVisitor world_object_update(updater); - - // For large creatures - Acore::ObjectUpdater largeObjectUpdater(t_diff, true); - TypeContainerVisitor grid_large_object_update(largeObjectUpdater); - TypeContainerVisitor world_large_object_update(largeObjectUpdater); - - // pussywizard: container for far creatures in combat with players - std::vector updateList; - updateList.reserve(10); - - // Update non-player active objects - for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end();) - { - WorldObject* obj = *m_activeNonPlayersIter; - ++m_activeNonPlayersIter; - - if (obj && obj->IsInWorld()) - VisitNearbyCellsOf(obj, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - } - - // Update players and their associated objects + // Update players for (m_mapRefIter = m_mapRefMgr.begin(); m_mapRefIter != m_mapRefMgr.end(); ++m_mapRefIter) { Player* player = m_mapRefIter->GetSource(); @@ -654,53 +571,37 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) continue; player->Update(s_diff); - VisitNearbyCellsOfPlayer(player, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - // If player is using far sight, update viewpoint - if (WorldObject* viewPoint = player->GetViewpoint()) + if (_updatableObjectListRecheckTimer.Passed()) { - if (Creature* viewCreature = viewPoint->ToCreature()) - { - VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - } - else if (DynamicObject* viewObject = viewPoint->ToDynObject()) - { - VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - } - } + MarkNearbyCellsOf(player); - // handle updates for creatures in combat with player and are more than X yards away - if (player->IsInCombat()) - { - updateList.clear(); - float rangeSq = player->GetGridActivationRange() - 1.0f; - rangeSq *= rangeSq; - - HostileReference* ref = player->getHostileRefMgr().getFirst(); - while (ref) + // If player is using far sight, update viewpoint + if (WorldObject* viewPoint = player->GetViewpoint()) { - if (Unit* unit = ref->GetSource()->GetOwner()) - if (Creature* cre = unit->ToCreature()) - if (cre->FindMap() == player->FindMap() && cre->GetExactDist2dSq(player) > rangeSq) - updateList.push_back(cre); - ref = ref->next(); + if (Creature* viewCreature = viewPoint->ToCreature()) + MarkNearbyCellsOf(viewCreature); + else if (DynamicObject* viewObject = viewPoint->ToDynObject()) + MarkNearbyCellsOf(viewObject); } - - for (Creature* cre : updateList) - VisitNearbyCellsOf(cre, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); } } - // Update transports - pussywizard: transports updated after VisitNearbyCellsOf, grids around are loaded, everything ok - for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) + if (_updatableObjectListRecheckTimer.Passed()) { - MotionTransport* transport = *_transportsUpdateIter; - ++_transportsUpdateIter; + // Mark all cells near active objects + for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end(); ++m_activeNonPlayersIter) + { + WorldObject* obj = *m_activeNonPlayersIter; + if (!obj || !obj->IsInWorld()) + continue; - if (transport->IsInWorld()) - transport->Update(t_diff); + MarkNearbyCellsOf(obj); + } } + UpdateNonPlayerObjects(t_diff); + SendObjectUpdates(); ///- Process necessary scripts @@ -728,6 +629,101 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) METRIC_TAG("map_instanceid", std::to_string(GetInstanceId()))); } +void Map::UpdateNonPlayerObjects(uint32 const diff) +{ + for (WorldObject* obj : _pendingAddUpdatableObjectList) + _AddObjectToUpdateList(obj); + _pendingAddUpdatableObjectList.clear(); + + if (_updatableObjectListRecheckTimer.Passed()) + { + for (uint32 i = 0; i < _updatableObjectList.size();) + { + WorldObject* obj = _updatableObjectList[i]; + if (!obj->IsInWorld()) + { + ++i; + continue; + } + + obj->Update(diff); + + if (!obj->IsUpdateNeeded()) + { + _RemoveObjectFromUpdateList(obj); + // Intentional no iteration here, obj is swapped with last element in + // _updatableObjectList so next loop will update that object at the same index + } + else + ++i; + } + _updatableObjectListRecheckTimer.Reset(); + } + else + { + for (uint32 i = 0; i < _updatableObjectList.size(); ++i) + { + WorldObject* obj = _updatableObjectList[i]; + if (!obj->IsInWorld()) + continue; + + obj->Update(diff); + } + } +} + +void Map::AddObjectToPendingUpdateList(WorldObject* obj) +{ + if (!obj->CanBeAddedToMapUpdateList()) + return; + + UpdatableMapObject* mapUpdatableObject = dynamic_cast(obj); + if (mapUpdatableObject->GetUpdateState() != UpdatableMapObject::UpdateState::NotUpdating) + return; + + _pendingAddUpdatableObjectList.insert(obj); + mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::PendingAdd); +} + +// Internal use only +void Map::_AddObjectToUpdateList(WorldObject* obj) +{ + UpdatableMapObject* mapUpdatableObject = dynamic_cast(obj); + ASSERT(mapUpdatableObject && mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::PendingAdd); + + mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::Updating); + mapUpdatableObject->SetMapUpdateListOffset(_updatableObjectList.size()); + _updatableObjectList.push_back(obj); +} + +// Internal use only +void Map::_RemoveObjectFromUpdateList(WorldObject* obj) +{ + UpdatableMapObject* mapUpdatableObject = dynamic_cast(obj); + ASSERT(mapUpdatableObject && mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::Updating); + + if (obj != _updatableObjectList.back()) + { + dynamic_cast(_updatableObjectList.back())->SetMapUpdateListOffset(mapUpdatableObject->GetMapUpdateListOffset()); + std::swap(_updatableObjectList[mapUpdatableObject->GetMapUpdateListOffset()], _updatableObjectList.back()); + } + + _updatableObjectList.pop_back(); + mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::NotUpdating); +} + +void Map::RemoveObjectFromMapUpdateList(WorldObject* obj) +{ + if (!obj->CanBeAddedToMapUpdateList()) + return; + + UpdatableMapObject* mapUpdatableObject = dynamic_cast(obj); + if (mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::PendingAdd) + _pendingAddUpdatableObjectList.erase(obj); + else if (mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::Updating) + _RemoveObjectFromUpdateList(obj); +} + void Map::HandleDelayedVisibility() { if (i_objectsForDelayedVisibility.empty()) @@ -795,7 +791,10 @@ void Map::RemoveFromMap(T* obj, bool remove) obj->ResetMap(); if (remove) + { + RemoveObjectFromMapUpdateList(obj); DeleteFromWorld(obj); + } } template<> @@ -831,6 +830,10 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove) obj->ResetMap(); + // Transports are never actually deleted, but it *should* be safe to clear + // from update list when removing from world + RemoveObjectFromMapUpdateList(obj); + if (remove) { // if option set then object already saved at this moment diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 33bb8238d..7f70020dc 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -34,6 +34,7 @@ #include "Position.h" #include "SharedDefines.h" #include "TaskScheduler.h" +#include "Timer.h" #include "GridTerrainData.h" #include #include @@ -85,6 +86,7 @@ struct ScriptAction #define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations #define MIN_UNLOAD_DELAY 1 // immediate unload +#define UPDATABLE_OBJECT_LIST_RECHECK_TIMER 30 * IN_MILLISECONDS // Time to recheck update object list struct PositionFullTerrainStatus { @@ -181,14 +183,7 @@ public: template bool AddToMap(T*, bool checkTransport = false); template void RemoveFromMap(T*, bool); - void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor); - void VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor); + void MarkNearbyCellsOf(WorldObject* obj); virtual void Update(const uint32, const uint32, bool thread = true); @@ -317,9 +312,6 @@ public: void resetMarkedCells() { marked_cells.reset(); } bool isCellMarked(uint32 pCellId) { return marked_cells.test(pCellId); } void markCell(uint32 pCellId) { marked_cells.set(pCellId); } - void resetMarkedCellsLarge() { marked_cells_large.reset(); } - bool isCellMarkedLarge(uint32 pCellId) { return marked_cells_large.test(pCellId); } - void markCellLarge(uint32 pCellId) { marked_cells_large.set(pCellId); } [[nodiscard]] bool HavePlayers() const { return !m_mapRefMgr.IsEmpty(); } [[nodiscard]] uint32 GetPlayersCountExceptGMs() const; @@ -512,6 +504,12 @@ public: uint32 GetCreatedCellsInGridCount(uint16 const x, uint16 const y); uint32 GetCreatedCellsInMapCount(); + void AddObjectToPendingUpdateList(WorldObject* obj); + void RemoveObjectFromMapUpdateList(WorldObject* obj); + + typedef std::vector UpdatableObjectList; + typedef std::unordered_set PendingAddUpdatableObjectList; + private: template void InitializeObject(T* obj); @@ -576,7 +574,6 @@ private: Map* m_parentMap; std::bitset marked_cells; - std::bitset marked_cells_large; bool i_scriptLock; std::unordered_set i_objectsToRemove; @@ -610,6 +607,11 @@ private: m_activeNonPlayers.erase(obj); } + void UpdateNonPlayerObjects(uint32 const diff); + + void _AddObjectToUpdateList(WorldObject* obj); + void _RemoveObjectFromUpdateList(WorldObject* obj); + std::unordered_map _creatureRespawnTimes; std::unordered_map _goRespawnTimes; @@ -637,6 +639,10 @@ private: std::unordered_set _corpseBones; std::unordered_set _updateObjects; + + UpdatableObjectList _updatableObjectList; + PendingAddUpdatableObjectList _pendingAddUpdatableObjectList; + IntervalTimer _updatableObjectListRecheckTimer; }; enum InstanceResetMethod diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 5b593a4bf..8189fd505 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -926,6 +926,20 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const return Impl[slot]->GetMovementGeneratorType(); } +bool MotionMaster::HasMovementGeneratorType(MovementGeneratorType type) const +{ + if (empty() && type == IDLE_MOTION_TYPE) + return true; + + for (int i = _top; i >= 0; --i) + { + if (Impl[i] && Impl[i]->GetMovementGeneratorType() == type) + return true; + } + + return false; +} + // Xinef: Escort system uint32 MotionMaster::GetCurrentSplineId() const { diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 756867548..248263d7b 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -240,6 +240,7 @@ public: [[nodiscard]] MovementGeneratorType GetCurrentMovementGeneratorType() const; [[nodiscard]] MovementGeneratorType GetMotionSlotType(int slot) const; + bool HasMovementGeneratorType(MovementGeneratorType type) const; [[nodiscard]] uint32 GetCurrentSplineId() const; // Xinef: Escort system void propagateSpeedChange();