Permalink
Browse files

[Performance] Reworked how client to NPC aggro checks are made

	- Before when reverse aggro checks were done (client to NPC), checks would happen every 750 millseconds where a client would
		check an entire entity list with distance calcs and other checks for aggro, with many clients in a zone and many NPC's this would
		add a lot of unecessary overhead. A temporary adjustment on 3/25 was made and upped the check to 6 seconds.
	- Now, there is a new methodology to scanning. The client will build a cache list of NPC's within close range as defined in new rule:
		RULE_INT(Range, ClientNPCScan, 300) and will also get any NPC that has an aggro range beyond that defined range to use in
		the frequent checks for aggro, the result is far less overhead
	- Client scanning changes when moving versus not moving, the client will scan aggro every 500 milliseconds while moving, and
		3000 millseconds aggro check when not moving, with a 6000ms re-fetch for close NPC's
	- A demo of these changes can be found here:
		https://youtu.be/aGroiwLSTVU
  • Loading branch information...
Akkadius committed Mar 28, 2017
1 parent 940f3b0 commit 4b6ce1c19edae8a4db9d473ffa1790900858da04
Showing with 191 additions and 105 deletions.
  1. +15 −2 changelog.txt
  2. +1 −0 common/ruletypes.h
  3. +1 −1 zone/aggro.cpp
  4. +5 −2 zone/client.cpp
  5. +5 −1 zone/client.h
  6. +28 −0 zone/client_packet.cpp
  7. +120 −99 zone/client_process.cpp
  8. +15 −0 zone/entity.cpp
  9. +1 −0 zone/entity.h
View
@@ -1,8 +1,21 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 03/27/2017 ==
Akkadius: [Performance] Reworked how client to NPC aggro checks are made
- Before when reverse aggro checks were done (client to NPC), checks would happen every 750 millseconds where a client would
check an entire entity list with distance calcs and other checks for aggro, with many clients in a zone and many NPC's this would
add a lot of unecessary overhead. A temporary adjustment on 3/25 was made and upped the check to 6 seconds.
- Now, there is a new methodology to scanning. The client will build a cache list of NPC's within close range as defined in new rule:
RULE_INT(Range, ClientNPCScan, 300) and will also get any NPC that has an aggro range beyond that defined range to use in
the frequent checks for aggro, the result is far less overhead
- Client scanning changes when moving versus not moving, the client will scan aggro every 500 milliseconds while moving, and
3000 millseconds aggro check when not moving, with a 6000ms re-fetch for close NPC's
- A demo of these changes can be found here:
https://youtu.be/aGroiwLSTVU
== 03/25/2017 ==
Akkadius: Reduced CPU footprint in non-combat zones doing constant checks for combat related activities
Akkadius: Reduced CPU footprint in cases where a client is checking for aggro excessively every 750 millseconds. This has
Akkadius: [Performance] Reduced CPU footprint in non-combat zones doing constant checks for combat related activities
Akkadius: [Performance] Reduced CPU footprint in cases where a client is checking for aggro excessively every 750 millseconds. This has
been adjusted to 6 seconds per new rule RULE_INT(Aggro, ClientAggroCheckInterval)
- When zones have many players, with many NPC's, this adds up quickly
View
@@ -559,6 +559,7 @@ RULE_INT(Range, SpellMessages, 75)
RULE_INT(Range, SongMessages, 75)
RULE_INT(Range, MobPositionUpdates, 600)
RULE_INT(Range, CriticalDamage, 80)
RULE_INT(Range, ClientNPCScan, 300)
RULE_CATEGORY_END()
View
@@ -284,7 +284,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
if(( t1 > iAggroRange)
|| ( t2 > iAggroRange)
|| ( t3 > iAggroRange)
||(mob->IsInvisible(this))
|| (mob->IsInvisible(this))
|| (mob->IsClient() &&
(!mob->CastToClient()->Connected()
|| mob->CastToClient()->IsLD()
View
@@ -135,7 +135,7 @@ Client::Client(EQStreamInterface* ieqs)
forget_timer(0),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
#ifdef REVERSE_AGGRO
scanarea_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
#endif
tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval),
@@ -160,7 +160,8 @@ Client::Client(EQStreamInterface* ieqs)
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false)
m_dirtyautohaters(false),
npc_close_scan_timer(6000)
{
for(int cf=0; cf < _FilterCount; cf++)
ClientFilters[cf] = FilterShow;
@@ -358,6 +359,8 @@ Client::~Client() {
m_tradeskill_object = nullptr;
}
close_npcs.clear();
if(IsDueling() && GetDuelTarget() != 0) {
Entity* entity = entity_list.GetID(GetDuelTarget());
if(entity != nullptr && entity->IsClient()) {
View
@@ -221,6 +221,9 @@ class Client : public Mob
Client(EQStreamInterface * ieqs);
~Client();
std::unordered_map<NPC *, float> close_npcs;
bool is_client_moving;
//abstract virtual function implementations required by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
@@ -1458,7 +1461,7 @@ class Client : public Mob
Timer forget_timer; // our 2 min everybody forgets you timer
Timer autosave_timer;
#ifdef REVERSE_AGGRO
Timer scanarea_timer;
Timer client_scan_npc_aggro_timer;
#endif
Timer tribute_timer;
@@ -1477,6 +1480,7 @@ class Client : public Mob
Timer helm_toggle_timer;
Timer light_update_timer;
Timer aggro_meter_timer;
Timer npc_close_scan_timer;
glm::vec3 m_Proximity;
View
@@ -4580,6 +4580,34 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
rewind_timer.Start(30000, true);
}
/* Handle client aggro scanning timers NPCs */
is_client_moving = (ppu->y_pos == m_Position.y && ppu->x_pos == m_Position.x) ? false : true;
if (is_client_moving) {
Log.Out(Logs::Detail, Logs::Normal, "ClientUpdate: Client is moving - scan timer is: %u", client_scan_npc_aggro_timer.GetDuration());
if (client_scan_npc_aggro_timer.GetDuration() > 1000) {
npc_close_scan_timer.Disable();
npc_close_scan_timer.Start(500);
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(500);
}
}
else {
Log.Out(Logs::Detail, Logs::Normal, "ClientUpdate: Client is NOT moving - scan timer is: %u", client_scan_npc_aggro_timer.GetDuration());
if (client_scan_npc_aggro_timer.GetDuration() < 1000) {
npc_close_scan_timer.Disable();
npc_close_scan_timer.Start(6000);
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(3000);
}
}
// Outgoing client packet
float tmpheading = EQ19toFloat(ppu->heading);
/* The clients send an update at best every 1.3 seconds
Oops, something went wrong.

0 comments on commit 4b6ce1c

Please sign in to comment.