[11193] Optimize visibility update and AI notification code. Optimization decreases amount of visibility and AI notification operations in 8-10 times, and as a result, lowers CPU usage by 30-40%

You need to update your config file, new "Visibility.RelocationLowerLimit" and "Visibility.AIRelocationNotifyDelay' options added

Special thanks to Ambal for code hints and advices
Thanks to Undergarun and kero99 for making tests
This commit is contained in:
SilverIce 2011-02-22 02:32:49 +02:00
parent 60b2e2c8ba
commit 724092d9e5
10 changed files with 117 additions and 62 deletions

View file

@ -125,7 +125,7 @@ m_lootMoney(0), m_lootGroupRecipientId(0),
m_corpseDecayTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(5.0f), m_corpseDecayTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(5.0f),
m_subtype(subtype), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0), m_subtype(subtype), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false),
m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_needNotify(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
m_creatureInfo(NULL), m_splineFlags(SPLINEFLAG_WALKMODE) m_creatureInfo(NULL), m_splineFlags(SPLINEFLAG_WALKMODE)
{ {
@ -426,15 +426,6 @@ uint32 Creature::ChooseDisplayId(const CreatureInfo *cinfo, const CreatureData *
void Creature::Update(uint32 update_diff, uint32 diff) void Creature::Update(uint32 update_diff, uint32 diff)
{ {
if (m_needNotify)
{
m_needNotify = false;
RelocationNotify();
if (!IsInWorld())
return;
}
switch( m_deathState ) switch( m_deathState )
{ {
case JUST_ALIVED: case JUST_ALIVED:
@ -2352,13 +2343,6 @@ void Creature::SendAreaSpiritHealerQueryOpcode(Player *pl)
pl->SendDirectMessage(&data); pl->SendDirectMessage(&data);
} }
void Creature::RelocationNotify()
{
MaNGOS::CreatureRelocationNotifier relocationNotifier(*this);
float radius = MAX_CREATURE_ATTACK_RADIUS * sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO);
Cell::VisitAllObjects(this, relocationNotifier, radius);
}
void Creature::ApplyGameEventSpells(GameEventCreatureData const* eventData, bool activated) void Creature::ApplyGameEventSpells(GameEventCreatureData const* eventData, bool activated)
{ {
uint32 cast_spell = activated ? eventData->spell_id_start : eventData->spell_id_end; uint32 cast_spell = activated ? eventData->spell_id_start : eventData->spell_id_end;

View file

@ -647,14 +647,11 @@ class MANGOS_DLL_SPEC Creature : public Unit
void SetActiveObjectState(bool on); void SetActiveObjectState(bool on);
void SetNeedNotify() { m_needNotify = true; }
void SendAreaSpiritHealerQueryOpcode(Player *pl); void SendAreaSpiritHealerQueryOpcode(Player *pl);
protected: protected:
bool CreateFromProto(uint32 guidlow,uint32 Entry, Team team, const CreatureData *data = NULL, GameEventCreatureData const* eventData =NULL); bool CreateFromProto(uint32 guidlow,uint32 Entry, Team team, const CreatureData *data = NULL, GameEventCreatureData const* eventData =NULL);
bool InitEntry(uint32 entry, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL); bool InitEntry(uint32 entry, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL);
void RelocationNotify();
uint32 m_groupLootTimer; // (msecs)timer used for group loot uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 m_groupLootId; // used to find group which is looting corpse uint32 m_groupLootId; // used to find group which is looting corpse
@ -691,7 +688,6 @@ class MANGOS_DLL_SPEC Creature : public Unit
bool m_regenHealth; bool m_regenHealth;
bool m_AI_locked; bool m_AI_locked;
bool m_isDeadByDefault; bool m_isDeadByDefault;
bool m_needNotify;
SpellSchoolMask m_meleeDamageSchoolMask; SpellSchoolMask m_meleeDamageSchoolMask;
uint32 m_originalEntry; uint32 m_originalEntry;

View file

@ -47,11 +47,8 @@ inline void MaNGOS::ObjectUpdater::Visit(CreatureMapType &m)
} }
} }
inline void PlayerCreatureRelocationWorker(Player* pl, WorldObject const* viewPoint, Creature* c) inline void PlayerCreatureRelocationWorker(Player* pl, Creature* c)
{ {
// update creature visibility at player/creature move
pl->UpdateVisibilityOf(viewPoint,c);
// Creature AI reaction // Creature AI reaction
if (!c->hasUnitState(UNIT_STAT_LOST_CONTROL)) if (!c->hasUnitState(UNIT_STAT_LOST_CONTROL))
{ {
@ -80,11 +77,12 @@ inline void MaNGOS::PlayerRelocationNotifier::Visit(CreatureMapType &m)
if (!i_player.isAlive() || i_player.IsTaxiFlying()) if (!i_player.isAlive() || i_player.IsTaxiFlying())
return; return;
WorldObject const* viewPoint = i_player.GetCamera().GetBody();
for(CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) for(CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
if (iter->getSource()->isAlive()) {
PlayerCreatureRelocationWorker(&i_player, viewPoint, iter->getSource()); Creature* c = iter->getSource();
if (c->isAlive())
PlayerCreatureRelocationWorker(&i_player, c);
}
} }
template<> template<>
@ -94,9 +92,11 @@ inline void MaNGOS::CreatureRelocationNotifier::Visit(PlayerMapType &m)
return; return;
for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
if (Player* player = iter->getSource()) {
if (player->isAlive() && !player->IsTaxiFlying()) Player* player = iter->getSource();
PlayerCreatureRelocationWorker(player, player->GetCamera().GetBody(), &i_creature); if (player->isAlive() && !player->IsTaxiFlying())
PlayerCreatureRelocationWorker(player, &i_creature);
}
} }
template<> template<>

View file

@ -207,13 +207,13 @@ void Map::AddNotifier(T* , Cell const& , CellPair const& )
template<> template<>
void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair) void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair)
{ {
PlayerRelocationNotify(obj,cell,cellpair); obj->SheduleAINotify(0);
} }
template<> template<>
void Map::AddNotifier(Creature* obj, Cell const&, CellPair const&) void Map::AddNotifier(Creature* obj, Cell const&, CellPair const&)
{ {
obj->SetNeedNotify(); obj->SheduleAINotify(0);
} }
void void
@ -708,10 +708,7 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati
player->GetViewPoint().Event_GridChanged(&(*newGrid)(new_cell.CellX(),new_cell.CellY())); player->GetViewPoint().Event_GridChanged(&(*newGrid)(new_cell.CellX(),new_cell.CellY()));
} }
player->GetViewPoint().Call_UpdateVisibilityForOwner(); player->OnRelocated();
// if move then update what player see and who seen
UpdateObjectVisibility(player, new_cell, new_val);
PlayerRelocationNotify(player,new_cell,new_val);
NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY()); NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE ) if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
@ -731,39 +728,31 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang
CellPair new_val = MaNGOS::ComputeCellPair(x, y); CellPair new_val = MaNGOS::ComputeCellPair(x, y);
Cell new_cell(new_val); Cell new_cell(new_val);
// delay creature move for grid/cell to grid/cell moves
if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell)) if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
{ {
DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
// do move or do move to respawn or remove creature if previous all fail // do move or do move to respawn or remove creature if previous all fail
if(CreatureCellRelocation(creature,new_cell)) if (CreatureCellRelocation(creature,new_cell))
{ {
// update pos // update pos
creature->Relocate(x, y, z, ang); creature->Relocate(x, y, z, ang);
creature->OnRelocated();
// in diffcell/diffgrid case notifiers called in Creature::Update
creature->SetNeedNotify();
} }
else // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
// creature coordinates will be updated and notifiers send
else if (!CreatureRespawnRelocation(creature))
{ {
// if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid // ... or unload (if respawn grid also not loaded)
// creature coordinates will be updated and notifiers send DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",creature->GetGUIDLow(),creature->GetEntry());
if(!CreatureRespawnRelocation(creature))
{
// ... or unload (if respawn grid also not loaded)
DEBUG_FILTER_LOG(LOG_FILTER_CREATURE_MOVES, "Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",creature->GetGUIDLow(),creature->GetEntry());
creature->SetNeedNotify();
}
} }
} }
else else
{ {
creature->Relocate(x, y, z, ang); creature->Relocate(x, y, z, ang);
creature->SetNeedNotify(); creature->OnRelocated();
} }
creature->GetViewPoint().Call_UpdateVisibilityForOwner();
MANGOS_ASSERT(CheckGridIntegrity(creature,true)); MANGOS_ASSERT(CheckGridIntegrity(creature,true));
} }
@ -847,7 +836,7 @@ bool Map::CreatureRespawnRelocation(Creature *c)
{ {
c->Relocate(resp_x, resp_y, resp_z, resp_o); c->Relocate(resp_x, resp_y, resp_z, resp_o);
c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators
c->SetNeedNotify(); c->OnRelocated();
return true; return true;
} }
else else

