1 #include "CombatLogManager.h"
2 #include "../universe/Meter.h"
3 #include "../universe/UniverseObject.h"
4 #include "../universe/Enums.h"
5 #include "../util/Serialize.h"
6 #include "../util/Serialize.ipp"
7 #include "../util/Logger.h"
8 #include "CombatEvents.h"
9 
10 
11 namespace {
MaxHealth(const UniverseObject & object)12     static float MaxHealth(const UniverseObject& object) {
13         if (object.ObjectType() == OBJ_SHIP) {
14             return object.GetMeter(METER_MAX_STRUCTURE)->Current();
15 
16         } else if ( object.ObjectType() == OBJ_PLANET ) {
17             const Meter* defense = object.GetMeter(METER_MAX_DEFENSE);
18             const Meter* shield = object.GetMeter(METER_MAX_SHIELD);
19             const Meter* construction = object.GetMeter(METER_TARGET_CONSTRUCTION);
20 
21             float ret = 0.0f;
22             if (defense)
23                 ret += defense->Current();
24             if (shield)
25                 ret += shield->Current();
26             if (construction)
27                 ret += construction->Current();
28             return ret;
29         }
30 
31         return 0.0f;
32     }
33 
CurrentHealth(const UniverseObject & object)34     static float CurrentHealth(const UniverseObject& object) {
35         if (object.ObjectType() == OBJ_SHIP) {
36             return object.GetMeter(METER_STRUCTURE)->Current();
37 
38         } else if (object.ObjectType() == OBJ_PLANET) {
39             const Meter* defense = object.GetMeter(METER_DEFENSE);
40             const Meter* shield = object.GetMeter(METER_SHIELD);
41             const Meter* construction = object.GetMeter(METER_CONSTRUCTION);
42 
43             float ret = 0.0f;
44             if (defense)
45                 ret += defense->Current();
46             if (shield)
47                 ret += shield->Current();
48             if (construction)
49                 ret += construction->Current();
50             return ret;
51         }
52 
53         return 0.0f;
54     }
55 
FillState(CombatParticipantState & state,const UniverseObject & object)56     static void FillState(CombatParticipantState& state, const UniverseObject& object) {
57         state.current_health = CurrentHealth(object);
58         state.max_health = MaxHealth(object);
59     };
60 }
61 
CombatParticipantState()62 CombatParticipantState::CombatParticipantState() {}
63 
CombatParticipantState(const UniverseObject & object)64 CombatParticipantState::CombatParticipantState(const UniverseObject& object)
65 { FillState(*this, object); }
66 
67 ////////////////////////////////////////////////
68 // CombatLog
69 ////////////////////////////////////////////////
CombatLog()70 CombatLog::CombatLog() :
71     turn(INVALID_GAME_TURN),
72     system_id(INVALID_OBJECT_ID)
73 {}
74 
CombatLog(const CombatInfo & combat_info)75 CombatLog::CombatLog(const CombatInfo& combat_info) :
76     turn(combat_info.turn),
77     system_id(combat_info.system_id),
78     empire_ids(combat_info.empire_ids),
79     object_ids(),
80     damaged_object_ids(combat_info.damaged_object_ids),
81     destroyed_object_ids(combat_info.destroyed_object_ids),
82     combat_events(combat_info.combat_events)
83 {
84     // compile all remaining and destroyed objects' ids
85     object_ids = combat_info.destroyed_object_ids;
86     for (const auto& obj : combat_info.objects.all()) {
87         object_ids.insert(obj->ID());
88         participant_states[obj->ID()] = CombatParticipantState(*obj);
89     }
90 }
91 
92 template <typename Archive>
serialize(Archive & ar,const unsigned int version)93 void CombatParticipantState::serialize(Archive& ar, const unsigned int version)
94 {
95     ar & BOOST_SERIALIZATION_NVP(current_health)
96        & BOOST_SERIALIZATION_NVP(max_health);
97 }
98 
99 template <typename Archive>
serialize(Archive & ar,const unsigned int version)100 void CombatLog::serialize(Archive& ar, const unsigned int version)
101 {
102     // CombatEvents are serialized only through
103     // pointers to their base class.
104     // Therefore we need to manually register their types
105     // in the archive.
106     ar.template register_type<WeaponFireEvent>();
107     ar.template register_type<IncapacitationEvent>();
108     ar.template register_type<BoutBeginEvent>();
109     ar.template register_type<InitialStealthEvent>();
110     ar.template register_type<StealthChangeEvent>();
111     ar.template register_type<WeaponsPlatformEvent>();
112 
113     ar  & BOOST_SERIALIZATION_NVP(turn)
114         & BOOST_SERIALIZATION_NVP(system_id)
115         & BOOST_SERIALIZATION_NVP(empire_ids)
116         & BOOST_SERIALIZATION_NVP(object_ids)
117         & BOOST_SERIALIZATION_NVP(damaged_object_ids)
118         & BOOST_SERIALIZATION_NVP(destroyed_object_ids);
119 
120     if (combat_events.size() > 1)
121         TraceLogger() << "CombatLog::serialize turn " << turn << "  combat at " << system_id << "  combat events size: " << combat_events.size();
122     try {
123         ar  & BOOST_SERIALIZATION_NVP(combat_events);
124     } catch (const std::exception& e) {
125         ErrorLogger() << "combat events serializing failed!: caught exception: " << e.what();
126     }
127 
128     ar & BOOST_SERIALIZATION_NVP(participant_states);
129 }
130 
131 template
132 void CombatLog::serialize<freeorion_bin_iarchive>(freeorion_bin_iarchive& ar, const unsigned int version);
133 template
134 void CombatLog::serialize<freeorion_bin_oarchive>(freeorion_bin_oarchive& ar, const unsigned int version);
135 template
136 void CombatLog::serialize<freeorion_xml_iarchive>(freeorion_xml_iarchive& ar, const unsigned int version);
137 template
138 void CombatLog::serialize<freeorion_xml_oarchive>(freeorion_xml_oarchive& ar, const unsigned int version);
139 
140 
141 
142 ////////////////////////////////////////////////
143 // CombatLogManagerImpl
144 ////////////////////////////////////////////////
145 
146 class CombatLogManager::Impl {
147     public:
148     Impl();
149 
150       /** \name Accessors */ //@{
151     /** Return the requested combat log or boost::none.*/
152     boost::optional<const CombatLog&> GetLog(int log_id) const;
153 
154     /** Return the ids of all incomplete logs or none.*/
155     boost::optional<std::vector<int>> IncompleteLogIDs() const;
156     //@}
157 
158     /** \name Mutators */ //@{
159     int  AddLog(const CombatLog& log);   // adds log, returns unique log id
160     /** Replace incomplete log with \p id with \p log. */
161     void CompleteLog(int id, const CombatLog& log);
162     void Clear();
163 
164     /** Serialize log headers so that the receiving LogManager can then request
165         complete logs in the background.*/
166     template <typename Archive>
167     void SerializeIncompleteLogs(Archive& ar, const unsigned int version);
168     //@}
169 
170     void GetLogsToSerialize(std::map<int, CombatLog>& logs, int encoding_empire) const;
171     void SetLog(int log_id, const CombatLog& log);
172 
173     friend class boost::serialization::access;
174     template <typename Archive>
175     void serialize(Archive& ar, const unsigned int version);
176 
177     private:
178     std::unordered_map<int, CombatLog>  m_logs;
179     std::set<int>                       m_incomplete_logs;  // Set of logs ids that do not have bodies and need to be fetched from the server
180     int                                 m_latest_log_id;
181 };
182 
Impl()183 CombatLogManager::Impl::Impl() :
184     m_latest_log_id(-1)
185 {}
186 
GetLog(int log_id) const187 boost::optional<const CombatLog&> CombatLogManager::Impl::GetLog(int log_id) const {
188     auto it = m_logs.find(log_id);
189     if (it != m_logs.end())
190         return it->second;
191     return boost::none;
192 }
193 
AddLog(const CombatLog & log)194 int CombatLogManager::Impl::AddLog(const CombatLog& log) {
195     int new_log_id = ++m_latest_log_id;
196     m_logs[new_log_id] = log;
197     return new_log_id;
198 }
199 
CompleteLog(int id,const CombatLog & log)200 void CombatLogManager::Impl::CompleteLog(int id, const CombatLog& log) {
201     auto incomplete_it = m_incomplete_logs.find(id);
202     if (incomplete_it == m_incomplete_logs.end()) {
203         ErrorLogger() << "CombatLogManager::Impl::CompleteLog id = " << id << " is not an incomplete log, so log is being discarded.";
204         return;
205     }
206     m_incomplete_logs.erase(incomplete_it);
207     m_logs[id] = log;
208 
209     if (id > m_latest_log_id) {
210         for (++m_latest_log_id; m_latest_log_id <= id; ++m_latest_log_id) {
211             m_incomplete_logs.insert(m_latest_log_id);
212         }
213         ErrorLogger() << "CombatLogManager::Impl::CompleteLog id = " << id << " is greater than m_latest_log_id, m_latest_log_id was increased and intervening logs will be requested.";
214     }
215 }
216 
Clear()217 void CombatLogManager::Impl::Clear() {
218     m_logs.clear();
219     m_incomplete_logs.clear();
220     m_latest_log_id = -1;
221 }
222 
GetLogsToSerialize(std::map<int,CombatLog> & logs,int encoding_empire) const223 void CombatLogManager::Impl::GetLogsToSerialize(
224     std::map<int, CombatLog>& logs, int encoding_empire) const
225 {
226     // TODO: filter logs by who should have access to them
227     for (auto it = m_logs.begin(); it != m_logs.end(); ++it)
228         logs.insert({it->first, it->second});
229 }
230 
SetLog(int log_id,const CombatLog & log)231 void CombatLogManager::Impl::SetLog(int log_id, const CombatLog& log)
232 { m_logs[log_id] = log; }
233 
IncompleteLogIDs() const234 boost::optional<std::vector<int>> CombatLogManager::Impl::IncompleteLogIDs() const {
235     if (m_incomplete_logs.empty())
236         return boost::none;
237 
238     // Set the log ids in reverse order so that if the server only has time to
239     // send one log it is the most recent combat log, which is the one most
240     // likely of interest to the player.
241     std::vector<int> ids;
242     for (auto rit = m_incomplete_logs.rbegin(); rit != m_incomplete_logs.rend(); ++rit)
243         ids.push_back(*rit);
244 
245     return ids;
246 }
247 
248 template <typename Archive>
SerializeIncompleteLogs(Archive & ar,const unsigned int version)249 void CombatLogManager::Impl::SerializeIncompleteLogs(Archive& ar, const unsigned int version)
250 {
251     int old_latest_log_id = m_latest_log_id;
252     ar & BOOST_SERIALIZATION_NVP(m_latest_log_id);
253 
254     // If the new m_latest_log_id is greater than the old one then add all
255     // of the new ids to the incomplete log set.
256     if (Archive::is_loading::value && m_latest_log_id > old_latest_log_id)
257         for (++old_latest_log_id; old_latest_log_id <= m_latest_log_id; ++old_latest_log_id)
258             m_incomplete_logs.insert(old_latest_log_id);
259 }
260 
261 template <typename Archive>
serialize(Archive & ar,const unsigned int version)262 void CombatLogManager::Impl::serialize(Archive& ar, const unsigned int version)
263 {
264     std::map<int, CombatLog> logs;
265 
266     if (Archive::is_saving::value) {
267         GetLogsToSerialize(logs, GetUniverse().EncodingEmpire());
268     }
269 
270     ar  & BOOST_SERIALIZATION_NVP(logs)
271         & BOOST_SERIALIZATION_NVP(m_latest_log_id);
272 
273     if (Archive::is_loading::value) {
274         // copy new logs, but don't erase old ones
275         for (auto& log : logs)
276            SetLog(log.first, log.second);
277     }
278 }
279 
280 
CombatLogManager()281 CombatLogManager::CombatLogManager() :
282     m_impl(new Impl)
283 {}
284 
285 // Require here because CombatLogManagerImpl is defined in this file.
~CombatLogManager()286 CombatLogManager::~CombatLogManager() {}
287 
GetLog(int log_id) const288 boost::optional<const CombatLog&> CombatLogManager::GetLog(int log_id) const
289 { return m_impl->GetLog(log_id); }
290 
AddNewLog(const CombatLog & log)291 int CombatLogManager::AddNewLog(const CombatLog& log)
292 { return m_impl->AddLog(log); }
293 
CompleteLog(int id,const CombatLog & log)294 void CombatLogManager::CompleteLog(int id, const CombatLog& log)
295 { m_impl->CompleteLog(id, log); }
296 
Clear()297 void CombatLogManager::Clear()
298 { return m_impl->Clear(); }
299 
IncompleteLogIDs() const300 boost::optional<std::vector<int>> CombatLogManager::IncompleteLogIDs() const
301 { return m_impl->IncompleteLogIDs(); }
302 
GetCombatLogManager()303 CombatLogManager& CombatLogManager::GetCombatLogManager() {
304     static CombatLogManager manager;
305     return manager;
306 }
307 
308 template <typename Archive>
SerializeIncompleteLogs(Archive & ar,const unsigned int version)309 void CombatLogManager::SerializeIncompleteLogs(Archive& ar, const unsigned int version)
310 { m_impl->SerializeIncompleteLogs(ar, version); }
311 
312 template
313 void CombatLogManager::SerializeIncompleteLogs<freeorion_bin_oarchive>(freeorion_bin_oarchive& ar, const unsigned int version);
314 
315 template
316 void CombatLogManager::SerializeIncompleteLogs<freeorion_bin_iarchive>(freeorion_bin_iarchive& ar, const unsigned int version);
317 
318 template
319 void CombatLogManager::SerializeIncompleteLogs<freeorion_xml_oarchive>(freeorion_xml_oarchive& ar, const unsigned int version);
320 
321 template
322 void CombatLogManager::SerializeIncompleteLogs<freeorion_xml_iarchive>(freeorion_xml_iarchive& ar, const unsigned int version);
323 
324 template <typename Archive>
serialize(Archive & ar,const unsigned int version)325 void CombatLogManager::serialize(Archive& ar, const unsigned int version)
326 { m_impl->serialize(ar, version); }
327 
328 template
329 void CombatLogManager::serialize<freeorion_bin_oarchive>(freeorion_bin_oarchive& ar, const unsigned int version);
330 
331 template
332 void CombatLogManager::serialize<freeorion_bin_iarchive>(freeorion_bin_iarchive& ar, const unsigned int version);
333 
334 template
335 void CombatLogManager::serialize<freeorion_xml_oarchive>(freeorion_xml_oarchive& ar, const unsigned int version);
336 
337 template
338 void CombatLogManager::serialize<freeorion_xml_iarchive>(freeorion_xml_iarchive& ar, const unsigned int version);
339 
340 ///////////////////////////////////////////////////////////
341 // Free Functions                                        //
342 ///////////////////////////////////////////////////////////
GetCombatLogManager()343 CombatLogManager& GetCombatLogManager()
344 { return CombatLogManager::GetCombatLogManager(); }
345 
GetCombatLog(int log_id)346 boost::optional<const CombatLog&> GetCombatLog(int log_id)
347 { return GetCombatLogManager().GetLog(log_id); }
348