1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "ObjectManager.h"
11
12 #include "../Context.h"
13 #include "../ParkImporter.h"
14 #include "../core/Console.hpp"
15 #include "../core/Memory.hpp"
16 #include "../localisation/StringIds.h"
17 #include "../util/Util.h"
18 #include "FootpathItemObject.h"
19 #include "LargeSceneryObject.h"
20 #include "Object.h"
21 #include "ObjectList.h"
22 #include "ObjectRepository.h"
23 #include "RideObject.h"
24 #include "SceneryGroupObject.h"
25 #include "SmallSceneryObject.h"
26 #include "WallObject.h"
27
28 #include <algorithm>
29 #include <array>
30 #include <memory>
31 #include <mutex>
32 #include <thread>
33 #include <unordered_set>
34
35 class ObjectManager final : public IObjectManager
36 {
37 private:
38 IObjectRepository& _objectRepository;
39
40 std::vector<Object*> _loadedObjects;
41 std::array<std::vector<ObjectEntryIndex>, RIDE_TYPE_COUNT> _rideTypeToObjectMap;
42
43 // Used to return a safe empty vector back from GetAllRideEntries, can be removed when std::span is available
44 std::vector<ObjectEntryIndex> _nullRideTypeEntries;
45
46 public:
ObjectManager(IObjectRepository & objectRepository)47 explicit ObjectManager(IObjectRepository& objectRepository)
48 : _objectRepository(objectRepository)
49 {
50 _loadedObjects.resize(OBJECT_ENTRY_COUNT);
51
52 UpdateSceneryGroupIndexes();
53 ResetTypeToRideEntryIndexMap();
54 }
55
~ObjectManager()56 ~ObjectManager() override
57 {
58 UnloadAll();
59 }
60
GetLoadedObject(size_t index)61 Object* GetLoadedObject(size_t index) override
62 {
63 if (index >= _loadedObjects.size())
64 {
65 return nullptr;
66 }
67 return _loadedObjects[index];
68 }
69
GetLoadedObject(ObjectType objectType,size_t index)70 Object* GetLoadedObject(ObjectType objectType, size_t index) override
71 {
72 // This is sometimes done deliberately (to avoid boilerplate), so no need to log_warn for this.
73 if (index == OBJECT_ENTRY_INDEX_NULL)
74 {
75 return nullptr;
76 }
77
78 if (index >= static_cast<size_t>(object_entry_group_counts[EnumValue(objectType)]))
79 {
80 #ifdef DEBUG
81 log_warning("Object index %u exceeds maximum for type %d.", index, objectType);
82 #endif
83 return nullptr;
84 }
85
86 auto objectIndex = GetIndexFromTypeEntry(objectType, index);
87 return GetLoadedObject(objectIndex);
88 }
89
GetLoadedObject(const ObjectEntryDescriptor & entry)90 Object* GetLoadedObject(const ObjectEntryDescriptor& entry) override
91 {
92 const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry);
93 if (ori == nullptr)
94 return nullptr;
95
96 return ori->LoadedObject.get();
97 }
98
GetLoadedObjectEntryIndex(std::string_view identifier)99 ObjectEntryIndex GetLoadedObjectEntryIndex(std::string_view identifier) override
100 {
101 const auto* obj = GetLoadedObject(ObjectEntryDescriptor(identifier));
102 if (obj != nullptr)
103 {
104 return GetLoadedObjectEntryIndex(obj);
105 }
106 return OBJECT_ENTRY_INDEX_NULL;
107 }
108
GetLoadedObjectEntryIndex(const ObjectEntryDescriptor & descriptor)109 ObjectEntryIndex GetLoadedObjectEntryIndex(const ObjectEntryDescriptor& descriptor) override
110 {
111 auto obj = GetLoadedObject(descriptor);
112 if (obj != nullptr)
113 {
114 return GetLoadedObjectEntryIndex(obj);
115 }
116 return OBJECT_ENTRY_INDEX_NULL;
117 }
118
GetLoadedObjectEntryIndex(const Object * object)119 ObjectEntryIndex GetLoadedObjectEntryIndex(const Object* object) override
120 {
121 ObjectEntryIndex result = OBJECT_ENTRY_INDEX_NULL;
122 size_t index = GetLoadedObjectIndex(object);
123 if (index != SIZE_MAX)
124 {
125 get_type_entry_index(index, nullptr, &result);
126 }
127 return result;
128 }
129
LoadObject(std::string_view identifier)130 Object* LoadObject(std::string_view identifier) override
131 {
132 const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier);
133 return RepositoryItemToObject(ori);
134 }
135
LoadObject(const rct_object_entry * entry)136 Object* LoadObject(const rct_object_entry* entry) override
137 {
138 const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry);
139 return RepositoryItemToObject(ori);
140 }
141
LoadObject(const ObjectEntryDescriptor & descriptor)142 Object* LoadObject(const ObjectEntryDescriptor& descriptor) override
143 {
144 const ObjectRepositoryItem* ori = _objectRepository.FindObject(descriptor);
145 return RepositoryItemToObject(ori);
146 }
147
LoadObjects(const ObjectList & objectList)148 void LoadObjects(const ObjectList& objectList) override
149 {
150 // Find all the required objects
151 auto requiredObjects = GetRequiredObjects(objectList);
152
153 // Load the required objects
154 LoadObjects(requiredObjects);
155
156 // Load defaults.
157 LoadDefaultObjects();
158
159 // Update indices.
160 UpdateSceneryGroupIndexes();
161 ResetTypeToRideEntryIndexMap();
162 }
163
UnloadObjects(const std::vector<ObjectEntryDescriptor> & entries)164 void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) override
165 {
166 // TODO there are two performance issues here:
167 // - FindObject for every entry which is a dictionary lookup
168 // - GetLoadedObjectIndex for every entry which enumerates _loadedList
169
170 size_t numObjectsUnloaded = 0;
171 for (const auto& descriptor : entries)
172 {
173 const auto* ori = _objectRepository.FindObject(descriptor);
174 if (ori != nullptr)
175 {
176 auto* loadedObject = ori->LoadedObject.get();
177 if (loadedObject != nullptr)
178 {
179 UnloadObject(loadedObject);
180 numObjectsUnloaded++;
181 }
182 }
183 }
184
185 if (numObjectsUnloaded > 0)
186 {
187 UpdateSceneryGroupIndexes();
188 ResetTypeToRideEntryIndexMap();
189 }
190 }
191
UnloadAll()192 void UnloadAll() override
193 {
194 for (auto* object : _loadedObjects)
195 {
196 UnloadObject(object);
197 }
198 UpdateSceneryGroupIndexes();
199 ResetTypeToRideEntryIndexMap();
200 }
201
ResetObjects()202 void ResetObjects() override
203 {
204 for (auto& loadedObject : _loadedObjects)
205 {
206 if (loadedObject != nullptr)
207 {
208 loadedObject->Unload();
209 loadedObject->Load();
210 }
211 }
212 UpdateSceneryGroupIndexes();
213 ResetTypeToRideEntryIndexMap();
214 }
215
GetPackableObjects()216 std::vector<const ObjectRepositoryItem*> GetPackableObjects() override
217 {
218 std::vector<const ObjectRepositoryItem*> objects;
219 size_t numObjects = _objectRepository.GetNumObjects();
220 for (size_t i = 0; i < numObjects; i++)
221 {
222 // TODO: remove ObjectGeneration::DAT check when the NSF is here
223 const ObjectRepositoryItem* item = &_objectRepository.GetObjects()[i];
224 if (item->LoadedObject != nullptr && IsObjectCustom(item) && item->LoadedObject->GetLegacyData() != nullptr
225 && item->LoadedObject->GetGeneration() == ObjectGeneration::DAT)
226 {
227 objects.push_back(item);
228 }
229 }
230 return objects;
231 }
232
LoadDefaultObjects()233 void LoadDefaultObjects() override
234 {
235 // We currently will load new object types here that apply to all
236 // loaded RCT1 and RCT2 save files.
237
238 // Surfaces
239 LoadObject("rct2.surface.grass");
240 LoadObject("rct2.surface.sand");
241 LoadObject("rct2.surface.dirt");
242 LoadObject("rct2.surface.rock");
243 LoadObject("rct2.surface.martian");
244 LoadObject("rct2.surface.chequerboard");
245 LoadObject("rct2.surface.grassclumps");
246 LoadObject("rct2.surface.ice");
247 LoadObject("rct2.surface.gridred");
248 LoadObject("rct2.surface.gridyellow");
249 LoadObject("rct2.surface.gridpurple");
250 LoadObject("rct2.surface.gridgreen");
251 LoadObject("rct2.surface.sandred");
252 LoadObject("rct2.surface.sandbrown");
253 LoadObject("rct1.aa.surface.roofred");
254 LoadObject("rct1.ll.surface.roofgrey");
255 LoadObject("rct1.ll.surface.rust");
256 LoadObject("rct1.ll.surface.wood");
257
258 // Edges
259 LoadObject("rct2.edge.rock");
260 LoadObject("rct2.edge.woodred");
261 LoadObject("rct2.edge.woodblack");
262 LoadObject("rct2.edge.ice");
263 LoadObject("rct1.edge.brick");
264 LoadObject("rct1.edge.iron");
265 LoadObject("rct1.aa.edge.grey");
266 LoadObject("rct1.aa.edge.yellow");
267 LoadObject("rct1.aa.edge.red");
268 LoadObject("rct1.ll.edge.purple");
269 LoadObject("rct1.ll.edge.green");
270 LoadObject("rct1.ll.edge.stonebrown");
271 LoadObject("rct1.ll.edge.stonegrey");
272 LoadObject("rct1.ll.edge.skyscrapera");
273 LoadObject("rct1.ll.edge.skyscraperb");
274
275 // Stations
276 LoadObject("rct2.station.plain");
277 LoadObject("rct2.station.wooden");
278 LoadObject("rct2.station.canvastent");
279 LoadObject("rct2.station.castlegrey");
280 LoadObject("rct2.station.castlebrown");
281 LoadObject("rct2.station.jungle");
282 LoadObject("rct2.station.log");
283 LoadObject("rct2.station.classical");
284 LoadObject("rct2.station.abstract");
285 LoadObject("rct2.station.snow");
286 LoadObject("rct2.station.pagoda");
287 LoadObject("rct2.station.space");
288 LoadObject("openrct2.station.noentrance");
289
290 // Music
291 auto baseIndex = GetIndexFromTypeEntry(ObjectType::Music, 0);
292 LoadObject(baseIndex + MUSIC_STYLE_DODGEMS_BEAT, "rct2.music.dodgems");
293 LoadObject(baseIndex + MUSIC_STYLE_FAIRGROUND_ORGAN, "rct2.music.fairground");
294 LoadObject(baseIndex + MUSIC_STYLE_ROMAN_FANFARE, "rct2.music.roman");
295 LoadObject(baseIndex + MUSIC_STYLE_ORIENTAL, "rct2.music.oriental");
296 LoadObject(baseIndex + MUSIC_STYLE_MARTIAN, "rct2.music.martian");
297 LoadObject(baseIndex + MUSIC_STYLE_JUNGLE_DRUMS, "rct2.music.jungle");
298 LoadObject(baseIndex + MUSIC_STYLE_EGYPTIAN, "rct2.music.egyptian");
299 LoadObject(baseIndex + MUSIC_STYLE_TOYLAND, "rct2.music.toyland");
300 LoadObject(baseIndex + MUSIC_STYLE_SPACE, "rct2.music.space");
301 LoadObject(baseIndex + MUSIC_STYLE_HORROR, "rct2.music.horror");
302 LoadObject(baseIndex + MUSIC_STYLE_TECHNO, "rct2.music.techno");
303 LoadObject(baseIndex + MUSIC_STYLE_GENTLE, "rct2.music.gentle");
304 LoadObject(baseIndex + MUSIC_STYLE_SUMMER, "rct2.music.summer");
305 LoadObject(baseIndex + MUSIC_STYLE_WATER, "rct2.music.water");
306 LoadObject(baseIndex + MUSIC_STYLE_WILD_WEST, "rct2.music.wildwest");
307 LoadObject(baseIndex + MUSIC_STYLE_JURASSIC, "rct2.music.jurassic");
308 LoadObject(baseIndex + MUSIC_STYLE_ROCK, "rct2.music.rock1");
309 LoadObject(baseIndex + MUSIC_STYLE_RAGTIME, "rct2.music.ragtime");
310 LoadObject(baseIndex + MUSIC_STYLE_FANTASY, "rct2.music.fantasy");
311 LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_2, "rct2.music.rock2");
312 LoadObject(baseIndex + MUSIC_STYLE_ICE, "rct2.music.ice");
313 LoadObject(baseIndex + MUSIC_STYLE_SNOW, "rct2.music.snow");
314 LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_1, "rct2.music.custom1");
315 LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_2, "rct2.music.custom2");
316 LoadObject(baseIndex + MUSIC_STYLE_MEDIEVAL, "rct2.music.medieval");
317 LoadObject(baseIndex + MUSIC_STYLE_URBAN, "rct2.music.urban");
318 LoadObject(baseIndex + MUSIC_STYLE_ORGAN, "rct2.music.organ");
319 LoadObject(baseIndex + MUSIC_STYLE_MECHANICAL, "rct2.music.mechanical");
320 LoadObject(baseIndex + MUSIC_STYLE_MODERN, "rct2.music.modern");
321 LoadObject(baseIndex + MUSIC_STYLE_PIRATES, "rct2.music.pirate");
322 LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_3, "rct2.music.rock3");
323 LoadObject(baseIndex + MUSIC_STYLE_CANDY_STYLE, "rct2.music.candy");
324 }
325
GetObjectSourceGameString(const ObjectSourceGame sourceGame)326 static rct_string_id GetObjectSourceGameString(const ObjectSourceGame sourceGame)
327 {
328 switch (sourceGame)
329 {
330 case ObjectSourceGame::RCT1:
331 return STR_SCENARIO_CATEGORY_RCT1;
332 case ObjectSourceGame::AddedAttractions:
333 return STR_SCENARIO_CATEGORY_RCT1_AA;
334 case ObjectSourceGame::LoopyLandscapes:
335 return STR_SCENARIO_CATEGORY_RCT1_LL;
336 case ObjectSourceGame::RCT2:
337 return STR_ROLLERCOASTER_TYCOON_2_DROPDOWN;
338 case ObjectSourceGame::WackyWorlds:
339 return STR_OBJECT_FILTER_WW;
340 case ObjectSourceGame::TimeTwister:
341 return STR_OBJECT_FILTER_TT;
342 case ObjectSourceGame::OpenRCT2Official:
343 return STR_OBJECT_FILTER_OPENRCT2_OFFICIAL;
344 default:
345 return STR_OBJECT_FILTER_CUSTOM;
346 }
347 }
348
GetAllRideEntries(uint8_t rideType)349 const std::vector<ObjectEntryIndex>& GetAllRideEntries(uint8_t rideType) override
350 {
351 if (rideType >= RIDE_TYPE_COUNT)
352 {
353 // Return an empty vector
354 return _nullRideTypeEntries;
355 }
356 return _rideTypeToObjectMap[rideType];
357 }
358
359 private:
LoadObject(int32_t slot,std::string_view identifier)360 Object* LoadObject(int32_t slot, std::string_view identifier)
361 {
362 const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier);
363 return RepositoryItemToObject(ori, slot);
364 }
365
RepositoryItemToObject(const ObjectRepositoryItem * ori,std::optional<int32_t> slot={})366 Object* RepositoryItemToObject(const ObjectRepositoryItem* ori, std::optional<int32_t> slot = {})
367 {
368 if (ori == nullptr)
369 return nullptr;
370
371 Object* loadedObject = ori->LoadedObject.get();
372 if (loadedObject != nullptr)
373 return loadedObject;
374
375 ObjectType objectType = ori->Type;
376 if (slot)
377 {
378 if (_loadedObjects.size() > static_cast<size_t>(*slot) && _loadedObjects[*slot] != nullptr)
379 {
380 // Slot already taken
381 return nullptr;
382 }
383 }
384 else
385 {
386 slot = FindSpareSlot(objectType);
387 }
388 if (slot.has_value())
389 {
390 auto* object = GetOrLoadObject(ori);
391 if (object != nullptr)
392 {
393 if (_loadedObjects.size() <= static_cast<size_t>(*slot))
394 {
395 _loadedObjects.resize(slot.value() + 1);
396 }
397 loadedObject = object;
398 _loadedObjects[slot.value()] = object;
399 UpdateSceneryGroupIndexes();
400 ResetTypeToRideEntryIndexMap();
401 }
402 }
403 return loadedObject;
404 }
405
FindSpareSlot(ObjectType objectType)406 std::optional<int32_t> FindSpareSlot(ObjectType objectType)
407 {
408 size_t firstIndex = GetIndexFromTypeEntry(objectType, 0);
409 size_t endIndex = firstIndex + object_entry_group_counts[EnumValue(objectType)];
410 for (size_t i = firstIndex; i < endIndex; i++)
411 {
412 if (_loadedObjects.size() <= i)
413 {
414 _loadedObjects.resize(i + 1);
415 return static_cast<int32_t>(i);
416 }
417 if (_loadedObjects[i] == nullptr)
418 {
419 return static_cast<int32_t>(i);
420 }
421 }
422 return std::nullopt;
423 }
424
GetLoadedObjectIndex(const Object * object)425 size_t GetLoadedObjectIndex(const Object* object)
426 {
427 Guard::ArgumentNotNull(object, GUARD_LINE);
428
429 auto result = std::numeric_limits<size_t>().max();
430 auto it = std::find(_loadedObjects.begin(), _loadedObjects.end(), object);
431 if (it != _loadedObjects.end())
432 {
433 result = std::distance(_loadedObjects.begin(), it);
434 }
435 return result;
436 }
437
UnloadObject(Object * object)438 void UnloadObject(Object* object)
439 {
440 if (object == nullptr)
441 return;
442
443 object->Unload();
444
445 // TODO try to prevent doing a repository search
446 const auto* ori = _objectRepository.FindObject(object->GetDescriptor());
447 if (ori != nullptr)
448 {
449 _objectRepository.UnregisterLoadedObject(ori, object);
450 }
451
452 // Because it's possible to have the same loaded object for multiple
453 // slots, we have to make sure find and set all of them to nullptr
454 std::replace(_loadedObjects.begin(), _loadedObjects.end(), object, static_cast<Object*>(nullptr));
455 }
456
UnloadObjectsExcept(const std::vector<Object * > & newLoadedObjects)457 void UnloadObjectsExcept(const std::vector<Object*>& newLoadedObjects)
458 {
459 // Build a hash set for quick checking
460 auto exceptSet = std::unordered_set<Object*>();
461 for (auto& object : newLoadedObjects)
462 {
463 if (object != nullptr)
464 {
465 exceptSet.insert(object);
466 }
467 }
468
469 // Unload objects that are not in the hash set
470 size_t totalObjectsLoaded = 0;
471 size_t numObjectsUnloaded = 0;
472 for (auto* object : _loadedObjects)
473 {
474 if (object == nullptr)
475 continue;
476
477 totalObjectsLoaded++;
478 if (exceptSet.find(object) == exceptSet.end())
479 {
480 UnloadObject(object);
481 numObjectsUnloaded++;
482 }
483 }
484
485 log_verbose("%u / %u objects unloaded", numObjectsUnloaded, totalObjectsLoaded);
486 }
487
UpdateSceneryGroupIndexes(Object * object)488 template<typename T> void UpdateSceneryGroupIndexes(Object* object)
489 {
490 auto* sceneryEntry = static_cast<T*>(object->GetLegacyData());
491 sceneryEntry->scenery_tab_id = GetPrimarySceneryGroupEntryIndex(object);
492 }
493
UpdateSceneryGroupIndexes()494 void UpdateSceneryGroupIndexes()
495 {
496 for (auto* loadedObject : _loadedObjects)
497 {
498 // The list can contain unused slots, skip them.
499 if (loadedObject == nullptr)
500 continue;
501
502 switch (loadedObject->GetObjectType())
503 {
504 case ObjectType::SmallScenery:
505 UpdateSceneryGroupIndexes<SmallSceneryEntry>(loadedObject);
506 break;
507 case ObjectType::LargeScenery:
508 UpdateSceneryGroupIndexes<LargeSceneryEntry>(loadedObject);
509 break;
510 case ObjectType::Walls:
511 UpdateSceneryGroupIndexes<WallSceneryEntry>(loadedObject);
512 break;
513 case ObjectType::Banners:
514 UpdateSceneryGroupIndexes<BannerSceneryEntry>(loadedObject);
515 break;
516 case ObjectType::PathBits:
517 UpdateSceneryGroupIndexes<PathBitEntry>(loadedObject);
518 break;
519 case ObjectType::SceneryGroup:
520 {
521 auto sgObject = dynamic_cast<SceneryGroupObject*>(loadedObject);
522 sgObject->UpdateEntryIndexes();
523 break;
524 }
525 default:
526 // This switch only handles scenery ObjectTypes.
527 break;
528 }
529 }
530
531 // HACK Scenery window will lose its tabs after changing the scenery group indexing
532 // for now just close it, but it will be better to later tell it to invalidate the tabs
533 window_close_by_class(WC_SCENERY);
534 }
535
GetPrimarySceneryGroupEntryIndex(Object * loadedObject)536 ObjectEntryIndex GetPrimarySceneryGroupEntryIndex(Object* loadedObject)
537 {
538 auto* sceneryObject = dynamic_cast<SceneryObject*>(loadedObject);
539 const auto& primarySGEntry = sceneryObject->GetPrimarySceneryGroup();
540 Object* sgObject = GetLoadedObject(primarySGEntry);
541
542 auto entryIndex = OBJECT_ENTRY_INDEX_NULL;
543 if (sgObject != nullptr)
544 {
545 entryIndex = GetLoadedObjectEntryIndex(sgObject);
546 }
547 return entryIndex;
548 }
549
GetRequiredObjects(const ObjectList & objectList)550 std::vector<const ObjectRepositoryItem*> GetRequiredObjects(const ObjectList& objectList)
551 {
552 std::vector<const ObjectRepositoryItem*> requiredObjects;
553 std::vector<ObjectEntryDescriptor> missingObjects;
554
555 for (auto objectType = ObjectType::Ride; objectType < ObjectType::Count; objectType++)
556 {
557 auto maxObjectsOfType = static_cast<ObjectEntryIndex>(object_entry_group_counts[EnumValue(objectType)]);
558 for (ObjectEntryIndex i = 0; i < maxObjectsOfType; i++)
559 {
560 const ObjectRepositoryItem* ori = nullptr;
561 const auto& entry = objectList.GetObject(objectType, i);
562 if (entry.HasValue())
563 {
564 ori = _objectRepository.FindObject(entry);
565 if (ori == nullptr && entry.GetType() != ObjectType::ScenarioText)
566 {
567 missingObjects.push_back(entry);
568 ReportMissingObject(entry);
569 }
570 }
571 requiredObjects.push_back(ori);
572 }
573 }
574
575 if (!missingObjects.empty())
576 {
577 throw ObjectLoadException(std::move(missingObjects));
578 }
579
580 return requiredObjects;
581 }
582
ParallelFor(const std::vector<T> & items,TFunc func)583 template<typename T, typename TFunc> static void ParallelFor(const std::vector<T>& items, TFunc func)
584 {
585 auto partitions = std::thread::hardware_concurrency();
586 auto partitionSize = (items.size() + (partitions - 1)) / partitions;
587 std::vector<std::thread> threads;
588 for (size_t n = 0; n < partitions; n++)
589 {
590 auto begin = n * partitionSize;
591 auto end = std::min(items.size(), begin + partitionSize);
592 threads.emplace_back(
593 [func](size_t pbegin, size_t pend) {
594 for (size_t i = pbegin; i < pend; i++)
595 {
596 func(i);
597 }
598 },
599 begin, end);
600 }
601 for (auto& t : threads)
602 {
603 t.join();
604 }
605 }
606
LoadObjects(std::vector<const ObjectRepositoryItem * > & requiredObjects)607 void LoadObjects(std::vector<const ObjectRepositoryItem*>& requiredObjects)
608 {
609 std::vector<Object*> objects;
610 std::vector<Object*> newLoadedObjects;
611 std::vector<ObjectEntryDescriptor> badObjects;
612 objects.resize(OBJECT_ENTRY_COUNT);
613 newLoadedObjects.reserve(OBJECT_ENTRY_COUNT);
614
615 // Read objects
616 std::mutex commonMutex;
617 ParallelFor(requiredObjects, [this, &commonMutex, requiredObjects, &objects, &badObjects, &newLoadedObjects](size_t i) {
618 auto* requiredObject = requiredObjects[i];
619 Object* object = nullptr;
620 if (requiredObject != nullptr)
621 {
622 auto* loadedObject = requiredObject->LoadedObject.get();
623 if (loadedObject == nullptr)
624 {
625 // Object requires to be loaded, if the object successfully loads it will register it
626 // as a loaded object otherwise placed into the badObjects list.
627 auto newObject = _objectRepository.LoadObject(requiredObject);
628 std::lock_guard<std::mutex> guard(commonMutex);
629 if (newObject == nullptr)
630 {
631 badObjects.push_back(ObjectEntryDescriptor(requiredObject->ObjectEntry));
632 ReportObjectLoadProblem(&requiredObject->ObjectEntry);
633 }
634 else
635 {
636 object = newObject.get();
637 newLoadedObjects.push_back(object);
638 // Connect the ori to the registered object
639 _objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject));
640 }
641 }
642 else
643 {
644 object = loadedObject;
645 }
646 }
647 objects[i] = object;
648 });
649
650 // Load objects
651 for (auto* obj : newLoadedObjects)
652 {
653 obj->Load();
654 }
655
656 if (!badObjects.empty())
657 {
658 // Unload all the new objects we loaded
659 for (auto* object : newLoadedObjects)
660 {
661 UnloadObject(object);
662 }
663 throw ObjectLoadException(std::move(badObjects));
664 }
665
666 // Unload objects which are not in the required list.
667 if (objects.empty())
668 {
669 UnloadAll();
670 }
671 else
672 {
673 UnloadObjectsExcept(objects);
674 }
675
676 _loadedObjects = std::move(objects);
677
678 log_verbose("%u / %u new objects loaded", newLoadedObjects.size(), requiredObjects.size());
679 }
680
GetOrLoadObject(const ObjectRepositoryItem * ori)681 Object* GetOrLoadObject(const ObjectRepositoryItem* ori)
682 {
683 auto* loadedObject = ori->LoadedObject.get();
684 if (loadedObject != nullptr)
685 return loadedObject;
686
687 // Try to load object
688 auto object = _objectRepository.LoadObject(ori);
689 if (object != nullptr)
690 {
691 loadedObject = object.get();
692
693 object->Load();
694
695 // Connect the ori to the registered object
696 _objectRepository.RegisterLoadedObject(ori, std::move(object));
697 }
698
699 return loadedObject;
700 }
701
ResetTypeToRideEntryIndexMap()702 void ResetTypeToRideEntryIndexMap()
703 {
704 // Clear all ride objects
705 for (auto& v : _rideTypeToObjectMap)
706 {
707 v.clear();
708 }
709
710 // Build object lists
711 const auto maxRideObjects = static_cast<size_t>(object_entry_group_counts[EnumValue(ObjectType::Ride)]);
712 for (size_t i = 0; i < maxRideObjects; i++)
713 {
714 auto* rideObject = static_cast<RideObject*>(GetLoadedObject(ObjectType::Ride, i));
715 if (rideObject == nullptr)
716 continue;
717
718 const auto* entry = static_cast<rct_ride_entry*>(rideObject->GetLegacyData());
719 if (entry == nullptr)
720 continue;
721
722 for (auto rideType : entry->ride_type)
723 {
724 if (rideType < _rideTypeToObjectMap.size())
725 {
726 auto& v = _rideTypeToObjectMap[rideType];
727 v.push_back(static_cast<ObjectEntryIndex>(i));
728 }
729 }
730 }
731 }
732
ReportMissingObject(const ObjectEntryDescriptor & entry)733 static void ReportMissingObject(const ObjectEntryDescriptor& entry)
734 {
735 std::string name(entry.GetName());
736 Console::Error::WriteLine("[%s] Object not found.", name.c_str());
737 }
738
ReportObjectLoadProblem(const rct_object_entry * entry)739 void ReportObjectLoadProblem(const rct_object_entry* entry)
740 {
741 utf8 objName[DAT_NAME_LENGTH + 1] = { 0 };
742 std::copy_n(entry->name, DAT_NAME_LENGTH, objName);
743 Console::Error::WriteLine("[%s] Object could not be loaded.", objName);
744 }
745
GetIndexFromTypeEntry(ObjectType objectType,size_t entryIndex)746 static int32_t GetIndexFromTypeEntry(ObjectType objectType, size_t entryIndex)
747 {
748 int32_t result = 0;
749 for (int32_t i = 0; i < EnumValue(objectType); i++)
750 {
751 result += object_entry_group_counts[i];
752 }
753 result += static_cast<int32_t>(entryIndex);
754 return result;
755 }
756 };
757
CreateObjectManager(IObjectRepository & objectRepository)758 std::unique_ptr<IObjectManager> CreateObjectManager(IObjectRepository& objectRepository)
759 {
760 return std::make_unique<ObjectManager>(objectRepository);
761 }
762
object_manager_get_loaded_object(const ObjectEntryDescriptor & entry)763 Object* object_manager_get_loaded_object(const ObjectEntryDescriptor& entry)
764 {
765 auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
766 Object* loadedObject = objectManager.GetLoadedObject(entry);
767 return loadedObject;
768 }
769
object_manager_get_loaded_object_entry_index(const Object * loadedObject)770 ObjectEntryIndex object_manager_get_loaded_object_entry_index(const Object* loadedObject)
771 {
772 auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
773 auto entryIndex = objectManager.GetLoadedObjectEntryIndex(loadedObject);
774 return entryIndex;
775 }
776
object_manager_get_loaded_object_entry_index(const ObjectEntryDescriptor & entry)777 ObjectEntryIndex object_manager_get_loaded_object_entry_index(const ObjectEntryDescriptor& entry)
778 {
779 return object_manager_get_loaded_object_entry_index(object_manager_get_loaded_object(entry));
780 }
781
object_manager_load_object(const rct_object_entry * entry)782 Object* object_manager_load_object(const rct_object_entry* entry)
783 {
784 auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
785 Object* loadedObject = objectManager.LoadObject(entry);
786 return loadedObject;
787 }
788
object_manager_unload_objects(const std::vector<ObjectEntryDescriptor> & entries)789 void object_manager_unload_objects(const std::vector<ObjectEntryDescriptor>& entries)
790 {
791 auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
792 objectManager.UnloadObjects(entries);
793 }
794
object_manager_unload_all_objects()795 void object_manager_unload_all_objects()
796 {
797 auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
798 objectManager.UnloadAll();
799 }
800
object_manager_get_source_game_string(const ObjectSourceGame sourceGame)801 rct_string_id object_manager_get_source_game_string(const ObjectSourceGame sourceGame)
802 {
803 return ObjectManager::GetObjectSourceGameString(sourceGame);
804 }
805