View file

@ -218,6 +218,7 @@ Unit::Unit()
m_AuraFlags = 0; m_AuraFlags = 0;
m_Visibility = VISIBILITY_ON; m_Visibility = VISIBILITY_ON;
m_AINotifySheduled = false;
m_detectInvisibilityMask = 0; m_detectInvisibilityMask = 0;
m_invisibilityMask = 0; m_invisibilityMask = 0;
@ -8089,12 +8090,9 @@ void Unit::SetVisibility(UnitVisibility x)
} }
} }
Map *m = GetMap(); GetViewPoint().Call_UpdateVisibilityForOwner();
UpdateObjectVisibility();
if(GetTypeId()==TYPEID_PLAYER) SheduleAINotify(0);
m->PlayerRelocation((Player*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
else
m->CreatureRelocation((Creature*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
GetViewPoint().Event_ViewPointVisibilityChanged(); GetViewPoint().Event_ViewPointVisibilityChanged();
} }
@ -10827,3 +10825,62 @@ bool Unit::IsAllowedDamageInArea(Unit* pVictim) const
return true; return true;
} }
class RelocationNotifyEvent : public BasicEvent
{
public:
RelocationNotifyEvent(Unit& owner) : BasicEvent(), m_owner(owner)
{
m_owner._SetAINotifySheduled(true);
}
bool Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
float radius = MAX_CREATURE_ATTACK_RADIUS * sWorld.getConfig(CONFIG_FLOAT_RATE_CREATURE_AGGRO);
if (m_owner.GetTypeId() == TYPEID_PLAYER)
{
MaNGOS::PlayerRelocationNotifier notify((Player&)m_owner);
Cell::VisitAllObjects(&m_owner,notify,radius);
}
else //if(m_owner.GetTypeId() == TYPEID_UNIT)
{
MaNGOS::CreatureRelocationNotifier notify((Creature&)m_owner);
Cell::VisitAllObjects(&m_owner,notify,radius);
}
m_owner._SetAINotifySheduled(false);
return true;
}
void Abort(uint64)
{
m_owner._SetAINotifySheduled(false);
}
private:
Unit& m_owner;
};
void Unit::SheduleAINotify(uint32 delay)
{
if (!IsAINotifySheduled())
m_Events.AddEvent(new RelocationNotifyEvent(*this), m_Events.CalculateTime(delay));
}
void Unit::OnRelocated()
{
// switch to use G3D::Vector3 is good idea, maybe
float dx = m_last_notified_position.x - GetPositionX();
float dy = m_last_notified_position.y - GetPositionY();
float dz = m_last_notified_position.z - GetPositionZ();
float distsq = dx*dx+dy*dy+dz*dz;
if (distsq > World::GetRelocationLowerLimitSq())
{
m_last_notified_position.x = GetPositionX();
m_last_notified_position.y = GetPositionY();
m_last_notified_position.z = GetPositionZ();
GetViewPoint().Call_UpdateVisibilityForOwner();
UpdateObjectVisibility();
}
SheduleAINotify(World::GetRelocationAINotifyDelay());
}

