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