1 #include "ObjectMap.h"
2
3 #include "Universe.h"
4 #include "UniverseObject.h"
5 #include "Ship.h"
6 #include "Fleet.h"
7 #include "Planet.h"
8 #include "System.h"
9 #include "Building.h"
10 #include "Field.h"
11 #include "Enums.h"
12 #include "../util/Logger.h"
13 #include "../util/AppInterface.h"
14
15
16 #define FOR_EACH_SPECIALIZED_MAP(f, ...) { f(m_resource_centers, ##__VA_ARGS__); \
17 f(m_pop_centers, ##__VA_ARGS__); \
18 f(m_ships, ##__VA_ARGS__); \
19 f(m_fleets, ##__VA_ARGS__); \
20 f(m_planets, ##__VA_ARGS__); \
21 f(m_systems, ##__VA_ARGS__); \
22 f(m_buildings, ##__VA_ARGS__); \
23 f(m_fields, ##__VA_ARGS__); }
24
25 #define FOR_EACH_MAP(f, ...) { f(m_objects, ##__VA_ARGS__); \
26 FOR_EACH_SPECIALIZED_MAP(f, ##__VA_ARGS__); }
27
28 #define FOR_EACH_EXISTING_MAP(f, ...) { f(m_existing_objects, ##__VA_ARGS__); \
29 f(m_existing_resource_centers, ##__VA_ARGS__); \
30 f(m_existing_pop_centers, ##__VA_ARGS__); \
31 f(m_existing_ships, ##__VA_ARGS__); \
32 f(m_existing_fleets, ##__VA_ARGS__); \
33 f(m_existing_planets, ##__VA_ARGS__); \
34 f(m_existing_systems, ##__VA_ARGS__); \
35 f(m_existing_buildings, ##__VA_ARGS__); \
36 f(m_existing_fields, ##__VA_ARGS__); }
37
38
39 namespace {
40 template <typename T>
ClearMap(ObjectMap::container_type<T> & map)41 static void ClearMap(ObjectMap::container_type<T>& map)
42 { map.clear(); }
43
44 template <typename T>
TryInsertIntoMap(ObjectMap::container_type<T> & map,std::shared_ptr<UniverseObject> item)45 static void TryInsertIntoMap(ObjectMap::container_type<T>& map, std::shared_ptr<UniverseObject> item)
46 {
47 if (dynamic_cast<T*>(item.get()))
48 map[item->ID()] = std::dynamic_pointer_cast<T, UniverseObject>(item);
49 }
50
51 template <typename T>
EraseFromMap(ObjectMap::container_type<T> & map,int id)52 void EraseFromMap(ObjectMap::container_type<T>& map, int id)
53 { map.erase(id); }
54 }
55
56
57 /////////////////////////////////////////////
58 // class ObjectMap
59 /////////////////////////////////////////////
ObjectMap()60 ObjectMap::ObjectMap()
61 {}
62
~ObjectMap()63 ObjectMap::~ObjectMap()
64 {}
65
Copy(const ObjectMap & copied_map,int empire_id)66 void ObjectMap::Copy(const ObjectMap& copied_map, int empire_id/* = ALL_EMPIRES*/) {
67 if (&copied_map == this)
68 return;
69
70 // loop through objects in copied map, copying or cloning each depending
71 // on whether there already is a corresponding object in this map
72 for (const auto& obj : copied_map.all())
73 this->CopyObject(obj, empire_id);
74 }
75
CopyForSerialize(const ObjectMap & copied_map)76 void ObjectMap::CopyForSerialize(const ObjectMap& copied_map) {
77 if (&copied_map == this)
78 return;
79
80 // note: the following relies upon only m_objects actually getting serialized by ObjectMap::serialize
81 m_objects.insert(copied_map.m_objects.begin(), copied_map.m_objects.end());
82 }
83
CopyObject(std::shared_ptr<const UniverseObject> source,int empire_id)84 void ObjectMap::CopyObject(std::shared_ptr<const UniverseObject> source, int empire_id/* = ALL_EMPIRES*/) {
85 if (!source)
86 return;
87
88 int source_id = source->ID();
89
90 // can empire see object at all? if not, skip copying object's info
91 if (GetUniverse().GetObjectVisibilityByEmpire(source_id, empire_id) <= VIS_NO_VISIBILITY)
92 return;
93
94 if (auto destination = this->get(source_id)) {
95 destination->Copy(source, empire_id); // there already is a version of this object present in this ObjectMap, so just update it
96 } else {
97 insertCore(std::shared_ptr<UniverseObject>(source->Clone()), empire_id); // this object is not yet present in this ObjectMap, so add a new UniverseObject object for it
98 }
99 }
100
Clone(int empire_id) const101 ObjectMap* ObjectMap::Clone(int empire_id) const {
102 ObjectMap* result = new ObjectMap();
103 result->Copy(*this, empire_id);
104 return result;
105 }
106
empty() const107 bool ObjectMap::empty() const
108 { return m_objects.empty(); }
109
HighestObjectID() const110 int ObjectMap::HighestObjectID() const {
111 if (m_objects.empty())
112 return INVALID_OBJECT_ID;
113 return m_objects.rbegin()->first;
114 }
115
insertCore(std::shared_ptr<UniverseObject> item,int empire_id)116 void ObjectMap::insertCore(std::shared_ptr<UniverseObject> item, int empire_id/* = ALL_EMPIRES*/) {
117 FOR_EACH_MAP(TryInsertIntoMap, item);
118 if (item &&
119 !GetUniverse().EmpireKnownDestroyedObjectIDs(empire_id).count(item->ID()))
120 {
121 auto this_item = this->get(item->ID());
122 m_existing_objects[item->ID()] = this_item;
123 switch (item->ObjectType()) {
124 case OBJ_BUILDING:
125 m_existing_buildings[item->ID()] = this_item;
126 break;
127 case OBJ_FIELD:
128 m_existing_fields[item->ID()] = this_item;
129 break;
130 case OBJ_FLEET:
131 m_existing_fleets[item->ID()] = this_item;
132 break;
133 case OBJ_PLANET:
134 m_existing_planets[item->ID()] = this_item;
135 m_existing_pop_centers[item->ID()] = this_item;
136 m_existing_resource_centers[item->ID()] = this_item;
137 break;
138 case OBJ_POP_CENTER:
139 m_existing_pop_centers[item->ID()] = this_item;
140 break;
141 case OBJ_PROD_CENTER:
142 m_existing_resource_centers[item->ID()] = this_item;
143 break;
144 case OBJ_SHIP:
145 m_existing_ships[item->ID()] = this_item;
146 break;
147 case OBJ_SYSTEM:
148 m_existing_systems[item->ID()] = this_item;
149 break;
150 case OBJ_FIGHTER:
151 default:
152 break;
153 }
154 }
155 }
156
erase(int id)157 std::shared_ptr<UniverseObject> ObjectMap::erase(int id) {
158 // search for object in objects map
159 auto it = m_objects.find(id);
160 if (it == m_objects.end())
161 return nullptr;
162 //DebugLogger() << "Object was removed: " << it->second->Dump();
163 // object found, so store pointer for later...
164 auto result = it->second;
165 // and erase from pointer maps
166 m_objects.erase(it);
167 FOR_EACH_SPECIALIZED_MAP(EraseFromMap, id);
168 FOR_EACH_EXISTING_MAP(EraseFromMap, id);
169 return result;
170 }
171
clear()172 void ObjectMap::clear() {
173 FOR_EACH_MAP(ClearMap);
174 FOR_EACH_EXISTING_MAP(ClearMap);
175 }
176
swap(ObjectMap & rhs)177 void ObjectMap::swap(ObjectMap& rhs) {
178 // SwapMap uses ObjectMap::Map<T> but this function isn't available for the existing maps,
179 // so the FOR_EACH_EXISTING_MAP macro doesn't work with with SwapMap
180 // and it is instead necessary to write them out explicitly.
181 m_existing_objects.swap(rhs.m_existing_objects);
182 m_existing_buildings.swap(rhs.m_existing_buildings);
183 m_existing_fields.swap(rhs.m_existing_fields);
184 m_existing_fleets.swap(rhs.m_existing_fleets);
185 m_existing_ships.swap(rhs.m_existing_ships);
186 m_existing_planets.swap(rhs.m_existing_planets);
187 m_existing_pop_centers.swap(rhs.m_existing_pop_centers);
188 m_existing_resource_centers.swap(rhs.m_existing_resource_centers);
189 m_existing_systems.swap(rhs.m_existing_systems);
190 FOR_EACH_MAP(SwapMap, rhs);
191 }
192
FindExistingObjectIDs() const193 std::vector<int> ObjectMap::FindExistingObjectIDs() const {
194 std::vector<int> result;
195 for (const auto& entry : m_existing_objects)
196 { result.push_back(entry.first); }
197 return result;
198 }
199
UpdateCurrentDestroyedObjects(const std::set<int> & destroyed_object_ids)200 void ObjectMap::UpdateCurrentDestroyedObjects(const std::set<int>& destroyed_object_ids) {
201 FOR_EACH_EXISTING_MAP(ClearMap);
202 for (const auto& entry : m_objects) {
203 if (!entry.second)
204 continue;
205 if (destroyed_object_ids.count(entry.first))
206 continue;
207 auto this_item = this->get(entry.first);
208 m_existing_objects[entry.first] = this_item;
209 switch (entry.second->ObjectType()) {
210 case OBJ_BUILDING:
211 m_existing_buildings[entry.first] = this_item;
212 break;
213 case OBJ_FIELD:
214 m_existing_fields[entry.first] = this_item;
215 break;
216 case OBJ_FLEET:
217 m_existing_fleets[entry.first] = this_item;
218 break;
219 case OBJ_PLANET:
220 m_existing_planets[entry.first] = this_item;
221 m_existing_pop_centers[entry.first] = this_item;
222 m_existing_resource_centers[entry.first] = this_item;
223 break;
224 case OBJ_POP_CENTER:
225 m_existing_pop_centers[entry.first] = this_item;
226 break;
227 case OBJ_PROD_CENTER:
228 m_existing_resource_centers[entry.first] = this_item;
229 break;
230 case OBJ_SHIP:
231 m_existing_ships[entry.first] = this_item;
232 break;
233 case OBJ_SYSTEM:
234 m_existing_systems[entry.first] = this_item;
235 break;
236 case OBJ_FIGHTER:
237 default:
238 break;
239 }
240 }
241 }
242
AuditContainment(const std::set<int> & destroyed_object_ids)243 void ObjectMap::AuditContainment(const std::set<int>& destroyed_object_ids) {
244 // determine all objects that some other object thinks contains them
245 std::map<int, std::set<int>> contained_objs;
246 std::map<int, std::set<int>> contained_planets;
247 std::map<int, std::set<int>> contained_buildings;
248 std::map<int, std::set<int>> contained_fleets;
249 std::map<int, std::set<int>> contained_ships;
250 std::map<int, std::set<int>> contained_fields;
251
252 for (const auto& contained : all()) {
253 if (destroyed_object_ids.count(contained->ID()))
254 continue;
255
256 int contained_id = contained->ID();
257 int sys_id = contained->SystemID();
258 int alt_id = contained->ContainerObjectID(); // planet or fleet id for a building or ship, or system id again for a fleet, field, or planet
259 UniverseObjectType type = contained->ObjectType();
260 if (type == OBJ_SYSTEM)
261 continue;
262
263 // store systems' contained objects
264 if (this->get(sys_id)) { // although this is expected to be a system, can't use Object<System> here due to CopyForSerialize not copying the type-specific objects info
265 contained_objs[sys_id].insert(contained_id);
266
267 if (type == OBJ_PLANET)
268 contained_planets[sys_id].insert(contained_id);
269 else if (type == OBJ_BUILDING)
270 contained_buildings[sys_id].insert(contained_id);
271 else if (type == OBJ_FLEET)
272 contained_fleets[sys_id].insert(contained_id);
273 else if (type == OBJ_SHIP)
274 contained_ships[sys_id].insert(contained_id);
275 else if (type == OBJ_FIELD)
276 contained_fields[sys_id].insert(contained_id);
277 }
278
279 // store planets' contained buildings
280 if (type == OBJ_BUILDING && this->get(alt_id))
281 contained_buildings[alt_id].insert(contained_id);
282
283 // store fleets' contained ships
284 if (type == OBJ_SHIP && this->get(alt_id))
285 contained_ships[alt_id].insert(contained_id);
286 }
287
288 // set contained objects of all possible containers
289 for (const auto& obj : all()) {
290 if (obj->ObjectType() == OBJ_SYSTEM) {
291 auto sys = std::dynamic_pointer_cast<System>(obj);
292 if (!sys)
293 continue;
294 sys->m_objects = contained_objs[sys->ID()];
295 sys->m_planets = contained_planets[sys->ID()];
296 sys->m_buildings = contained_buildings[sys->ID()];
297 sys->m_fleets = contained_fleets[sys->ID()];
298 sys->m_ships = contained_ships[sys->ID()];
299 sys->m_fields = contained_fields[sys->ID()];
300
301 } else if (obj->ObjectType() == OBJ_PLANET) {
302 auto plt = std::dynamic_pointer_cast<Planet>(obj);
303 if (!plt)
304 continue;
305 plt->m_buildings = contained_buildings[plt->ID()];
306
307 } else if (obj->ObjectType() == OBJ_FLEET) {
308 auto flt = std::dynamic_pointer_cast<Fleet>(obj);
309 if (!flt)
310 continue;
311 flt->m_ships = contained_ships[flt->ID()];
312 }
313 }
314 }
315
CopyObjectsToSpecializedMaps()316 void ObjectMap::CopyObjectsToSpecializedMaps() {
317 FOR_EACH_SPECIALIZED_MAP(ClearMap);
318 for (const auto& entry : Map<UniverseObject>())
319 { FOR_EACH_SPECIALIZED_MAP(TryInsertIntoMap, entry.second); }
320 }
321
Dump(unsigned short ntabs) const322 std::string ObjectMap::Dump(unsigned short ntabs) const {
323 std::ostringstream dump_stream;
324 dump_stream << "ObjectMap contains UniverseObjects: " << std::endl;
325 for (const auto& obj : all())
326 dump_stream << obj->Dump(ntabs) << std::endl;
327 dump_stream << std::endl;
328 return dump_stream.str();
329 }
330
ExistingObject(int id) const331 std::shared_ptr<const UniverseObject> ObjectMap::ExistingObject(int id) const {
332 auto it = m_existing_objects.find(id);
333 if (it != m_existing_objects.end())
334 return it->second;
335 return nullptr;
336 }
337
338 // Static helpers
339
340 template <typename T>
SwapMap(ObjectMap::container_type<T> & map,ObjectMap & rhs)341 void ObjectMap::SwapMap(ObjectMap::container_type<T>& map, ObjectMap& rhs)
342 { map.swap(rhs.Map<T>()); }
343
344 // template specializations
345
346 template <>
Map() const347 const ObjectMap::container_type<UniverseObject>& ObjectMap::Map() const
348 { return m_objects; }
349
350 template <>
Map() const351 const ObjectMap::container_type<ResourceCenter>& ObjectMap::Map() const
352 { return m_resource_centers; }
353
354 template <>
Map() const355 const ObjectMap::container_type<PopCenter>& ObjectMap::Map() const
356 { return m_pop_centers; }
357
358 template <>
Map() const359 const ObjectMap::container_type<Ship>& ObjectMap::Map() const
360 { return m_ships; }
361
362 template <>
Map() const363 const ObjectMap::container_type<Fleet>& ObjectMap::Map() const
364 { return m_fleets; }
365
366 template <>
Map() const367 const ObjectMap::container_type<Planet>& ObjectMap::Map() const
368 { return m_planets; }
369
370 template <>
Map() const371 const ObjectMap::container_type<System>& ObjectMap::Map() const
372 { return m_systems; }
373
374 template <>
Map() const375 const ObjectMap::container_type<Building>& ObjectMap::Map() const
376 { return m_buildings; }
377
378 template <>
Map() const379 const ObjectMap::container_type<Field>& ObjectMap::Map() const
380 { return m_fields; }
381
382 template <>
Map()383 ObjectMap::container_type<UniverseObject>& ObjectMap::Map()
384 { return m_objects; }
385
386 template <>
Map()387 ObjectMap::container_type<ResourceCenter>& ObjectMap::Map()
388 { return m_resource_centers; }
389
390 template <>
Map()391 ObjectMap::container_type<PopCenter>& ObjectMap::Map()
392 { return m_pop_centers; }
393
394 template <>
Map()395 ObjectMap::container_type<Ship>& ObjectMap::Map()
396 { return m_ships; }
397
398 template <>
Map()399 ObjectMap::container_type<Fleet>& ObjectMap::Map()
400 { return m_fleets; }
401
402 template <>
Map()403 ObjectMap::container_type<Planet>& ObjectMap::Map()
404 { return m_planets; }
405
406 template <>
Map()407 ObjectMap::container_type<System>& ObjectMap::Map()
408 { return m_systems; }
409
410 template <>
Map()411 ObjectMap::container_type<Building>& ObjectMap::Map()
412 { return m_buildings; }
413
414 template <>
Map()415 ObjectMap::container_type<Field>& ObjectMap::Map()
416 { return m_fields; }
417