Gaia-ECS v1.0.0
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
15namespace gaia {
16 namespace ecs {
17 struct EntityContainer;
18 }
19
20 namespace cnt {
21 template <>
22 struct to_page_storage_id<ecs::EntityContainer> {
23 static page_storage_id get(const ecs::EntityContainer& item) noexcept;
24 };
25 } // namespace cnt
26
27 namespace ecs {
28 class Chunk;
29 class Archetype;
30 class World;
31#if GAIA_USE_WEAK_ENTITY
32 struct WeakEntityTracker;
33#endif
34
35 struct EntityContainerCtx {
36 bool isEntity;
37 bool isPair;
38 EntityKind kind;
39 };
40
41 using EntityContainerFlagsType = uint16_t;
42 enum EntityContainerFlags : EntityContainerFlagsType {
43 OnDelete_Remove = 1 << 0,
44 OnDelete_Delete = 1 << 1,
45 OnDelete_Error = 1 << 2,
46 OnDeleteTarget_Remove = 1 << 3,
47 OnDeleteTarget_Delete = 1 << 4,
48 OnDeleteTarget_Error = 1 << 5,
49 HasAcyclic = 1 << 6,
50 HasCantCombine = 1 << 7,
51 IsExclusive = 1 << 8,
52 HasAliasOf = 1 << 9,
53 IsSingleton = 1 << 10,
54 DeleteRequested = 1 << 11,
55 RefDecreased = 1 << 12, // GAIA_USE_SAFE_ENTITY
56 Load = 1 << 13, // EntityContainer is being loaded from a file
57 IsObserved = 1 << 14, // At least one observer is registered for this term.
58 IsDontFragment = 1 << 15,
59 };
60
61 struct EntityContainer {
64 uint32_t idx;
65
67 // Bits in this section need to be 1:1 with Entity internal data
69
70 struct EntityData {
72 uint32_t gen : 28;
74 uint32_t ent : 1;
76 uint32_t pair : 1;
78 uint32_t kind : 1;
84 uint32_t dis : 1;
85 };
86
87 union {
88 EntityData data;
89 uint32_t dataRaw;
90 };
91
93
95 uint16_t row;
97 uint16_t flags = 0;
98
99#if GAIA_USE_SAFE_ENTITY
102 uint32_t refCnt = 1;
103#else
105 uint32_t unused = 0;
106#endif
107
108#if GAIA_USE_WEAK_ENTITY
109 WeakEntityTracker* pWeakTracker = nullptr;
110#endif
111
113 Archetype* pArchetype = nullptr;
115 Chunk* pChunk = nullptr;
117 const Entity* pEntity = nullptr;
118 // uint8_t depthDependsOn = 0;
119
120 EntityContainer() = default;
121
122 GAIA_NODISCARD static EntityContainer create(uint32_t index, uint32_t generation, void* pCtx) {
123 auto* ctx = (EntityContainerCtx*)pCtx;
124
125 EntityContainer ec{};
126 ec.idx = index;
127 ec.data.gen = generation;
128 ec.data.ent = (uint32_t)ctx->isEntity;
129 ec.data.pair = (uint32_t)ctx->isPair;
130 ec.data.kind = (uint32_t)ctx->kind;
131 return ec;
132 }
133
134 GAIA_NODISCARD static Entity handle(const EntityContainer& ec) {
135 return Entity(ec.idx, ec.data.gen, (bool)ec.data.ent, (bool)ec.data.pair, (EntityKind)ec.data.kind);
136 }
137
138 void req_del() {
139 flags |= DeleteRequested;
140 }
141 };
142
143 struct PairRecords {
144 private:
146 cnt::map<EntityLookupKey, EntityContainer> m_records;
147
148 public:
150 void clear() {
151 m_records = {};
152 }
153
157 GAIA_NODISCARD bool contains(Entity entity) const {
158 GAIA_ASSERT(entity.pair());
159 return m_records.contains(EntityLookupKey(entity));
160 }
161
165 GAIA_NODISCARD EntityContainer* find(Entity entity) {
166 GAIA_ASSERT(entity.pair());
167
168 const auto it = m_records.find(EntityLookupKey(entity));
169 if (it == m_records.end())
170 return nullptr;
171
172 return &it->second;
173 }
174
178 GAIA_NODISCARD const EntityContainer* find(Entity entity) const {
179 GAIA_ASSERT(entity.pair());
180
181 const auto it = m_records.find(EntityLookupKey(entity));
182 if (it == m_records.end())
183 return nullptr;
184
185 return &it->second;
186 }
187
191 void add(Entity entity, EntityContainer&& ec) {
192 GAIA_ASSERT(entity.pair());
193 m_records.emplace(EntityLookupKey(entity), GAIA_MOV(ec));
194 }
195
200 GAIA_NODISCARD bool try_add(Entity entity, EntityContainer&& ec) {
201 GAIA_ASSERT(entity.pair());
202
203 if (contains(entity))
204 return false;
205
206 add(entity, GAIA_MOV(ec));
207 return true;
208 }
209
212 void remove(Entity entity) {
213 GAIA_ASSERT(entity.pair());
214 m_records.erase(EntityLookupKey(entity));
215 }
216
221 GAIA_NODISCARD bool remove(Entity entity, EntityContainer& ec) {
222 GAIA_ASSERT(entity.pair());
223
224 const auto it = m_records.find(EntityLookupKey(entity));
225 if (it == m_records.end())
226 return false;
227
228 ec = GAIA_MOV(it->second);
229 m_records.erase(it);
230 return true;
231 }
232
235 GAIA_NODISCARD auto begin() {
236 return m_records.begin();
237 }
238
241 GAIA_NODISCARD auto begin() const {
242 return m_records.begin();
243 }
244
247 GAIA_NODISCARD auto end() {
248 return m_records.end();
249 }
250
253 GAIA_NODISCARD auto end() const {
254 return m_records.end();
255 }
256 };
257
258 struct EntityContainers {
259 private:
261 PairRecords m_pairRecords;
262
263 public:
266 cnt::paged_ilist<EntityContainer, Entity> entities;
267
269 void pair_records_clear() {
270 m_pairRecords.clear();
271 }
272
276 GAIA_NODISCARD bool pair_record_contains(Entity entity) const {
277 return m_pairRecords.contains(entity);
278 }
279
283 GAIA_NODISCARD EntityContainer* pair_record_find(Entity entity) {
284 return m_pairRecords.find(entity);
285 }
286
290 GAIA_NODISCARD const EntityContainer* pair_record_find(Entity entity) const {
291 return m_pairRecords.find(entity);
292 }
293
297 void pair_record_add(Entity entity, EntityContainer&& ec) {
298 m_pairRecords.add(entity, GAIA_MOV(ec));
299 }
300
305 GAIA_NODISCARD bool pair_record_try_add(Entity entity, EntityContainer&& ec) {
306 return m_pairRecords.try_add(entity, GAIA_MOV(ec));
307 }
308
311 void pair_record_remove(Entity entity) {
312 m_pairRecords.remove(entity);
313 }
314
319 GAIA_NODISCARD bool pair_record_remove(Entity entity, EntityContainer& ec) {
320 return m_pairRecords.remove(entity, ec);
321 }
322
325 GAIA_NODISCARD auto pair_record_begin() {
326 return m_pairRecords.begin();
327 }
328
331 GAIA_NODISCARD auto pair_record_begin() const {
332 return m_pairRecords.begin();
333 }
334
337 GAIA_NODISCARD auto pair_record_end() {
338 return m_pairRecords.end();
339 }
340
343 GAIA_NODISCARD auto pair_record_end() const {
344 return m_pairRecords.end();
345 }
346
347 EntityContainer& operator[](Entity entity) {
348 if (!entity.pair())
349 return entities[entity.id()];
350
351 auto* pPair = m_pairRecords.find(entity);
352 GAIA_ASSERT(pPair != nullptr);
353 return *pPair;
354 }
355
356 const EntityContainer& operator[](Entity entity) const {
357 if (!entity.pair())
358 return entities[entity.id()];
359
360 const auto* pPair = m_pairRecords.find(entity);
361 GAIA_ASSERT(pPair != nullptr);
362 return *pPair;
363 }
364 };
365
366 } // namespace ecs
367
368 namespace cnt {
369 template <>
370 struct ilist_handle_traits<ecs::Entity> {
371 static ecs::Entity make(uint32_t id, uint32_t gen, const ecs::Entity& prev) {
372 return ecs::Entity((ecs::EntityId)id, gen, prev.entity(), prev.pair(), prev.kind());
373 }
374 };
375 } // namespace cnt
376
377 namespace ecs {
378
379#if GAIA_USE_SAFE_ENTITY
383 class SafeEntity {
384 World* m_w = nullptr;
385 Entity m_entity;
386
387 public:
388 SafeEntity() = default;
389 SafeEntity(World& w, Entity entity): m_w(&w), m_entity(entity) {
390 auto& ec = fetch_mut(w, entity);
391 ++ec.refCnt;
392 }
393
394 ~SafeEntity() {
395 // EntityContainer can be null only from moved-from SharedEntities.
396 // This is not a common occurrence.
397 if GAIA_UNLIKELY (m_w == nullptr)
398 return;
399
400 auto& ec = fetch_mut(*m_w, m_entity);
401 GAIA_ASSERT(ec.refCnt > 0);
402 --ec.refCnt;
403 if (ec.refCnt == 0)
404 del(*m_w, m_entity);
405 }
406
407 SafeEntity(const SafeEntity& other): m_w(other.m_w), m_entity(other.m_entity) {
408 auto& ec = fetch_mut(*m_w, m_entity);
409 ++ec.refCnt;
410 }
411 SafeEntity& operator=(const SafeEntity& other) {
412 GAIA_ASSERT(core::addressof(other) != this);
413
414 m_w = other.m_w;
415 m_entity = other.m_entity;
416
417 auto& ec = fetch_mut(*m_w, m_entity);
418 ++ec.refCnt;
419 return *this;
420 }
421
422 SafeEntity(SafeEntity&& other) noexcept: m_w(other.m_w), m_entity(other.m_entity) {
423 other.m_w = nullptr;
424 other.m_entity = EntityBad;
425 }
426 SafeEntity& operator=(SafeEntity&& other) noexcept {
427 GAIA_ASSERT(core::addressof(other) != this);
428
429 m_w = other.m_w;
430 m_entity = other.m_entity;
431
432 other.m_w = nullptr;
433 other.m_entity = EntityBad;
434 return *this;
435 }
436
437 template <typename Serializer>
438 void save(Serializer& s) const {
439 s.save(m_entity);
440 }
441 template <typename Serializer>
442 void load(Serializer& s) {
443 s.load(m_entity);
444 }
445
446 GAIA_NODISCARD Entity entity() const noexcept {
447 return m_entity;
448 }
449 GAIA_NODISCARD operator Entity() const noexcept {
450 return m_entity;
451 }
452
453 bool operator==(const SafeEntity& other) const noexcept {
454 return m_entity == other.entity();
455 }
456 };
457
458 inline bool operator==(const SafeEntity& e1, Entity e2) noexcept {
459 return e1.entity() == e2;
460 }
461 inline bool operator==(Entity e1, const SafeEntity& e2) noexcept {
462 return e1 == e2.entity();
463 }
464
465 inline bool operator!=(const SafeEntity& e1, Entity e2) noexcept {
466 return e1.entity() != e2;
467 }
468 inline bool operator!=(Entity e1, const SafeEntity& e2) noexcept {
469 return e1 != e2.entity();
470 }
471#endif
472
473#if GAIA_USE_WEAK_ENTITY
474 class WeakEntity;
475
476 struct WeakEntityTracker {
477 GAIA_USE_SMALLBLOCK(WeakEntityTracker)
478
479 WeakEntityTracker* next = nullptr;
480 WeakEntityTracker* prev = nullptr;
481 WeakEntity* pWeakEntity = nullptr;
482 };
483
494 class WeakEntity {
495 friend class World;
496 friend struct WeakEntityTracker;
497
498 World* m_w = nullptr;
499 WeakEntityTracker* m_pTracker = nullptr;
500 Entity m_entity;
501
502 public:
503 WeakEntity() = default;
504 WeakEntity(World& w, Entity entity): m_w(&w), m_pTracker(new WeakEntityTracker()), m_entity(entity) {
505 set_tracker();
506 }
507 ~WeakEntity() {
508 del_tracker();
509 }
510
511 WeakEntity(const WeakEntity& other): m_w(other.m_w), m_entity(other.m_entity) {
512 if (other.m_pTracker == nullptr)
513 return;
514
515 m_pTracker = new WeakEntityTracker();
516 set_tracker();
517 }
518 WeakEntity& operator=(const WeakEntity& other) {
519 GAIA_ASSERT(core::addressof(other) != this);
520
521 del_tracker();
522
523 m_w = other.m_w;
524 m_entity = other.m_entity;
525
526 if (other.m_pTracker != nullptr) {
527 m_pTracker = new WeakEntityTracker();
528 set_tracker();
529 }
530
531 return *this;
532 }
533
534 WeakEntity(WeakEntity&& other) noexcept: m_w(other.m_w), m_pTracker(other.m_pTracker), m_entity(other.m_entity) {
535 other.m_w = nullptr;
536 if (other.m_pTracker != nullptr)
537 other.m_pTracker->pWeakEntity = this;
538 other.m_pTracker = nullptr;
539 other.m_entity = EntityBad;
540 }
541 WeakEntity& operator=(WeakEntity&& other) noexcept {
542 GAIA_ASSERT(core::addressof(other) != this);
543
544 del_tracker();
545
546 m_w = other.m_w;
547 m_pTracker = other.m_pTracker;
548 m_entity = other.m_entity;
549
550 other.m_w = nullptr;
551 if (other.m_pTracker != nullptr)
552 other.m_pTracker->pWeakEntity = this;
553 other.m_pTracker = nullptr;
554 other.m_entity = EntityBad;
555 return *this;
556 }
557
558 void set_tracker() {
559 GAIA_ASSERT(m_pTracker != nullptr);
560 GAIA_ASSERT(m_w != nullptr);
561 GAIA_ASSERT(m_entity != EntityBad);
562
563 m_pTracker->pWeakEntity = this;
564 m_pTracker->prev = nullptr;
565
566 auto& ec = fetch_mut(*m_w, m_entity);
567 m_pTracker->next = ec.pWeakTracker;
568 if (ec.pWeakTracker != nullptr)
569 ec.pWeakTracker->prev = m_pTracker;
570 ec.pWeakTracker = m_pTracker;
571 }
572
573 void del_tracker() {
574 if (m_pTracker == nullptr)
575 return;
576
577 if (m_pTracker->next != nullptr)
578 m_pTracker->next->prev = m_pTracker->prev;
579 if (m_pTracker->prev != nullptr)
580 m_pTracker->prev->next = m_pTracker->next;
581
582 if (m_w != nullptr && valid(*m_w, m_entity)) {
583 auto& ec = fetch_mut(*m_w, m_entity);
584 if (ec.pWeakTracker == m_pTracker)
585 ec.pWeakTracker = m_pTracker->next;
586 }
587
588 delete m_pTracker;
589 m_pTracker = nullptr;
590 }
591
592 template <typename Serializer>
593 void save(Serializer& s) const {
594 s.save(m_entity.val);
595 }
596 template <typename Serializer>
597 void load(Serializer& s) {
598 del_tracker();
599 Identifier id{};
600 s.load(id);
601 m_entity = Entity(id);
602 if (m_w != nullptr && m_entity != EntityBad) {
603 m_pTracker = new WeakEntityTracker();
604 set_tracker();
605 }
606 }
607
608 GAIA_NODISCARD Entity entity() const noexcept {
609 return m_entity;
610 }
611 GAIA_NODISCARD operator Entity() const noexcept {
612 return entity();
613 }
614
615 bool operator==(const WeakEntity& other) const noexcept {
616 return m_entity == other.entity();
617 }
618 };
619
620 inline bool operator==(const WeakEntity& e1, Entity e2) noexcept {
621 return e1.entity() == e2;
622 }
623 inline bool operator==(Entity e1, const WeakEntity& e2) noexcept {
624 return e1 == e2.entity();
625 }
626
627 inline bool operator!=(const WeakEntity& e1, Entity e2) noexcept {
628 return e1.entity() != e2;
629 }
630 inline bool operator!=(Entity e1, const WeakEntity& e2) noexcept {
631 return e1 != e2.entity();
632 }
633#endif
634 } // namespace ecs
635
636 namespace cnt {
637 inline page_storage_id to_page_storage_id<ecs::EntityContainer>::get(const ecs::EntityContainer& item) noexcept {
638 return item.idx;
639 }
640 } // namespace cnt
641} // namespace gaia
void load(Reader &reader, T &data)
Read data using Reader at compile-time.
Definition ser_ct.h:112
void save(Writer &writer, const T &data)
Write data using Writer at compile-time.
Definition ser_ct.h:101
static TItemHandle make(uint32_t id, uint32_t gen, const TItemHandle &prev)
Rebuilds a handle after its generation changes.
Definition ilist.h:497
static page_storage_id get(const T &item) noexcept
Applies the default conversion for an item.
Definition paged_storage.h:47