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