From 719098a97ce43225a0d9a3bd51b1358690efb3f6 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 25 Mar 2017 23:32:49 -0500 Subject: [PATCH 1/2] [Performance] Reduced CPU footprint in non-combat zones doing constant checks for combat related activities --- changelog.txt | 3 ++ zone/client_process.cpp | 2 +- zone/mob_ai.cpp | 107 ++++++++++++++++++++++------------------ 3 files changed, 62 insertions(+), 50 deletions(-) diff --git a/changelog.txt b/changelog.txt index 9514c812c..ea457ed67 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/25/2017 == +Akkadius: Reduced CPU footprint in non-combat zones doing constant checks for combat related activities + == 03/12/2017 == Akkadius: - Implemented range rules for packets and other functions diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 57ed6283c..b2a010542 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -616,7 +616,7 @@ bool Client::Process() { //At this point, we are still connected, everything important has taken //place, now check to see if anybody wants to aggro us. // only if client is not feigned - if(ret && !GetFeigned() && scanarea_timer.Check()) { + if(zone->CanDoCombat() && ret && !GetFeigned() && scanarea_timer.Check()) { entity_list.CheckClientAggro(this); } #endif diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index f6e6fa019..d0298ac55 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -726,6 +726,7 @@ void Client::AI_SpellCast() void Client::AI_Process() { + if (!IsAIControlled()) return; @@ -956,6 +957,10 @@ void Mob::AI_Process() { bool engaged = IsEngaged(); bool doranged = false; + if (!zone->CanDoCombat()) { + engaged = false; + } + // Begin: Additions for Wiz Fear Code // if(RuleB(Combat, EnableFearPathing)){ @@ -1016,13 +1021,14 @@ void Mob::AI_Process() { SetTarget(hate_list.GetClosestEntOnHateList(this)); else { - if(AI_target_check_timer->Check()) + if (AI_target_check_timer->Check()) { if (IsFocused()) { if (!target) { SetTarget(hate_list.GetEntWithMostHateOnList(this)); } - } else { + } + else { if (!ImprovedTaunt()) SetTarget(hate_list.GetEntWithMostHateOnList(this)); } @@ -1042,28 +1048,29 @@ void Mob::AI_Process() { #ifdef BOTS if (IsPet() && GetOwner() && GetOwner()->IsBot() && target == GetOwner()) { - // this blocks all pet attacks against owner..bot pet test (copied above check) - RemoveFromHateList(this); - return; + // this blocks all pet attacks against owner..bot pet test (copied above check) + RemoveFromHateList(this); + return; } #endif //BOTS - if(DivineAura()) + if (DivineAura()) return; - auto npcSpawnPoint = CastToNPC()->GetSpawnPoint(); - if(GetSpecialAbility(TETHER)) { + auto npcSpawnPoint = CastToNPC()->GetSpawnPoint(); + if (GetSpecialAbility(TETHER)) { float tether_range = static_cast(GetSpecialAbilityParam(TETHER, 0)); tether_range = tether_range > 0.0f ? tether_range * tether_range : pAggroRange * pAggroRange; - if(DistanceSquaredNoZ(m_Position, npcSpawnPoint) > tether_range) { + if (DistanceSquaredNoZ(m_Position, npcSpawnPoint) > tether_range) { GMMove(npcSpawnPoint.x, npcSpawnPoint.y, npcSpawnPoint.z, npcSpawnPoint.w); } - } else if(GetSpecialAbility(LEASH)) { + } + else if (GetSpecialAbility(LEASH)) { float leash_range = static_cast(GetSpecialAbilityParam(LEASH, 0)); leash_range = leash_range > 0.0f ? leash_range * leash_range : pAggroRange * pAggroRange; - if(DistanceSquaredNoZ(m_Position, npcSpawnPoint) > leash_range) { + if (DistanceSquaredNoZ(m_Position, npcSpawnPoint) > leash_range) { GMMove(npcSpawnPoint.x, npcSpawnPoint.y, npcSpawnPoint.z, npcSpawnPoint.w); SetHP(GetMaxHP()); BuffFadeAll(); @@ -1080,16 +1087,16 @@ void Mob::AI_Process() { { if (AI_movement_timer->Check()) { - if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) + if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SendPosition(); } SetCurrentSpeed(0); } - if(IsMoving()) + if (IsMoving()) { - if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) + if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SendPosition(); @@ -1098,13 +1105,13 @@ void Mob::AI_Process() { } //casting checked above... - if(target && !IsStunned() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled()) { + if (target && !IsStunned() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled()) { //we should check to see if they die mid-attacks, previous //crap of checking target for null was not gunna cut it //try main hand first - if(attack_timer.Check()) { + if (attack_timer.Check()) { DoMainHandAttackRounds(target); TriggerDefensiveProcs(target, EQEmu::inventory::slotPrimary, false); @@ -1150,17 +1157,17 @@ void Mob::AI_Process() { if (owner) { int16 flurry_chance = owner->aabonuses.PetFlurry + - owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry; + owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry; if (flurry_chance && zone->random.Roll(flurry_chance)) Flurry(nullptr); } } - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()){ - if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] || aabonuses.PC_Pet_Rampage[0]){ + if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] || aabonuses.PC_Pet_Rampage[0]) { int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0]; - if(zone->random.Roll(chance)) { + if (zone->random.Roll(chance)) { Rampage(nullptr); } } @@ -1170,30 +1177,30 @@ void Mob::AI_Process() { { int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0); rampage_chance = rampage_chance > 0 ? rampage_chance : 20; - if(zone->random.Roll(rampage_chance)) { + if (zone->random.Roll(rampage_chance)) { ExtraAttackOptions opts; int cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 3); - if(cur > 0) { + if (cur > 0) { opts.damage_flat = cur; } cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 4); - if(cur > 0) { + if (cur > 0) { opts.armor_pen_percent = cur / 100.0f; } cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 5); - if(cur > 0) { + if (cur > 0) { opts.armor_pen_flat = cur; } cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 6); - if(cur > 0) { + if (cur > 0) { opts.crit_percent = cur / 100.0f; } cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 7); - if(cur > 0) { + if (cur > 0) { opts.crit_flat = cur; } Rampage(&opts); @@ -1205,30 +1212,30 @@ void Mob::AI_Process() { { int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0); rampage_chance = rampage_chance > 0 ? rampage_chance : 20; - if(zone->random.Roll(rampage_chance)) { + if (zone->random.Roll(rampage_chance)) { ExtraAttackOptions opts; int cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 3); - if(cur > 0) { + if (cur > 0) { opts.damage_flat = cur; } cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 4); - if(cur > 0) { + if (cur > 0) { opts.armor_pen_percent = cur / 100.0f; } cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 5); - if(cur > 0) { + if (cur > 0) { opts.armor_pen_flat = cur; } cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 6); - if(cur > 0) { + if (cur > 0) { opts.crit_percent = cur / 100.0f; } cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 7); - if(cur > 0) { + if (cur > 0) { opts.crit_flat = cur; } @@ -1243,7 +1250,7 @@ void Mob::AI_Process() { DoOffHandAttackRounds(target); //now special attacks (kick, etc) - if(IsNPC()) + if (IsNPC()) CastToNPC()->DoClassAttacks(target); } @@ -1252,18 +1259,19 @@ void Mob::AI_Process() { else { //we cannot reach our target... //underwater stuff only works with water maps in the zone! - if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { - auto targetPosition = glm::vec3(target->GetX(), target->GetY(), target->GetZ()); - if(!zone->watermap->InLiquid(targetPosition)) { + if (IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { + auto targetPosition = glm::vec3(target->GetX(), target->GetY(), target->GetZ()); + if (!zone->watermap->InLiquid(targetPosition)) { Mob *tar = hate_list.GetEntWithMostHateOnList(this); - if(tar == target) { + if (tar == target) { WipeHateList(); Heal(); BuffFadeAll(); AI_walking_timer->Start(100); pLastFightingDelayMoving = Timer::GetCurrentTime(); return; - } else if(tar != nullptr) { + } + else if (tar != nullptr) { SetTarget(tar); return; } @@ -1274,35 +1282,35 @@ void Mob::AI_Process() { if (!HateSummon()) { //could not summon them, check ranged... - if(GetSpecialAbility(SPECATK_RANGED_ATK)) + if (GetSpecialAbility(SPECATK_RANGED_ATK)) doranged = true; // Now pursue // TODO: Check here for another person on hate list with close hate value - if(AI_PursueCastCheck()){ + if (AI_PursueCastCheck()) { //we did something, so do not process movement. } else if (AI_movement_timer->Check()) { - if(!IsRooted()) { + if (!IsRooted()) { Log.Out(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); - if(!RuleB(Pathing, Aggro) || !zone->pathing) + if (!RuleB(Pathing, Aggro) || !zone->pathing) CalculateNewPosition2(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed()); else { bool WaypointChanged, NodeReached; glm::vec3 Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(), - GetRunspeed(), WaypointChanged, NodeReached); + GetRunspeed(), WaypointChanged, NodeReached); - if(WaypointChanged) + if (WaypointChanged) tar_ndx = 20; CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); } } - else if(IsMoving()) { + else if (IsMoving()) { SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); SetCurrentSpeed(0); @@ -1311,11 +1319,12 @@ void Mob::AI_Process() { } } } - else - { + else { + if (m_PlayerState & static_cast(PlayerState::Aggressive)) SendRemovePlayerState(PlayerState::Aggressive); - if(AI_feign_remember_timer->Check()) { + + if(!zone->CanDoCombat() && AI_feign_remember_timer->Check()) { // 6/14/06 // Improved Feign Death Memory // check to see if any of our previous feigned targets have gotten up. @@ -1340,7 +1349,7 @@ void Mob::AI_Process() { { //we processed a spell action, so do nothing else. } - else if (AI_scan_area_timer->Check()) + else if (!zone->CanDoCombat() && AI_scan_area_timer->Check()) { /* * This is where NPCs look around to see if they want to attack anybody. From aa25946dc2d6aefb8978fa7990d8d104e1e8d97c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 26 Mar 2017 00:03:51 -0500 Subject: [PATCH 2/2] [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 --- changelog.txt | 3 +++ common/features.h | 2 +- common/ruletypes.h | 1 + zone/client.cpp | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index ea457ed67..b2c71a440 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 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 + 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 == 03/12/2017 == Akkadius: diff --git a/common/features.h b/common/features.h index 4140014d8..863004666 100644 --- a/common/features.h +++ b/common/features.h @@ -157,7 +157,7 @@ enum { //timer settings, all in milliseconds AIscanarea_delay = 6000, AIfeignremember_delay = 500, AItarget_check_duration = 500, - AIClientScanarea_delay = 750, //used in REVERSE_AGGRO + // AIClientScanarea_delay = 750, //used in REVERSE_AGGRO AIassistcheck_delay = 3000, //now often a fighting NPC will yell for help AI_check_signal_timer_delay = 500, // How often EVENT_SIGNAL checks are processed ClientProximity_interval = 150, diff --git a/common/ruletypes.h b/common/ruletypes.h index 0bac4d6df..a6653da8e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -536,6 +536,7 @@ RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum am RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference. RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live RULE_BOOL(Aggro, UseLevelAggro, true) // Level 18+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true) +RULE_INT(Aggro, ClientAggroCheckInterval, 6) // Interval in which clients actually check for aggro - in seconds RULE_CATEGORY_END() RULE_CATEGORY(TaskSystem) diff --git a/zone/client.cpp b/zone/client.cpp index fa683a267..2170d8ba9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -133,9 +133,9 @@ Client::Client(EQStreamInterface* ieqs) fishing_timer(8000), endupkeep_timer(1000), forget_timer(0), - autosave_timer(RuleI(Character, AutosaveIntervalS)*1000), + autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), #ifdef REVERSE_AGGRO - scanarea_timer(AIClientScanarea_delay), + scanarea_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), #endif tribute_timer(Tribute_duration), proximity_timer(ClientProximity_interval),