diff --git a/src/engine/db/world_characters.cpp b/src/engine/db/world_characters.cpp index 6183ef3b4..885181aee 100644 --- a/src/engine/db/world_characters.cpp +++ b/src/engine/db/world_characters.cpp @@ -52,6 +52,10 @@ void Characters::push_front(const CharData::shared_ptr &character) { } character->subscribe_for_rnum_changes(m_rnum_change_observer); + if (!character->IsNpc()) { + m_players.insert(character.get()); // #3414: все игроки в игре + } + if (character->purged()) { /* * Anton Gorev (2017/10/29): It is possible is character quit the game without @@ -166,6 +170,8 @@ void Characters::remove(CharData *character) { character->ZeroCooldowns(); chardata_cooldown_list.erase(clist); } + m_active.erase(character); // #3414: убрать из активного набора + m_players.erase(character); // #3414: убрать из списка игроков m_list.erase(index_i->second); m_character_raw_ptr_to_character_ptr.erase(index_i); auto tmp_it = std::find_if(combat_list.begin(), combat_list.end(), [character] (auto it) {return (it.ch == character); }); diff --git a/src/engine/db/world_characters.h b/src/engine/db/world_characters.h index 081e9b162..9185b33f6 100644 --- a/src/engine/db/world_characters.h +++ b/src/engine/db/world_characters.h @@ -7,6 +7,7 @@ #include #include #include +#include class Characters { public: @@ -54,6 +55,22 @@ class Characters { void AddToExtractedList(CharData *ch); void PurgeExtractedList(); + // #3414: активный набор для point_update. Мобы помечаются в mobile_activity + // (mark_active) и сбрасываются каждый point_update (clear_active); игроки + // держатся постоянно (наполняется в push_front, чистится в remove). + void mark_active(CharData *ch) { m_active.insert(ch); } + const auto &active() const { return m_active; } + const auto &players() const { return m_players; } + void clear_active() { m_active.clear(); } + // Снимок набора для point_update: активные мобы + все игроки (одним вектором). + std::vector active_and_players() const { + std::vector result; + result.reserve(m_active.size() + m_players.size()); + result.insert(result.end(), m_active.begin(), m_active.end()); + result.insert(result.end(), m_players.begin(), m_players.end()); + return result; + } + private: using character_raw_ptr_to_character_ptr_t = std::unordered_map; using set_t = std::unordered_set; @@ -63,6 +80,8 @@ class Characters { std::unordered_set m_extracted_list; character_raw_ptr_to_character_ptr_t m_character_raw_ptr_to_character_ptr; vnum_to_characters_set_t m_vnum_to_characters_set; + std::unordered_set m_active; // #3414: активные мобы (за окно между point_update) + std::unordered_set m_players; // #3414: все игроки в игре CharacterRNum_ChangeObserver::shared_ptr m_rnum_change_observer; list_t m_purge_list; set_t m_purge_set; diff --git a/src/gameplay/ai/mobact.cpp b/src/gameplay/ai/mobact.cpp index dfd52d61d..1e301a055 100644 --- a/src/gameplay/ai/mobact.cpp +++ b/src/gameplay/ai/mobact.cpp @@ -931,6 +931,7 @@ void mobile_activity(int activity_level, int missed_pulses) { continue; } ++processed_mobs; + character_list.mark_active(ch.get()); // #3414: запомнить активного моба для point_update // Examine call for special procedure if (ch->IsFlagged(EMobFlag::kSpec) && !no_specials) { diff --git a/src/gameplay/core/game_limits.cpp b/src/gameplay/core/game_limits.cpp index 32027078a..dd84b45f2 100644 --- a/src/gameplay/core/game_limits.cpp +++ b/src/gameplay/core/game_limits.cpp @@ -1534,13 +1534,17 @@ void point_update() { double t_cond = 0.0, t_hp = 0.0, t_mob = 0.0, t_move = 0.0, t_pos = 0.0, t_idle = 0.0; std::size_t scanned = 0, profiled_chars = 0; - for (auto &ch : character_list) { - const auto i = ch.get(); - ++scanned; + // #3414: вместо скана всего character_list -- только активные мобы (их + // помечает mobile_activity) и все игроки в игре. Снимок, чтобы отложенные + // extract не ломали обход set'ов между итерациями. + const auto to_update = character_list.active_and_players(); + character_list.clear_active(); - if (i->purged() || (i->IsNpc() && !i->in_used_zone())) { + for (auto *i : to_update) { + ++scanned; + if (i->purged()) { continue; - } + } if (i->IsNpc()) { i->inc_restore_timer(kSecsPerMudHour);