View file

@ -1954,6 +1954,11 @@ class MANGOS_DLL_SPEC Unit : public WorldObject
// Movement info // Movement info
MovementInfo m_movementInfo; MovementInfo m_movementInfo;
void SheduleAINotify(uint32 delay);
bool IsAINotifySheduled() const { return m_AINotifySheduled;}
void _SetAINotifySheduled(bool on) { m_AINotifySheduled = on;} // only for call from RelocationNotifyEvent class code
void OnRelocated();
protected: protected:
explicit Unit (); explicit Unit ();
@ -2020,6 +2025,8 @@ class MANGOS_DLL_SPEC Unit : public WorldObject
uint32 m_castCounter; // count casts chain of triggered spells for prevent infinity cast crashes uint32 m_castCounter; // count casts chain of triggered spells for prevent infinity cast crashes
UnitVisibility m_Visibility; UnitVisibility m_Visibility;
Position m_last_notified_position;
bool m_AINotifySheduled;
Diminishing m_Diminishing; Diminishing m_Diminishing;
// Manage all Units threatening us // Manage all Units threatening us

View file

@ -77,6 +77,9 @@ float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE;
float World::m_VisibleUnitGreyDistance = 0; float World::m_VisibleUnitGreyDistance = 0;
float World::m_VisibleObjectGreyDistance = 0; float World::m_VisibleObjectGreyDistance = 0;
float World::m_relocation_lower_limit_sq = 10.f * 10.f;
uint32 World::m_relocation_ai_notify_delay = 1000u;
/// World constructor /// World constructor
World::World() World::World()
{ {
@ -768,6 +771,9 @@ void World::LoadConfigSettings(bool reload)
setConfig(CONFIG_BOOL_PET_UNSUMMON_AT_MOUNT, "PetUnsummonAtMount", true); setConfig(CONFIG_BOOL_PET_UNSUMMON_AT_MOUNT, "PetUnsummonAtMount", true);
m_relocation_ai_notify_delay = sConfig.GetIntDefault("Visibility.AIRelocationNotifyDelay", 1000u);
m_relocation_lower_limit_sq = pow(sConfig.GetFloatDefault("Visibility.RelocationLowerLimit",10), 2);
m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE) if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
{ {

View file

@ -565,6 +565,9 @@ class World
static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; }
static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; }
static float GetRelocationLowerLimitSq() { return m_relocation_lower_limit_sq; }
static uint32 GetRelocationAINotifyDelay() { return m_relocation_ai_notify_delay; }
void ProcessCliCommands(); void ProcessCliCommands();
void QueueCliCommand(CliCommandHolder* commandHolder) { cliCmdQueue.add(commandHolder); } void QueueCliCommand(CliCommandHolder* commandHolder) { cliCmdQueue.add(commandHolder); }
@ -654,6 +657,9 @@ class World
static float m_VisibleUnitGreyDistance; static float m_VisibleUnitGreyDistance;
static float m_VisibleObjectGreyDistance; static float m_VisibleObjectGreyDistance;
static float m_relocation_lower_limit_sq;
static uint32 m_relocation_ai_notify_delay;
// CLI command holder to be thread safe // CLI command holder to be thread safe
ACE_Based::LockedQueue<CliCommandHolder*,ACE_Thread_Mutex> cliCmdQueue; ACE_Based::LockedQueue<CliCommandHolder*,ACE_Thread_Mutex> cliCmdQueue;

View file

@ -1138,6 +1138,14 @@ GM.AllowAchievementGain = 1
# Visibility grey distance for dynobjects/gameobjects/corpses/creature bodies # Visibility grey distance for dynobjects/gameobjects/corpses/creature bodies
# Default: 10 (yards) # Default: 10 (yards)
# #
# Visibility.RelocationLowerLimit
# Object's visibility update called, when distance between current object's position and position,
# where visiblity was updated last time, reaches RelocationLoverLimit value
# Default: 10 (yards)
#
# Visibility.AIRelocationNotifyDelay
# Delay time between creature AI reactions on nearby movements
# Default: 1000 (milliseconds)
# #
################################################################################################################### ###################################################################################################################
@ -1148,6 +1156,8 @@ Visibility.Distance.BGArenas = 180
Visibility.Distance.InFlight = 100 Visibility.Distance.InFlight = 100
Visibility.Distance.Grey.Unit = 1 Visibility.Distance.Grey.Unit = 1
Visibility.Distance.Grey.Object = 10 Visibility.Distance.Grey.Object = 10
Visibility.RelocationLowerLimit = 10
Visibility.AIRelocationNotifyDelay = 1000
################################################################################################################### ###################################################################################################################
# SERVER RATES # SERVER RATES

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "11192" #define REVISION_NR "11193"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__