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#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "Reflection/BsRTTIType.h"
7#include "Scene/BsPrefabDiff.h"
8#include "Serialization/BsSerializedObject.h"
9#include "Scene/BsGameObjectManager.h"
10#include "Serialization/BsBinarySerializer.h"
11#include "Utility/BsUtility.h"
12
13namespace bs
14{
15 /** @cond RTTI */
16 /** @addtogroup RTTI-Impl-Core
17 * @{
18 */
19
20 class BS_CORE_EXPORT PrefabComponentDiffRTTI : public RTTIType < PrefabComponentDiff, IReflectable, PrefabComponentDiffRTTI >
21 {
22 private:
23 BS_BEGIN_RTTI_MEMBERS
24 BS_RTTI_MEMBER_PLAIN(id, 0)
25 BS_RTTI_MEMBER_REFLPTR(data, 1)
26 BS_END_RTTI_MEMBERS
27 public:
28 const String& getRTTIName() override
29 {
30 static String name = "PrefabComponentDiff";
31 return name;
32 }
33
34 UINT32 getRTTIId() override
35 {
36 return TID_PrefabComponentDiff;
37 }
38
39 SPtr<IReflectable> newRTTIObject() override
40 {
41 return bs_shared_ptr_new<PrefabComponentDiff>();
42 }
43 };
44
45 class BS_CORE_EXPORT PrefabObjectDiffRTTI : public RTTIType < PrefabObjectDiff, IReflectable, PrefabObjectDiffRTTI >
46 {
47 private:
48 BS_BEGIN_RTTI_MEMBERS
49 BS_RTTI_MEMBER_PLAIN(id, 0)
50 BS_RTTI_MEMBER_PLAIN(name, 1)
51
52 BS_RTTI_MEMBER_REFLPTR_ARRAY(componentDiffs, 2)
53 BS_RTTI_MEMBER_PLAIN_ARRAY(removedComponents, 3)
54 BS_RTTI_MEMBER_REFLPTR_ARRAY(addedComponents, 4)
55 BS_RTTI_MEMBER_REFLPTR_ARRAY(childDiffs, 5)
56
57 BS_RTTI_MEMBER_PLAIN_ARRAY(removedChildren, 6)
58 BS_RTTI_MEMBER_REFLPTR_ARRAY(addedChildren, 7)
59
60 BS_RTTI_MEMBER_PLAIN(position, 8)
61 BS_RTTI_MEMBER_PLAIN(rotation, 9)
62 BS_RTTI_MEMBER_PLAIN(scale, 10)
63 BS_RTTI_MEMBER_PLAIN(isActive, 11)
64 BS_RTTI_MEMBER_PLAIN(soFlags, 12)
65 BS_END_RTTI_MEMBERS
66 public:
67 const String& getRTTIName() override
68 {
69 static String name = "PrefabObjectDiff";
70 return name;
71 }
72
73 UINT32 getRTTIId() override
74 {
75 return TID_PrefabObjectDiff;
76 }
77
78 SPtr<IReflectable> newRTTIObject() override
79 {
80 return bs_shared_ptr_new<PrefabObjectDiff>();
81 }
82 };
83
84 class BS_CORE_EXPORT PrefabDiffRTTI : public RTTIType < PrefabDiff, IReflectable, PrefabDiffRTTI >
85 {
86 /** Contains data about a game object handle serialized in a prefab diff. */
87 struct SerializedHandle
88 {
89 SPtr<SerializedObject> object;
90 SPtr<GameObjectHandleBase> handle;
91 };
92
93 private:
94 BS_BEGIN_RTTI_MEMBERS
95 BS_RTTI_MEMBER_REFLPTR(mRoot, 0)
96 BS_END_RTTI_MEMBERS
97 public:
98 void onDeserializationStarted(IReflectable* obj, SerializationContext* context) override
99 {
100 PrefabDiff* prefabDiff = static_cast<PrefabDiff*>(obj);
101
102 BS_ASSERT(context != nullptr && rtti_is_of_type<CoreSerializationContext>(context));
103 auto coreContext = static_cast<CoreSerializationContext*>(context);
104
105 if (coreContext->goState)
106 {
107 coreContext->goState->registerOnDeserializationEndCallback(
108 std::bind(&PrefabDiffRTTI::delayedOnDeserializationEnded, prefabDiff));
109 }
110 }
111
112 void onDeserializationEnded(IReflectable* obj, SerializationContext* context) override
113 {
114 BS_ASSERT(context != nullptr && rtti_is_of_type<CoreSerializationContext>(context));
115 const auto coreContext = static_cast<CoreSerializationContext*>(context);
116 BS_ASSERT(coreContext->goState);
117
118 // Make sure to deserialize all game object handles since their IDs need to be updated. Normally they are
119 // updated automatically upon deserialization but since we store them in intermediate form we need to manually
120 // deserialize and reserialize them in order to update their IDs.
121 PrefabDiff* prefabDiff = static_cast<PrefabDiff*>(obj);
122
123 Stack<SPtr<PrefabObjectDiff>> todo;
124
125 if (prefabDiff->mRoot != nullptr)
126 todo.push(prefabDiff->mRoot);
127
128 UnorderedSet<SPtr<SerializedObject>> handleObjects;
129
130 while (!todo.empty())
131 {
132 SPtr<PrefabObjectDiff> current = todo.top();
133 todo.pop();
134
135 for (auto& component : current->addedComponents)
136 findGameObjectHandles(component, handleObjects);
137
138 for (auto& child : current->addedChildren)
139 findGameObjectHandles(child, handleObjects);
140
141 for (auto& component : current->componentDiffs)
142 findGameObjectHandles(component->data, handleObjects);
143
144 for (auto& child : current->childDiffs)
145 todo.push(child);
146 }
147
148 Vector<SerializedHandle> handleData(handleObjects.size());
149
150 UINT32 idx = 0;
151 for (auto& handleObject : handleObjects)
152 {
153 SerializedHandle& handle = handleData[idx];
154
155 handle.object = handleObject;
156 handle.handle = std::static_pointer_cast<GameObjectHandleBase>(handleObject->decode(context));
157
158 idx++;
159 }
160
161 prefabDiff->mRTTIData = handleData;
162 }
163
164 /**
165 * Decodes GameObjectHandles from their binary format, because during deserialization GameObjectManager will update
166 * all object IDs and we want to keep the handles up to date.So we deserialize them and allow them to be updated
167 * before storing them back into binary format.
168 */
169 static void delayedOnDeserializationEnded(PrefabDiff* prefabDiff)
170 {
171 Vector<SerializedHandle>& handleData = any_cast_ref<Vector<SerializedHandle>>(prefabDiff->mRTTIData);
172
173 for (auto& serializedHandle : handleData)
174 {
175 if (serializedHandle.handle != nullptr)
176 *serializedHandle.object = *SerializedObject::create(*serializedHandle.handle);
177 }
178
179 prefabDiff->mRTTIData = nullptr;
180 }
181
182 /** Scans the entire hierarchy and find all serialized GameObjectHandle objects. */
183 static void findGameObjectHandles(const SPtr<SerializedObject>& serializedObject, UnorderedSet<SPtr<SerializedObject>>& handleObjects)
184 {
185 for (auto& subObject : serializedObject->subObjects)
186 {
187 RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId);
188 if (rtti == nullptr)
189 continue;
190
191 if (rtti->getRTTIId() == TID_GameObjectHandleBase)
192 {
193 handleObjects.insert(serializedObject);
194 return;
195 }
196
197 for (auto& child : subObject.entries)
198 {
199 RTTIField* curGenericField = rtti->findField(child.second.fieldId);
200 if (curGenericField == nullptr)
201 continue;
202
203 SPtr<SerializedInstance> entryData = child.second.serialized;
204 if (entryData == nullptr)
205 continue;
206
207 if (rtti_is_of_type<SerializedArray>(entryData))
208 {
209 SPtr<SerializedArray> arrayData = std::static_pointer_cast<SerializedArray>(entryData);
210
211 for (auto& arrayElem : arrayData->entries)
212 {
213 if (arrayElem.second.serialized != nullptr && rtti_is_of_type<SerializedObject>(arrayElem.second.serialized))
214 {
215 SPtr<SerializedObject> arrayElemData = std::static_pointer_cast<SerializedObject>(arrayElem.second.serialized);
216 findGameObjectHandles(arrayElemData, handleObjects);
217 }
218 }
219 }
220 else if(rtti_is_of_type<SerializedObject>(entryData))
221 {
222 SPtr<SerializedObject> fieldObjectData = std::static_pointer_cast<SerializedObject>(entryData);
223 findGameObjectHandles(fieldObjectData, handleObjects);
224 }
225 }
226 }
227 }
228
229 const String& getRTTIName() override
230 {
231 static String name = "PrefabDiff";
232 return name;
233 }
234
235 UINT32 getRTTIId() override
236 {
237 return TID_PrefabDiff;
238 }
239
240 SPtr<IReflectable> newRTTIObject() override
241 {
242 return bs_shared_ptr_new<PrefabDiff>();
243 }
244 };
245
246 /** @} */
247 /** @endcond */
248}
249