Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
entity_container.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstdint>
5#include <type_traits>
6
7#include "gaia/cnt/ilist.h"
8#include "gaia/cnt/map.h"
9#include "gaia/cnt/paged_storage.h"
10#include "gaia/ecs/api.h"
11#include "gaia/ecs/id.h"
12#include "gaia/mem/smallblock_allocator.h"
13
14namespace gaia {
15 namespace ecs {
16 struct EntityContainer;
17 }
18
19 namespace cnt {
20 template <>
21 struct to_page_storage_id<ecs::EntityContainer> {
22 static page_storage_id get(const ecs::EntityContainer& item) noexcept;
23 };
24 } // namespace cnt
25
26 namespace ecs {
27 class Chunk;
28 class Archetype;
29 class World;
30#if GAIA_USE_WEAK_ENTITY
31 struct WeakEntityTracker;
32#endif
33
35 bool isEntity;
36 bool isPair;
37 EntityKind kind;
38 };
39
40 using EntityContainerFlagsType = uint16_t;
41 enum EntityContainerFlags : EntityContainerFlagsType {
42 OnDelete_Remove = 1 << 0,
43 OnDelete_Delete = 1 << 1,
44 OnDelete_Error = 1 << 2,
45 OnDeleteTarget_Remove = 1 << 3,
46 OnDeleteTarget_Delete = 1 << 4,
47 OnDeleteTarget_Error = 1 << 5,
48 HasAcyclic = 1 << 6,
49 HasCantCombine = 1 << 7,
50 IsExclusive = 1 << 8,
51 HasAliasOf = 1 << 9,
52 IsSingleton = 1 << 10,
53 DeleteRequested = 1 << 11,
54 RefDecreased = 1 << 12, // GAIA_USE_SAFE_ENTITY
55 Load = 1 << 13, // EntityContainer is being loaded from a file
56 IsObserved = 1 << 14, // At least one observer is registered for this term.
57 IsDontFragment = 1 << 15,
58 };
59
63 uint32_t idx;
64
66 // Bits in this section need to be 1:1 with Entity internal data
68
69 struct EntityData {
71 uint32_t gen : 28;
73 uint32_t ent : 1;
75 uint32_t pair : 1;
77 uint32_t kind : 1;
83 uint32_t dis : 1;
84 };
85
86 union {
87 EntityData data;
88 uint32_t dataRaw;
89 };
90
92
94 uint16_t row;
96 uint16_t flags = 0;
97
98#if GAIA_USE_SAFE_ENTITY
101 uint32_t refCnt = 1;
102#else
104 uint32_t unused = 0;
105#endif
106
107#if GAIA_USE_WEAK_ENTITY
108 WeakEntityTracker* pWeakTracker = nullptr;
109#endif
110
116 const Entity* pEntity = nullptr;
117 // uint8_t depthDependsOn = 0;
118
119 EntityContainer() = default;
120
121 GAIA_NODISCARD static EntityContainer create(uint32_t index, uint32_t generation, void* pCtx) {
122 auto* ctx = (EntityContainerCtx*)pCtx;
123
124 EntityContainer ec{};
125 ec.idx = index;
126 ec.data.gen = generation;
127 ec.data.ent = (uint32_t)ctx->isEntity;
128 ec.data.pair = (uint32_t)ctx->isPair;
129 ec.data.kind = (uint32_t)ctx->kind;
130 return ec;
131 }
132
133 GAIA_NODISCARD static Entity handle(const EntityContainer& ec) {
134 return Entity(ec.idx, ec.data.gen, (bool)ec.data.ent, (bool)ec.data.pair, (EntityKind)ec.data.kind);
135 }
136
137 void req_del() {
138 flags |= DeleteRequested;
139 }
140 };
141
149
150 EntityContainer& operator[](Entity entity) {
151 return entity.pair() //
152 ? pairs.find(EntityLookupKey(entity))->second
153 : entities[entity.id()];
154 }
155
156 const EntityContainer& operator[](Entity entity) const {
157 return entity.pair() //
158 ? pairs.find(EntityLookupKey(entity))->second
159 : entities[entity.id()];
160 }
161 };
162
163 } // namespace ecs
164
165 namespace cnt {
166 template <>
167 struct ilist_handle_traits<ecs::Entity> {
168 static ecs::Entity make(uint32_t id, uint32_t gen, const ecs::Entity& prev) {
169 return ecs::Entity((ecs::EntityId)id, gen, prev.entity(), prev.pair(), prev.kind());
170 }
171 };
172 } // namespace cnt
173
174 namespace ecs {
175
176#if GAIA_USE_SAFE_ENTITY
180 class SafeEntity {
181 World* m_w = nullptr;
182 Entity m_entity;
183
184 public:
185 SafeEntity() = default;
186 SafeEntity(World& w, Entity entity): m_w(&w), m_entity(entity) {
187 auto& ec = fetch_mut(w, entity);
188 ++ec.refCnt;
189 }
190
191 ~SafeEntity() {
192 // EntityContainer can be null only from moved-from SharedEntities.
193 // This is not a common occurrence.
194 if GAIA_UNLIKELY (m_w == nullptr)
195 return;
196
197 auto& ec = fetch_mut(*m_w, m_entity);
198 GAIA_ASSERT(ec.refCnt > 0);
199 --ec.refCnt;
200 if (ec.refCnt == 0)
201 del(*m_w, m_entity);
202 }
203
204 SafeEntity(const SafeEntity& other): m_w(other.m_w), m_entity(other.m_entity) {
205 auto& ec = fetch_mut(*m_w, m_entity);
206 ++ec.refCnt;
207 }
208 SafeEntity& operator=(const SafeEntity& other) {
209 GAIA_ASSERT(core::addressof(other) != this);
210
211 m_w = other.m_w;
212 m_entity = other.m_entity;
213
214 auto& ec = fetch_mut(*m_w, m_entity);
215 ++ec.refCnt;
216 return *this;
217 }
218
219 SafeEntity(SafeEntity&& other) noexcept: m_w(other.m_w), m_entity(other.m_entity) {
220 other.m_w = nullptr;
221 other.m_entity = EntityBad;
222 }
223 SafeEntity& operator=(SafeEntity&& other) noexcept {
224 GAIA_ASSERT(core::addressof(other) != this);
225
226 m_w = other.m_w;
227 m_entity = other.m_entity;
228
229 other.m_w = nullptr;
230 other.m_entity = EntityBad;
231 return *this;
232 }
233
234 template <typename Serializer>
235 void save(Serializer& s) const {
236 s.save(m_entity);
237 }
238 template <typename Serializer>
239 void load(Serializer& s) {
240 s.load(m_entity);
241 }
242
243 GAIA_NODISCARD Entity entity() const noexcept {
244 return m_entity;
245 }
246 GAIA_NODISCARD operator Entity() const noexcept {
247 return m_entity;
248 }
249
250 bool operator==(const SafeEntity& other) const noexcept {
251 return m_entity == other.entity();
252 }
253 };
254
255 inline bool operator==(const SafeEntity& e1, Entity e2) noexcept {
256 return e1.entity() == e2;
257 }
258 inline bool operator==(Entity e1, const SafeEntity& e2) noexcept {
259 return e1 == e2.entity();
260 }
261
262 inline bool operator!=(const SafeEntity& e1, Entity e2) noexcept {
263 return e1.entity() != e2;
264 }
265 inline bool operator!=(Entity e1, const SafeEntity& e2) noexcept {
266 return e1 != e2.entity();
267 }
268#endif
269
270#if GAIA_USE_WEAK_ENTITY
271 class WeakEntity;
272
273 struct WeakEntityTracker {
274 GAIA_USE_SMALLBLOCK(WeakEntityTracker)
275
276 WeakEntityTracker* next = nullptr;
277 WeakEntityTracker* prev = nullptr;
278 WeakEntity* pWeakEntity = nullptr;
279 };
280
291 class WeakEntity {
292 friend class World;
293 friend struct WeakEntityTracker;
294
295 World* m_w = nullptr;
296 WeakEntityTracker* m_pTracker = nullptr;
297 Entity m_entity;
298
299 public:
300 WeakEntity() = default;
301 WeakEntity(World& w, Entity entity): m_w(&w), m_pTracker(new WeakEntityTracker()), m_entity(entity) {
302 set_tracker();
303 }
304 ~WeakEntity() {
305 del_tracker();
306 }
307
308 WeakEntity(const WeakEntity& other): m_w(other.m_w), m_entity(other.m_entity) {
309 if (other.m_pTracker == nullptr)
310 return;
311
312 m_pTracker = new WeakEntityTracker();
313 set_tracker();
314 }
315 WeakEntity& operator=(const WeakEntity& other) {
316 GAIA_ASSERT(core::addressof(other) != this);
317
318 del_tracker();
319
320 m_w = other.m_w;
321 m_entity = other.m_entity;
322
323 if (other.m_pTracker != nullptr) {
324 m_pTracker = new WeakEntityTracker();
325 set_tracker();
326 }
327
328 return *this;
329 }
330
331 WeakEntity(WeakEntity&& other) noexcept: m_w(other.m_w), m_pTracker(other.m_pTracker), m_entity(other.m_entity) {
332 other.m_w = nullptr;
333 if (other.m_pTracker != nullptr)
334 other.m_pTracker->pWeakEntity = this;
335 other.m_pTracker = nullptr;
336 other.m_entity = EntityBad;
337 }
338 WeakEntity& operator=(WeakEntity&& other) noexcept {
339 GAIA_ASSERT(core::addressof(other) != this);
340
341 del_tracker();
342
343 m_w = other.m_w;
344 m_pTracker = other.m_pTracker;
345 m_entity = other.m_entity;
346
347 other.m_w = nullptr;
348 if (other.m_pTracker != nullptr)
349 other.m_pTracker->pWeakEntity = this;
350 other.m_pTracker = nullptr;
351 other.m_entity = EntityBad;
352 return *this;
353 }
354
355 void set_tracker() {
356 GAIA_ASSERT(m_pTracker != nullptr);
357 GAIA_ASSERT(m_w != nullptr);
358 GAIA_ASSERT(m_entity != EntityBad);
359
360 m_pTracker->pWeakEntity = this;
361 m_pTracker->prev = nullptr;
362
363 auto& ec = fetch_mut(*m_w, m_entity);
364 m_pTracker->next = ec.pWeakTracker;
365 if (ec.pWeakTracker != nullptr)
366 ec.pWeakTracker->prev = m_pTracker;
367 ec.pWeakTracker = m_pTracker;
368 }
369
370 void del_tracker() {
371 if (m_pTracker == nullptr)
372 return;
373
374 if (m_pTracker->next != nullptr)
375 m_pTracker->next->prev = m_pTracker->prev;
376 if (m_pTracker->prev != nullptr)
377 m_pTracker->prev->next = m_pTracker->next;
378
379 if (m_w != nullptr && valid(*m_w, m_entity)) {
380 auto& ec = fetch_mut(*m_w, m_entity);
381 if (ec.pWeakTracker == m_pTracker)
382 ec.pWeakTracker = m_pTracker->next;
383 }
384
385 delete m_pTracker;
386 m_pTracker = nullptr;
387 }
388
389 template <typename Serializer>
390 void save(Serializer& s) const {
391 s.save(m_entity.val);
392 }
393 template <typename Serializer>
394 void load(Serializer& s) {
395 del_tracker();
396 Identifier id{};
397 s.load(id);
398 m_entity = Entity(id);
399 if (m_w != nullptr && m_entity != EntityBad) {
400 m_pTracker = new WeakEntityTracker();
401 set_tracker();
402 }
403 }
404
405 GAIA_NODISCARD Entity entity() const noexcept {
406 return m_entity;
407 }
408 GAIA_NODISCARD operator Entity() const noexcept {
409 return entity();
410 }
411
412 bool operator==(const WeakEntity& other) const noexcept {
413 return m_entity == other.entity();
414 }
415 };
416
417 inline bool operator==(const WeakEntity& e1, Entity e2) noexcept {
418 return e1.entity() == e2;
419 }
420 inline bool operator==(Entity e1, const WeakEntity& e2) noexcept {
421 return e1 == e2.entity();
422 }
423
424 inline bool operator!=(const WeakEntity& e1, Entity e2) noexcept {
425 return e1.entity() != e2;
426 }
427 inline bool operator!=(Entity e1, const WeakEntity& e2) noexcept {
428 return e1 != e2.entity();
429 }
430#endif
431 } // namespace ecs
432
433 namespace cnt {
434 inline page_storage_id to_page_storage_id<ecs::EntityContainer>::get(const ecs::EntityContainer& item) noexcept {
435 return item.idx;
436 }
437 } // namespace cnt
438} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition archetype.h:83
Definition chunk.h:35
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition ilist.h:399
static TItemHandle make(uint32_t id, uint32_t gen, const TItemHandle &prev)
Rebuilds a handle after its generation changes.
Definition ilist.h:405
Definition paged_storage.h:37
Definition entity_container.h:34
Definition entity_container.h:69
uint32_t ent
0-component, 1-entity
Definition entity_container.h:73
uint32_t gen
Generation ID of the record.
Definition entity_container.h:71
uint32_t kind
Component kind.
Definition entity_container.h:77
uint32_t dis
Disabled Entity does not use this bit (always zero) so we steal it for special purposes....
Definition entity_container.h:83
uint32_t pair
0-ordinary, 1-pair
Definition entity_container.h:75
Definition entity_container.h:60
uint32_t unused
Currently unused area.
Definition entity_container.h:104
uint16_t row
Row at which the entity is stored in the chunk.
Definition entity_container.h:94
uint16_t flags
Flags.
Definition entity_container.h:96
const Entity * pEntity
Cached pointer to the entity's current slot inside the owning chunk.
Definition entity_container.h:116
uint32_t idx
Allocated items: Index in the list. Deleted items: Index of the next deleted item in the list.
Definition entity_container.h:63
Archetype * pArchetype
Archetype (stable address)
Definition entity_container.h:112
Chunk * pChunk
Chunk the entity currently resides in (stable address)
Definition entity_container.h:114
Definition entity_container.h:142
cnt::paged_ilist< EntityContainer, Entity > entities
Implicit list of entities. Used for look-ups only when searching for entities in chunks + data valida...
Definition entity_container.h:145
cnt::map< EntityLookupKey, EntityContainer > pairs
Just like m_recs.entities, but stores pairs. Needs to be a map because pair ids are huge numbers.
Definition entity_container.h:148
Hashmap lookup structure used for Entity.
Definition id.h:468
Definition id.h:247