mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2025-11-29 17:38:24 +08:00
feat(Core/Maps): Improve map object updater (#22392)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Creature>, public MovableMapObject
|
||||
class Creature : public Unit, public GridObject<Creature>, 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);
|
||||
|
||||
@@ -31,7 +31,7 @@ enum DynamicObjectType
|
||||
DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2,
|
||||
};
|
||||
|
||||
class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MovableMapObject
|
||||
class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MovableMapObject, public UpdatableMapObject
|
||||
{
|
||||
public:
|
||||
DynamicObject(bool isWorldObject);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ enum LootState
|
||||
// 5 sec for bobber catch
|
||||
#define FISHING_BOBBER_READY_TIME 5
|
||||
|
||||
class GameObject : public WorldObject, public GridObject<GameObject>, public MovableMapObject
|
||||
class GameObject : public WorldObject, public GridObject<GameObject>, 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1683,6 +1683,8 @@ template <class T>
|
||||
void Player::UpdateVisibilityOf(T* target, UpdateData& data,
|
||||
std::vector<Unit*>& visibleNow)
|
||||
{
|
||||
GetMap()->AddObjectToPendingUpdateList(target);
|
||||
|
||||
if (HaveAtClient(target))
|
||||
{
|
||||
if (!CanSeeOrDetect(target, false, true))
|
||||
|
||||
@@ -116,6 +116,9 @@ void GridObjectUnloader::Visit(GridRefMgr<T>& 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;
|
||||
}
|
||||
|
||||
@@ -354,19 +354,6 @@ void MessageDistDelivererToHostile::Visit(DynamicObjectMapType& m)
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ObjectUpdater::Visit(GridRefMgr<T>& m)
|
||||
{
|
||||
T* obj;
|
||||
for (typename GridRefMgr<T>::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<Creature>(CreatureMapType&);
|
||||
template void ObjectUpdater::Visit<GameObject>(GameObjectMapType&);
|
||||
template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType&);
|
||||
|
||||
@@ -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<class T> void Visit(GridRefMgr<T>& m);
|
||||
void Visit(PlayerMapType&) {}
|
||||
void Visit(CorpseMapType&) {}
|
||||
};
|
||||
|
||||
// SEARCHERS & LIST SEARCHERS & WORKERS
|
||||
|
||||
// WorldObject searchers & workers
|
||||
|
||||
@@ -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<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& 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<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor)
|
||||
void Map::MarkNearbyCellsOf(WorldObject* obj)
|
||||
{
|
||||
// Check for valid position
|
||||
if (!obj->IsPositionValid())
|
||||
@@ -538,30 +501,13 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::Objec
|
||||
|
||||
// Update mobs/objects in ALL visible cells around object!
|
||||
CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), 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<Acore::ObjectUpdater, GridTypeMapContainer> grid_object_update(updater);
|
||||
|
||||
// For pets
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer> world_object_update(updater);
|
||||
|
||||
// For large creatures
|
||||
Acore::ObjectUpdater largeObjectUpdater(t_diff, true);
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer> grid_large_object_update(largeObjectUpdater);
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer> world_large_object_update(largeObjectUpdater);
|
||||
|
||||
// pussywizard: container for far creatures in combat with players
|
||||
std::vector<Creature*> 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<UpdatableMapObject*>(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<UpdatableMapObject*>(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<UpdatableMapObject*>(obj);
|
||||
ASSERT(mapUpdatableObject && mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::Updating);
|
||||
|
||||
if (obj != _updatableObjectList.back())
|
||||
{
|
||||
dynamic_cast<UpdatableMapObject*>(_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<UpdatableMapObject*>(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
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "Position.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "Timer.h"
|
||||
#include "GridTerrainData.h"
|
||||
#include <bitset>
|
||||
#include <list>
|
||||
@@ -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<class T> bool AddToMap(T*, bool checkTransport = false);
|
||||
template<class T> void RemoveFromMap(T*, bool);
|
||||
|
||||
void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor);
|
||||
void VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& 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<WorldObject*> UpdatableObjectList;
|
||||
typedef std::unordered_set<WorldObject*> PendingAddUpdatableObjectList;
|
||||
|
||||
private:
|
||||
|
||||
template<class T> void InitializeObject(T* obj);
|
||||
@@ -576,7 +574,6 @@ private:
|
||||
Map* m_parentMap;
|
||||
|
||||
std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP * TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells;
|
||||
std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP * TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells_large;
|
||||
|
||||
bool i_scriptLock;
|
||||
std::unordered_set<WorldObject*> 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<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
|
||||
|
||||
@@ -637,6 +639,10 @@ private:
|
||||
std::unordered_set<Corpse*> _corpseBones;
|
||||
|
||||
std::unordered_set<Object*> _updateObjects;
|
||||
|
||||
UpdatableObjectList _updatableObjectList;
|
||||
PendingAddUpdatableObjectList _pendingAddUpdatableObjectList;
|
||||
IntervalTimer _updatableObjectListRecheckTimer;
|
||||
};
|
||||
|
||||
enum InstanceResetMethod
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user