1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Scene/BsGameObjectManager.h"
4#include "Scene/BsGameObject.h"
5
6namespace bs
7{
8 GameObjectManager::~GameObjectManager()
9 {
10 destroyQueuedObjects();
11 }
12
13 GameObjectHandleBase GameObjectManager::getObject(UINT64 id) const
14 {
15 Lock lock(mMutex);
16
17 const auto iterFind = mObjects.find(id);
18 if (iterFind != mObjects.end())
19 return iterFind->second;
20
21 return nullptr;
22 }
23
24 bool GameObjectManager::tryGetObject(UINT64 id, GameObjectHandleBase& object) const
25 {
26 Lock lock(mMutex);
27
28 const auto iterFind = mObjects.find(id);
29 if (iterFind != mObjects.end())
30 {
31 object = iterFind->second;
32 return true;
33 }
34
35 return false;
36 }
37
38 bool GameObjectManager::objectExists(UINT64 id) const
39 {
40 Lock lock(mMutex);
41
42 return mObjects.find(id) != mObjects.end();
43 }
44
45 void GameObjectManager::remapId(UINT64 oldId, UINT64 newId)
46 {
47 if (oldId == newId)
48 return;
49
50 Lock lock(mMutex);
51 mObjects[newId] = mObjects[oldId];
52 mObjects.erase(oldId);
53 }
54
55 UINT64 GameObjectManager::reserveId()
56 {
57 return mNextAvailableID.fetch_add(1, std::memory_order_relaxed);
58 }
59
60 void GameObjectManager::queueForDestroy(const GameObjectHandleBase& object)
61 {
62 if (object.isDestroyed())
63 return;
64
65 const UINT64 instanceId = object->getInstanceId();
66 mQueuedForDestroy[instanceId] = object;
67 }
68
69 void GameObjectManager::destroyQueuedObjects()
70 {
71 for (auto& objPair : mQueuedForDestroy)
72 objPair.second->destroyInternal(objPair.second, true);
73
74 mQueuedForDestroy.clear();
75 }
76
77 GameObjectHandleBase GameObjectManager::registerObject(const SPtr<GameObject>& object)
78 {
79 const UINT64 id = mNextAvailableID.fetch_add(1, std::memory_order_relaxed);
80 object->initialize(object, id);
81
82 GameObjectHandleBase handle(object);
83 {
84 Lock lock(mMutex);
85 mObjects[id] = handle;
86 }
87
88 return handle;
89 }
90
91 void GameObjectManager::unregisterObject(GameObjectHandleBase& object)
92 {
93 {
94 Lock lock(mMutex);
95 mObjects.erase(object->getInstanceId());
96 }
97
98 onDestroyed(static_object_cast<GameObject>(object));
99 object.destroy();
100 }
101
102 GameObjectDeserializationState::GameObjectDeserializationState(UINT32 options)
103 :mOptions(options)
104 { }
105
106 GameObjectDeserializationState::~GameObjectDeserializationState()
107 {
108 BS_ASSERT(mUnresolvedHandles.empty() && "Deserialization state being destroyed before all handles are resolved.");
109 BS_ASSERT(mDeserializedObjects.empty() && "Deserialization state being destroyed before all objects are resolved.");
110 }
111
112 void GameObjectDeserializationState::resolve()
113 {
114 for (auto& entry : mUnresolvedHandles)
115 {
116 UINT64 instanceId = entry.originalInstanceId;
117
118 bool isInternalReference = false;
119
120 const auto findIter = mIdMapping.find(instanceId);
121 if (findIter != mIdMapping.end())
122 {
123 if ((mOptions & GODM_UseNewIds) != 0)
124 instanceId = findIter->second;
125
126 isInternalReference = true;
127 }
128
129 if (isInternalReference)
130 {
131 const auto findIterObj = mDeserializedObjects.find(instanceId);
132
133 if (findIterObj != mDeserializedObjects.end())
134 entry.handle._resolve(findIterObj->second);
135 else
136 {
137 if ((mOptions & GODM_KeepMissing) == 0)
138 entry.handle._resolve(nullptr);
139 }
140 }
141 else if (!isInternalReference && (mOptions & GODM_RestoreExternal) != 0)
142 {
143 HGameObject obj;
144 if(GameObjectManager::instance().tryGetObject(instanceId, obj))
145 entry.handle._resolve(obj);
146 else
147 {
148 if ((mOptions & GODM_KeepMissing) == 0)
149 entry.handle._resolve(nullptr);
150 }
151 }
152 else
153 {
154 if ((mOptions & GODM_KeepMissing) == 0)
155 entry.handle._resolve(nullptr);
156 }
157 }
158
159 for (auto iter = mEndCallbacks.rbegin(); iter != mEndCallbacks.rend(); ++iter)
160 {
161 (*iter)();
162 }
163
164 mIdMapping.clear();
165 mUnresolvedHandles.clear();
166 mEndCallbacks.clear();
167 mUnresolvedHandleData.clear();
168 mDeserializedObjects.clear();
169 }
170
171 void GameObjectDeserializationState::registerUnresolvedHandle(UINT64 originalId, GameObjectHandleBase& object)
172 {
173 // All handles that are deserialized during a single begin/endDeserialization session pointing to the same object
174 // must share the same GameObjectHandleData as that makes certain operations in other systems much simpler.
175 // Therefore we store all the unresolved handles, and if a handle pointing to the same object was already
176 // processed, or that object was already created we replace the handle's internal GameObjectHandleData.
177
178 // Update the provided handle to ensure all handles pointing to the same object share the same handle data
179 bool foundHandleData = false;
180
181 // Search object that are currently being deserialized
182 const auto iterFind = mIdMapping.find(originalId);
183 if (iterFind != mIdMapping.end())
184 {
185 const auto iterFind2 = mDeserializedObjects.find(iterFind->second);
186 if (iterFind2 != mDeserializedObjects.end())
187 {
188 object.mData = iterFind2->second.mData;
189 foundHandleData = true;
190 }
191 }
192
193 // Search previously deserialized handles
194 if (!foundHandleData)
195 {
196 auto iterFind = mUnresolvedHandleData.find(originalId);
197 if (iterFind != mUnresolvedHandleData.end())
198 {
199 object.mData = iterFind->second;
200 foundHandleData = true;
201 }
202 }
203
204 // If still not found, this is the first such handle so register its handle data
205 if (!foundHandleData)
206 mUnresolvedHandleData[originalId] = object.mData;
207
208 mUnresolvedHandles.push_back({ originalId, object });
209 }
210
211 void GameObjectDeserializationState::registerObject(UINT64 originalId, GameObjectHandleBase& object)
212 {
213 assert(originalId != 0 && "Invalid game object ID.");
214
215 const auto iterFind = mUnresolvedHandleData.find(originalId);
216 if (iterFind != mUnresolvedHandleData.end())
217 {
218 SPtr<GameObject> ptr = object.getInternalPtr();
219
220 object.mData = iterFind->second;
221 object._setHandleData(ptr);
222 }
223
224 const UINT64 newId = object->getInstanceId();
225 mIdMapping[originalId] = newId;
226 mDeserializedObjects[newId] = object;
227 }
228
229 void GameObjectDeserializationState::registerOnDeserializationEndCallback(std::function<void()> callback)
230 {
231 mEndCallbacks.push_back(callback);
232 }
233}