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
5namespace bs
6{
7 /** @addtogroup Implementation
8 * @{
9 */
10
11 class GameObjectManager;
12
13 template <typename T>
14 class GameObjectHandle;
15
16 /** Contains instance data that is held by all GameObject handles. */
17 struct GameObjectInstanceData
18 {
19 GameObjectInstanceData()
20 :object(nullptr), mInstanceId(0)
21 { }
22
23 SPtr<GameObject> object;
24 UINT64 mInstanceId;
25 };
26
27 typedef SPtr<GameObjectInstanceData> GameObjectInstanceDataPtr;
28
29 /** Internal data shared between GameObject handles. */
30 struct BS_CORE_EXPORT GameObjectHandleData
31 {
32 GameObjectHandleData() = default;
33
34 GameObjectHandleData(SPtr<GameObjectInstanceData> ptr)
35 :mPtr(std::move(ptr))
36 { }
37
38 SPtr<GameObjectInstanceData> mPtr;
39 };
40
41 /**
42 * A handle that can point to various types of game objects. It primarily keeps track if the object is still alive,
43 * so anything still referencing it doesn't accidentally use it.
44 *
45 * @note
46 * This class exists because references between game objects should be quite loose. For example one game object should
47 * be able to reference another one without the other one knowing. But if that is the case I also need to handle the
48 * case when the other object we're referencing has been deleted, and that is the main purpose of this class.
49 */
50 class BS_CORE_EXPORT GameObjectHandleBase : public IReflectable
51 {
52 public:
53 GameObjectHandleBase()
54 : mData(bs_shared_ptr_new<GameObjectHandleData>(nullptr))
55 { }
56
57 /**
58 * Returns true if the object the handle is pointing to has been destroyed.
59 *
60 * @param[in] checkQueued Game objects can be queued for destruction but not actually destroyed yet, and still
61 * accessible. If this is false this method will return true only if the object is
62 * completely inaccessible (fully destroyed). If this is true this method will return true
63 * if object is completely inaccessible or if it is just queued for destruction.
64 */
65 bool isDestroyed(bool checkQueued = false) const;
66
67 /** Returns the instance ID of the object the handle is referencing. */
68 UINT64 getInstanceId() const { return mData->mPtr != nullptr ? mData->mPtr->mInstanceId : 0; }
69
70 /**
71 * Returns pointer to the referenced GameObject.
72 *
73 * @note Throws exception if the GameObject was destroyed.
74 */
75 GameObject* get() const
76 {
77 throwIfDestroyed();
78
79 return mData->mPtr->object.get();
80 }
81
82 /**
83 * Returns a smart pointer to the referenced GameObject.
84 *
85 * @note Throws exception if the GameObject was destroyed.
86 */
87 SPtr<GameObject> getInternalPtr() const
88 {
89 throwIfDestroyed();
90
91 return mData->mPtr->object;
92 }
93
94 /**
95 * Returns pointer to the referenced GameObject.
96 *
97 * @note Throws exception if the GameObject was destroyed.
98 */
99 GameObject* operator->() const { return get(); }
100
101 /**
102 * Returns reference to the referenced GameObject.
103 *
104 * @note Throws exception if the GameObject was destroyed.
105 */
106 GameObject& operator*() const { return *get(); }
107
108 public: // ***** INTERNAL ******
109 /** @name Internal
110 * @{
111 */
112
113 /** Returns internal handle data. */
114 const SPtr<GameObjectHandleData>& _getHandleData() const { return mData; }
115
116 /** Resolves a handle to a proper GameObject in case it was created uninitialized. */
117 void _resolve(const GameObjectHandleBase& object) { mData->mPtr = object.mData->mPtr; }
118
119 /** Changes the GameObject instance the handle is pointing to. */
120 void _setHandleData(const SPtr<GameObject>& object);
121
122 /** @} */
123
124 protected:
125 friend class GameObjectManager;
126 friend class GameObjectDeserializationState;
127
128 template<class _Ty1, class _Ty2>
129 friend bool operator==(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right);
130
131 GameObjectHandleBase(const SPtr<GameObject>& ptr);
132
133 GameObjectHandleBase(SPtr<GameObjectHandleData> data)
134 : mData(std::move(data))
135 { }
136
137 GameObjectHandleBase(std::nullptr_t ptr)
138 : mData(bs_shared_ptr_new<GameObjectHandleData>(nullptr))
139 { }
140
141 /** Throws an exception if the referenced GameObject has been destroyed. */
142 void throwIfDestroyed() const;
143
144 /** Invalidates the handle signifying the referenced object was destroyed. */
145 void destroy()
146 {
147 // It's important not to clear mData->mPtr as some code might rely
148 // on it. (for example for restoring lost handles)
149
150 if (mData->mPtr != nullptr)
151 mData->mPtr->object = nullptr;
152 }
153
154 SPtr<GameObjectHandleData> mData;
155
156 /************************************************************************/
157 /* RTTI */
158 /************************************************************************/
159 public:
160 friend class GameObjectHandleRTTI;
161 static RTTITypeBase* getRTTIStatic();
162 RTTITypeBase* getRTTI() const override;
163 };
164
165 /** @} */
166
167 /** @addtogroup Scene
168 * @{
169 */
170
171 /**
172 * @copydoc GameObjectHandleBase
173 *
174 * @note It is important this class contains no data since we often value cast it to its base.
175 */
176 template <typename T>
177 class GameObjectHandle : public GameObjectHandleBase
178 {
179 public:
180 /** Constructs a new empty handle. */
181 GameObjectHandle()
182 :GameObjectHandleBase()
183 {
184 mData = bs_shared_ptr_new<GameObjectHandleData>();
185 }
186
187 /** Copy constructor from another handle of the same type. */
188 GameObjectHandle(const GameObjectHandle<T>& ptr) = default;
189
190 /** Move constructor from another handle of the same type. */
191 GameObjectHandle(GameObjectHandle<T>&& ptr) = default;
192
193 /** Invalidates the handle. */
194 GameObjectHandle<T>& operator=(std::nullptr_t ptr)
195 {
196 mData = bs_shared_ptr_new<GameObjectHandleData>();
197
198 return *this;
199 }
200
201 /** Copy assignment */
202 GameObjectHandle<T>& operator=(const GameObjectHandle<T>& other) = default;
203
204 /** Move assignment */
205 GameObjectHandle<T>& operator=(GameObjectHandle<T>&& other) = default;
206
207 /**
208 * Returns a pointer to the referenced GameObject.
209 *
210 * @note Throws exception if the GameObject was destroyed.
211 */
212 T* get() const
213 {
214 throwIfDestroyed();
215
216 return reinterpret_cast<T*>(mData->mPtr->object.get());
217 }
218
219 /**
220 * Returns a smart pointer to the referenced GameObject.
221 *
222 * @note Throws exception if the GameObject was destroyed.
223 */
224 SPtr<T> getInternalPtr() const
225 {
226 throwIfDestroyed();
227
228 return std::static_pointer_cast<T>(mData->mPtr->object);
229 }
230
231 /**
232 * Returns pointer to the referenced GameObject.
233 *
234 * @note Throws exception if the GameObject was destroyed.
235 */
236 T* operator->() const { return get(); }
237
238 /**
239 * Returns reference to the referenced GameObject.
240 *
241 * @note Throws exception if the GameObject was destroyed.
242 */
243 T& operator*() const { return *get(); }
244
245 public: // ***** INTERNAL ******
246 /** @name Internal
247 * @{
248 */
249
250 template<class _Ty>
251 struct Bool_struct
252 {
253 int _Member;
254 };
255
256 /**
257 * Allows direct conversion of handle to bool.
258 *
259 * @note
260 * This is needed because we can't directly convert to bool since then we can assign pointer to bool and that's
261 * weird.
262 */
263 operator int Bool_struct<T>::*() const
264 {
265 return (((mData->mPtr != nullptr) && (mData->mPtr->object != nullptr)) ? &Bool_struct<T>::_Member : 0);
266 }
267
268 /** @} */
269
270 protected:
271 template<class _Ty1, class _Ty2>
272 friend GameObjectHandle<_Ty1> static_object_cast(const GameObjectHandle<_Ty2>& other);
273
274 template<class _Ty1>
275 friend GameObjectHandle<_Ty1> static_object_cast(const GameObjectHandleBase& other);
276
277 GameObjectHandle(SPtr<GameObjectHandleData> data)
278 :GameObjectHandleBase(std::move(data))
279 { }
280 };
281
282 /** Casts one GameObject handle type to another. */
283 template<class _Ty1, class _Ty2>
284 GameObjectHandle<_Ty1> static_object_cast(const GameObjectHandle<_Ty2>& other)
285 {
286 return GameObjectHandle<_Ty1>(other._getHandleData());
287 }
288
289 /** Casts a generic GameObject handle to a specific one . */
290 template<class T>
291 GameObjectHandle<T> static_object_cast(const GameObjectHandleBase& other)
292 {
293 return GameObjectHandle<T>(other._getHandleData());
294 }
295
296 /** Compares if two handles point to the same GameObject. */
297 template<class _Ty1, class _Ty2>
298 bool operator==(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right)
299 {
300 return (_Left.mData == nullptr && _Right.mData == nullptr) ||
301 (_Left.mData != nullptr && _Right.mData != nullptr && _Left.getInstanceId() == _Right.getInstanceId());
302 }
303
304 /** Compares if two handles point to different GameObject%s. */
305 template<class _Ty1, class _Ty2>
306 bool operator!=(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right)
307 {
308 return (!(_Left == _Right));
309 }
310
311 /** @} */
312}
313