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