From 7b9d88b70b2074d789b5db304069c2fa8acd5572 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 3 Jan 2021 16:40:56 -0600 Subject: [PATCH 1/3] Fix a situation where guards don't scan fast enough because they stand idle, moving mobs will add themselves to guards --- zone/npc.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index a5ccfac76..e7ce31806 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -715,7 +715,7 @@ bool NPC::Process() } return false; } - + if (IsStunned() && stunned_timer.Check()) { Mob::UnStun(); this->spun_timer.Disable(); @@ -724,7 +724,7 @@ bool NPC::Process() SpellProcess(); if (mob_close_scan_timer.Check()) { - entity_list.ScanCloseMobs(close_mobs, this); + entity_list.ScanCloseMobs(close_mobs, this, true); } const uint16 npc_mob_close_scan_timer_moving = 6000; @@ -1752,7 +1752,7 @@ void NPC::PickPocket(Client* thief) steal_item = false; break; } - + auto item_inst = database.CreateItem(loot_selection[random].first, loot_selection[random].second); if (item_inst == nullptr) { steal_item = false; @@ -1778,7 +1778,7 @@ void NPC::PickPocket(Client* thief) while (!steal_item && has_coin) { uint32 coin_amount = zone->random.Int(1, (steal_skill / 25) + 1); - + int coin_type = PickPocketPlatinum; while (coin_type <= PickPocketCopper) { if (money[coin_type]) { @@ -2514,10 +2514,10 @@ void NPC::LevelScale() { max_hp += (random_level - level) * 100; base_hp += (random_level - level) * 100; } - + current_hp = max_hp; } - + // Don't add max_dmg to dynamically scaled NPCs since this will be calculated later if (max_dmg > 0 || skip_auto_scale) { @@ -2817,7 +2817,7 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { // I believe that the assumption is, barring no entry in npc_faction_entries // that two npcs on like faction con ally to each other. This catches cases - // where an npc is on a faction but has no hits (hence no entry in + // where an npc is on a faction but has no hits (hence no entry in // npc_faction_entries). if (GetPrimaryFaction() == other_faction) From f5817677df39bc04e2d1a8b598d0a0f5b7449808 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 3 Jan 2021 16:42:34 -0600 Subject: [PATCH 2/3] Only add self to others when moving --- zone/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index e7ce31806..434781841 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -724,7 +724,7 @@ bool NPC::Process() SpellProcess(); if (mob_close_scan_timer.Check()) { - entity_list.ScanCloseMobs(close_mobs, this, true); + entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); } const uint16 npc_mob_close_scan_timer_moving = 6000; From 53bbbbba1d006071c15a06567b8160b7c9be914d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 3 Jan 2021 17:07:10 -0600 Subject: [PATCH 3/3] Add comments around close mob system [skip ci] --- zone/entity.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 659048d35..cfba6eb7d 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2693,6 +2693,36 @@ void EntityList::RemoveAuraFromMobs(Mob *aura) } /** + * The purpose of this system is so that we cache relevant entities that are "close" + * + * In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality + * we only care about entities closest to us + * + * A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation + * of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds) + * versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close + * lists remain always up to date + * + * Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute + * After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is actually far less today) + * + * Places in the code where this logic makes a huge impact + * + * Aggro checks (zone wide -> close) + * Aura processing (zone wide -> close) + * AE Taunt (zone wide -> close) + * AOE Spells (zone wide -> close) + * Bard Pulse AOE (zone wide -> close) + * Mass Group Buff (zone wide -> close) + * AE Attack (zone wide -> close) + * Packet QueueCloseClients (zone wide -> close) + * Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close) + * AI Yell for Help (NPC Assist other NPCs) (zone wide -> close) + * + * All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude + * less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire + * entity list (zone wide) + * * @param close_mobs * @param scanning_mob */ @@ -5190,6 +5220,8 @@ void EntityList::ReloadMerchants() { * If we have a distance requested that is greater than our scanning distance * then we return the full list * + * See comments @EntityList::ScanCloseMobs for system explanation + * * @param mob * @param distance * @return