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 | namespace 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 | |