49 friend class ECSSystem;
50 friend class ECSSystemManager;
53 friend void lock(
World&);
54 friend void unlock(
World&);
59 using TFunc_Void_With_Entity = void(
Entity);
60 static void func_void_with_entity([[maybe_unused]]
Entity entity) {}
75 uint32_t m_nextQuerySerId = 0;
115 ArchetypeId m_nextArchetypeId = 0;
133 ecs::Query m_systemsQuery;
142 uint32_t m_defragLastArchetypeIdx = 0;
144 uint32_t m_defragEntitiesPerTick = 100;
147 uint32_t m_worldVersion = 0;
149 uint32_t m_structuralChangesLocked = 0;
154 m_pCmdBufferST(cmd_buffer_st_create(*
this)),
156 m_pCmdBufferMT(cmd_buffer_mt_create(*
this)) {
162 cmd_buffer_destroy(*m_pCmdBufferST);
163 cmd_buffer_destroy(*m_pCmdBufferMT);
176 template <
bool UseCache = true>
178 if constexpr (UseCache) {
180 *
const_cast<World*
>(
this), m_queryCache,
182 m_nextArchetypeId, m_worldVersion, m_archetypesById, m_entityToArchetypeMap, m_archetypes);
186 *
const_cast<World*
>(
this),
188 m_nextArchetypeId, m_worldVersion, m_archetypesById, m_entityToArchetypeMap, m_archetypes);
197 GAIA_ASSERT(valid(entity));
199 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
200 return m_recs[entity];
203 GAIA_NODISCARD
const EntityContainer& fetch(Entity entity)
const {
205 GAIA_ASSERT(valid(entity));
207 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
208 return m_recs[entity];
211 GAIA_NODISCARD
static bool is_req_del(
const EntityContainer& ec) {
212 if ((ec.flags & EntityContainerFlags::DeleteRequested) != 0)
214 if (ec.pArchetype !=
nullptr && ec.pArchetype->is_req_del())
222 void set_serializer(ser::ISerializer* pSerializer) {
223 if (pSerializer ==
nullptr) {
225 m_pSerializer = &m_binarySerializer;
229 m_pSerializer = pSerializer;
232 ser::ISerializer* get_serializer()
const {
233 return m_pSerializer;
259 m_world(world), m_pArchetypeSrc(ec.pArchetype), m_pChunkSrc(ec.pChunk), m_rowSrc(ec.row),
260 m_pArchetype(ec.pArchetype), m_entity(entity) {
262 GAIA_ASSERT(ec.
pChunk->entity_view()[ec.
row] == entity);
266 const auto& ec = world.fetch(entity);
267 m_pArchetypeSrc = ec.pArchetype;
268 m_pChunkSrc = ec.pChunk;
271 m_pArchetype = ec.pArchetype;
287 if (m_pArchetype ==
nullptr)
291 if (m_pArchetypeSrc != m_pArchetype) {
292 auto& ec = m_world.fetch(m_entity);
293 GAIA_ASSERT(ec.pArchetype == m_pArchetypeSrc);
299 m_world.move_entity_raw(m_entity, ec, *m_pArchetype);
302 if (m_targetNameKey.str() !=
nullptr) {
303 const auto compIdx = ec.pChunk->comp_idx(GAIA_ID(
EntityDesc));
305 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut_gen<
false>(compIdx, ec.row));
306 GAIA_ASSERT(core::check_alignment(pDesc));
307 *pDesc = {m_targetNameKey.str(), m_targetNameKey.len()};
313 m_pArchetypeSrc = ec.pArchetype;
314 m_pChunkSrc = ec.pChunk;
319#if GAIA_ASSERT_ENABLED
320 auto& ec = m_world.fetch(m_entity);
321 GAIA_ASSERT(ec.pChunk == m_pChunkSrc);
325 if (m_targetNameKey.str() !=
nullptr) {
328 GAIA_ASSERT(core::check_alignment(pDesc));
329 *pDesc = {m_targetNameKey.str(), m_targetNameKey.len()};
334 m_pArchetype =
nullptr;
335 m_targetNameKey = {};
345 void name(
const char* name, uint32_t len = 0) {
346 name_inter<true>(name, len);
360 void name_raw(
const char* name, uint32_t len = 0) {
361 name_inter<false>(name, len);
369 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(
EntityDesc));
370 if (compIdx == BadIndex)
374 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->comp_ptr(compIdx, m_rowSrc));
375 GAIA_ASSERT(core::check_alignment(pDesc));
376 if (pDesc->name ==
nullptr)
385 m_targetNameKey = {};
391 GAIA_PROF_SCOPE(EntityBuilder::add);
392 GAIA_ASSERT(m_world.valid(m_entity));
393 GAIA_ASSERT(m_world.valid(entity));
402 GAIA_PROF_SCOPE(EntityBuilder::add);
403 GAIA_ASSERT(m_world.valid(m_entity));
404 GAIA_ASSERT(m_world.valid(
pair.first()));
405 GAIA_ASSERT(m_world.valid(
pair.second()));
415 return add(
Pair(Is, entityBase));
423 return static_cast<const World&
>(m_world).is(entity, entityBase);
428 return add(
Pair(ChildOf, parent));
432 template <
typename T>
435 const auto rel = m_world.add<
typename T::rel>().entity;
436 const auto tgt = m_world.add<
typename T::tgt>().entity;
441 return m_world.add<T>().entity;
445#if GAIA_USE_VARIADIC_API
446 template <
typename... T>
448 (verify_comp<T>(), ...);
449 (add(register_component<T>()), ...);
453 template <
typename T>
456 add(register_component<T>());
464 GAIA_PROF_SCOPE(EntityBuilder::del);
465 GAIA_ASSERT(m_world.valid(m_entity));
466 GAIA_ASSERT(m_world.valid(entity));
474 GAIA_PROF_SCOPE(EntityBuilder::add);
475 GAIA_ASSERT(m_world.valid(m_entity));
476 GAIA_ASSERT(m_world.valid(
pair.first()));
477 GAIA_ASSERT(m_world.valid(
pair.second()));
482#if GAIA_USE_VARIADIC_API
483 template <
typename... T>
485 (verify_comp<T>(), ...);
486 (del(register_component<T>()), ...);
490 template <
typename T>
493 del(register_component<T>());
501 void trigger_add_hooks() {
502#if GAIA_ENABLE_ADD_DEL_HOOKS
505 for (
auto entity: tl_new_comps) {
506 const auto& item = m_world.comp_cache().get(entity);
507 const auto& hooks = ComponentCache::hooks(item);
508 if (hooks.func_add !=
nullptr)
509 hooks.func_add(m_world, item, m_entity);
511 tl_new_comps.clear();
519 void trigger_del_hooks() {
520#if GAIA_ENABLE_ADD_DEL_HOOKS
523 for (
auto entity: tl_del_comps) {
524 const auto& item = m_world.comp_cache().get(entity);
525 const auto& hooks = ComponentCache::hooks(item);
526 if (hooks.func_del !=
nullptr)
527 hooks.func_del(m_world, item, m_entity);
529 tl_del_comps.clear();
535 bool handle_add_entity(Entity entity) {
536 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> targets;
538 const auto& ecMain = m_world.fetch(entity);
541 if ((ecMain.flags & EntityContainerFlags::HasCantCombine) != 0) {
542 m_world.targets(entity, CantCombine, [&targets](Entity target) {
543 targets.push_back(target);
545 for (
auto e: targets) {
546 if (m_pArchetype->
has(e)) {
547#if GAIA_ASSERT_ENABLED
548 GAIA_ASSERT2(
false,
"Trying to add an entity which can't be combined with the source");
549 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
559 const auto& ecRel = m_world.m_recs.entities[entity.id()];
560 if ((ecRel.flags & EntityContainerFlags::IsExclusive) != 0) {
562 entity.id(), ecRel.data.gen, (
bool)ecRel.data.ent, (
bool)ecRel.data.pair,
563 (EntityKind)ecRel.data.kind);
564 auto tgt = m_world.get(entity.gen());
569 m_world.targets_if(m_entity, rel, [&targets](Entity target) {
570 targets.push_back(target);
573 return targets.size() < 2;
576 const auto targetsCnt = targets.size();
577 if (targetsCnt > 1) {
578#if GAIA_ASSERT_ENABLED
580 false,
"Trying to add a pair with exclusive relationship but there are multiple targets present. "
581 "Make sure to add the Exclusive property before any relationships with it are created.");
582 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
589 const auto tgtNew = *targets.begin();
590 if (targetsCnt == 1 && tgt != tgtNew) {
595 if (!can_del(entity)) {
596#if GAIA_ASSERT_ENABLED
598 false,
"Trying to replace an exclusive relationship but the entity which is getting removed has "
600 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
605 handle_del(ecs::Pair(rel, tgtNew));
613 m_world.targets(entity, Requires, [&targets](Entity target) {
614 targets.push_back(target);
617 for (
auto e: targets) {
618 auto* pArchetype = m_pArchetype;
619 handle_add<false>(e);
620 if (m_pArchetype != pArchetype)
621 handle_add_entity(e);
628 GAIA_NODISCARD
bool has_Requires_tgt(Entity entity)
const {
630 auto ids = m_pArchetype->ids_view();
632 if (m_world.has(e, Pair(Requires, entity)))
639 static void set_flag(EntityContainerFlagsType& flags, EntityContainerFlags flag,
bool enable) {
646 void set_flag(Entity entity, EntityContainerFlags flag,
bool enable) {
647 auto& ec = m_world.fetch(entity);
648 set_flag(ec.flags, flag, enable);
651 void try_set_flags(Entity entity,
bool enable) {
652 auto& ecMain = m_world.fetch(m_entity);
653 try_set_CantCombine(ecMain, entity, enable);
655 auto& ec = m_world.fetch(entity);
656 try_set_Is(ec, entity, enable);
657 try_set_IsExclusive(ecMain, entity, enable);
658 try_set_IsSingleton(ecMain, entity, enable);
659 try_set_OnDelete(ecMain, entity, enable);
660 try_set_OnDeleteTarget(entity, enable);
663 void try_set_Is(EntityContainer& ec, Entity entity,
bool enable) {
664 if (!entity.pair() || entity.id() != Is.id())
667 set_flag(ec.flags, EntityContainerFlags::HasAliasOf, enable);
670 void try_set_CantCombine(EntityContainer& ec, Entity entity,
bool enable) {
671 if (!entity.pair() || entity.id() != CantCombine.id())
674 GAIA_ASSERT(entity != m_entity);
683 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
true);
684 else if ((ec.flags & EntityContainerFlags::HasCantCombine) != 0) {
685 uint32_t targets = 0;
686 m_world.targets(m_entity, CantCombine, [&targets]([[maybe_unused]] Entity entity) {
690 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
false);
694 void try_set_IsExclusive(EntityContainer& ec, Entity entity,
bool enable) {
695 if (entity.pair() || entity.id() != Exclusive.id())
698 set_flag(ec.flags, EntityContainerFlags::IsExclusive, enable);
701 void try_set_OnDeleteTarget(Entity entity,
bool enable) {
705 const auto rel = m_world.get(entity.id());
706 const auto tgt = m_world.get(entity.gen());
710 if (m_world.has(rel, Pair(OnDeleteTarget, Delete)))
711 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Delete, enable);
712 else if (m_world.has(rel, Pair(OnDeleteTarget, Remove)))
713 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Remove, enable);
714 else if (m_world.has(rel, Pair(OnDeleteTarget, Error)))
715 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Error, enable);
718 void try_set_OnDelete(EntityContainer& ec, Entity entity,
bool enable) {
719 if (entity == Pair(OnDelete, Delete))
720 set_flag(ec.flags, EntityContainerFlags::OnDelete_Delete, enable);
721 else if (entity == Pair(OnDelete, Remove))
722 set_flag(ec.flags, EntityContainerFlags::OnDelete_Remove, enable);
723 else if (entity == Pair(OnDelete, Error))
724 set_flag(ec.flags, EntityContainerFlags::OnDelete_Error, enable);
727 void try_set_IsSingleton(EntityContainer& ec, Entity entity,
bool enable) {
728 const bool isSingleton = enable && m_entity == entity;
729 set_flag(ec.flags, EntityContainerFlags::IsSingleton, isSingleton);
732 void handle_DependsOn(Entity entity,
bool enable) {
774 template <
bool IsBootstrap>
775 bool handle_add(Entity entity) {
776#if GAIA_ASSERT_ENABLED
777 World::verify_add(m_world, *m_pArchetype, m_entity, entity);
781 if (m_pArchetype->
has(entity))
784 try_set_flags(entity,
true);
787 if (entity.pair() && entity.id() == Is.id()) {
788 auto e = m_world.get(entity.gen());
790 EntityLookupKey entityKey(m_entity);
791 EntityLookupKey eKey(e);
794 auto& entity_to_e = m_world.m_entityToAsTargets[entityKey];
795 entity_to_e.insert(eKey);
797 auto& e_to_entity = m_world.m_entityToAsRelations[eKey];
798 e_to_entity.insert(entityKey);
805 m_world.invalidate_queries_for_entity({Is, e});
808 m_pArchetype = m_world.foc_archetype_add(m_pArchetype, entity);
810 if constexpr (!IsBootstrap) {
811 handle_DependsOn(entity,
true);
813 tl_new_comps.push_back(entity);
819 void handle_del(Entity entity) {
820#if GAIA_ASSERT_ENABLED
821 World::verify_del(m_world, *m_pArchetype, m_entity, entity);
825 if (!m_pArchetype->
has(entity))
828 try_set_flags(entity,
false);
829 handle_DependsOn(entity,
false);
832 if (entity.pair() && entity.id() == Is.id()) {
833 auto e = m_world.get(entity.gen());
835 EntityLookupKey entityKey(m_entity);
836 EntityLookupKey eKey(e);
839 m_world.invalidate_queries_for_entity({Is, e});
843 const auto it = m_world.m_entityToAsTargets.find(entityKey);
844 GAIA_ASSERT(it != m_world.m_entityToAsTargets.end());
845 auto& set = it->second;
846 GAIA_ASSERT(!set.empty());
851 m_world.m_entityToAsTargets.erase(it);
856 const auto it = m_world.m_entityToAsRelations.find(eKey);
857 GAIA_ASSERT(it != m_world.m_entityToAsRelations.end());
858 auto& set = it->second;
859 GAIA_ASSERT(!set.empty());
860 set.erase(entityKey);
864 m_world.m_entityToAsRelations.erase(it);
868 m_pArchetype = m_world.foc_archetype_del(m_pArchetype, entity);
871 tl_del_comps.push_back(entity);
874 void add_inter(Entity entity) {
875 GAIA_ASSERT(!is_wildcard(entity));
879 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
882 if (!handle_add_entity(entity))
885 handle_add<false>(entity);
888 void add_inter_init(Entity entity) {
889 GAIA_ASSERT(!is_wildcard(entity));
893 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
896 if (!handle_add_entity(entity))
899 handle_add<true>(entity);
902 GAIA_NODISCARD
bool can_del(Entity entity)
const noexcept {
903 if (has_Requires_tgt(entity))
909 bool del_inter(Entity entity) {
910 if (!can_del(entity))
917 void del_inter(EntityNameLookupKey key) {
918 const auto it = m_world.m_nameToEntity.find(key);
922 GAIA_ASSERT(it != m_world.m_nameToEntity.end());
923 if (it != m_world.m_nameToEntity.end()) {
925 if (it->first.owned())
926 mem::mem_free((
void*)key.str());
928 m_world.m_nameToEntity.erase(it);
932 template <
bool IsOwned>
933 void name_inter(
const char* name, uint32_t len) {
935 GAIA_ASSERT(!m_entity.pair());
940 if (name ==
nullptr) {
941 GAIA_ASSERT(len == 0);
946 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
950#if GAIA_ASSERT_ENABLED
952 const bool hasInvalidCharacter = name[i] ==
'.';
953 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity names");
954 if (hasInvalidCharacter)
959 EntityNameLookupKey key(
960 name, len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len, IsOwned);
964 auto it = m_world.m_nameToEntity.find(key);
965 if (it == m_world.m_nameToEntity.end()) {
967 if (m_targetNameKey.str() !=
nullptr) {
968 del_inter(m_targetNameKey);
970 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(EntityDesc));
971 if (compIdx != BadIndex) {
972 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->comp_ptr_mut(compIdx, m_rowSrc));
973 GAIA_ASSERT(core::check_alignment(pDesc));
974 if (pDesc->name !=
nullptr) {
975 del_inter(EntityNameLookupKey(pDesc->name, pDesc->len, 0));
976 pDesc->name =
nullptr;
980 add_inter(GAIA_ID(EntityDesc));
985 it = m_world.m_nameToEntity.emplace(key, m_entity).first;
987#if GAIA_ASSERT_ENABLED
988 if (it->second != m_entity)
989 GAIA_ASSERT(
false &&
"Trying to set non-unique name for an entity");
996 if constexpr (IsOwned) {
998 char* entityStr = (
char*)mem::mem_alloc(key.len() + 1);
999 memcpy((
void*)entityStr, (
const void*)name, key.len() + 1);
1000 entityStr[key.len()] = 0;
1002 m_targetNameKey = EntityNameLookupKey(entityStr, key.len(), 1, {key.hash()});
1009 m_targetNameKey = key;
1020 GAIA_NODISCARD ComponentCache& comp_cache_mut() {
1023 GAIA_NODISCARD
const ComponentCache& comp_cache()
const {
1027 GAIA_NODISCARD QuerySerMap& query_ser_map() {
1028 return m_querySerMap;
1037 return entity.pair()
1038 ? valid_pair(entity)
1039 : valid_entity(entity);
1048 GAIA_ASSERT(valid_entity_id(
id));
1050 const auto& ec = m_recs.
entities[id];
1051 return Entity(
id, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind);
1054 template <
typename T>
1055 GAIA_NODISCARD
Entity get()
const {
1058 using CT = component_type_t<T>;
1059 using FT =
typename CT::TypeFull;
1061 const auto* pItem = comp_cache().
find<FT>();
1062 GAIA_ASSERT(pItem !=
nullptr);
1063 return pItem->entity;
1079 GAIA_NODISCARD
Entity add(EntityKind kind = EntityKind::EK_Gen) {
1080 return add(*m_pEntityArchetype,
true,
false, kind);
1086 template <
typename Func = TFunc_Vo
id_With_Entity>
1087 void add_n(uint32_t count, Func func = func_void_with_entity) {
1088 add_entity_n(*m_pEntityArchetype, count, func);
1097 template <
typename Func = TFunc_Vo
id_With_Entity>
1098 void add_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
1099 auto& ec = m_recs.
entities[entity.id()];
1101 GAIA_ASSERT(ec.pArchetype !=
nullptr);
1102 GAIA_ASSERT(ec.pChunk !=
nullptr);
1104 add_entity_n(*ec.pArchetype, count, func);
1110 template <
typename T>
1114 using CT = component_type_t<T>;
1115 using FT =
typename CT::TypeFull;
1116 constexpr auto kind = CT::Kind;
1118 const auto* pItem = comp_cache().
find<FT>();
1119 if (pItem !=
nullptr)
1122 const auto entity = add(*m_pCompArchetype,
false,
false, kind);
1124 const auto& item = comp_cache_mut().
add<FT>(entity);
1125 sset<Component>(item.entity) = item.
comp;
1129 name_raw(item.entity, item.name.str(), item.name.len());
1156 template <
typename T>
1168 template <
typename T>
1170 static_assert(core::is_raw_v<T>);
1174 const auto& ec = fetch(entity);
1176 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
1186 template <typename T, typename U = typename actual_type_t<T>::Type>
1190 builder.
add(
object);
1193 const auto& ec = m_recs.
entities[entity.id()];
1195 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
1206 GAIA_ASSERT(!entity.pair());
1207 GAIA_ASSERT(valid(entity));
1213 for (uint32_t i = (uint32_t)ids.size() - 1; i != (uint32_t)-1; --i)
1229 GAIA_ASSERT(!srcEntity.pair());
1230 GAIA_ASSERT(valid(srcEntity));
1232 auto& ec = m_recs.
entities[srcEntity.id()];
1233 GAIA_ASSERT(ec.pArchetype !=
nullptr);
1234 GAIA_ASSERT(ec.pChunk !=
nullptr);
1236 auto* pDstArchetype = ec.pArchetype;
1241 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
1243 const auto dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
1244 auto& ecDst = m_recs.
entities[dstEntity.id()];
1246 Chunk::copy_foreign_entity_data(ec.pChunk, ec.row, ecDst.pChunk, ecDst.row);
1251 const auto dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
1252 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
1265 template <
typename Func = TFunc_Vo
id_With_Entity>
1266 void copy_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
1267 GAIA_ASSERT(!entity.pair());
1268 GAIA_ASSERT(valid(entity));
1270 auto& ec = m_recs.
entities[entity.id()];
1272 GAIA_ASSERT(ec.pChunk !=
nullptr);
1273 GAIA_ASSERT(ec.pArchetype !=
nullptr);
1275 auto* pSrcChunk = ec.pChunk;
1277 auto* pDstArchetype = ec.pArchetype;
1279 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
1284 const auto srcRow = ec.row;
1288 uint32_t left = count;
1290 auto* pDstChunk = pDstArchetype->foc_free_chunk();
1291 const uint32_t originalChunkSize = pDstChunk->size();
1292 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
1293 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
1295 GAIA_FOR(toCreate) {
1296 const auto entityNew = m_recs.
entities.alloc(&ctx);
1297 auto& ecNew = m_recs.
entities[entityNew.id()];
1298 store_entity(ecNew, entityNew, pDstArchetype, pDstChunk);
1300#if GAIA_ASSERT_ENABLED
1301 GAIA_ASSERT(ecNew.pChunk == pDstChunk);
1302 auto entityExpected = pDstChunk->entity_view()[ecNew.row];
1303 GAIA_ASSERT(entityExpected == entityNew);
1306 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, ecNew.row);
1310 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
1313 it.set_archetype(pDstArchetype);
1314 it.set_chunk(pDstChunk);
1315 it.
set_range((uint16_t)originalChunkSize, (uint16_t)toCreate);
1318 auto entities = pDstChunk->entity_view();
1319 GAIA_FOR2(originalChunkSize, pDstChunk->size()) func(entities[i]);
1322 pDstChunk->update_versions();
1327 pDstArchetype = ec.pArchetype;
1332 const auto srcRow = ec.row;
1336 uint32_t left = count;
1338 auto* pDstChunk = pDstArchetype->foc_free_chunk();
1339 const uint32_t originalChunkSize = pDstChunk->size();
1340 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
1341 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
1343 GAIA_FOR(toCreate) {
1344 const auto entityNew = m_recs.
entities.alloc(&ctx);
1345 auto& ecNew = m_recs.
entities[entityNew.id()];
1346 store_entity(ecNew, entityNew, pDstArchetype, pDstChunk);
1348#if GAIA_ASSERT_ENABLED
1349 GAIA_ASSERT(ecNew.pChunk == pDstChunk);
1350 auto entityExpected = pDstChunk->entity_view()[ecNew.row];
1351 GAIA_ASSERT(entityExpected == entityNew);
1356 pDstArchetype->try_update_free_chunk_idx();
1359 pDstChunk->call_gen_ctors(originalChunkSize, toCreate);
1363 GAIA_PROF_SCOPE(World::copy_n_entity_data);
1365 auto srcRecs = pSrcChunk->comp_rec_view();
1368 GAIA_FOR(pSrcChunk->size_generic()) {
1369 const auto& rec = srcRecs[i];
1370 if (rec.comp.size() == 0U)
1373 const auto* pSrc = (
const void*)pSrcChunk->comp_ptr(i);
1374 GAIA_FOR_(toCreate, rowOffset) {
1375 auto* pDst = (
void*)pDstChunk->comp_ptr_mut(i);
1377 pDst, pSrc, originalChunkSize + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1383 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
1386 it.set_archetype(pDstArchetype);
1387 it.set_chunk(pDstChunk);
1388 it.
set_range((uint16_t)originalChunkSize, (uint16_t)toCreate);
1391 auto entities = pDstChunk->entity_view();
1392 GAIA_FOR2(originalChunkSize, pDstChunk->size()) func(entities[i]);
1395 pDstChunk->update_versions();
1407 if (!entity.pair()) {
1409 del_inter(
Pair(entity, All));
1410 del_inter(
Pair(All, entity));
1438 template <
typename T>
1440 using CT = component_type_t<T>;
1441 using FT =
typename CT::TypeFull;
1450 add(entityBase, entityBase);
1452 add(entity,
Pair(Is, entityBase));
1460 return is_inter<false>(entity, entityBase);
1470 return is_inter<true>(entity, entityBase);
1473 GAIA_NODISCARD
bool is_base(
Entity target)
const {
1474 GAIA_ASSERT(valid_entity(target));
1481 return it != m_entityToAsRelations.end();
1488 add(entity,
Pair(ChildOf, parent));
1494 return has(entity,
Pair(ChildOf, parent));
1506#if GAIA_ENABLE_HOOKS
1512 GAIA_ASSERT(valid(entity));
1514 auto& ec = m_recs.
entities[entity.id()];
1515 ec.pChunk->template modify<
1517#if GAIA_ENABLE_HOOKS
1532 GAIA_ASSERT(valid(entity));
1534 const auto& ec = m_recs.
entities[entity.id()];
1544 template <
typename T>
1547 return acc_mut(entity).mut<T>();
1556 template <
typename T>
1559 return acc_mut(entity).smut<T>();
1570 template <
typename T>
1573 return acc_mut(entity).mut<T>();
1584 GAIA_ASSERT(valid(entity));
1586 const auto& ec = m_recs.
entities[entity.id()];
1597 template <
typename T>
1599 return acc(entity).get<T>();
1609 if (entity.pair()) {
1610 if (entity ==
Pair(All, All))
1613 if (is_wildcard(entity)) {
1618 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
1624 if (it == m_recs.
pairs.end())
1627 const auto& ec = it->second;
1631#if GAIA_ASSERT_ENABLED
1633 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
1636 auto* pChunk = ec.pChunk;
1637 GAIA_ASSERT(pChunk !=
nullptr && ec.row < pChunk->size());
1646 if (entity.id() >= m_recs.
entities.size())
1650 const auto& ec = m_recs.
entities[entity.id()];
1654 auto* pChunk = ec.pChunk;
1655 return pChunk !=
nullptr && ec.row < pChunk->size();
1673 const auto& ec = fetch(entity);
1677 const auto* pArchetype = ec.pArchetype;
1679 if (
object.
pair()) {
1681 if (pArchetype->pairs() == 0)
1684 EntityId rel =
object.id();
1685 EntityId tgt =
object.gen();
1688 if (rel == All.id() && tgt == All.id())
1692 if (rel != All.id() && tgt == All.id()) {
1693 auto ids = pArchetype->ids_view();
1694 for (
auto id: ids) {
1705 if (rel == All.id() && tgt != All.id()) {
1706 auto ids = pArchetype->ids_view();
1707 for (
auto id: ids) {
1710 if (
id.gen() == tgt)
1718 return pArchetype->has(
object);
1737 template <
typename T>
1739 GAIA_ASSERT(valid(entity));
1741 const auto& ec = m_recs.
entities[entity.id()];
1745 return ec.pArchetype->has<T>();
1788 const auto& ec = m_recs.
entities[entity.id()];
1789 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
1790 if (compIdx == BadIndex) {
1794 if (!entity.entity())
1795 return m_compCache.
get(entity).
name.str();
1800 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
1801 GAIA_ASSERT(core::check_alignment(pDesc));
1809 GAIA_NODISCARD
const char*
name(EntityId entityId)
const {
1810 auto entity = get(entityId);
1811 return name(entity);
1821 GAIA_NODISCARD
Entity get(
const char* name, uint32_t len = 0)
const {
1822 if (name ==
nullptr || name[0] == 0)
1825 Entity parent = EntityBad;
1826 Entity child = EntityBad;
1827 uint32_t posDot = 0;
1831 while (name[len] !=
'\0')
1838 posDot = core::get_index(str,
'.');
1839 if (posDot == BadIndex)
1840 return name_to_entity(str);
1845 parent = name_to_entity(str.subspan(0, posDot));
1846 if (parent == EntityBad)
1850 str = str.subspan(posDot + 1);
1851 while (!str.empty()) {
1852 posDot = core::get_index(str,
'.');
1855 if (posDot == BadIndex) {
1856 child = name_to_entity(str);
1860 if (child == EntityBad || !this->child(child, parent))
1870 child = name_to_entity(str.subspan(0, posDot));
1874 if (child == EntityBad || !this->child(child, parent))
1880 str = str.subspan(posDot + 1);
1886 GAIA_NODISCARD
Entity get_inter(
const char* name, uint32_t len = 0)
const {
1887 if (name ==
nullptr || name[0] == 0)
1890 auto key = EntityNameLookupKey(name, len, 0);
1892 const auto it = m_nameToEntity.find(key);
1893 if (it != m_nameToEntity.end())
1897 const auto* pItem = m_compCache.
find(name, len);
1898 if (pItem !=
nullptr)
1912 if (it == m_targetsToRelations.end())
1924 GAIA_ASSERT(valid(entity));
1928 const auto& ec = fetch(entity);
1929 const auto* pArchetype = ec.pArchetype;
1932 if (pArchetype->pairs() == 0)
1935 auto ids = pArchetype->ids_view();
1939 if (e.gen() != target.id())
1942 const auto& ecRel = m_recs.
entities[e.id()];
1943 auto relation = ecRel.pChunk->entity_view()[ecRel.row];
1955 template <
typename Func>
1957 GAIA_ASSERT(valid(entity));
1961 const auto& ec = fetch(entity);
1962 const auto* pArchetype = ec.pArchetype;
1965 if (pArchetype->pairs() == 0)
1968 auto ids = pArchetype->ids_view();
1972 if (e.gen() != target.id())
1975 const auto& ecRel = m_recs.
entities[e.id()];
1976 auto relation = ecRel.pChunk->entity_view()[ecRel.row];
1987 template <
typename Func>
1989 GAIA_ASSERT(valid(entity));
1993 const auto& ec = fetch(entity);
1994 const auto* pArchetype = ec.pArchetype;
1997 if (pArchetype->pairs() == 0)
2000 auto ids = pArchetype->ids_view();
2004 if (e.gen() != target.id())
2007 const auto& ecRel = m_recs.
entities[e.id()];
2008 auto relation = ecRel.pChunk->entity_view()[ecRel.row];
2009 if (!func(relation))
2014 template <
typename Func>
2015 void as_relations_trav(
Entity target, Func func)
const {
2016 GAIA_ASSERT(valid(target));
2021 if (it == m_entityToAsRelations.end())
2024 const auto& set = it->second;
2025 for (
auto relation: set) {
2026 func(relation.entity());
2027 as_relations_trav(relation.entity(), func);
2031 template <
typename Func>
2032 GAIA_NODISCARD
bool as_relations_trav_if(Entity target, Func func)
const {
2033 GAIA_ASSERT(valid(target));
2037 const auto it = m_entityToAsRelations.find(EntityLookupKey(target));
2038 if (it == m_entityToAsRelations.end())
2041 const auto& set = it->second;
2042 for (
auto relation: set) {
2043 if (func(relation.entity()))
2045 if (as_relations_trav_if(relation.entity(), func))
2059 if (it == m_relationsToTargets.end())
2071 GAIA_ASSERT(valid(entity));
2072 if (!valid(relation))
2075 const auto& ec = fetch(entity);
2076 const auto* pArchetype = ec.pArchetype;
2079 if (pArchetype->pairs() == 0)
2082 auto ids = pArchetype->ids_view();
2086 if (e.id() != relation.id())
2089 const auto& ecTarget = m_recs.
entities[e.gen()];
2090 auto target = ecTarget.pChunk->entity_view()[ecTarget.row];
2102 template <
typename Func>
2104 GAIA_ASSERT(valid(entity));
2105 if (!valid(relation))
2108 const auto& ec = fetch(entity);
2109 const auto* pArchetype = ec.pArchetype;
2112 if (pArchetype->pairs() == 0)
2115 auto ids = pArchetype->ids_view();
2119 if (e.id() != relation.id())
2122 const auto& ecTarget = m_recs.
entities[e.gen()];
2123 auto target = ecTarget.pChunk->entity_view()[ecTarget.row];
2134 template <
typename Func>
2136 GAIA_ASSERT(valid(entity));
2137 if (!valid(relation))
2140 const auto& ec = fetch(entity);
2141 const auto* pArchetype = ec.pArchetype;
2144 if (pArchetype->pairs() == 0)
2147 auto ids = pArchetype->ids_view();
2151 if (e.id() != relation.id())
2154 const auto& ecTarget = m_recs.
entities[e.gen()];
2155 auto target = ecTarget.pChunk->entity_view()[ecTarget.row];
2161 template <
typename Func>
2162 void as_targets_trav(
Entity relation, Func func)
const {
2163 GAIA_ASSERT(valid(relation));
2164 if (!valid(relation))
2168 if (it == m_entityToAsTargets.end())
2171 const auto& set = it->second;
2172 for (
auto target: set) {
2173 func(target.entity());
2174 as_targets_trav(target.entity(), func);
2178 template <
typename Func>
2179 bool as_targets_trav_if(Entity relation, Func func)
const {
2180 GAIA_ASSERT(valid(relation));
2181 if (!valid(relation))
2184 const auto it = m_entityToAsTargets.find(EntityLookupKey(relation));
2185 if (it == m_entityToAsTargets.end())
2188 const auto& set = it->second;
2189 for (
auto target: set) {
2190 if (func(target.entity()))
2192 if (as_targets_trav(target.entity(), func))
2201 CommandBufferST& cmd_buffer_st()
const {
2202 return *m_pCmdBufferST;
2205 CommandBufferMT& cmd_buffer_mt()
const {
2206 return *m_pCmdBufferMT;
2211#if GAIA_SYSTEMS_ENABLED
2214 void systems_init();
2221 SystemBuilder system();
2232 GAIA_ASSERT(valid(entity));
2234 auto& ec = m_recs.
entities[entity.id()];
2235 auto& archetype = *ec.pArchetype;
2236#if GAIA_ASSERT_ENABLED
2237 verify_enable(*
this, archetype, entity);
2239 archetype.enable_entity(ec.pChunk, ec.row, enable, m_recs);
2247 GAIA_ASSERT(valid(entity));
2249 const auto& ec = m_recs.
entities[entity.id()];
2250 const bool entityStateInContainer = !ec.data.dis;
2251#if GAIA_ASSERT_ENABLED
2252 const bool entityStateInChunk = ec.pChunk->enabled(ec.row);
2253 GAIA_ASSERT(entityStateInChunk == entityStateInContainer);
2255 return entityStateInContainer;
2264 GAIA_ASSERT(entity.id() < m_recs.
entities.size());
2265 const auto& ec = m_recs.
entities[entity.id()];
2275 GAIA_ASSERT(entity.id() < m_recs.
entities.size());
2276 const auto& ec = m_recs.
entities[entity.id()];
2283 GAIA_NODISCARD uint32_t
size()
const {
2284 return m_recs.
entities.item_count();
2290 return m_worldVersion;
2301 auto& ec = fetch(entity);
2302 const auto prevLifespan = ec.pArchetype->max_lifespan();
2303 ec.pArchetype->set_max_lifespan(lifespan);
2305 if (prevLifespan == 0) {
2307 try_enqueue_archetype_for_deletion(*ec.pArchetype);
2335 m_pRootArchetype =
nullptr;
2336 m_pEntityArchetype =
nullptr;
2337 m_pCompArchetype =
nullptr;
2338 m_nextArchetypeId = 0;
2339 m_defragLastArchetypeIdx = 0;
2347 m_defragEntitiesPerTick = value;
2354 GAIA_LOG_N(
"Archetypes:%u", (uint32_t)m_archetypes.size());
2355 for (
auto* pArchetype: m_archetypes)
2356 Archetype::diag(*
this, *pArchetype);
2362 comp_cache().diag();
2368 validate_entities();
2370 GAIA_LOG_N(
"Deleted entities: %u", (uint32_t)m_recs.
entities.get_free_items());
2371 if (m_recs.
entities.get_free_items() != 0U) {
2372 GAIA_LOG_N(
" --> %u", (uint32_t)m_recs.
entities.get_next_free_item());
2376 while (fe != IdentifierIdBad) {
2377 GAIA_LOG_N(
" --> %u", m_recs.
entities[fe].idx);
2380 if (iters > m_recs.
entities.get_free_items())
2384 if ((iters == 0U) || iters > m_recs.
entities.get_free_items())
2385 GAIA_LOG_E(
" Entities recycle list contains inconsistent data!");
2398 void cleanup_inter() {
2399 GAIA_PROF_SCOPE(World::cleanup_inter);
2408 for (
auto* pArchetype: m_archetypes)
2409 Archetype::destroy(pArchetype);
2411 m_entityToAsRelations = {};
2412 m_entityToAsTargets = {};
2413 m_targetsToRelations = {};
2414 m_relationsToTargets = {};
2417 m_archetypesById = {};
2418 m_archetypesByHash = {};
2420 m_reqArchetypesToDel = {};
2421 m_reqEntitiesToDel = {};
2423 m_entitiesToDel = {};
2425 m_archetypesToDel = {};
2430 m_entityToArchetypeMap = {};
2431 m_queryCache.clear();
2436 for (
auto& pair: m_nameToEntity) {
2437 if (!pair.first.owned())
2440 mem::mem_free((
void*)pair.first.str());
2442 m_nameToEntity = {};
2446 m_compCache.clear();
2449 GAIA_NODISCARD
static bool valid(
const EntityContainer& ec, [[maybe_unused]] Entity entityExpected) {
2454 auto* pChunk = ec.pChunk;
2455 if (pChunk ==
nullptr || ec.row >= pChunk->size())
2458#if GAIA_ASSERT_ENABLED
2459 const auto entityPresent = ec.pChunk->entity_view()[ec.row];
2460 GAIA_ASSERT(entityExpected == entityPresent);
2461 if (entityExpected != entityPresent)
2470 GAIA_NODISCARD
bool valid_pair(Entity entity)
const {
2471 if (entity == EntityBad)
2474 GAIA_ASSERT(entity.pair());
2479 if (is_wildcard(entity))
2482 const auto it = m_recs.
pairs.find(EntityLookupKey(entity));
2483 if (it == m_recs.
pairs.end())
2486 const auto& ec = it->second;
2487 return valid(ec, entity);
2492 GAIA_NODISCARD
bool valid_entity(Entity entity)
const {
2493 if (entity == EntityBad)
2496 GAIA_ASSERT(!entity.pair());
2501 if (entity.id() >= m_recs.
entities.size())
2504 const auto& ec = m_recs.
entities[entity.id()];
2505 return valid(ec, entity);
2511 GAIA_NODISCARD
bool valid_entity_id(EntityId entityId)
const {
2512 if (entityId == EntityBad.id())
2516 if (entityId >= m_recs.
entities.size())
2519 const auto& ec = m_recs.
entities[entityId];
2520 if (ec.data.pair != 0)
2524 ec, Entity(entityId, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind));
2531 GAIA_ASSERT(m_structuralChangesLocked != (uint32_t)-1);
2532 ++m_structuralChangesLocked;
2539 GAIA_ASSERT(m_structuralChangesLocked > 0);
2540 --m_structuralChangesLocked;
2546 return m_structuralChangesLocked != 0;
2556 auto& s = *m_pSerializer;
2561 s.save((uint32_t)0);
2565 const auto lastCoreComponentId = GAIA_ID(LastCoreComponent).id();
2566 s.save(lastCoreComponentId);
2574 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) == 0);
2577#if GAIA_USE_SAFE_ENTITY
2580 s.save((uint32_t)0);
2583 uint32_t archetypeIdx = ec.pArchetype->list_idx();
2584 s.save(archetypeIdx);
2585 uint32_t chunkIdx = ec.pChunk->idx();
2589 const auto recEntities = (uint32_t)m_recs.
entities.size();
2590 const auto newEntities = recEntities - lastCoreComponentId;
2591 s.save(newEntities);
2592 GAIA_FOR2(lastCoreComponentId, recEntities) {
2593 const auto& ec = m_recs.
entities[i];
2594 saveEntityContainer(ec);
2598 uint32_t pairsCnt = 0;
2601 if (
pair.first.entity().id() < lastCoreComponentId &&
pair.first.entity().gen() < lastCoreComponentId)
2611 if (
pair.first.entity().id() < lastCoreComponentId &&
pair.first.entity().gen() < lastCoreComponentId)
2614 saveEntityContainer(
pair.second);
2618 s.save(m_recs.
entities.m_nextFreeIdx);
2619 s.save(m_recs.
entities.m_freeItems);
2624 s.save((uint32_t)m_archetypes.size());
2625 for (
auto* pArchetype: m_archetypes) {
2626 s.save((uint32_t)pArchetype->ids_view().size());
2627 for (
auto e: pArchetype->ids_view())
2630 pArchetype->save(*m_pSerializer);
2633 s.save(m_worldVersion);
2638 s.save((uint32_t)m_nameToEntity.size());
2639 for (
const auto&
pair: m_nameToEntity) {
2640 s.save(
pair.second);
2641 const bool isOwnedStr =
pair.first.owned();
2646 const auto* str =
pair.first.str();
2647 const uint32_t len =
pair.first.len();
2649 s.save_raw(str, len, ser::serialization_type_id::c8);
2654 else if (!
pair.second.comp()) {
2655 const auto* str =
pair.first.str();
2656 const uint32_t len =
pair.first.len();
2658 const auto ptr_val = (uint64_t)str;
2659 s.save_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
2672 auto& s = (pOutputSerializer ==
nullptr) ? m_binarySerializer : *pOutputSerializer;
2678 uint32_t version = 0;
2681 GAIA_LOG_E(
"Unsupported world version %u. Expected 0.", version);
2686 uint32_t lastCoreComponentId = 0;
2687 s.load(lastCoreComponentId);
2696 ec.flags |= EntityContainerFlags::Load;
2698#if GAIA_USE_SAFE_ENTITY
2705 GAIA_ASSERT(ec.unused == 0);
2709 uint32_t archetypeIdx = 0;
2710 s.load(archetypeIdx);
2711 ec.pArchetype = (
Archetype*)((uintptr_t)archetypeIdx);
2713 uint32_t chunkIdx = 0;
2715 ec.pChunk = (
Chunk*)((uintptr_t)chunkIdx);
2718 uint32_t newEntities = 0;
2719 s.load(newEntities);
2720 GAIA_FOR(newEntities) {
2722 loadEntityContainer(ec);
2724 m_recs.
entities.m_items.add_item(GAIA_MOV(ec));
2727 uint32_t pairsCnt = 0;
2729 GAIA_FOR(pairsCnt) {
2731 loadEntityContainer(ec);
2736 s.load(m_recs.
entities.m_nextFreeIdx);
2737 s.load(m_recs.
entities.m_freeItems);
2742 uint32_t archetypesSize = 0;
2743 s.load(archetypesSize);
2744 m_archetypes.reserve(archetypesSize);
2745 GAIA_FOR(archetypesSize) {
2746 uint32_t idsSize = 0;
2748 Entity ids[ChunkHeader::MAX_COMPONENTS];
2749 GAIA_FOR_(idsSize, j) {
2754 const auto hashLookup = calc_lookup_hash({&ids[0], idsSize}).hash;
2756 auto* pArchetype = find_archetype({hashLookup}, {&ids[0], idsSize});
2757 if (pArchetype ==
nullptr) {
2759 pArchetype = create_archetype({&ids[0], idsSize});
2760 pArchetype->set_hashes({hashLookup});
2766 reg_archetype(pArchetype);
2770 pArchetype->load(s);
2773 s.load(m_worldVersion);
2781 if ((ec.flags & EntityContainerFlags::Load) == 0)
2783 ec.flags &= ~EntityContainerFlags::Load;
2785 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
2786 ec.pArchetype = m_archetypes[archetypeIdx];
2787 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
2788 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
2791 auto& ec =
pair.second;
2793 if ((ec.flags & EntityContainerFlags::Load) == 0)
2795 ec.flags &= ~EntityContainerFlags::Load;
2797 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
2798 ec.pArchetype = m_archetypes[archetypeIdx];
2799 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
2800 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
2813 const auto& ec = fetch(entity);
2814 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
2815 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
2816 GAIA_ASSERT(core::check_alignment(pDesc));
2818 bool isOwned =
false;
2821 if (entity.comp()) {
2824 const auto& ci = comp_cache().
get(entity);
2825 pDesc->
name = ci.name.str();
2827 GAIA_ASSERT(pDesc->len == ci.name.len());
2832 uint64_t ptr_val = 0;
2833 s.load_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
2836 pDesc->name = (
const char*)ptr_val;
2848 const char* entityStr = (
const char*)(s.data() + s.tell());
2849 s.seek(s.tell() + len);
2853 pDesc->name =
nullptr;
2858 name(entity, entityStr, len);
2867 void sort_archetypes() {
2870 return a->id() < b->id();
2874 core::sort(m_archetypes, sort_cond{}, [&](uint32_t left, uint32_t right) {
2875 Archetype* tmp = m_archetypes[left];
2877 m_archetypes[right]->list_idx(left);
2878 m_archetypes[left]->list_idx(right);
2880 m_archetypes.data()[left] = (Archetype*)m_archetypes[right];
2881 m_archetypes.data()[right] = tmp;
2888 void remove_chunk(Archetype& archetype, Chunk& chunk) {
2889 archetype.del(&chunk);
2890 try_enqueue_archetype_for_deletion(archetype);
2897 void remove_entity(Archetype& archetype, Chunk& chunk, uint16_t row) {
2898 archetype.remove_entity(chunk, row, m_recs);
2899 try_enqueue_chunk_for_deletion(archetype, chunk);
2903 void del_empty_chunks() {
2904 GAIA_PROF_SCOPE(World::del_empty_chunks);
2906 for (uint32_t i = 0; i < m_chunksToDel.size();) {
2907 auto* pArchetype = m_chunksToDel[i].pArchetype;
2908 auto* pChunk = m_chunksToDel[i].pChunk;
2911 if (!pChunk->empty()) {
2913 revive_archetype(*pArchetype);
2914 core::swap_erase(m_chunksToDel, i);
2919 if (pChunk->progress_death()) {
2925 remove_chunk(*pArchetype, *pChunk);
2926 core::swap_erase(m_chunksToDel, i);
2931 void del_empty_archetype(Archetype* pArchetype) {
2932 GAIA_PROF_SCOPE(World::del_empty_archetype);
2934 GAIA_ASSERT(pArchetype !=
nullptr);
2935 GAIA_ASSERT(pArchetype->empty() || pArchetype->is_req_del());
2936 GAIA_ASSERT(!pArchetype->dying() || pArchetype->is_req_del());
2938 unreg_archetype(pArchetype);
2939 Archetype::destroy(pArchetype);
2943 void del_empty_archetypes() {
2944 GAIA_PROF_SCOPE(World::del_empty_archetypes);
2946 cnt::sarray_ext<Archetype*, 512> tmp;
2954 auto remove_from_queries = [&]() {
2961 for (
auto& info: m_queryCache) {
2962 for (
auto* pArchetype: tmp)
2963 info.remove(pArchetype);
2966 for (
auto* pArchetype: tmp)
2967 del_empty_archetype(pArchetype);
2971 for (uint32_t i = 0; i < m_archetypesToDel.size();) {
2972 auto* pArchetype = m_archetypesToDel[i];
2975 if (!pArchetype->empty() || pArchetype->max_lifespan() == 0) {
2976 revive_archetype(*pArchetype);
2977 core::swap_erase(m_archetypesToDel, i);
2983 if (!pArchetype->is_req_del() && pArchetype->progress_death()) {
2988 tmp.push_back(pArchetype);
2991 core::swap_erase(m_archetypesToDel, i);
2994 if (tmp.size() == tmp.max_size())
2995 remove_from_queries();
2998 remove_from_queries();
3001 void revive_archetype(Archetype& archetype) {
3003 m_reqArchetypesToDel.erase(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
3006 void try_enqueue_chunk_for_deletion(Archetype& archetype, Chunk& chunk) {
3007 if (chunk.dying() || !chunk.empty())
3022 chunk.start_dying();
3024 m_chunksToDel.push_back({&archetype, &chunk});
3027 void try_enqueue_archetype_for_deletion(Archetype& archetype) {
3028 if (!archetype.ready_to_die())
3043 archetype.start_dying();
3045 m_archetypesToDel.push_back(&archetype);
3050 void defrag_chunks(uint32_t maxEntities) {
3051 GAIA_PROF_SCOPE(World::defrag_chunks);
3053 const auto maxIters = m_archetypes.size();
3055 GAIA_ASSERT(maxIters > 0);
3057 GAIA_FOR(maxIters) {
3058 const auto idx = (m_defragLastArchetypeIdx + 1) % maxIters;
3059 auto* pArchetype = m_archetypes[idx];
3060 defrag_archetype(*pArchetype, maxEntities);
3061 if (maxEntities == 0)
3064 m_defragLastArchetypeIdx = idx;
3072 void defrag_archetype(Archetype& archetype, uint32_t& maxEntities) {
3096 if (maxEntities == 0)
3099 const auto& chunks = archetype.chunks();
3100 if (chunks.size() < 2)
3104 uint32_t back = chunks.size() - 1;
3106 auto* pDstChunk = chunks[front];
3107 auto* pSrcChunk = chunks[back];
3110 while (front < back && (pDstChunk->full() || !pDstChunk->is_semi()))
3111 pDstChunk = chunks[++front];
3113 while (front < back && (pSrcChunk->empty() || !pSrcChunk->is_semi()))
3114 pSrcChunk = chunks[--back];
3116 const auto& props = archetype.props();
3117 const bool hasUniEnts =
3118 props.cntEntities > 0 && archetype.ids_view()[props.cntEntities - 1].kind() == EntityKind::EK_Uni;
3121 while (front < back) {
3122 pDstChunk = chunks[front];
3123 pSrcChunk = chunks[back];
3125 const uint32_t entitiesInSrcChunk = pSrcChunk->size();
3126 const uint32_t spaceInDstChunk = pDstChunk->capacity() - pDstChunk->size();
3127 const uint32_t entitiesToMoveSrc = core::get_min(entitiesInSrcChunk, maxEntities);
3128 const uint32_t entitiesToMove = core::get_min(entitiesToMoveSrc, spaceInDstChunk);
3132 auto rec = pSrcChunk->comp_rec_view();
3134 GAIA_FOR2(props.genEntities, props.cntEntities) {
3135 const auto* pSrcVal = (
const void*)pSrcChunk->comp_ptr(i, 0);
3136 const auto* pDstVal = (
const void*)pDstChunk->comp_ptr(i, 0);
3137 if (rec[i].pItem->cmp(pSrcVal, pDstVal)) {
3145 pDstChunk = chunks[++front];
3146 goto next_iteration;
3150 GAIA_FOR(entitiesToMove) {
3151 const auto lastSrcEntityIdx = entitiesInSrcChunk - i - 1;
3152 const auto entity = pSrcChunk->entity_view()[lastSrcEntityIdx];
3154 auto& ec = m_recs[entity];
3156 const auto srcRow = ec.row;
3157 const auto dstRow = pDstChunk->add_entity(entity);
3158 const bool wasEnabled = !ec.data.dis;
3161 archetype.enable_entity(pSrcChunk, srcRow,
true, m_recs);
3163 GAIA_ASSERT(srcRow == ec.row);
3166 pDstChunk->move_entity_data(entity, dstRow, m_recs);
3173 archetype.remove_entity_raw(*pSrcChunk, srcRow, m_recs);
3174 try_enqueue_chunk_for_deletion(archetype, *pSrcChunk);
3177 ec.pChunk = pDstChunk;
3178 ec.row = (uint16_t)dstRow;
3181 archetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
3185 if (entitiesToMove > 0) {
3186 pSrcChunk->update_world_version();
3187 pDstChunk->update_world_version();
3188 update_version(m_worldVersion);
3191 maxEntities -= entitiesToMove;
3192 if (maxEntities == 0)
3196 if (pSrcChunk->empty()) {
3197 while (front < back) {
3198 if (chunks[--back]->is_semi())
3206 while (front < back && pDstChunk->full())
3207 pDstChunk = chunks[++front];
3215 GAIA_NODISCARD Archetype* find_archetype(Archetype::LookupHash hashLookup, EntitySpan ids) {
3216 auto tmpArchetype = ArchetypeLookupChecker(ids);
3217 ArchetypeLookupKey key(hashLookup, &tmpArchetype);
3220 const auto it = m_archetypesByHash.find(key);
3221 if (it == m_archetypesByHash.end())
3224 auto* pArchetype = it->second;
3231 void add_entity_archetype_pair(Entity entity, Archetype* pArchetype) {
3232 GAIA_ASSERT(entity != Pair(All, All));
3234 EntityLookupKey entityKey(entity);
3235 const auto it = m_entityToArchetypeMap.find(entityKey);
3236 if (it == m_entityToArchetypeMap.end()) {
3237 m_entityToArchetypeMap.try_emplace(entityKey, ArchetypeDArray{pArchetype});
3241 auto& archetypes = it->second;
3242 if (!core::has(archetypes, pArchetype))
3243 archetypes.push_back(pArchetype);
3249 void del_entity_archetype_pair(Pair pair, Entity entityToRemove) {
3250 GAIA_ASSERT(pair != Pair(All, All));
3252 auto it = m_entityToArchetypeMap.find(EntityLookupKey(pair));
3253 auto& archetypes = it->second;
3257 for (uint32_t i = archetypes.size() - 1; i != (uint32_t)-1; --i) {
3258 const auto* pArchetype = archetypes[i];
3259 if (!pArchetype->has(entityToRemove))
3262 core::swap_erase_unsafe(archetypes, i);
3274 void del_entity_archetype_pairs(Entity entity) {
3278 GAIA_ASSERT(entity != Pair(All, All));
3280 m_entityToArchetypeMap.erase(EntityLookupKey(entity));
3282 if (entity.pair()) {
3283 const auto first = get(entity.id());
3284 const auto second = get(entity.gen());
3287 del_entity_archetype_pair(Pair(All, second), entity);
3289 del_entity_archetype_pair(Pair(first, All), entity);
3296 GAIA_NODISCARD Archetype* create_archetype(EntitySpan entities) {
3297 GAIA_ASSERT(m_nextArchetypeId < (
decltype(m_nextArchetypeId))-1);
3298 auto* pArchetype = Archetype::create(*
this, m_nextArchetypeId++, m_worldVersion, entities);
3300 for (
auto entity: entities) {
3301 add_entity_archetype_pair(entity, pArchetype);
3305 if (entity.pair()) {
3306 const auto first = get(entity.id());
3307 const auto second = get(entity.gen());
3310 add_entity_archetype_pair(Pair(All, second), pArchetype);
3312 add_entity_archetype_pair(Pair(first, All), pArchetype);
3321 void reg_archetype(Archetype* pArchetype) {
3322 GAIA_ASSERT(pArchetype !=
nullptr);
3329 GAIA_ASSERT(pArchetype->list_idx() == BadIndex);
3332 [[maybe_unused]]
const auto it0 =
3333 m_archetypesById.emplace(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()), pArchetype);
3334 [[maybe_unused]]
const auto it1 =
3335 m_archetypesByHash.emplace(ArchetypeLookupKey(pArchetype->lookup_hash(), pArchetype), pArchetype);
3337 GAIA_ASSERT(it0.second);
3338 GAIA_ASSERT(it1.second);
3340 pArchetype->list_idx(m_archetypes.size());
3341 m_archetypes.emplace_back(pArchetype);
3346 void unreg_archetype(Archetype* pArchetype) {
3347 GAIA_ASSERT(pArchetype !=
nullptr);
3351 (m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));
3354 GAIA_ASSERT(pArchetype->list_idx() != BadIndex);
3358 auto& edgeLefts = pArchetype->left_edges();
3359 for (
auto& itLeft: edgeLefts)
3360 remove_edge_from_archetype(pArchetype, itLeft.second, itLeft.first.entity());
3363 auto tmpArchetype = ArchetypeLookupChecker(pArchetype->ids_view());
3364 [[maybe_unused]]
const auto res0 =
3365 m_archetypesById.erase(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()));
3366 [[maybe_unused]]
const auto res1 =
3367 m_archetypesByHash.erase(ArchetypeLookupKey(pArchetype->lookup_hash(), &tmpArchetype));
3368 GAIA_ASSERT(res0 != 0);
3369 GAIA_ASSERT(res1 != 0);
3371 const auto idx = pArchetype->list_idx();
3372 GAIA_ASSERT(idx == core::get_index(m_archetypes, pArchetype));
3373 core::swap_erase(m_archetypes, idx);
3374 if (!m_archetypes.empty() && idx != m_archetypes.size())
3375 m_archetypes[idx]->list_idx(idx);
3378#if GAIA_ASSERT_ENABLED
3379 static void print_archetype_entities(
const World& world,
const Archetype& archetype, Entity entity,
bool adding) {
3380 auto ids = archetype.ids_view();
3382 GAIA_LOG_W(
"Currently present:");
3384 GAIA_LOG_W(
"> [%u] %s [%s]", i, entity_name(world, ids[i]), EntityKindString[(uint32_t)ids[i].kind()]);
3387 GAIA_LOG_W(
"Trying to %s:", adding ?
"add" :
"del");
3388 GAIA_LOG_W(
"> %s [%s]", entity_name(world, entity), EntityKindString[(uint32_t)entity.kind()]);
3391 static void verify_add(
const World& world, Archetype& archetype, Entity entity, Entity addEntity) {
3393 if (world.locked()) {
3394 GAIA_ASSERT2(
false,
"Trying to add an entity while the world is locked");
3395 GAIA_LOG_W(
"Trying to add an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
3396 print_archetype_entities(world, archetype, entity,
false);
3401 if (is_wildcard(addEntity)) {
3402 GAIA_ASSERT2(
false,
"Adding wildcard pairs is not supported");
3403 print_archetype_entities(world, archetype, addEntity,
true);
3408 auto ids = archetype.ids_view();
3409 if GAIA_UNLIKELY (ids.size() + 1 >= ChunkHeader::MAX_COMPONENTS) {
3410 GAIA_ASSERT2(
false,
"Trying to add too many entities to entity!");
3411 GAIA_LOG_W(
"Trying to add an entity to entity [%u:%u] but there's no space left!", entity.id(), entity.gen());
3412 print_archetype_entities(world, archetype, addEntity,
true);
3417 static void verify_del(
const World& world, Archetype& archetype, Entity entity, Entity delEntity) {
3419 if (world.locked()) {
3420 GAIA_ASSERT2(
false,
"Trying to delete an entity while the world is locked");
3421 GAIA_LOG_W(
"Trying to delete an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
3422 print_archetype_entities(world, archetype, entity,
false);
3427 if GAIA_UNLIKELY (!archetype.has(delEntity)) {
3428 GAIA_ASSERT2(
false,
"Trying to remove an entity which wasn't added");
3429 GAIA_LOG_W(
"Trying to del an entity from entity [%u:%u] but it was never added", entity.id(), entity.gen());
3430 print_archetype_entities(world, archetype, delEntity,
false);
3435 static void verify_enable(
const World& world, Archetype& archetype, Entity entity) {
3436 if (world.locked()) {
3437 GAIA_ASSERT2(
false,
"Trying to enable/disable an entity while the world is locked");
3438 GAIA_LOG_W(
"Trying to enable/disable an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
3439 print_archetype_entities(world, archetype, entity,
false);
3443 static void verify_move(
const World& world, Archetype& archetype, Entity entity) {
3444 if (world.locked()) {
3445 GAIA_ASSERT2(
false,
"Trying to move an entity while the world is locked");
3446 GAIA_LOG_W(
"Trying to move an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
3447 print_archetype_entities(world, archetype, entity,
false);
3457 GAIA_NODISCARD Archetype* foc_archetype_add(Archetype* pArchetypeLeft, Entity entity) {
3460 const auto edge = pArchetypeLeft->find_edge_right(entity);
3461 if (edge != ArchetypeIdHashPairBad) {
3462 auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
3464 GAIA_ASSERT(it != m_archetypesById.end());
3466 auto* pArchetypeRight = it->second;
3467 GAIA_ASSERT(pArchetypeRight !=
nullptr);
3468 return pArchetypeRight;
3473 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
3475 auto entsOld = pArchetypeLeft->ids_view();
3476 const auto entsOldCnt = entsOld.size();
3477 entsNew.resize((uint32_t)entsOld.size() + 1);
3478 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
3479 entsNew[(uint32_t)entsOld.size()] = entity;
3484 sort(entsNew, SortComponentCond{});
3487 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
3488 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
3489 if (pArchetypeRight ==
nullptr) {
3490 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
3491 pArchetypeRight->set_hashes({hashLookup});
3492 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
3493 reg_archetype(pArchetypeRight);
3496 return pArchetypeRight;
3504 GAIA_NODISCARD Archetype* foc_archetype_del(Archetype* pArchetypeRight, Entity entity) {
3507 const auto edge = pArchetypeRight->find_edge_left(entity);
3508 if (edge != ArchetypeIdHashPairBad)
3509 return m_archetypesById[edge];
3512 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
3513 auto entsOld = pArchetypeRight->ids_view();
3516 for (
const auto e: entsOld) {
3520 entsNew.push_back(e);
3524 GAIA_ASSERT(entsNew.size() != entsOld.size());
3527 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
3528 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
3529 if (pArchetype ==
nullptr) {
3530 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
3531 pArchetype->set_hashes({hashLookup});
3532 pArchetype->build_graph_edges(pArchetypeRight, entity);
3533 reg_archetype(pArchetype);
3541 GAIA_NODISCARD
const auto& archetypes()
const {
3542 return m_archetypesById;
3548 GAIA_NODISCARD Archetype& archetype(Entity entity) {
3549 const auto& ec = fetch(entity);
3550 return *ec.pArchetype;
3555 void del_name(EntityContainer& ec, Entity entity) {
3556 EntityBuilder(*
this, entity, ec).
del_name();
3561 void del_name(Entity entity) {
3562 EntityBuilder(*
this, entity).
del_name();
3567 void del_entity(Entity entity,
bool invalidate) {
3568 if (entity.pair() || entity == EntityBad)
3571 auto& ec = fetch(entity);
3572 del_entity_inter(ec, entity, invalidate);
3577 void del_entity(EntityContainer& ec, Entity entity,
bool invalidate) {
3578 if (entity.pair() || entity == EntityBad)
3581 del_entity_inter(ec, entity, invalidate);
3586 void del_entity_inter(EntityContainer& ec, Entity entity,
bool invalidate) {
3587 GAIA_ASSERT(entity.id() > GAIA_ID(LastCoreComponent).
id());
3591 if (m_recs.
entities.item_count() == 0)
3594#if GAIA_ASSERT_ENABLED
3595 auto* pChunk = ec.pChunk;
3596 GAIA_ASSERT(pChunk !=
nullptr);
3603 del_name(ec, entity);
3604 remove_entity(*ec.pArchetype, *ec.pChunk, ec.row);
3610 invalidate_entity(entity);
3617 void del_entities(Archetype& archetype) {
3618 for (
auto* pChunk: archetype.chunks()) {
3619 auto ids = pChunk->entity_view();
3624#if GAIA_ASSERT_ENABLED
3625 const auto& ec = fetch(e);
3628 GAIA_ASSERT((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) == 0);
3631 del_entity(e,
true);
3634 validate_chunk(pChunk);
3641 if (pChunk->dying()) {
3642 const auto idx = core::get_index(m_chunksToDel, {&archetype, pChunk});
3643 if (idx != BadIndex)
3644 core::swap_erase(m_chunksToDel, idx);
3647 remove_chunk(archetype, *pChunk);
3650 validate_entities();
3654 void del_inter(Entity entity) {
3655 auto on_delete = [
this](Entity entityToDel) {
3656 auto& ec = fetch(entityToDel);
3657 handle_del_entity(ec, entityToDel);
3660 if (is_wildcard(entity)) {
3661 const auto rel = get(entity.id());
3662 const auto tgt = get(entity.gen());
3665 if (rel == All && tgt == All) {
3666 GAIA_ASSERT2(
false,
"Not supported yet");
3669 else if (rel == All) {
3670 if (
const auto* pTargets = relations(tgt)) {
3673 cnt::darray_ext<Entity, 64> tmp;
3674 for (
auto key: *pTargets)
3675 tmp.push_back(key.entity());
3677 on_delete(Pair(e, tgt));
3681 else if (tgt == All) {
3682 if (
const auto* pRelations = targets(rel)) {
3685 cnt::darray_ext<Entity, 64> tmp;
3686 for (
auto key: *pRelations)
3687 tmp.push_back(key.entity());
3689 on_delete(Pair(rel, e));
3698 void del_finalize_archetypes() {
3699 GAIA_PROF_SCOPE(World::del_finalize_archetypes);
3701 for (
auto& key: m_reqArchetypesToDel) {
3702 auto* pArchetype = key.archetype();
3703 if (pArchetype ==
nullptr)
3706 del_entities(*pArchetype);
3713 m_reqArchetypesToDel.clear();
3717 void del_finalize_entities() {
3718 GAIA_PROF_SCOPE(World::del_finalize_entities);
3720 for (
auto it = m_reqEntitiesToDel.begin(); it != m_reqEntitiesToDel.end();) {
3721 const auto e = it->entity();
3724 if (m_entityToArchetypeMap.contains(*it)) {
3730 invalidate_entity(e);
3732 it = m_reqEntitiesToDel.erase(it);
3737 void del_finalize() {
3738 GAIA_PROF_SCOPE(World::del_finalize);
3740 del_finalize_archetypes();
3741 del_finalize_entities();
3744 GAIA_NODISCARD
bool archetype_cond_match(Archetype& archetype, Pair cond, Entity target)
const {
3749 auto ids = archetype.ids_view();
3751 if (target.pair()) {
3756 if (e.gen() != target.gen())
3759 const auto& ec = m_recs.
entities[e.id()];
3760 const auto entity = ec.pChunk->entity_view()[ec.row];
3761 if (!has(entity, cond))
3781 void move_to_archetype(Archetype& srcArchetype, Archetype& dstArchetype) {
3782 GAIA_ASSERT(&srcArchetype != &dstArchetype);
3784 bool updated =
false;
3786 for (
auto* pSrcChunk: srcArchetype.chunks()) {
3787 auto srcEnts = pSrcChunk->entity_view();
3788 if (srcEnts.empty())
3801 uint32_t i = (uint32_t)srcEnts.size();
3803 auto* pDstChunk = dstArchetype.foc_free_chunk();
3804 const uint32_t dstSpaceLeft = pDstChunk->capacity() - pDstChunk->size();
3805 const uint32_t cnt = core::get_min(dstSpaceLeft, i);
3806 for (uint32_t j = 0; j < cnt; ++j) {
3807 auto e = srcEnts[i - j - 1];
3808 move_entity(e, fetch(e), dstArchetype, *pDstChunk);
3811 pDstChunk->update_world_version();
3813 GAIA_ASSERT(cnt <= i);
3817 pSrcChunk->update_world_version();
3822 update_version(m_worldVersion);
3827 GAIA_NODISCARD Archetype* calc_dst_archetype_ent(Archetype* pArchetype, Entity entity) {
3828 GAIA_ASSERT(!is_wildcard(entity));
3830 auto ids = pArchetype->ids_view();
3831 for (
auto id: ids) {
3835 return foc_archetype_del(pArchetype,
id);
3844 GAIA_NODISCARD Archetype* calc_dst_archetype_all_ent(Archetype* pArchetype, Entity entity) {
3845 GAIA_ASSERT(is_wildcard(entity));
3847 Archetype* pDstArchetype = pArchetype;
3849 auto ids = pArchetype->ids_view();
3850 for (
auto id: ids) {
3851 if (!
id.pair() ||
id.gen() != entity.gen())
3854 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
3857 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
3863 GAIA_NODISCARD Archetype* calc_dst_archetype_ent_all(Archetype* pArchetype, Entity entity) {
3864 GAIA_ASSERT(is_wildcard(entity));
3866 Archetype* pDstArchetype = pArchetype;
3868 auto ids = pArchetype->ids_view();
3869 for (
auto id: ids) {
3870 if (!
id.pair() ||
id.
id() != entity.id())
3873 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
3876 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
3882 GAIA_NODISCARD Archetype* calc_dst_archetype_all_all(Archetype* pArchetype, [[maybe_unused]] Entity entity) {
3883 GAIA_ASSERT(is_wildcard(entity));
3885 Archetype* pDstArchetype = pArchetype;
3888 auto ids = pArchetype->ids_view();
3889 for (
auto id: ids) {
3893 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
3897 return found ? pDstArchetype :
nullptr;
3903 GAIA_NODISCARD Archetype* calc_dst_archetype(Archetype* pArchetype, Entity entity) {
3904 if (entity.pair()) {
3905 auto rel = entity.id();
3906 auto tgt = entity.gen();
3909 if (rel == All.id() || tgt == All.id()) {
3911 if (rel != All.id() && tgt == All.id())
3912 return calc_dst_archetype_ent_all(pArchetype, entity);
3915 if (rel == All.id() && tgt != All.id())
3916 return calc_dst_archetype_all_ent(pArchetype, entity);
3919 return calc_dst_archetype_all_all(pArchetype, EntityBad);
3924 return calc_dst_archetype_ent(pArchetype, entity);
3927 void req_del(Archetype& archetype) {
3928 if (archetype.is_req_del())
3931 archetype.req_del();
3932 m_reqArchetypesToDel.insert(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
3935 void req_del(EntityContainer& ec, Entity entity) {
3939 del_entity(ec, entity,
false);
3942 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
3946 void req_del_entities_with(Entity entity) {
3947 GAIA_PROF_SCOPE(World::req_del_entities_with);
3949 GAIA_ASSERT(entity != Pair(All, All));
3951 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
3952 if (it == m_entityToArchetypeMap.end())
3955 const auto& archetypes = it->second;
3956 for (
auto* pArchetype: archetypes)
3957 req_del(*pArchetype);
3962 void req_del_entities_with(Entity entity, Pair cond) {
3963 GAIA_PROF_SCOPE(World::req_del_entities_with);
3965 GAIA_ASSERT(entity != Pair(All, All));
3967 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
3968 if (it == m_entityToArchetypeMap.end())
3971 const auto& archetypes = it->second;
3972 for (
auto* pArchetype: archetypes) {
3974 if (!archetype_cond_match(*pArchetype, cond, entity))
3977 req_del(*pArchetype);
3982 void rem_from_entities(Entity entity) {
3983 GAIA_PROF_SCOPE(World::rem_from_entities);
3985 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
3986 if (it == m_entityToArchetypeMap.end())
3990 if (!entity.pair()) {
3991 auto& ec = fetch(entity);
3992 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
3993 auto ids = ec.pArchetype->ids_view();
3994 const auto idx = core::get_index(ids, entity);
3995 if (idx != BadIndex)
3996 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
4001 const auto& archetypes = it->second;
4002 for (
auto* pArchetype: archetypes) {
4003 if (pArchetype->is_req_del())
4006 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
4007 if (pDstArchetype !=
nullptr)
4008 move_to_archetype(*pArchetype, *pDstArchetype);
4014 void rem_from_entities(Entity entity, Pair cond) {
4015 GAIA_PROF_SCOPE(World::rem_from_entities);
4017 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
4018 if (it == m_entityToArchetypeMap.end())
4022 if (!entity.pair()) {
4023 auto& ec = fetch(entity);
4024 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
4025 auto ids = ec.pArchetype->ids_view();
4026 const auto idx = core::get_index(ids, entity);
4027 if (idx != BadIndex)
4028 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
4032 const auto& archetypes = it->second;
4033 for (
auto* pArchetype: archetypes) {
4034 if (pArchetype->is_req_del())
4038 if (!archetype_cond_match(*pArchetype, cond, entity))
4041 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
4042 if (pDstArchetype !=
nullptr)
4043 move_to_archetype(*pArchetype, *pDstArchetype);
4057 void handle_del_entity(EntityContainer& ec, Entity entity) {
4058 GAIA_PROF_SCOPE(World::handle_del_entity);
4060 GAIA_ASSERT(!is_wildcard(entity));
4062 if (entity.pair()) {
4063 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
4064 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
4066 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
4067 entity.gen(), name(entity), EntityKindString[entity.kind()]);
4071 const auto tgt = get(entity.gen());
4072 const auto& ecTgt = fetch(tgt);
4073 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0) {
4074 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted (target restriction)");
4076 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted (target restriction)",
4077 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
4081#if GAIA_USE_SAFE_ENTITY
4083 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
4085 ec.flags |= EntityContainerFlags::RefDecreased;
4093 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0) {
4095 req_del_entities_with(Pair(All, tgt), Pair(OnDeleteTarget, Delete));
4098 rem_from_entities(Pair(All, tgt));
4105 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
4107 req_del_entities_with(entity);
4110 rem_from_entities(entity);
4113 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
4114 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
4116 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
4117 entity.gen(), name(entity), EntityKindString[entity.kind()]);
4121 if ((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0) {
4122 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted (a pair's target)");
4124 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted (a pair's target)",
4125 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
4129#if GAIA_USE_SAFE_ENTITY
4131 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
4133 ec.flags |= EntityContainerFlags::RefDecreased;
4141 if ((ec.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0) {
4143 req_del_entities_with(Pair(All, entity), Pair(OnDeleteTarget, Delete));
4146 rem_from_entities(Pair(All, entity));
4153 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
4155 req_del_entities_with(entity);
4158 rem_from_entities(entity);
4163 req_del(ec, entity);
4165#if GAIA_USE_WEAK_ENTITY
4166 auto invalidateWeakEntity = [](WeakEntityTracker* pTracker) {
4167 GAIA_ASSERT(pTracker->pWeakEntity->m_pTracker == pTracker);
4168 pTracker->pWeakEntity->m_pTracker =
nullptr;
4169 pTracker->pWeakEntity->m_entity = EntityBad;
4174 if (ec.pWeakTracker !=
nullptr) {
4175 auto* pTracker = ec.pWeakTracker->next;
4176 while (pTracker !=
nullptr) {
4177 invalidateWeakEntity(pTracker);
4178 pTracker = pTracker->next;
4180 pTracker = ec.pWeakTracker->prev;
4181 while (pTracker !=
nullptr) {
4182 invalidateWeakEntity(pTracker);
4183 pTracker = pTracker->prev;
4185 invalidateWeakEntity(ec.pWeakTracker);
4194 void remove_edge_from_archetype(Archetype* pArchetype, ArchetypeGraphEdge edgeLeft, Entity edgeEntity) {
4195 GAIA_ASSERT(pArchetype !=
nullptr);
4197 const auto edgeLeftIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
4198 if (edgeLeftIt == m_archetypesById.end())
4201 auto* pArchetypeLeft = edgeLeftIt->second;
4202 GAIA_ASSERT(pArchetypeLeft !=
nullptr);
4205 pArchetypeLeft->del_graph_edges(pArchetype, edgeEntity);
4208 auto& archetypesRight = pArchetype->right_edges();
4209 for (
auto& it: archetypesRight) {
4210 const auto& edgeRight = it.second;
4211 const auto edgeRightIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
4212 if (edgeRightIt == m_archetypesById.end())
4215 auto* pArchetypeRight = edgeRightIt->second;
4218 pArchetype->del_graph_edges(pArchetypeRight, it.first.entity());
4222 void remove_edges(Entity entityToRemove) {
4223 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entityToRemove));
4224 if (it == m_entityToArchetypeMap.end())
4227 const auto& archetypes = it->second;
4228 for (
auto* pArchetype: archetypes)
4229 remove_edge_from_archetype(pArchetype, pArchetype->find_edge_left(entityToRemove), entityToRemove);
4232 void remove_edges_from_pairs(Entity entity) {
4238 const auto* tgts = targets(entity);
4239 if (tgts !=
nullptr) {
4240 for (
auto target: *tgts)
4241 remove_edges(Pair(entity, target.entity()));
4244 const auto* rels = relations(entity);
4245 if (rels !=
nullptr) {
4246 for (
auto relation: *rels)
4247 remove_edges(Pair(relation.entity(), entity));
4253 void del_graph_edges(Entity entity) {
4254 remove_edges(entity);
4255 remove_edges_from_pairs(entity);
4258 void del_reltgt_tgtrel_pairs(Entity entity) {
4259 auto delPair = [](PairMap& map, Entity source, Entity remove) {
4260 auto itTargets = map.find(EntityLookupKey(source));
4261 if (itTargets != map.end()) {
4262 auto& targets = itTargets->second;
4263 targets.erase(EntityLookupKey(remove));
4267 if (entity.pair()) {
4268 const auto it = m_recs.
pairs.find(EntityLookupKey(entity));
4269 if (it != m_recs.
pairs.end()) {
4271 m_recs.
pairs.erase(it);
4274 auto rel = get(entity.id());
4275 auto tgt = get(entity.gen());
4277 delPair(m_relationsToTargets, rel, tgt);
4278 delPair(m_relationsToTargets, All, tgt);
4279 delPair(m_targetsToRelations, tgt, rel);
4280 delPair(m_targetsToRelations, All, rel);
4284 auto& ec = m_recs.
entities.free(entity);
4287 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0)
4288 req_del(*ec.pArchetype);
4290 ec.pArchetype =
nullptr;
4291 ec.pChunk =
nullptr;
4292 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::DeleteRequested,
false);
4295 delPair(m_relationsToTargets, All, entity);
4296 delPair(m_targetsToRelations, All, entity);
4297 m_relationsToTargets.erase(EntityLookupKey(entity));
4298 m_targetsToRelations.erase(EntityLookupKey(entity));
4304 void invalidate_entity(Entity entity) {
4305 del_graph_edges(entity);
4306 del_reltgt_tgtrel_pairs(entity);
4307 del_entity_archetype_pairs(entity);
4314 void store_entity(EntityContainer& ec, Entity entity, Archetype* pArchetype, Chunk* pChunk) {
4315 GAIA_ASSERT(pArchetype !=
nullptr);
4316 GAIA_ASSERT(pChunk !=
nullptr);
4318 !locked() &&
"Entities can't be stored while the world is locked "
4319 "(structural changes are forbidden during this time!)");
4321 ec.pArchetype = pArchetype;
4323 ec.row = pChunk->add_entity(entity);
4324 GAIA_ASSERT(entity.pair() || ec.data.gen == entity.gen());
4333 void move_entity(Entity entity, EntityContainer& ec, Archetype& dstArchetype, Chunk& dstChunk) {
4334 GAIA_PROF_SCOPE(World::move_entity);
4336 auto* pDstChunk = &dstChunk;
4337 auto* pSrcChunk = ec.pChunk;
4339 GAIA_ASSERT(pDstChunk != pSrcChunk);
4341 const auto srcRow0 = ec.row;
4342 const auto dstRow = pDstChunk->add_entity(entity);
4343 const bool wasEnabled = !ec.data.dis;
4345 auto& srcArchetype = *ec.pArchetype;
4346#if GAIA_ASSERT_ENABLED
4347 verify_move(*
this, srcArchetype, entity);
4351 srcArchetype.enable_entity(pSrcChunk, srcRow0,
true, m_recs);
4353 const auto srcRow = ec.row;
4356 if (dstArchetype.id() == srcArchetype.id()) {
4357 pDstChunk->move_entity_data(entity, dstRow, m_recs);
4359 pDstChunk->move_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, dstRow);
4363 remove_entity(srcArchetype, *pSrcChunk, srcRow);
4366 dstArchetype.try_update_free_chunk_idx();
4369 ec.pArchetype = &dstArchetype;
4370 ec.pChunk = pDstChunk;
4371 ec.row = (uint16_t)dstRow;
4374 dstArchetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
4377 GAIA_ASSERT(valid(entity));
4378 validate_chunk(pSrcChunk);
4379 validate_chunk(pDstChunk);
4380 validate_entities();
4385 void move_entity_raw(Entity entity, EntityContainer& ec, Archetype& dstArchetype) {
4387 ec.pChunk->update_world_version();
4389 auto* pDstChunk = dstArchetype.foc_free_chunk();
4390 move_entity(entity, ec, dstArchetype, *pDstChunk);
4393 pDstChunk->update_world_version();
4394 update_version(m_worldVersion);
4399 Chunk* move_entity(Entity entity, Archetype& dstArchetype) {
4401 auto& ec = fetch(entity);
4402 if (ec.pArchetype == &dstArchetype)
4406 ec.pChunk->update_world_version();
4408 auto* pDstChunk = dstArchetype.foc_free_chunk();
4409 move_entity(entity, ec, dstArchetype, *pDstChunk);
4412 pDstChunk->update_world_version();
4413 update_version(m_worldVersion);
4418 void validate_archetype_edges([[maybe_unused]]
const Archetype* pArchetype)
const {
4419#if GAIA_ECS_VALIDATE_ARCHETYPE_GRAPH && GAIA_ASSERT_ENABLED
4420 GAIA_ASSERT(pArchetype !=
nullptr);
4423 const auto& archetypesLeft = pArchetype->left_edges();
4424 for (
const auto& it: archetypesLeft) {
4425 const auto& edge = it.second;
4426 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
4427 if (edgeIt == m_archetypesById.end())
4430 const auto entity = it.first.entity();
4431 const auto* pArchetypeRight = edgeIt->second;
4434 const auto edgeRight = pArchetypeRight->find_edge_right(entity);
4435 GAIA_ASSERT(edgeRight != ArchetypeIdHashPairBad);
4438 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
4439 GAIA_ASSERT(it2 != m_archetypesById.end());
4440 const auto* pArchetype2 = it2->second;
4441 GAIA_ASSERT(pArchetype2 == pArchetype);
4445 const auto& archetypesRight = pArchetype->right_edges();
4446 for (
const auto& it: archetypesRight) {
4447 const auto& edge = it.second;
4448 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
4449 if (edgeIt == m_archetypesById.end())
4452 const auto entity = it.first.entity();
4453 const auto* pArchetypeRight = edgeIt->second;
4456 const auto edgeLeft = pArchetypeRight->find_edge_left(entity);
4457 GAIA_ASSERT(edgeLeft != ArchetypeIdHashPairBad);
4460 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
4461 GAIA_ASSERT(it2 != m_archetypesById.end());
4462 const auto* pArchetype2 = it2->second;
4463 GAIA_ASSERT(pArchetype2 == pArchetype);
4469 void validate_entities()
const {
4470#if GAIA_ECS_VALIDATE_ENTITY_LIST
4476 void validate_chunk([[maybe_unused]] Chunk* pChunk)
const {
4477#if GAIA_ECS_VALIDATE_CHUNKS && GAIA_ASSERT_ENABLED
4478 GAIA_ASSERT(pChunk !=
nullptr);
4480 if (!pChunk->empty()) {
4483 for (
const auto& ec: m_recs.entities) {
4484 if (ec.pChunk != pChunk)
4488 for (
const auto& pair: m_recs.pairs) {
4489 if (pair.second.pChunk != pChunk)
4493 GAIA_ASSERT(cnt == pChunk->size());
4496 for (
const auto& ec: m_recs.entities) {
4497 GAIA_ASSERT(ec.pChunk != pChunk);
4499 for (
const auto& pair: m_recs.pairs) {
4500 GAIA_ASSERT(pair.second.pChunk != pChunk);
4509 template <
bool CheckIn>
4510 GAIA_NODISCARD
bool is_inter(Entity entity, Entity entityBase)
const {
4511 GAIA_ASSERT(valid_entity(entity));
4512 GAIA_ASSERT(valid_entity(entityBase));
4515 if (entity.pair() || entityBase.pair())
4518 if constexpr (!CheckIn) {
4519 if (entity == entityBase)
4523 const auto& ec = m_recs.
entities[entity.id()];
4524 const auto* pArchetype = ec.pArchetype;
4527 if (pArchetype->pairs_is() == 0)
4530 for (uint32_t i = 0; i < pArchetype->pairs_is(); ++i) {
4531 auto e = pArchetype->entity_from_pairs_as_idx(i);
4532 const auto& ecTarget = m_recs.
entities[e.gen()];
4533 auto target = ecTarget.pChunk->entity_view()[ecTarget.row];
4534 if (target == entityBase)
4537 if (is_inter<CheckIn>(target, entityBase))
4545 template <
bool CheckIn,
typename Func>
4546 void as_up_trav(Entity entity, Func func) {
4547 GAIA_ASSERT(valid_entity(entity));
4553 if constexpr (!CheckIn) {
4557 const auto& ec = m_recs.
entities[entity.id()];
4558 const auto* pArchetype = ec.pArchetype;
4561 if (pArchetype->pairs_is() == 0)
4564 for (uint32_t i = 0; i < pArchetype->pairs_is(); ++i) {
4565 auto e = pArchetype->entity_from_pairs_as_idx(i);
4566 const auto& ecTarget = m_recs.
entities[e.gen()];
4567 auto target = ecTarget.pChunk->entity_view()[ecTarget.row];
4570 as_up_trav<CheckIn>(target, func);
4574 template <
typename T>
4575 const ComponentCacheItem& reg_core_entity(Entity
id, Archetype* pArchetype) {
4576 auto comp = add(*pArchetype,
id.entity(),
id.pair(),
id.kind());
4577 const auto& ci = comp_cache_mut().
add<T>(id);
4578 GAIA_ASSERT(ci.entity ==
id);
4579 GAIA_ASSERT(comp ==
id);
4584 template <
typename T>
4585 const ComponentCacheItem& reg_core_entity(Entity
id) {
4586 return reg_core_entity<T>(
id, m_pRootArchetype);
4594#if GAIA_ECS_CHUNK_ALLOCATOR
4595 ChunkAllocator::get().flush();
4602 void assign_entity(Entity entity, Archetype& archetype) {
4603 GAIA_ASSERT(!entity.pair());
4605 auto* pChunk = archetype.foc_free_chunk();
4606 store_entity(m_recs.
entities[entity.id()], entity, &archetype, pChunk);
4607 pChunk->update_versions();
4608 archetype.try_update_free_chunk_idx();
4611 pChunk->call_gen_ctors(pChunk->size() - 1, 1);
4613#if GAIA_ASSERT_ENABLED
4614 const auto& ec = m_recs.
entities[entity.id()];
4615 GAIA_ASSERT(ec.pChunk == pChunk);
4616 auto entityExpected = pChunk->entity_view()[ec.row];
4617 GAIA_ASSERT(entityExpected == entity);
4624 void assign_pair(Entity entity, Archetype& archetype) {
4625 GAIA_ASSERT(entity.pair());
4628 GAIA_ASSERT(&archetype == m_pEntityArchetype);
4630 const auto it = m_recs.
pairs.find(EntityLookupKey(entity));
4631 if (it != m_recs.
pairs.end())
4635 EntityContainer ec{};
4636 ec.idx = entity.id();
4637 ec.data.gen = entity.gen();
4640 ec.data.kind = EntityKind::EK_Gen;
4642 auto* pChunk = archetype.foc_free_chunk();
4643 store_entity(ec, entity, &archetype, pChunk);
4644 pChunk->update_versions();
4645 archetype.try_update_free_chunk_idx();
4647 m_recs.
pairs.emplace(EntityLookupKey(entity), GAIA_MOV(ec));
4650 const auto rel = get(entity.id());
4651 const auto tgt = get(entity.gen());
4653 auto addPair = [](PairMap& map, Entity source, Entity add) {
4654 auto& ents = map[EntityLookupKey(source)];
4655 ents.insert(EntityLookupKey(add));
4658 addPair(m_relationsToTargets, rel, tgt);
4659 addPair(m_relationsToTargets, All, tgt);
4660 addPair(m_targetsToRelations, tgt, rel);
4661 addPair(m_targetsToRelations, All, rel);
4670 GAIA_NODISCARD Entity add(Archetype& archetype,
bool isEntity,
bool isPair, EntityKind kind) {
4671 EntityContainerCtx ctx{isEntity, isPair, kind};
4672 const auto entity = m_recs.
entities.alloc(&ctx);
4673 assign_entity(entity, archetype);
4682 template <
typename Func>
4683 void add_entity_n(Archetype& archetype, uint32_t count, Func func) {
4684 EntityContainerCtx ctx{
true,
false, EntityKind::EK_Gen};
4686 uint32_t left = count;
4688 auto* pChunk = archetype.foc_free_chunk();
4689 const uint32_t originalChunkSize = pChunk->size();
4690 const uint32_t freeSlotsInChunk = pChunk->capacity() - originalChunkSize;
4691 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
4693 GAIA_FOR(toCreate) {
4694 const auto entityNew = m_recs.
entities.alloc(&ctx);
4695 auto& ecNew = m_recs.
entities[entityNew.id()];
4696 store_entity(ecNew, entityNew, &archetype, pChunk);
4698#if GAIA_ASSERT_ENABLED
4699 GAIA_ASSERT(ecNew.pChunk == pChunk);
4700 auto entityExpected = pChunk->entity_view()[ecNew.row];
4701 GAIA_ASSERT(entityExpected == entityNew);
4706 archetype.try_update_free_chunk_idx();
4709 pChunk->call_gen_ctors(originalChunkSize, toCreate);
4713 auto entities = pChunk->entity_view();
4714 GAIA_FOR2(originalChunkSize, pChunk->size()) func(entities[i]);
4717 pChunk->update_versions();
4725 GAIA_PROF_SCOPE(World::gc);
4728 defrag_chunks(m_defragEntitiesPerTick);
4729 del_empty_archetypes();
4733 QuerySerBuffer& query_buffer(QueryId& serId) {
4735 if GAIA_UNLIKELY (serId == QueryIdBad) {
4736#if GAIA_ASSERT_ENABLED
4737 uint32_t safetyCounter = 0;
4741#if GAIA_ASSERT_ENABLED
4744 GAIA_ASSERT(safetyCounter < 100000);
4747 serId = ++m_nextQuerySerId;
4749 GAIA_ASSERT(serId != 0);
4754 auto ret = m_querySerMap.try_emplace(serId);
4758 return ret.first->second;
4762 return m_querySerMap[serId];
4765 void query_buffer_reset(QueryId& serId) {
4766 auto it = m_querySerMap.find(serId);
4767 if (it == m_querySerMap.end())
4770 m_querySerMap.erase(it);
4774 void invalidate_queries_for_entity(Pair is_pair) {
4775 GAIA_ASSERT(is_pair.first() == Is);
4788 auto e = is_pair.second();
4789 as_up_trav<false>(e, [&](Entity target) {
4796 auto expr = core::trim(exprRaw);
4798 if (expr[0] ==
'(') {
4799 if (expr.back() !=
')') {
4800 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
4804 const auto idStr = expr.subspan(1, expr.size() - 2);
4805 const auto commaIdx = core::get_index(idStr,
',');
4807 const auto first = name_to_entity(idStr.subspan(0, commaIdx));
4808 if (first == EntityBad)
4810 const auto second = name_to_entity(idStr.subspan(commaIdx + 1));
4811 if (second == EntityBad)
4814 return ecs::Pair(first, second);
4818 auto idStr = core::trim(expr);
4821 if (idStr.size() == 1 && idStr[0] ==
'*')
4824 return get_inter(idStr.data(), (uint32_t)idStr.size());
4829 auto expr = core::trim(exprRaw);
4831 if (expr[0] ==
'%') {
4832 if (expr[1] !=
'e') {
4833 GAIA_ASSERT2(
false,
"Expression '%' not terminated");
4837 auto id = (Identifier)va_arg(args,
unsigned long long);
4841 if (expr[0] ==
'(') {
4842 if (expr.back() !=
')') {
4843 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
4847 const auto idStr = expr.subspan(1, expr.size() - 2);
4848 const auto commaIdx = core::get_index(idStr,
',');
4850 const auto first = expr_to_entity(args, idStr.subspan(0, commaIdx));
4851 if (first == EntityBad)
4853 const auto second = expr_to_entity(args, idStr.subspan(commaIdx + 1));
4854 if (second == EntityBad)
4857 return ecs::Pair(first, second);
4861 auto idStr = core::trim(expr);
4864 if (idStr.size() == 1 && idStr[0] ==
'*')
4868 const auto* pItem = m_compCache.
find(idStr.data(), (uint32_t)idStr.size());
4869 if (pItem ==
nullptr) {
4870 GAIA_ASSERT2(
false,
"Component not found");
4871 GAIA_LOG_W(
"Component '%.*s' not found", (uint32_t)idStr.size(), idStr.data());
4875 return pItem->entity;