From 8d869e0939ca64a3012d4508c75ff88b9aff8fad Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Tue, 15 Oct 2024 20:37:58 +0200 Subject: [PATCH 1/9] add rule for hiding empire adopted and available policies to non-allied empires --- Empire/Empire.cpp | 56 +++++++++++++++++++++++++++++++++++++ Empire/Empire.h | 8 ++++++ default/stringtables/en.txt | 10 ++++++- util/GameRuleRanks.h | 1 + util/SerializeEmpire.cpp | 28 +++++++++++++++---- 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/Empire/Empire.cpp b/Empire/Empire.cpp index 37455a7a2cc..6f42eb2a61f 100644 --- a/Empire/Empire.cpp +++ b/Empire/Empire.cpp @@ -2,6 +2,8 @@ #include "../util/i18n.h" #include "../util/Random.h" +#include "../util/GameRules.h" +#include "../util/GameRuleRanks.h" #include "../util/Logger.h" #include "../util/AppInterface.h" #include "../util/SitRepEntry.h" @@ -64,6 +66,14 @@ namespace { } DeclareThreadSafeLogger(supply); + + void AddRules(GameRules& rules) { + // makes all policies cost 1 influence to adopt + rules.Add(UserStringNop("RULE_HIDDEN_POLICIES"), UserStringNop("RULE_HIDDEN_POLICIES_DESC"), + GameRuleCategories::GameRuleCategory::GENERAL, false, true, + GameRuleRanks::RULE_HIDDEN_POLICIES_RANK); + } + bool temp_bool = RegisterGameRules(&AddRules); } //////////// @@ -3112,3 +3122,49 @@ int Empire::TotalBuildingsOwned() const { counter += entry.second; return counter; } + +decltype(Empire::m_adopted_policies) +Empire::GetAdoptedPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const +{ + if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || + diplo_status == DiplomaticStatus::DIPLO_ALLIED || + !GetGameRules().Get("RULE_HIDDEN_POLICIES")) + { return m_adopted_policies; } + return {}; +} + +decltype(Empire::m_initial_adopted_policies) +Empire::GetInitialPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { + if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || + diplo_status == DiplomaticStatus::DIPLO_ALLIED || + !GetGameRules().Get("RULE_HIDDEN_POLICIES")) + { return m_adopted_policies; } + return {}; +} + +decltype(Empire::m_policy_adoption_total_duration) +Empire::GetAdoptionTotalDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { + if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || + diplo_status == DiplomaticStatus::DIPLO_ALLIED || + !GetGameRules().Get("RULE_HIDDEN_POLICIES")) + { return m_policy_adoption_total_duration; } + return {}; +} + +decltype(Empire::m_policy_adoption_current_duration) +Empire::GetAdoptionCurrentDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { + if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || + diplo_status == DiplomaticStatus::DIPLO_ALLIED || + !GetGameRules().Get("RULE_HIDDEN_POLICIES")) + { return m_policy_adoption_current_duration; } + return {}; +} + +decltype(Empire::m_available_policies) +Empire::GetAvailablePoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { + if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || + diplo_status == DiplomaticStatus::DIPLO_ALLIED || + !GetGameRules().Get("RULE_HIDDEN_POLICIES")) + { return m_available_policies; } + return {}; +} diff --git a/Empire/Empire.h b/Empire/Empire.h index b5859efe66a..f37468ffc70 100644 --- a/Empire/Empire.h +++ b/Empire/Empire.h @@ -522,6 +522,12 @@ class FO_COMMON_API Empire final { std::map m_policy_adoption_current_duration; ///< how many turns each currently-adopted policy has been adopted since it was last adopted. somewhat redundant with adoption_turn in AdoptionInfo, but seems necessary to avoid off-by-one issues between client and server std::set> m_available_policies; ///< names of unlocked policies + decltype(m_adopted_policies) GetAdoptedPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; + decltype(m_initial_adopted_policies) GetInitialPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; + decltype(m_policy_adoption_total_duration) GetAdoptionTotalDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; + decltype(m_policy_adoption_current_duration) GetAdoptionCurrentDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; + decltype(m_available_policies) GetAvailablePoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; + using StringFlatSet = boost::container::flat_set>; using StringIntMap = boost::container::flat_map>; using MeterMap = boost::container::flat_map>; @@ -602,6 +608,8 @@ class FO_COMMON_API Empire final { bool m_authenticated = false; ///< Empire's Player's authentication flag. Set if only player with empire's player's name should play this empire. bool m_eliminated = false; ///< Whether the empire has lost + + friend class boost::serialization::access; Empire(); template diff --git a/default/stringtables/en.txt b/default/stringtables/en.txt index 488ca181b92..84cfbab195f 100644 --- a/default/stringtables/en.txt +++ b/default/stringtables/en.txt @@ -1325,6 +1325,14 @@ RULE_BASIC_VIS_SYSTEM_INFO_SHOWN_DESC If Off: Basic level visible systems that have never been detected at higher visiblity have unknown name and star type.''' +RULE_HIDDEN_POLICIES +Hide Empire Policies + +RULE_HIDDEN_POLICIES_DESC +'''If On: Players who are not allied to an empire do not know other empires' adopted or available policies. + +If Off: All players know all other empires' adopted and available policies.''' + RULE_HABITABLE_SIZE_TINY Tiny planets Habitable size @@ -5702,7 +5710,7 @@ ADOPTED_POLICIES '''Adopted Policies: ''' NO_POLICIES_ADOPTED -No Policies Adopted +No Policies Known Adopted AVAILABLE_PARTS '''Available Parts: ''' diff --git a/util/GameRuleRanks.h b/util/GameRuleRanks.h index 7593e6cb21c..9b94f9d5424 100644 --- a/util/GameRuleRanks.h +++ b/util/GameRuleRanks.h @@ -11,6 +11,7 @@ namespace GameRuleRanks { RULE_RESEED_PRNG_SERVER_RANK = 1, RULE_EXTRASOLAR_SHIP_DETECTION_RANK = 2, RULE_BASIC_VIS_SYSTEM_INFO_SHOWN_RANK = 10, + RULE_HIDDEN_POLICIES_RANK = 20, RULE_NUM_COMBAT_ROUNDS_RANK = 30, RULE_AGGRESSIVE_SHIPS_COMBAT_VISIBLE_RANK = 35, RULE_OVERRIDE_VIS_LEVEL_RANK = 40, diff --git a/util/SerializeEmpire.cpp b/util/SerializeEmpire.cpp index 7f5edeb60e9..54194afb6c0 100644 --- a/util/SerializeEmpire.cpp +++ b/util/SerializeEmpire.cpp @@ -48,14 +48,15 @@ void Empire::serialize(Archive& ar, const unsigned int version) } - auto encoding_empire = GlobalSerializationEncodingForEmpire(); + const auto encoding_empire = GlobalSerializationEncodingForEmpire(); + const auto diplo_status = Empires().GetDiplomaticStatus(m_id, encoding_empire); // TODO: pass in diplo status map? + bool visible = (ALL_EMPIRES == encoding_empire) || (m_id == encoding_empire); // TODO: GameRule for all empire info known to other empires bool allied_visible = visible; if constexpr (Archive::is_saving::value) - allied_visible = allied_visible || Empires().GetDiplomaticStatus(m_id, GlobalSerializationEncodingForEmpire()) == - DiplomaticStatus::DIPLO_ALLIED; // TODO: pass in diplo status map? + allied_visible = allied_visible || diplo_status == DiplomaticStatus::DIPLO_ALLIED; TraceLogger() << "serializing empire " << m_id << ": " << m_name; TraceLogger() << "encoding empire: " << encoding_empire; @@ -97,13 +98,25 @@ void Empire::serialize(Archive& ar, const unsigned int version) m_available_policies.clear(); m_available_policies.insert(available_policies.begin(), available_policies.end()); - } else { + } else if constexpr (Archive::is_loading::value) { ar & BOOST_SERIALIZATION_NVP(m_adopted_policies) & BOOST_SERIALIZATION_NVP(m_initial_adopted_policies) & BOOST_SERIALIZATION_NVP(m_available_policies); + } else { + const auto adopted_to_serialize = GetAdoptedPoliciesToSerialize(encoding_empire, diplo_status); + const auto initial_to_serialize = GetInitialPoliciesToSerialize(encoding_empire, diplo_status); + const auto available_to_serialize = GetAvailablePoliciesToSerialize(encoding_empire, diplo_status); + ar & boost::serialization::make_nvp("m_adopted_policies", adopted_to_serialize) + & boost::serialization::make_nvp("m_initial_adopted_policies", initial_to_serialize) + & boost::serialization::make_nvp("m_available_policies", available_to_serialize); } - ar & BOOST_SERIALIZATION_NVP(m_policy_adoption_total_duration); + if constexpr (Archive::is_loading::value) { + ar & BOOST_SERIALIZATION_NVP(m_policy_adoption_total_duration); + } else { + const auto adopted_durations_to_serialize = GetAdoptionTotalDurationsToSerialize(encoding_empire, diplo_status); + ar & boost::serialization::make_nvp("m_policy_adoption_total_duration", adopted_durations_to_serialize); + } if (Archive::is_loading::value && version < 7) { const auto* app = IApp::GetApp(); @@ -114,8 +127,11 @@ void Empire::serialize(Archive& ar, const unsigned int version) m_policy_adoption_current_duration[policy_name] = app ? (current_turn - adoption_info.adoption_turn) : 0; } - } else { + } else if constexpr (Archive::is_loading::value) { ar & BOOST_SERIALIZATION_NVP(m_policy_adoption_current_duration); + } else { + const auto current_durations_to_serialize = GetAdoptionCurrentDurationsToSerialize(encoding_empire, diplo_status); + ar & boost::serialization::make_nvp("m_policy_adoption_current_duration", current_durations_to_serialize); } if (Archive::is_loading::value && version < 11) { From 63c7d8b47f6a2652b397e827cf837a48151bcd4c Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Tue, 15 Oct 2024 23:04:16 +0200 Subject: [PATCH 2/9] rangify --- Empire/Empire.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Empire/Empire.cpp b/Empire/Empire.cpp index 6f42eb2a61f..72a92fbe3d3 100644 --- a/Empire/Empire.cpp +++ b/Empire/Empire.cpp @@ -3107,20 +3107,14 @@ void Empire::RecordPlanetDepopulated(const Planet& planet) int Empire::TotalShipPartsOwned() const { // sum counts of all ship parts owned by this empire - int retval = 0; - - for (const auto& part_class : m_ship_part_class_owned) - retval += part_class.second; - - return retval; + auto owned_nums_rng = m_ship_part_class_owned | range_values; + return std::accumulate(owned_nums_rng.begin(), owned_nums_rng.end(), 0); } int Empire::TotalBuildingsOwned() const { // sum up counts for each building type owned by this empire - int counter = 0; - for (const auto& entry : m_building_types_owned) - counter += entry.second; - return counter; + auto owned_nums_rng = m_building_types_owned | range_values; + return std::accumulate(owned_nums_rng.begin(), owned_nums_rng.end(), 0); } decltype(Empire::m_adopted_policies) From 909c40935a249fa54c08e1a7f9b62021488ab030 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Tue, 15 Oct 2024 23:04:48 +0200 Subject: [PATCH 3/9] fix comment --- Empire/Empire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Empire/Empire.cpp b/Empire/Empire.cpp index 72a92fbe3d3..39486eb1226 100644 --- a/Empire/Empire.cpp +++ b/Empire/Empire.cpp @@ -68,7 +68,7 @@ namespace { DeclareThreadSafeLogger(supply); void AddRules(GameRules& rules) { - // makes all policies cost 1 influence to adopt + // makes all policies hidden to no allies rules.Add(UserStringNop("RULE_HIDDEN_POLICIES"), UserStringNop("RULE_HIDDEN_POLICIES_DESC"), GameRuleCategories::GameRuleCategory::GENERAL, false, true, GameRuleRanks::RULE_HIDDEN_POLICIES_RANK); From 8a98b89c416f5772f2b4dde2e3d323594b354e02 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Thu, 17 Oct 2024 18:14:53 +0200 Subject: [PATCH 4/9] -default constructors -make single parameter constructors explicit --- Empire/InfluenceQueue.h | 1 + Empire/ProductionQueue.h | 3 ++- Empire/ResearchQueue.h | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Empire/InfluenceQueue.h b/Empire/InfluenceQueue.h index 0857ab43bc2..cf874a4957e 100644 --- a/Empire/InfluenceQueue.h +++ b/Empire/InfluenceQueue.h @@ -51,6 +51,7 @@ struct FO_COMMON_API InfluenceQueue { using iterator = QueueType::iterator ; using const_iterator = QueueType::const_iterator; + InfluenceQueue() = default; explicit InfluenceQueue(int empire_id) : m_empire_id(empire_id) {} diff --git a/Empire/ProductionQueue.h b/Empire/ProductionQueue.h index 65e17a99ddd..324b73e0b2d 100644 --- a/Empire/ProductionQueue.h +++ b/Empire/ProductionQueue.h @@ -160,7 +160,8 @@ struct FO_COMMON_API ProductionQueue { /** The const ProductionQueue iterator type. Dereference yields a Element. */ typedef QueueType::const_iterator const_iterator; - ProductionQueue(int empire_id); + ProductionQueue() = default; + explicit ProductionQueue(int empire_id); [[nodiscard]] int ProjectsInProgress() const noexcept { return m_projects_in_progress; } ///< number of production projects currently (perhaps partially) funded. [[nodiscard]] float TotalPPsSpent() const; ///< number of PPs currently spent on the projects in this queue. diff --git a/Empire/ResearchQueue.h b/Empire/ResearchQueue.h index 45664b681d1..07ce53655c3 100644 --- a/Empire/ResearchQueue.h +++ b/Empire/ResearchQueue.h @@ -37,7 +37,8 @@ struct FO_COMMON_API ResearchQueue { /** The const ResearchQueue iterator type. Dereference yields an Element. */ typedef QueueType::const_iterator const_iterator; - ResearchQueue(int empire_id) : + ResearchQueue() = default; + explicit ResearchQueue(int empire_id) : m_empire_id(empire_id) {} From bc38390fcd96e49ea7ec0b892f0ee7a225500ee4 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Fri, 18 Oct 2024 18:53:29 +0200 Subject: [PATCH 5/9] rework info hiding to store filtered views within Empires and to update this before serialization --- Empire/Empire.cpp | 228 +++++++++++++++++++++++++++--------- Empire/Empire.h | 63 +++++++--- default/stringtables/en.txt | 8 ++ server/ServerApp.cpp | 45 ++++++- server/ServerFSM.cpp | 10 +- util/GameRuleRanks.h | 1 + util/SerializeEmpire.cpp | 85 +++++++------- 7 files changed, 321 insertions(+), 119 deletions(-) diff --git a/Empire/Empire.cpp b/Empire/Empire.cpp index 39486eb1226..e2e268e7075 100644 --- a/Empire/Empire.cpp +++ b/Empire/Empire.cpp @@ -72,6 +72,12 @@ namespace { rules.Add(UserStringNop("RULE_HIDDEN_POLICIES"), UserStringNop("RULE_HIDDEN_POLICIES_DESC"), GameRuleCategories::GameRuleCategory::GENERAL, false, true, GameRuleRanks::RULE_HIDDEN_POLICIES_RANK); + + // makes all techs hidden to no allies + rules.Add(UserStringNop("RULE_HIDDEN_TECHS_QUEUES_AVAILABILITIES"), + UserStringNop("RULE_HIDDEN_TECHS_QUEUES_AVAILABILITIES_DESC"), + GameRuleCategories::GameRuleCategory::GENERAL, false, true, + GameRuleRanks::RULE_HIDDEN_TECHS_RANK); } bool temp_bool = RegisterGameRules(&AddRules); } @@ -79,12 +85,6 @@ namespace { //////////// // Empire // //////////// -Empire::Empire() : - m_research_queue(m_id), - m_production_queue(m_id), - m_influence_queue(m_id) -{ Init(); } - Empire::Empire(std::string name, std::string player_name, int empire_id, EmpireColor color, bool authenticated) : m_id(empire_id), @@ -2559,8 +2559,8 @@ void Empire::CheckProductionProgress( // consume the item's special and meter consumption - for (auto& [special_name, consumption_map] : sc) { - for (auto [obj_id, consumption] : consumption_map) { + for (const auto& [special_name, consumption_map] : sc) { + for (const auto& [obj_id, consumption] : consumption_map) { auto obj = context.ContextObjects().getRaw(obj_id); if (!obj || !obj->HasSpecial(special_name)) continue; @@ -2570,7 +2570,7 @@ void Empire::CheckProductionProgress( } } for (const auto& [meter_type, consumption_map] : mc) { - for (const auto [obj_id, consumption] : consumption_map) { + for (const auto& [obj_id, consumption] : consumption_map) { auto* obj = context.ContextObjects().getRaw(obj_id); if (!obj) continue; @@ -3004,7 +3004,7 @@ void Empire::UpdateOwnedObjectCounters(const Universe& universe) { // update ship part counts m_ship_parts_owned.clear(); m_ship_part_class_owned.clear(); - for (const auto [design_id, design_count] : m_ship_designs_owned) { + for (const auto& [design_id, design_count] : m_ship_designs_owned) { const ShipDesign* design = universe.GetShipDesign(design_id); if (!design) continue; @@ -3117,48 +3117,168 @@ int Empire::TotalBuildingsOwned() const { return std::accumulate(owned_nums_rng.begin(), owned_nums_rng.end(), 0); } -decltype(Empire::m_adopted_policies) -Empire::GetAdoptedPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const -{ - if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || - diplo_status == DiplomaticStatus::DIPLO_ALLIED || - !GetGameRules().Get("RULE_HIDDEN_POLICIES")) - { return m_adopted_policies; } - return {}; -} - -decltype(Empire::m_initial_adopted_policies) -Empire::GetInitialPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { - if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || - diplo_status == DiplomaticStatus::DIPLO_ALLIED || - !GetGameRules().Get("RULE_HIDDEN_POLICIES")) - { return m_adopted_policies; } - return {}; -} - -decltype(Empire::m_policy_adoption_total_duration) -Empire::GetAdoptionTotalDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { - if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || - diplo_status == DiplomaticStatus::DIPLO_ALLIED || - !GetGameRules().Get("RULE_HIDDEN_POLICIES")) - { return m_policy_adoption_total_duration; } - return {}; -} - -decltype(Empire::m_policy_adoption_current_duration) -Empire::GetAdoptionCurrentDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { - if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || - diplo_status == DiplomaticStatus::DIPLO_ALLIED || - !GetGameRules().Get("RULE_HIDDEN_POLICIES")) - { return m_policy_adoption_current_duration; } - return {}; -} - -decltype(Empire::m_available_policies) -Empire::GetAvailablePoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const { - if (encoding_empire == ALL_EMPIRES || encoding_empire == m_id || - diplo_status == DiplomaticStatus::DIPLO_ALLIED || - !GetGameRules().Get("RULE_HIDDEN_POLICIES")) - { return m_available_policies; } - return {}; +void Empire::PrepPolicyInfoForSerialization(const ScriptingContext& context) { + m_adopted_policies_to_serialize_for_empires.clear(); + m_initial_adopted_policies_to_serialize_for_empires.clear(); + m_policy_adoption_total_duration_to_serialize_for_empires.clear(); + m_policy_adoption_current_duration_to_serialize_for_empires.clear(); + m_available_policies_to_serialize_for_empires.clear(); + + // no entry for an empire ID indicates that the true full state should be used for that empire ID + + if (!GetGameRules().Get("RULE_HIDDEN_POLICIES")) + return; // all empires see truth by default + + for (const auto eid : context.EmpireIDs()) { + if (eid == m_id) + continue; // true policies for self + if (context.ContextDiploStatus(m_id, eid) == DiplomaticStatus::DIPLO_ALLIED) + continue; // true policies for allies + + // default: no info + m_adopted_policies_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_initial_adopted_policies_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_policy_adoption_total_duration_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_policy_adoption_current_duration_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_available_policies_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + } +} + +const decltype(Empire::m_adopted_policies)& +Empire::GetAdoptedPoliciesToSerialize(int encoding_empire) const { + const auto it = m_adopted_policies_to_serialize_for_empires.find(encoding_empire); + return (it == m_adopted_policies_to_serialize_for_empires.end()) ? + m_adopted_policies : it->second; +} + +const decltype(Empire::m_initial_adopted_policies)& +Empire::GetInitialPoliciesToSerialize(int encoding_empire) const { + const auto it = m_initial_adopted_policies_to_serialize_for_empires.find(encoding_empire); + return (it == m_initial_adopted_policies_to_serialize_for_empires.end()) ? + m_initial_adopted_policies : it->second; +} + +const decltype(Empire::m_policy_adoption_total_duration)& +Empire::GetAdoptionTotalDurationsToSerialize(int encoding_empire) const { + const auto it = m_policy_adoption_total_duration_to_serialize_for_empires.find(encoding_empire); + return (it == m_policy_adoption_total_duration_to_serialize_for_empires.end()) ? + m_policy_adoption_total_duration : it->second; +} + +const decltype(Empire::m_policy_adoption_current_duration)& +Empire::GetAdoptionCurrentDurationsToSerialize(int encoding_empire) const { + const auto it = m_policy_adoption_current_duration_to_serialize_for_empires.find(encoding_empire); + return (it == m_policy_adoption_current_duration_to_serialize_for_empires.end()) ? + m_policy_adoption_current_duration : it->second; +} + +const decltype(Empire::m_available_policies)& +Empire::GetAvailablePoliciesToSerialize(int encoding_empire) const { + const auto it = m_available_policies_to_serialize_for_empires.find(encoding_empire); + return (it == m_available_policies_to_serialize_for_empires.end()) ? + m_available_policies : it->second; +} + +void Empire::PrepQueueAvailabilityInfoForSerialization(const ScriptingContext& context) { + m_techs_to_serialize_for_empires.clear(); + m_research_progress_to_serialize_for_empires.clear(); + m_production_queue_to_serialize_for_empires.clear(); + m_influence_queue_to_serialize_for_empires.clear(); + m_available_building_types_to_serialize_for_empires.clear(); + m_available_ship_parts_to_serialize_for_empires.clear(); + m_available_ship_hulls_to_serialize_for_empires.clear(); + + // no entry for an empire ID indicates that the true full state should be used for that empire ID + + if (!GetGameRules().Get("RULE_HIDDEN_TECHS_QUEUES_AVAILABILITIES")) + return; // all empires see truth by default + + for (const auto eid : context.EmpireIDs()) { + if (eid == m_id) + continue; // true policies for self + if (context.ContextDiploStatus(m_id, eid) == DiplomaticStatus::DIPLO_ALLIED) + continue; // true policies for allies + + // default: no info + m_techs_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + + m_research_progress_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_production_queue_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_influence_queue_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_available_building_types_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_available_ship_parts_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + m_available_ship_hulls_to_serialize_for_empires.emplace(std::piecewise_construct, + std::forward_as_tuple(eid), + std::forward_as_tuple()); + } +} + +const decltype(Empire::m_techs)& Empire::GetTechsToSerialize(int encoding_empire) { + const auto it = m_techs_to_serialize_for_empires.find(encoding_empire); + return (it == m_techs_to_serialize_for_empires.end()) ? + m_techs : it->second; +} + +const decltype(Empire::m_research_queue)& Empire::GetResearchQueueToSerialize(int encoding_empire) { + const auto it = m_research_queue_to_serialize_for_empires.find(encoding_empire); + return (it == m_research_queue_to_serialize_for_empires.end()) ? + m_research_queue : it->second; +} + +const decltype(Empire::m_research_progress)& Empire::GetResearchProgressToSerialize(int encoding_empire) { + const auto it = m_research_progress_to_serialize_for_empires.find(encoding_empire); + return (it == m_research_progress_to_serialize_for_empires.end()) ? + m_research_progress : it->second; +} + +const decltype(Empire::m_production_queue)& Empire::GetProductionQueueToSerialize(int encoding_empire) { + const auto it = m_production_queue_to_serialize_for_empires.find(encoding_empire); + return (it == m_production_queue_to_serialize_for_empires.end()) ? + m_production_queue : it->second; +} + +const decltype(Empire::m_influence_queue)& Empire::GetInfluenceQueueToSerialize(int encoding_empire) { + const auto it = m_influence_queue_to_serialize_for_empires.find(encoding_empire); + return (it == m_influence_queue_to_serialize_for_empires.end()) ? + m_influence_queue : it->second; +} + +const decltype(Empire::m_available_building_types)& Empire::GetAvailableBuildingsToSerialize(int encoding_empire) { + const auto it = m_available_building_types_to_serialize_for_empires.find(encoding_empire); + return (it == m_available_building_types_to_serialize_for_empires.end()) ? + m_available_building_types : it->second; +} + +const decltype(Empire::m_available_ship_parts)& Empire::GetAvailablePartsToSerialize(int encoding_empire) { + const auto it = m_available_ship_parts_to_serialize_for_empires.find(encoding_empire); + return (it == m_available_ship_parts_to_serialize_for_empires.end()) ? + m_available_ship_parts : it->second; +} + +const decltype(Empire::m_available_ship_hulls)& Empire::GetAvailableHullsToSerialize(int encoding_empire) { + const auto it = m_available_ship_hulls_to_serialize_for_empires.find(encoding_empire); + return (it == m_available_ship_hulls_to_serialize_for_empires.end()) ? + m_available_ship_hulls : it->second; } diff --git a/Empire/Empire.h b/Empire/Empire.h index f37468ffc70..704dcb3b116 100644 --- a/Empire/Empire.h +++ b/Empire/Empire.h @@ -184,7 +184,6 @@ class FO_COMMON_API Empire final { * is determined in Empire::UpdateSupplyUnobstructedSystems(). */ [[nodiscard]] bool PreservedLaneTravel(int start_system_id, int dest_system_id) const; - using IntSet = boost::container::flat_set; struct LaneEndpoints { int start = INVALID_OBJECT_ID; int end = INVALID_OBJECT_ID; @@ -200,6 +199,8 @@ class FO_COMMON_API Empire final { }; using LaneSet = boost::container::flat_set; + using IntSet = boost::container::flat_set; + [[nodiscard]] IntSet ExploredSystems() const; ///< ids of systems that this empire has explored [[nodiscard]] int TurnSystemExplored(int system_id) const; [[nodiscard]] LaneSet KnownStarlanes(const Universe& universe) const; ///< map from system id (start) to set of system ids (endpoints) of all starlanes known to this empire @@ -522,11 +523,23 @@ class FO_COMMON_API Empire final { std::map m_policy_adoption_current_duration; ///< how many turns each currently-adopted policy has been adopted since it was last adopted. somewhat redundant with adoption_turn in AdoptionInfo, but seems necessary to avoid off-by-one issues between client and server std::set> m_available_policies; ///< names of unlocked policies - decltype(m_adopted_policies) GetAdoptedPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; - decltype(m_initial_adopted_policies) GetInitialPoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; - decltype(m_policy_adoption_total_duration) GetAdoptionTotalDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; - decltype(m_policy_adoption_current_duration) GetAdoptionCurrentDurationsToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; - decltype(m_available_policies) GetAvailablePoliciesToSerialize(int encoding_empire, DiplomaticStatus diplo_status) const; +public: + // prepare policy info to serialize for various recipient empires + void PrepPolicyInfoForSerialization(const ScriptingContext& context); + +private: + std::map m_adopted_policies_to_serialize_for_empires; + std::map m_initial_adopted_policies_to_serialize_for_empires; + std::map m_policy_adoption_total_duration_to_serialize_for_empires; + std::map m_policy_adoption_current_duration_to_serialize_for_empires; + std::map m_available_policies_to_serialize_for_empires; + + const decltype(m_adopted_policies)& GetAdoptedPoliciesToSerialize(int encoding_empire) const; + const decltype(m_initial_adopted_policies)& GetInitialPoliciesToSerialize(int encoding_empire) const; + const decltype(m_policy_adoption_total_duration)& GetAdoptionTotalDurationsToSerialize(int encoding_empire) const; + const decltype(m_policy_adoption_current_duration)& GetAdoptionCurrentDurationsToSerialize(int encoding_empire) const; + const decltype(m_available_policies)& GetAvailablePoliciesToSerialize(int encoding_empire) const; + using StringFlatSet = boost::container::flat_set>; using StringIntMap = boost::container::flat_map>; @@ -598,20 +611,42 @@ class FO_COMMON_API Empire final { int m_auto_turn_count = 0; ///< auto-turn counter value int m_last_turn_received = INVALID_GAME_TURN; ///< last turn empire completedly received game state - /** The source id is the id of any object owned by the empire. It is - mutable so that Source() can be const and still cache its result. */ - mutable int m_source_id = INVALID_OBJECT_ID; +public: + // prepare tech, queue, and availability info for serialization for various empires + void PrepQueueAvailabilityInfoForSerialization(const ScriptingContext& context); - int m_outposts_owned = 0; ///< how many uncolonized outposts does this empire currently own? +private: + std::map m_techs_to_serialize_for_empires; + std::map m_research_queue_to_serialize_for_empires; + std::map m_research_progress_to_serialize_for_empires; + std::map m_production_queue_to_serialize_for_empires; + std::map m_influence_queue_to_serialize_for_empires; + std::map m_available_building_types_to_serialize_for_empires; + std::map m_available_ship_parts_to_serialize_for_empires; + std::map m_available_ship_hulls_to_serialize_for_empires; + + const decltype(Empire::m_techs)& GetTechsToSerialize(int encoding_empire); + const decltype(Empire::m_research_queue)& GetResearchQueueToSerialize(int encoding_empire); + const decltype(Empire::m_research_progress)& GetResearchProgressToSerialize(int encoding_empire); + const decltype(Empire::m_production_queue)& GetProductionQueueToSerialize(int encoding_empire); + const decltype(Empire::m_influence_queue)& GetInfluenceQueueToSerialize(int encoding_empire); + const decltype(Empire::m_available_building_types)& GetAvailableBuildingsToSerialize(int encoding_empire); + const decltype(Empire::m_available_ship_parts)& GetAvailablePartsToSerialize(int encoding_empire); + const decltype(Empire::m_available_ship_hulls)& GetAvailableHullsToSerialize(int encoding_empire); - bool m_ready = false; ///< readiness status of empire - bool m_authenticated = false; ///< Empire's Player's authentication flag. Set if only player with empire's player's name should play this empire. - bool m_eliminated = false; ///< Whether the empire has lost + /** The source id is the id of any object owned by the empire. It is + mutable so that Source() can be const and still cache its result. */ + mutable int m_source_id = INVALID_OBJECT_ID; + + int m_outposts_owned = 0; ///< how many uncolonized outposts does this empire currently own? + bool m_ready = false; ///< readiness status of empire + bool m_authenticated = false; ///< Empire's Player's authentication flag. Set if only player with empire's player's name should play this empire. + bool m_eliminated = false; ///< Whether the empire has lost friend class boost::serialization::access; - Empire(); + Empire() { Init(); } template void serialize(Archive& ar, const unsigned int version); }; diff --git a/default/stringtables/en.txt b/default/stringtables/en.txt index 84cfbab195f..f2229abb6ae 100644 --- a/default/stringtables/en.txt +++ b/default/stringtables/en.txt @@ -1333,6 +1333,14 @@ RULE_HIDDEN_POLICIES_DESC If Off: All players know all other empires' adopted and available policies.''' +RULE_HIDDEN_TECHS_QUEUES_AVAILABILITIES +Hide Empire Techs, Queues, and Availabilites + +RULE_HIDDEN_TECHS_QUEUES_AVAILABILITIES_DESC +'''If On: Players who are not allied to an empire do not know other empires' researched techs, queues and part, hull, or building availabilities. + +If Off: All players know all other empires' researched techs, queues, and availabilities.''' + RULE_HABITABLE_SIZE_TINY Tiny planets Habitable size diff --git a/server/ServerApp.cpp b/server/ServerApp.cpp index ff6606a2ab2..2ebace25b14 100644 --- a/server/ServerApp.cpp +++ b/server/ServerApp.cpp @@ -928,6 +928,15 @@ bool ServerApp::NewGameInitVerifyJoiners(const std::vector& pla void ServerApp::SendNewGameStartMessages() { std::map player_info_map = GetPlayerInfoMap(); + const ScriptingContext context{m_universe, m_empires, m_galaxy_setup_data, + m_species_manager, m_supply_manager}; + + for (auto& empire : m_empires | range_values) { + empire->UpdateOwnedObjectCounters(m_universe); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } + // send new game start messages DebugLogger() << "SendGameStartMessages: Sending GameStartMessages to players"; for (auto player_connection_it = m_networking.established_begin(); // can't easily use range for loop due to non-standard begin and end @@ -1468,6 +1477,15 @@ void ServerApp::LoadGameInit(const std::vector& player_save_ const auto player_info_map = GetPlayerInfoMap(); + + + for (auto& empire : m_empires | range_values) { + empire->UpdateOwnedObjectCounters(m_universe); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } + + // assemble player state information, and send game start messages DebugLogger() << "ServerApp::CommonGameInit: Sending GameStartMessages to players"; @@ -2090,12 +2108,20 @@ int ServerApp::AddPlayerIntoGame(const PlayerConnectionPtr& player_connection, i if (GetOptionsDB().Get("network.server.drop-empire-ready")) { // drop ready status empire->SetReady(false); - m_networking.SendMessageAll(PlayerStatusMessage(Message::PlayerStatus::PLAYING_TURN, - empire_id)); + m_networking.SendMessageAll(PlayerStatusMessage(Message::PlayerStatus::PLAYING_TURN, empire_id)); } - auto player_info_map = GetPlayerInfoMap(); - bool use_binary_serialization = player_connection->IsBinarySerializationUsed(); + const auto player_info_map = GetPlayerInfoMap(); + const bool use_binary_serialization = player_connection->IsBinarySerializationUsed(); + + const ScriptingContext context{m_universe, m_empires, m_galaxy_setup_data, + m_species_manager, m_supply_manager}; + + for (auto& empire : m_empires | range_values) { + empire->UpdateOwnedObjectCounters(m_universe); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } player_connection->SendMessage(GameStartMessage( m_single_player_game, empire_id, @@ -4313,6 +4339,12 @@ void ServerApp::PreCombatProcessTurns() { // indicate that the clients are waiting for their new Universes m_networking.SendMessageAll(TurnProgressMessage(Message::TurnProgressPhase::DOWNLOADING)); + for (auto& empire : m_empires | range_values) { + empire->UpdateOwnedObjectCounters(m_universe); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } + // send partial turn updates to all players after orders and movement // exclude those without empire and who are not Observer or Moderator for (auto player_it = m_networking.established_begin(); @@ -4639,8 +4671,11 @@ void ServerApp::PostCombatProcessTurns() { // misc. other updates and records m_universe.UpdateStatRecords(context); - for (auto& empire : m_empires | range_values) + for (auto& empire : m_empires | range_values) { empire->UpdateOwnedObjectCounters(m_universe); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } // indicate that the clients are waiting for their new gamestate diff --git a/server/ServerFSM.cpp b/server/ServerFSM.cpp index 3118c0bd41a..d0744711933 100644 --- a/server/ServerFSM.cpp +++ b/server/ServerFSM.cpp @@ -3418,7 +3418,15 @@ sc::result WaitingForTurnEnd::react(const RevertOrders& msg) { server.ClearEmpireTurnOrders(empire_id); // re-send player initial turn update - bool use_binary_serialization = sender->IsBinarySerializationUsed(); + const ScriptingContext context{server.GetUniverse(), server.Empires(), server.GetGalaxySetupData(), + server.GetSpeciesManager(), server.GetSupplyManager()}; + for (auto& empire : server.Empires() | range_values) { + empire->UpdateOwnedObjectCounters(server.GetUniverse()); + empire->PrepQueueAvailabilityInfoForSerialization(context); + empire->PrepPolicyInfoForSerialization(context); + } + + const bool use_binary_serialization = sender->IsBinarySerializationUsed(); sender->SendMessage(TurnUpdateMessage(empire_id, server.CurrentTurn(), server.Empires(), server.GetUniverse(), server.GetSpeciesManager(), GetCombatLogManager(), diff --git a/util/GameRuleRanks.h b/util/GameRuleRanks.h index 9b94f9d5424..a6a8554c4b6 100644 --- a/util/GameRuleRanks.h +++ b/util/GameRuleRanks.h @@ -12,6 +12,7 @@ namespace GameRuleRanks { RULE_EXTRASOLAR_SHIP_DETECTION_RANK = 2, RULE_BASIC_VIS_SYSTEM_INFO_SHOWN_RANK = 10, RULE_HIDDEN_POLICIES_RANK = 20, + RULE_HIDDEN_TECHS_RANK = 21, RULE_NUM_COMBAT_ROUNDS_RANK = 30, RULE_AGGRESSIVE_SHIPS_COMBAT_VISIBLE_RANK = 35, RULE_OVERRIDE_VIS_LEVEL_RANK = 40, diff --git a/util/SerializeEmpire.cpp b/util/SerializeEmpire.cpp index 54194afb6c0..4546ab438ef 100644 --- a/util/SerializeEmpire.cpp +++ b/util/SerializeEmpire.cpp @@ -38,6 +38,8 @@ void Empire::serialize(Archive& ar, const unsigned int version) ar & BOOST_SERIALIZATION_NVP(m_capital_id) & BOOST_SERIALIZATION_NVP(m_source_id) & BOOST_SERIALIZATION_NVP(m_eliminated); + + if (Archive::is_loading::value && version < 13) { std::set victories; ar & boost::serialization::make_nvp("m_victories", victories); @@ -49,23 +51,8 @@ void Empire::serialize(Archive& ar, const unsigned int version) const auto encoding_empire = GlobalSerializationEncodingForEmpire(); - const auto diplo_status = Empires().GetDiplomaticStatus(m_id, encoding_empire); // TODO: pass in diplo status map? - - bool visible = - (ALL_EMPIRES == encoding_empire) || - (m_id == encoding_empire); // TODO: GameRule for all empire info known to other empires - bool allied_visible = visible; - if constexpr (Archive::is_saving::value) - allied_visible = allied_visible || diplo_status == DiplomaticStatus::DIPLO_ALLIED; - - TraceLogger() << "serializing empire " << m_id << ": " << m_name; - TraceLogger() << "encoding empire: " << encoding_empire; - if constexpr (Archive::is_loading::value) { - TraceLogger() << std::string(visible ? "visible" : "NOT visible"); - } else { - TraceLogger() << std::string(visible ? "visible" : "NOT visible") << " / " - << std::string(allied_visible ? "allied visible" : "NOT allied visible"); - } + TraceLogger() << "serializing empire " << m_id << ": " << m_name + << " for encoding empire: " << encoding_empire; if (Archive::is_loading::value && version < 11) { std::map techs; @@ -77,8 +64,12 @@ void Empire::serialize(Archive& ar, const unsigned int version) ar & boost::serialization::make_nvp("m_techs", techs); m_techs.insert(boost::container::ordered_unique_range, techs.begin(), techs.end()); + } else if constexpr (Archive::is_loading::value) { + ar & boost::serialization::make_nvp("m_techs", m_techs); + } else { - ar & BOOST_SERIALIZATION_NVP(m_techs); + const auto& techs_to_serialize = GetTechsToSerialize(encoding_empire); + ar & boost::serialization::make_nvp("m_techs", techs_to_serialize); } if (Archive::is_loading::value && version < 10) { @@ -103,9 +94,9 @@ void Empire::serialize(Archive& ar, const unsigned int version) & BOOST_SERIALIZATION_NVP(m_initial_adopted_policies) & BOOST_SERIALIZATION_NVP(m_available_policies); } else { - const auto adopted_to_serialize = GetAdoptedPoliciesToSerialize(encoding_empire, diplo_status); - const auto initial_to_serialize = GetInitialPoliciesToSerialize(encoding_empire, diplo_status); - const auto available_to_serialize = GetAvailablePoliciesToSerialize(encoding_empire, diplo_status); + const auto& adopted_to_serialize = GetAdoptedPoliciesToSerialize(encoding_empire); + const auto& initial_to_serialize = GetInitialPoliciesToSerialize(encoding_empire); + const auto& available_to_serialize = GetAvailablePoliciesToSerialize(encoding_empire); ar & boost::serialization::make_nvp("m_adopted_policies", adopted_to_serialize) & boost::serialization::make_nvp("m_initial_adopted_policies", initial_to_serialize) & boost::serialization::make_nvp("m_available_policies", available_to_serialize); @@ -114,7 +105,7 @@ void Empire::serialize(Archive& ar, const unsigned int version) if constexpr (Archive::is_loading::value) { ar & BOOST_SERIALIZATION_NVP(m_policy_adoption_total_duration); } else { - const auto adopted_durations_to_serialize = GetAdoptionTotalDurationsToSerialize(encoding_empire, diplo_status); + const auto& adopted_durations_to_serialize = GetAdoptionTotalDurationsToSerialize(encoding_empire); ar & boost::serialization::make_nvp("m_policy_adoption_total_duration", adopted_durations_to_serialize); } @@ -127,10 +118,12 @@ void Empire::serialize(Archive& ar, const unsigned int version) m_policy_adoption_current_duration[policy_name] = app ? (current_turn - adoption_info.adoption_turn) : 0; } + } else if constexpr (Archive::is_loading::value) { ar & BOOST_SERIALIZATION_NVP(m_policy_adoption_current_duration); + } else { - const auto current_durations_to_serialize = GetAdoptionCurrentDurationsToSerialize(encoding_empire, diplo_status); + const auto& current_durations_to_serialize = GetAdoptionCurrentDurationsToSerialize(encoding_empire); ar & boost::serialization::make_nvp("m_policy_adoption_current_duration", current_durations_to_serialize); } @@ -149,33 +142,14 @@ void Empire::serialize(Archive& ar, const unsigned int version) ar & BOOST_SERIALIZATION_NVP(m_meters); } - if (Archive::is_saving::value && !allied_visible) { - // don't send what other empires building and researching - // and which building and ship parts are available to them - ResearchQueue empty_research_queue(m_id); - decltype(this->m_research_progress) empty_research_progress; - ProductionQueue empty_production_queue(m_id); - decltype(this->m_available_building_types) empty_string_set; - static_assert(std::is_same_vm_available_ship_parts)>); - static_assert(std::is_same_vm_available_ship_hulls)>); - InfluenceQueue empty_influence_queue(m_id); - - ar & boost::serialization::make_nvp("m_research_queue", empty_research_queue) - & boost::serialization::make_nvp("m_research_progress", empty_research_progress) - & boost::serialization::make_nvp("m_production_queue", empty_production_queue) - & boost::serialization::make_nvp("m_influence_queue", empty_influence_queue) - & boost::serialization::make_nvp("m_available_building_types", empty_string_set) - & boost::serialization::make_nvp("m_available_part_types", empty_string_set) - & boost::serialization::make_nvp("m_available_hull_types", empty_string_set); - } else { - // processing all data on deserialization, saving to savegame, - // or sending data to the current empire itself + if constexpr (Archive::is_loading::value) { + // loading ar & BOOST_SERIALIZATION_NVP(m_research_queue) & BOOST_SERIALIZATION_NVP(m_research_progress) & BOOST_SERIALIZATION_NVP(m_production_queue) & BOOST_SERIALIZATION_NVP(m_influence_queue); - if (Archive::is_loading::value && version < 13) { + if (version < 13) { std::set buf; ar & boost::serialization::make_nvp("m_available_building_types", buf); m_available_building_types.insert(boost::container::ordered_unique_range, buf.begin(), buf.end()); @@ -191,12 +165,32 @@ void Empire::serialize(Archive& ar, const unsigned int version) & boost::serialization::make_nvp("m_available_part_types", m_available_ship_parts) & boost::serialization::make_nvp("m_available_hull_types", m_available_ship_hulls); } + + } else { + const auto& research_queue = GetResearchQueueToSerialize(encoding_empire); + const auto& research_progress = GetResearchProgressToSerialize(encoding_empire); + const auto& prod_queue = GetProductionQueueToSerialize(encoding_empire); + const auto& influence_queue = GetInfluenceQueueToSerialize(encoding_empire); + const auto& buildings = GetAvailableBuildingsToSerialize(encoding_empire); + const auto& parts = GetAvailablePartsToSerialize(encoding_empire); + const auto& hulls = GetAvailableHullsToSerialize(encoding_empire); + + ar & boost::serialization::make_nvp("m_research_queue", research_queue) + & boost::serialization::make_nvp("m_research_progress", research_progress) + & boost::serialization::make_nvp("m_production_queue", prod_queue) + & boost::serialization::make_nvp("m_influence_queue", influence_queue) + & boost::serialization::make_nvp("m_available_building_types", buildings) + & boost::serialization::make_nvp("m_available_part_types", parts) + & boost::serialization::make_nvp("m_available_hull_types", hulls); } ar & BOOST_SERIALIZATION_NVP(m_supply_system_ranges); ar & BOOST_SERIALIZATION_NVP(m_supply_unobstructed_systems); ar & BOOST_SERIALIZATION_NVP(m_preserved_system_exit_lanes); + + const bool visible = (ALL_EMPIRES == encoding_empire) || (m_id == encoding_empire); + if (visible) { try { ar & boost::serialization::make_nvp("m_ship_designs", m_known_ship_designs); @@ -269,6 +263,7 @@ void Empire::serialize(Archive& ar, const unsigned int version) } } + ar & BOOST_SERIALIZATION_NVP(m_authenticated); ar & BOOST_SERIALIZATION_NVP(m_ready); ar & BOOST_SERIALIZATION_NVP(m_auto_turn_count); From e6688f2bf4180c454666ad371508066f02d07f59 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Fri, 18 Oct 2024 19:04:43 +0200 Subject: [PATCH 6/9] don't add stuff to std::namespace --- Empire/Empire.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Empire/Empire.cpp b/Empire/Empire.cpp index e2e268e7075..12c8a7f80ad 100644 --- a/Empire/Empire.cpp +++ b/Empire/Empire.cpp @@ -31,10 +31,13 @@ #include #include #if !defined(__cpp_lib_integer_comparison_functions) -namespace std { - inline auto cmp_greater_equal(auto&& lhs, auto&& rhs) { return lhs >= rhs; } - inline auto cmp_less_equal(auto&& lhs, auto&& rhs) { return lhs <= rhs; } +namespace { + constexpr auto cmp_greater_equal(const auto& lhs, const auto& rhs) { return lhs >= rhs; } + constexpr auto cmp_less_equal(const auto& lhs, const auto& rhs) { return lhs <= rhs; } } +#else +using std::cmp_greater_equal; +using std::cmp_less_equal; #endif namespace { @@ -268,7 +271,7 @@ void Empire::AdoptPolicy(const std::string& name, const std::string& category, // convert to vector std::vector adopted_policies_in_category(total_slots_in_category, ""); for (auto& [adopted_policy_slot, adopted_policy_name] : adopted_policies_in_category_map) { - if (adopted_policy_slot < 0 || std::cmp_greater_equal(adopted_policy_slot, adopted_policies_in_category.size())) { + if (adopted_policy_slot < 0 || cmp_greater_equal(adopted_policy_slot, adopted_policies_in_category.size())) { ErrorLogger() << "AdoptPolicy somehow got slot " << adopted_policy_slot << " of adopted policy " << adopted_policy_name << " outside the suitable range with total slots size: " << adopted_policies_in_category.size(); continue; @@ -829,7 +832,7 @@ bool Empire::ShipHullAvailable(const std::string& name) const { return m_available_ship_hulls.contains(name); } float Empire::ProductionStatus(int i, const ScriptingContext& context) const { - if (0 > i || std::cmp_greater_equal(i, m_production_queue.size())) + if (0 > i || cmp_greater_equal(i, m_production_queue.size())) return -1.0f; const float item_progress = m_production_queue[i].progress; const auto item_cost = m_production_queue[i].ProductionCostAndTime(context).first; @@ -1593,7 +1596,7 @@ void Empire::PlaceTechInQueue(const std::string& name, int pos) { auto it = m_research_queue.find(name); - if (pos < 0 || std::cmp_less_equal(m_research_queue.size(), pos)) { + if (pos < 0 || cmp_less_equal(m_research_queue.size(), pos)) { // default to putting at end bool paused = false; if (it != m_research_queue.end()) { @@ -1700,14 +1703,14 @@ void Empire::PlaceProductionOnQueue(const ProductionQueue::ProductionItem& item, } ProductionQueue::Element elem{item, m_id, uuid, number, number, blocksize, location}; - if (pos < 0 || std::cmp_less_equal(m_production_queue.size(), pos)) + if (pos < 0 || cmp_less_equal(m_production_queue.size(), pos)) m_production_queue.push_back(std::move(elem)); else m_production_queue.insert(m_production_queue.begin() + pos, std::move(elem)); } void Empire::SetProductionQuantityAndBlocksize(int index, int quantity, int blocksize) { - if (index < 0 || std::cmp_less_equal(m_production_queue.size(), index)) + if (index < 0 || cmp_less_equal(m_production_queue.size(), index)) throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item."); DebugLogger() << "Empire::SetProductionQuantityAndBlocksize() called for item "<< m_production_queue[index].item.name << "with new quant " << quantity << " and new blocksize " << blocksize; if (quantity < 1) @@ -1732,7 +1735,7 @@ void Empire::SetProductionQuantityAndBlocksize(int index, int quantity, int bloc void Empire::SplitIncompleteProductionItem(int index, boost::uuids::uuid uuid) { DebugLogger() << "Empire::SplitIncompleteProductionItem() called for index " << index; - if (index < 0 || std::cmp_less_equal(m_production_queue.size(), index)) + if (index < 0 || cmp_less_equal(m_production_queue.size(), index)) throw std::runtime_error("Empire::SplitIncompleteProductionItem() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item."); if (m_production_queue[index].item.build_type == BuildType::BT_BUILDING) throw std::runtime_error("Empire::SplitIncompleteProductionItem() : Attempted to split a production item that is not a ship."); @@ -1751,7 +1754,7 @@ void Empire::SplitIncompleteProductionItem(int index, boost::uuids::uuid uuid) { void Empire::DuplicateProductionItem(int index, boost::uuids::uuid uuid) { DebugLogger() << "Empire::DuplicateProductionItem() called for index " << index << " with new UUID: " << boost::uuids::to_string(uuid); - if (index < 0 || std::cmp_less_equal(m_production_queue.size(), index)) + if (index < 0 || cmp_less_equal(m_production_queue.size(), index)) throw std::runtime_error("Empire::DuplicateProductionItem() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item."); auto& elem = m_production_queue[index]; @@ -1759,13 +1762,13 @@ void Empire::DuplicateProductionItem(int index, boost::uuids::uuid uuid) { } void Empire::SetProductionRallyPoint(int index, int rally_point_id) { - if (index < 0 || std::cmp_less_equal(m_production_queue.size(), index)) + if (index < 0 || cmp_less_equal(m_production_queue.size(), index)) throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item."); m_production_queue[index].rally_point_id = rally_point_id; } void Empire::SetProductionQuantity(int index, int quantity) { - if (index < 0 || std::cmp_less_equal(m_production_queue.size(), index)) + if (index < 0 || cmp_less_equal(m_production_queue.size(), index)) throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item."); if (quantity < 1) throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to set the quantity of a build run to a value less than zero."); From df75a5a88de26bc1fc93557ebf9d61324b42e973 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Fri, 18 Oct 2024 20:32:19 +0200 Subject: [PATCH 7/9] warnings, clamping, and safety checks that fix possible crash when rendering a progress bar for a missing building type --- UI/CUIControls.cpp | 37 +++++++++++++++++++++---------------- UI/ProductionWnd.cpp | 20 +++++++++++++------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/UI/CUIControls.cpp b/UI/CUIControls.cpp index 04ad6f257a2..eeb948c316c 100644 --- a/UI/CUIControls.cpp +++ b/UI/CUIControls.cpp @@ -2066,13 +2066,16 @@ void ResourceInfoPanel::DoLayout() { MultiTurnProgressBar::MultiTurnProgressBar(int num_segments, float percent_completed, float percent_predicted, GG::Clr bar_color, GG::Clr bg_color, GG::Clr outline_color) : Control(GG::X0, GG::Y0, GG::X1, GG::Y1, GG::NO_WND_FLAGS), - m_num_segments(num_segments), + m_num_segments(std::min(2048, std::max(1, num_segments))), m_perc_completed(percent_completed), m_perc_predicted(percent_predicted), m_clr_bar(bar_color), m_clr_bg(bg_color), m_clr_outline(outline_color) { + if (m_num_segments > 1000) + WarnLogger() << "Very many segments in MultiTurnProgressBar!"; + // validate percentage values if (m_perc_completed < 0.0f || m_perc_predicted < 0.0f || (m_perc_completed + m_perc_predicted) > 1.0f) @@ -2118,21 +2121,23 @@ void MultiTurnProgressBar::Render() { // segment lines GG::GL2DVertexBuffer segment_verts; GG::GLRGBAColorBuffer segment_colors; - if (m_num_segments > 1u) { - segment_verts.reserve(std::size_t{2u} * m_num_segments); - segment_colors.reserve(std::size_t{2u} * m_num_segments); - - GG::Clr current_colour(GG::DarkenClr(m_clr_bar)); - - for (int n = 1; n < static_cast(m_num_segments); ++n) { - GG::X separator_x(ul.x + Width() * n / static_cast(m_num_segments)); - if (separator_x > comp_rect.lr.x) - current_colour = GG::LightenClr(m_clr_bg); - segment_verts.store(separator_x, ul.y); - segment_verts.store(separator_x, lr.y); - segment_colors.store(current_colour); - segment_colors.store(current_colour); - } + if (m_num_segments > 1u && m_num_segments < Value(Width())) { + try { + segment_verts.reserve(std::size_t{2u} * m_num_segments); + segment_colors.reserve(std::size_t{2u} * m_num_segments); + + GG::Clr current_colour(GG::DarkenClr(m_clr_bar)); + + for (int n = 1; n < static_cast(m_num_segments); ++n) { + GG::X separator_x(ul.x + Width() * n / static_cast(m_num_segments)); + if (separator_x > comp_rect.lr.x) + current_colour = GG::LightenClr(m_clr_bg); + segment_verts.store(separator_x, ul.y); + segment_verts.store(separator_x, lr.y); + segment_colors.store(current_colour); + segment_colors.store(current_colour); + } + } catch (...) {} } glDisable(GL_TEXTURE_2D); diff --git a/UI/ProductionWnd.cpp b/UI/ProductionWnd.cpp index f001d9f6112..a09a9f7a6c6 100644 --- a/UI/ProductionWnd.cpp +++ b/UI/ProductionWnd.cpp @@ -340,13 +340,16 @@ namespace { const ScriptingContext context; SetDragDropDataType(BuildDesignatorWnd::PRODUCTION_ITEM_DROP_TYPE); - auto [total_cost, minimum_turns] = elem.ProductionCostAndTime(context); + auto [total_cost, minimum_turns] = elem.ProductionCostAndTime(context); // may return {-1.0f, -1} total_cost *= elem.blocksize; + total_cost = std::max(total_cost, 0.0f); + minimum_turns = std::max(1, minimum_turns); + auto empire = context.GetEmpire(GGHumanClientApp::GetApp()->EmpireID()); - float pp_accumulated = empire ? empire->ProductionStatus(queue_index, context) : 0.0f; // returns as PP - if (pp_accumulated == -1.0f) - pp_accumulated = 0.0f; + const float pp_accumulated = std::max( + 0.0f, + empire ? empire->ProductionStatus(queue_index, context) : 0.0f); // returns as PP panel = GG::Wnd::Create( GG::X0, GG::Y0, ClientWidth() - X_MARGIN, @@ -410,7 +413,10 @@ namespace { m_turn_spending(turn_spending), m_total_cost(total_cost), m_completed_progress(completed_progress) - {} + { + if (m_total_turns < 1 || m_total_cost < 0) + WarnLogger() << "Low turns or total cost"; + } void QueueProductionItemPanel::CompleteConstruction() { GG::Control::CompleteConstruction(); @@ -523,11 +529,11 @@ namespace { outline_color = GG::LightenClr(outline_color); m_progress_bar = GG::Wnd::Create( - m_total_turns, perc_complete, next_progress, + std::max(m_total_turns, 1), perc_complete, next_progress, GG::LightenClr(ClientUI::TechWndProgressBarBackgroundColor()), ClientUI::TechWndProgressBarColor(), outline_color); - double max_spending_per_turn = m_total_cost / m_total_turns; + double max_spending_per_turn = m_total_cost / std::max(m_total_turns, 1); std::string turn_spending_text = boost::io::str(FlexibleFormat(UserString("PRODUCTION_TURN_COST_STR")) % DoubleToString(m_turn_spending, 3, false) % DoubleToString(max_spending_per_turn, 3, false)); From 184741959f5739088f3b9d2c6c3b7edfa8baa595 Mon Sep 17 00:00:00 2001 From: geoffthemedio Date: Fri, 18 Oct 2024 20:32:39 +0200 Subject: [PATCH 8/9] separate out string construction --- UI/CUIWnd.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UI/CUIWnd.cpp b/UI/CUIWnd.cpp index 391a79f3956..c7f889982de 100644 --- a/UI/CUIWnd.cpp +++ b/UI/CUIWnd.cpp @@ -208,8 +208,9 @@ void CUIWnd::InitSizeMove(GG::Pt ul, GG::Pt lr) { // If the window has already had its default position specified (either in the ctor // or a previous call to this function), apply this position to the window. + const std::string option_window_left_name = option_prefix + window_mode + ".left"; if (db.Get(option_initialized_name) || - db.Get(option_prefix + window_mode + ".left") == INVALID_X) + db.Get(option_window_left_name) == INVALID_X) { SetDefaultedOptions(); SizeMove(ul, lr); From 42cc7dfadb91c23f0780538a241519612b7fafe9 Mon Sep 17 00:00:00 2001 From: O01eg Date: Sat, 19 Oct 2024 20:08:45 +0400 Subject: [PATCH 9/9] Fix use of deprecated asyncio.get_event_loop --- server/ServerFramework.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/ServerFramework.cpp b/server/ServerFramework.cpp index 6f14e71d0c8..856818fbd7b 100644 --- a/server/ServerFramework.cpp +++ b/server/ServerFramework.cpp @@ -110,7 +110,8 @@ auto PythonServer::InitModules() -> bool if (GetOptionsDB().Get("network.server.python.asyncio-interval") > 0) { py::object asyncio = py::import("asyncio"); - m_asyncio_event_loop = asyncio.attr("get_event_loop")(); + m_asyncio_event_loop = asyncio.attr("new_event_loop")(); + asyncio.attr("set_event_loop")(m_asyncio_event_loop); } DebugLogger() << "Server Python modules successfully initialized!";