diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 8e17fb951..be7f01e14 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6289,7 +6289,20 @@ INSERT INTO `items_evolving_details` VALUES )", .content_schema_update = true - } + }, + ManifestEntry{ + .version = 9291, + .description = "2025_01_21_add_remove_zone_fields", + .check = "SHOW COLUMNS FROM `zone` LIKE 'client_update_range'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE zone DROP COLUMN IF EXISTS npc_update_range; +ALTER TABLE zone DROP COLUMN IF EXISTS max_movement_update_range; +ALTER TABLE `zone` ADD COLUMN `client_update_range` int(11) NOT NULL DEFAULT 600 AFTER `npc_max_aggro_dist`; +)", + .content_schema_update = true + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 4d4188554..7ec2bab3f 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -143,6 +143,7 @@ namespace Logs { Corpses, XTargets, EvolveItem, + PositionUpdate, MaxCategoryID /* Don't Remove this */ }; @@ -244,7 +245,8 @@ namespace Logs { "EqTime", "Corpses", "XTargets", - "EvolveItem" + "EvolveItem", + "PositionUpdate" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 638657f64..bb65cece5 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -854,6 +854,16 @@ OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogPositionUpdate(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::PositionUpdate))\ + OutF(LogSys, Logs::General, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPositionUpdateDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::PositionUpdate))\ + OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 3ba11466d..1b4293109 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -111,7 +111,7 @@ public: int32_t fast_regen_mana; int32_t fast_regen_endurance; int32_t npc_max_aggro_dist; - uint32_t max_movement_update_range; + uint32_t client_update_range; int32_t underworld_teleport_index; int32_t lava_damage; int32_t min_lava_damage; @@ -220,7 +220,7 @@ public: "fast_regen_mana", "fast_regen_endurance", "npc_max_aggro_dist", - "max_movement_update_range", + "client_update_range", "underworld_teleport_index", "lava_damage", "min_lava_damage", @@ -325,7 +325,7 @@ public: "fast_regen_mana", "fast_regen_endurance", "npc_max_aggro_dist", - "max_movement_update_range", + "client_update_range", "underworld_teleport_index", "lava_damage", "min_lava_damage", @@ -464,7 +464,7 @@ public: e.fast_regen_mana = 180; e.fast_regen_endurance = 180; e.npc_max_aggro_dist = 600; - e.max_movement_update_range = 600; + e.client_update_range = 600; e.underworld_teleport_index = 0; e.lava_damage = 50; e.min_lava_damage = 10; @@ -599,7 +599,7 @@ public: e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -730,7 +730,7 @@ public: v.push_back(columns[89] + " = " + std::to_string(e.fast_regen_mana)); v.push_back(columns[90] + " = " + std::to_string(e.fast_regen_endurance)); v.push_back(columns[91] + " = " + std::to_string(e.npc_max_aggro_dist)); - v.push_back(columns[92] + " = " + std::to_string(e.max_movement_update_range)); + v.push_back(columns[92] + " = " + std::to_string(e.client_update_range)); v.push_back(columns[93] + " = " + std::to_string(e.underworld_teleport_index)); v.push_back(columns[94] + " = " + std::to_string(e.lava_damage)); v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage)); @@ -850,7 +850,7 @@ public: v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -978,7 +978,7 @@ public: v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -1110,7 +1110,7 @@ public: e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -1233,7 +1233,7 @@ public: e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -1406,7 +1406,7 @@ public: v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -1527,7 +1527,7 @@ public: v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); diff --git a/common/timer.h b/common/timer.h index 6678be952..599f7b2f2 100644 --- a/common/timer.h +++ b/common/timer.h @@ -86,6 +86,9 @@ struct BenchTimer void reset() { start_time = clock::now(); } // this is seconds double elapsed() { return std::chrono::duration (clock::now() - start_time).count(); } + std::chrono::milliseconds::rep elapsedMilliseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } + std::chrono::microseconds::rep elapsedMicroseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } + std::chrono::nanoseconds::rep elapsedNanoseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } private: std::chrono::time_point start_time; }; diff --git a/common/version.h b/common/version.h index ba2bb092b..84163134f 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9290 +#define CURRENT_BINARY_DATABASE_VERSION 9291 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/common/zone_store.cpp b/common/zone_store.cpp index 23a59c31e..fa64bd492 100644 --- a/common/zone_store.cpp +++ b/common/zone_store.cpp @@ -676,12 +676,6 @@ int ZoneStore::GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version) return z ? z->npc_max_aggro_dist : DEFAULT_ZONE_MAX_AGGRO_DISTANCE; } -uint32 ZoneStore::GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version) -{ - const auto& z = GetZoneVersionWithFallback(zone_id, version); - return z ? z->max_movement_update_range : DEFAULT_ZONE_MAX_MOVEMENT_UPDATE_RANGE; -} - int8 ZoneStore::GetZoneMinimumExpansion(uint32 zone_id, int version) { const auto& z = GetZoneVersionWithFallback(zone_id, version); diff --git a/common/zone_store.h b/common/zone_store.h index b63e807f2..d8ee7233d 100644 --- a/common/zone_store.h +++ b/common/zone_store.h @@ -94,7 +94,6 @@ public: int GetZoneFastRegenMana(uint32 zone_id, int version = 0); int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0); int GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version = 0); - uint32 GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version = 0); int8 GetZoneMinimumExpansion(uint32 zone_id, int version = 0); int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0); const std::string GetZoneContentFlags(uint32 zone_id, int version = 0); diff --git a/zone/bot.cpp b/zone/bot.cpp index aa746e767..b0f32b406 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7390,39 +7390,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { return; } -/** - * @param close_mobs - * @param scanning_mob - */ -void EntityList::ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob) -{ - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - - close_mobs.clear(); - - for (auto& e : mob_list) { - auto mob = e.second; - - if (!mob->IsClient()) { - continue; - } - - if (mob->GetID() <= 0) { - continue; - } - - float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); - if (distance <= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - else if (mob->GetAggroRange() >= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - } - - LogAIScanCloseDetail("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); -} - uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid) { uint8 need_healed = 0; diff --git a/zone/client.cpp b/zone/client.cpp index f6286f47c..667142f30 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -157,7 +157,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( endupkeep_timer(1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), - m_client_zone_wide_full_position_update_timer(5 * 60 * 1000), + m_client_bulk_npc_pos_update_timer(60 * 1000), tribute_timer(Tribute_duration), proximity_timer(ClientProximity_interval), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), @@ -12939,50 +12939,77 @@ void Client::SendTopLevelInventory() } } -// On a normal basis we limit mob movement updates based on distance -// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes -// -// For very large zones we will also force a full update based on distance -// -// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear -// to full stop when they are actually still pathing -void Client::CheckSendBulkClientPositionUpdate() +void Client::CheckSendBulkNpcPositions() { float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition()); - bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance(); + float update_range = RuleI(Range, MobCloseScanDistance); + bool moved_far_enough_before_bulk_update = distance_moved >= update_range; bool is_ready_to_update = ( - m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update + m_client_bulk_npc_pos_update_timer.Check() || moved_far_enough_before_bulk_update ); - if (IsMoving() && is_ready_to_update) { - LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName()); - + int updated_count = 0; + int skipped_count = 0; + if (is_ready_to_update) { auto &mob_movement_manager = MobMovementManager::Get(); - auto &mob_list = entity_list.GetMobList(); - for (auto &it : mob_list) { - Mob *entity = it.second; - if (!entity->IsNPC()) { + for (auto &e: entity_list.GetMobList()) { + Mob *mob = e.second; + if (!mob->IsNPC()) { continue; } int animation_speed = 0; - if (entity->IsMoving()) { - if (entity->IsRunning()) { - animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed()); + if (mob->IsMoving()) { + if (mob->IsRunning()) { + animation_speed = (mob->IsFeared() ? mob->GetFearSpeed() : mob->GetRunspeed()); } else { - animation_speed = entity->GetWalkspeed(); + animation_speed = mob->GetWalkspeed(); } } - mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this); + // if we have seen this mob before, and it hasn't moved, skip it + if (m_last_seen_mob_position.contains(mob->GetID())) { + if (m_last_seen_mob_position[mob->GetID()] == mob->GetPosition()) { + LogPositionUpdateDetail( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + GetCleanName() + ); + skipped_count++; + continue; + } + } + + mob_movement_manager.SendCommandToClients( + mob, + 0.0, + 0.0, + 0.0, + 0.0, + animation_speed, + ClientRangeAny, + this + ); + + updated_count++; } + LogPositionUpdate( + "[{}] Sent [{}] bulk updated NPC positions, skipped [{}] distance_moved [{}] update_range [{}]", + GetCleanName(), + updated_count, + skipped_count, + distance_moved, + update_range + ); + m_last_position_before_bulk_update = GetPosition(); } } + const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval); const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval); @@ -13123,3 +13150,32 @@ void Client::SetAAEXPPercentage(uint8 percentage) SendAlternateAdvancementStats(); SendAlternateAdvancementTable(); } + +void Client::BroadcastPositionUpdate() +{ + EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer; + + memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(GetX()); + spu->y_pos = FloatToEQ19(GetY()); + spu->z_pos = FloatToEQ19(GetZ()); + spu->heading = FloatToEQ12(GetHeading()); + spu->delta_x = FloatToEQ13(0); + spu->delta_y = FloatToEQ13(0); + spu->delta_z = FloatToEQ13(0); + spu->delta_heading = FloatToEQ10(0); + spu->animation = 0; + + entity_list.QueueCloseClients(this, &outapp, true, zone->GetClientUpdateRange()); + + Group *g = GetGroup(); + if (g) { + for (auto & m : g->members) { + if (m && m->IsClient() && m != this) { + m->CastToClient()->QueuePacket(&outapp); + } + } + } +} diff --git a/zone/client.h b/zone/client.h index e8a93bac5..3ec680a87 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2087,12 +2087,13 @@ private: Timer m_client_npc_aggro_scan_timer; void CheckClientToNpcAggroTimer(); void ClientToNpcAggroProcess(); + void BroadcastPositionUpdate(); // bulk position updates glm::vec4 m_last_position_before_bulk_update; - Timer m_client_zone_wide_full_position_update_timer; + Timer m_client_bulk_npc_pos_update_timer; Timer m_position_update_timer; - void CheckSendBulkClientPositionUpdate(); + void CheckSendBulkNpcPositions(); void BulkSendInventoryItems(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 38c3e299d..066cb49cf 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -971,6 +971,16 @@ void Client::CompleteConnect() RecordStats(); AutoGrantAAPoints(); + // set initial position for mob tracking + m_last_seen_mob_position.reserve(entity_list.GetMobList().size()); + for (auto& mob : entity_list.GetMobList()) { + if (!mob.second->IsNPC()) { + continue; + } + + m_last_seen_mob_position[mob.second->GetID()] = mob.second->GetPosition(); + } + // enforce some rules.. if (!CanEnterZone()) { LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName()); @@ -4956,7 +4966,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { CheckScanCloseMobsMovingTimer(); } - CheckSendBulkClientPositionUpdate(); + CheckSendBulkNpcPositions(); int32 new_animation = ppu->animation; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index fd0028268..2f70dcb2d 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -122,7 +122,7 @@ bool Client::Process() { /* I haven't naturally updated my position in 10 seconds, updating manually */ if (!IsMoving() && m_position_update_timer.Check()) { - SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); + BroadcastPositionUpdate(); } if (mana_timer.Check()) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index c451db90b..af2d2eaf6 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5653,16 +5653,6 @@ int Perl__GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version) return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version); } -uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id); -} - -uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version); -} - int8 Perl__GetZoneMinimumExpansion(uint32 zone_id) { return zone_store.GetZoneMinimumExpansion(zone_id); @@ -6112,8 +6102,6 @@ void perl_register_quest() package.add("GetZoneMaximumExpansion", (int8(*)(uint32, int))&Perl__GetZoneMaximumExpansion); package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel); package.add("GetZoneMaximumLevel", (uint8(*)(uint32, int))&Perl__GetZoneMaximumLevel); - package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32))&Perl__GetZoneMaximumMovementUpdateRange); - package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32, int))&Perl__GetZoneMaximumMovementUpdateRange); package.add("GetZoneMaximumPlayers", (int(*)(uint32))&Perl__GetZoneMaximumPlayers); package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers); package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip); diff --git a/zone/entity.cpp b/zone/entity.cpp index fab1be99f..85f492c58 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1717,15 +1717,6 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a } } -/** - * @param sender - * @param app - * @param ignore_sender - * @param distance - * @param skipped_mob - * @param is_ack_required - * @param filter - */ void EntityList::QueueCloseClients( Mob *sender, const EQApplicationPacket *app, @@ -1742,7 +1733,7 @@ void EntityList::QueueCloseClients( } if (distance <= 0) { - distance = 600; + distance = zone->GetClientUpdateRange(); } float distance_squared = distance * distance; @@ -2878,6 +2869,8 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) ); it->second->m_close_mobs.erase(entity_id); + it->second->m_last_seen_mob_position.erase(entity_id); + ++it; } @@ -2931,6 +2924,9 @@ void EntityList::RemoveAuraFromMobs(Mob *aura) // 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) + +BenchTimer g_scan_bench_timer; + void EntityList::ScanCloseMobs(Mob *scanning_mob) { if (!scanning_mob) { @@ -2941,7 +2937,9 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) return; } - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + g_scan_bench_timer.reset(); + + float scan_range = RuleI(Range, MobCloseScanDistance); // Reserve memory in m_close_mobs to avoid frequent re-allocations if not already reserved. // Assuming mob_list.size() as an upper bound for reservation. @@ -2957,7 +2955,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) continue; } - float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + float distance = Distance(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { // add mob to scanning_mob's close list and vice versa // check if the mob is already in the close mobs list before inserting @@ -2969,10 +2967,11 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) } LogAIScanClose( - "[{}] Scanning close list > list_size [{}] moving [{}]", + "[{}] Scanning close list > list_size [{}] moving [{}] elapsed [{}] us", scanning_mob->GetCleanName(), scanning_mob->m_close_mobs.size(), - scanning_mob->IsMoving() ? "true" : "false" + scanning_mob->IsMoving() ? "true" : "false", + g_scan_bench_timer.elapsedMicroseconds() ); } @@ -5759,10 +5758,6 @@ void EntityList::ReloadMerchants() { * then we return the full list * * See comments @EntityList::ScanCloseMobs for system explanation - * - * @param mob - * @param distance - * @return */ std::unordered_map &EntityList::GetCloseMobList(Mob *mob, float distance) { diff --git a/zone/entity.h b/zone/entity.h index 33313f14e..f073a4fb5 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -631,8 +631,6 @@ private: bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff - void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); - void GetBotList(std::list &b_list); private: std::list bot_list; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 6567d2476..bc1b04416 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4668,16 +4668,6 @@ int lua_get_zone_npc_maximum_aggro_distance(uint32 zone_id, int version) return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version); } -uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id); -} - -uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id, int version) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version); -} - int8 lua_get_zone_minimum_expansion(uint32 zone_id) { return zone_store.GetZoneMinimumExpansion(zone_id); @@ -6287,8 +6277,6 @@ luabind::scope lua_register_general() { luabind::def("get_zone_fast_regen_endurance", (int(*)(uint32,int))&lua_get_zone_fast_regen_endurance), luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32))&lua_get_zone_npc_maximum_aggro_distance), luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32,int))&lua_get_zone_npc_maximum_aggro_distance), - luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32))&lua_get_zone_maximum_movement_update_range), - luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32,int))&lua_get_zone_maximum_movement_update_range), luabind::def("get_zone_minimum_expansion", (int8(*)(uint32))&lua_get_zone_minimum_expansion), luabind::def("get_zone_minimum_expansion", (int8(*)(uint32,int))&lua_get_zone_minimum_expansion), luabind::def("get_zone_maximum_expansion", (int8(*)(uint32))&lua_get_zone_maximum_expansion), diff --git a/zone/mob.cpp b/zone/mob.cpp index dd62d9329..41ed631bb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -129,6 +129,7 @@ Mob::Mob( position_update_melee_push_timer(500), hate_list_cleanup_timer(6000), m_scan_close_mobs_timer(6000), + m_see_close_mobs_timer(1000), m_mob_check_moving_timer(1000), bot_attack_flag_timer(10000) { @@ -8612,15 +8613,15 @@ std::unordered_map &Mob::GetCloseMobList(float distance) void Mob::ClearDataBucketCache() { if (IsOfClientBot()) { - uint64 id = 0; + uint64 id = 0; DataBucketLoadType::Type t{}; if (IsBot()) { id = CastToBot()->GetBotID(); - t = DataBucketLoadType::Bot; + t = DataBucketLoadType::Bot; } else if (IsClient()) { id = CastToClient()->CharacterID(); - t = DataBucketLoadType::Client; + t = DataBucketLoadType::Client; } DataBucket::DeleteFromCache(id, t); diff --git a/zone/mob.h b/zone/mob.h index 297aa80cb..e4a495d6b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -201,9 +201,11 @@ public: void DisplayInfo(Mob *mob); - std::unordered_map m_close_mobs; - Timer m_scan_close_mobs_timer; - Timer m_mob_check_moving_timer; + std::unordered_map m_close_mobs; + std::unordered_map m_last_seen_mob_position; + Timer m_scan_close_mobs_timer; + Timer m_see_close_mobs_timer; + Timer m_mob_check_moving_timer; // Bot attack flag Timer bot_attack_flag_timer; diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 3bb5996a2..661c046a1 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -851,12 +851,24 @@ void MobMovementManager::SendCommandToClients( _impl->Stats.TotalSentPosition++; } + if (c->m_last_seen_mob_position.contains(mob->GetID())) { + if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) { + LogPositionUpdate( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + c->GetCleanName() + ); + continue; + } + } + c->QueuePacket(&outapp, false); + c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition(); } } else { float short_range = RuleR(Pathing, ShortMovementUpdateRange); - float long_range = zone->GetNpcPositionUpdateDistance(); + float long_range = RuleI(Range, MobCloseScanDistance); for (auto &c : _impl->Clients) { if (single_client && c != single_client) { @@ -901,7 +913,19 @@ void MobMovementManager::SendCommandToClients( _impl->Stats.TotalSentPosition++; } + if (c->m_last_seen_mob_position.contains(mob->GetID())) { + if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) { + LogPositionUpdate( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + c->GetCleanName() + ); + continue; + } + } + c->QueuePacket(&outapp, false); + c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition(); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index 38f597e63..ba5782c34 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -997,8 +997,8 @@ NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 npc_type->current_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 2254; - npc_type->gender = 2; + npc_type->race = 127; + npc_type->gender = 0; npc_type->class_ = 9; npc_type->deity = 1; npc_type->level = 200; diff --git a/zone/spells.cpp b/zone/spells.cpp index 4ca95255c..723c6833e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7378,15 +7378,17 @@ void Mob::SetHP(int64 hp) void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec) { - NPC *node = nullptr; + NPC *node = nullptr; + for (const auto &n: entity_list.GetNPCList()) { - if (n.second->GetCleanName() == node_name) { + if (n.second->GetEntityVariable("node_parent_id") == std::to_string(GetID())) { node = n.second; break; } } if (!node) { node = NPC::SpawnNodeNPC(node_name, "", GetPosition()); + node->SetEntityVariable("node_parent_id", std::to_string(GetID())); } } diff --git a/zone/zone.cpp b/zone/zone.cpp index f02be2e2b..5488ed914 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1091,7 +1091,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) mMovementManager = &MobMovementManager::Get(); - SetNpcPositionUpdateDistance(0); SetQuestHotReloadQueued(false); } @@ -1374,17 +1373,17 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version) newzone_data.suspend_buffs = z->suspendbuffs; // local attributes - can_bind = z->canbind != 0; - is_city = z->canbind == 2; - can_combat = z->cancombat != 0; - can_levitate = z->canlevitate != 0; - can_castoutdoor = z->castoutdoor != 0; - is_hotzone = z->hotzone != 0; - max_movement_update_range = z->max_movement_update_range; - default_ruleset = z->ruleset; - allow_mercs = true; - m_graveyard_id = z->graveyard_id; - m_max_clients = z->maxclients; + can_bind = z->canbind != 0; + is_city = z->canbind == 2; + can_combat = z->cancombat != 0; + can_levitate = z->canlevitate != 0; + can_castoutdoor = z->castoutdoor != 0; + is_hotzone = z->hotzone != 0; + m_client_update_range = z->client_update_range; + default_ruleset = z->ruleset; + allow_mercs = true; + m_graveyard_id = z->graveyard_id; + m_max_clients = z->maxclients; SetIdleWhenEmpty(z->idle_when_empty); SetSecondsBeforeIdle(z->seconds_before_idle); @@ -1538,10 +1537,6 @@ bool Zone::Process() { if (adv_data && !did_adventure_actions) { DoAdventureActions(); } - - if (GetNpcPositionUpdateDistance() == 0) { - CalculateNpcUpdateDistanceSpread(); - } } if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) { @@ -2735,62 +2730,6 @@ void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) { m_ucss_available = ucss_available; } -int Zone::GetNpcPositionUpdateDistance() const -{ - return npc_position_update_distance; -} - -void Zone::SetNpcPositionUpdateDistance(int in_npc_position_update_distance) -{ - Zone::npc_position_update_distance = in_npc_position_update_distance; -} - -void Zone::CalculateNpcUpdateDistanceSpread() -{ - float max_x = 0; - float max_y = 0; - float min_x = 0; - float min_y = 0; - - auto &mob_list = entity_list.GetMobList(); - - for (auto &it : mob_list) { - Mob *entity = it.second; - if (!entity->IsNPC()) { - continue; - } - - if (entity->GetX() <= min_x) { - min_x = entity->GetX(); - } - - if (entity->GetY() <= min_y) { - min_y = entity->GetY(); - } - - if (entity->GetX() >= max_x) { - max_x = entity->GetX(); - } - - if (entity->GetY() >= max_y) { - max_y = entity->GetY(); - } - } - - int x_spread = int(std::abs(max_x - min_x)); - int y_spread = int(std::abs(max_y - min_y)); - int combined_spread = int(std::abs((x_spread + y_spread) / 2)); - int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange())); - - SetNpcPositionUpdateDistance(update_distance); - - Log(Logs::General, Logs::Debug, - "NPC update spread distance set to [%i] combined_spread [%i]", - update_distance, - combined_spread - ); -} - bool Zone::IsQuestHotReloadQueued() const { return quest_hot_reload_queued; diff --git a/zone/zone.h b/zone/zone.h index c29a89b29..1d46a0415 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -156,9 +156,6 @@ public: bool SaveZoneCFG(); bool DoesAlternateCurrencyExist(uint32 currency_id); - int GetNpcPositionUpdateDistance() const; - void SetNpcPositionUpdateDistance(int in_npc_position_update_distance); - char *adv_data; const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location); @@ -417,7 +414,7 @@ public: SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message)); }; - double GetMaxMovementUpdateRange() const { return max_movement_update_range; } + double GetClientUpdateRange() const { return m_client_update_range; } void SetIsHotzone(bool is_hotzone); @@ -469,7 +466,7 @@ private: bool staticzone; bool zone_has_current_time; bool quest_hot_reload_queued; - double max_movement_update_range; + double m_client_update_range; char *long_name; char *map_name; char *short_name;