177 inline static bool s_enableUniqueNameDuplicateAssert =
true;
182#if GAIA_OBSERVERS_ENABLED
183 friend class ObserverRegistry;
184 friend struct ObserverRegistry::DiffDispatcher;
185 friend struct ObserverRegistry::DirectDispatcher;
186 friend struct ObserverRegistry::SharedDispatch;
190 friend void lock(
World&);
191 friend void unlock(
World&);
193 friend void query_match_scratch_release(
World&,
bool);
194 friend uint32_t world_component_index_bucket_size(
const World&,
Entity);
197 template <
typename T>
198 friend decltype(
auto) world_direct_entity_arg(
World& world,
Entity entity);
199 template <
typename T>
200 friend decltype(
auto) world_direct_entity_arg_raw(
World& world,
Entity entity);
201 template <
typename T>
202 friend decltype(
auto) world_query_entity_arg_by_id(
World& world,
Entity entity,
Entity id);
203 template <
typename T>
204 friend decltype(
auto) world_query_entity_arg_by_id_raw(
World& world,
Entity entity,
Entity id);
210 using TFunc_Void_With_Entity = void(
Entity);
211 static void func_void_with_entity([[maybe_unused]]
Entity entity) {}
217 struct ExclusiveAdjunctStore {
223 uint32_t srcToTgtCnt = 0;
228 struct SparseComponentStoreErased {
229 void* pStore =
nullptr;
230 void (*func_del)(
void*, Entity) =
nullptr;
231 bool (*func_has)(
const void*, Entity) =
nullptr;
232 bool (*func_copy_entity)(
void*, Entity, Entity) =
nullptr;
233 uint32_t (*func_count)(
const void*) =
nullptr;
234 void (*func_collect_entities)(
const void*, cnt::darray<Entity>&) =
nullptr;
235 bool (*func_for_each_entity)(
const void*,
void*, bool (*)(
void*, Entity)) =
nullptr;
236 void (*func_clear_store)(
void*) =
nullptr;
237 void (*func_del_store)(
void*) =
nullptr;
240 template <
typename T>
241 struct SparseComponentStore final {
242 GAIA_USE_SMALLBLOCK(SparseComponentStore)
244 cnt::sparse_storage<SparseComponentRecord<T>> data;
246 static cnt::sparse_id sid(Entity entity) {
247 return (cnt::sparse_id)entity.id();
250 T& add(Entity entity) {
251 const auto sparseId = sid(entity);
252 if (data.has(sparseId))
253 return data[sparseId].value;
255 auto& item = data.add(SparseComponentRecord<T>{entity});
259 T& mut(Entity entity) {
260 GAIA_ASSERT(data.has(sid(entity)));
261 return data[sid(entity)].value;
264 const T& get(Entity entity)
const {
265 GAIA_ASSERT(data.has(sid(entity)));
266 return data[sid(entity)].value;
269 void del_entity(Entity entity) {
270 const auto sparseId = sid(entity);
271 if (!data.empty() && data.has(sparseId))
275 bool has(Entity entity)
const {
278 return data.has(sid(entity));
281 uint32_t count()
const {
282 return (uint32_t)data.size();
285 void collect_entities(cnt::darray<Entity>& out)
const {
286 out.reserve(out.size() + (uint32_t)data.size());
287 for (
const auto& item: data)
288 out.push_back(item.entity);
296 template <
typename T>
297 static SparseComponentStoreErased make_sparse_component_store_erased(SparseComponentStore<T>* pStore) {
298 SparseComponentStoreErased store{};
299 store.pStore = pStore;
300 store.func_del = [](
void* pStoreRaw, Entity entity) {
301 static_cast<SparseComponentStore<T>*
>(pStoreRaw)->del_entity(entity);
303 store.func_has = [](
const void* pStoreRaw, Entity entity) {
304 return static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->has(entity);
306 store.func_copy_entity = [](
void* pStoreRaw, Entity dstEntity, Entity srcEntity) {
307 auto* pStore =
static_cast<SparseComponentStore<T>*
>(pStoreRaw);
308 if (!pStore->has(srcEntity))
311 auto& dst = pStore->add(dstEntity);
312 dst = pStore->get(srcEntity);
315 store.func_count = [](
const void* pStoreRaw) {
316 return static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->count();
318 store.func_collect_entities = [](
const void* pStoreRaw, cnt::darray<Entity>& out) {
319 static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->collect_entities(out);
321 store.func_for_each_entity = [](
const void* pStoreRaw,
void* pCtx, bool (*func)(
void*, Entity)) {
322 for (
const auto& item: static_cast<const SparseComponentStore<T>*>(pStoreRaw)->data) {
323 if (!func(pCtx, item.entity))
328 store.func_clear_store = [](
void* pStoreRaw) {
329 static_cast<SparseComponentStore<T>*
>(pStoreRaw)->clear_store();
331 store.func_del_store = [](
void* pStoreRaw) {
332 delete static_cast<SparseComponentStore<T>*
>(pStoreRaw);
338 typename TApi,
typename TValue,
bool DeriveFromValue = std::is_class_v<TValue> && !std::is_final_v<TValue>>
339 class SetWriteProxyTyped;
341 template <
typename TApi,
typename TValue>
342 class SetWriteProxyTyped<TApi, TValue, true>:
public TValue {
343 World* m_pWorld =
nullptr;
344 Entity m_entity = EntityBad;
345 Entity m_term = EntityBad;
348 if (m_pWorld ==
nullptr)
351 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
356 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
357 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
359 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
360 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
362 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
363 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
365 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
366 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
367 m_term(other.m_term) {
368 other.m_pWorld =
nullptr;
371 ~SetWriteProxyTyped() {
375 SetWriteProxyTyped& operator=(
const TValue& value) {
376 static_cast<TValue&
>(*this) = value;
380 SetWriteProxyTyped& operator=(TValue&& value) {
381 static_cast<TValue&
>(*this) = GAIA_MOV(value);
385 GAIA_NODISCARD
operator TValue&() {
389 GAIA_NODISCARD
operator const TValue&()
const {
394 template <
typename TApi,
typename TValue>
395 class SetWriteProxyTyped<TApi, TValue, false> {
396 World* m_pWorld =
nullptr;
397 Entity m_entity = EntityBad;
398 Entity m_term = EntityBad;
402 if (m_pWorld ==
nullptr)
405 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term, m_value);
410 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
411 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
413 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
414 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
416 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
417 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
419 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
420 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
421 other.m_pWorld =
nullptr;
424 ~SetWriteProxyTyped() {
428 SetWriteProxyTyped& operator=(
const TValue& value) {
433 SetWriteProxyTyped& operator=(TValue&& value) {
434 m_value = GAIA_MOV(value);
438 GAIA_NODISCARD
operator TValue&() {
442 GAIA_NODISCARD
operator const TValue&()
const {
446 GAIA_NODISCARD TValue* operator->() {
450 GAIA_NODISCARD
const TValue* operator->()
const {
455 template <
typename TValue,
bool DeriveFromValue = std::is_
class_v<TValue> && !std::is_final_v<TValue>>
456 class SetWriteProxyObject;
458 template <
typename TValue>
459 class SetWriteProxyObject<TValue, true>:
public TValue {
460 World* m_pWorld =
nullptr;
461 Entity m_entity = EntityBad;
462 Entity m_term = EntityBad;
465 if (m_pWorld ==
nullptr)
468 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
473 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
474 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
476 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
477 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
479 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
480 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
482 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
483 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
484 m_term(other.m_term) {
485 other.m_pWorld =
nullptr;
488 ~SetWriteProxyObject() {
492 SetWriteProxyObject& operator=(
const TValue& value) {
493 static_cast<TValue&
>(*this) = value;
497 SetWriteProxyObject& operator=(TValue&& value) {
498 static_cast<TValue&
>(*this) = GAIA_MOV(value);
502 GAIA_NODISCARD
operator TValue&() {
506 GAIA_NODISCARD
operator const TValue&()
const {
511 template <
typename TValue>
512 class SetWriteProxyObject<TValue, false> {
513 World* m_pWorld =
nullptr;
514 Entity m_entity = EntityBad;
515 Entity m_term = EntityBad;
519 if (m_pWorld ==
nullptr)
522 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term, m_value);
527 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
528 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
530 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
531 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
533 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
534 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
536 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
537 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
538 other.m_pWorld =
nullptr;
541 ~SetWriteProxyObject() {
545 SetWriteProxyObject& operator=(
const TValue& value) {
550 SetWriteProxyObject& operator=(TValue&& value) {
551 m_value = GAIA_MOV(value);
555 GAIA_NODISCARD
operator TValue&() {
559 GAIA_NODISCARD
operator const TValue&()
const {
563 GAIA_NODISCARD TValue* operator->() {
567 GAIA_NODISCARD
const TValue* operator->()
const {
572 template <
typename T>
573 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity) {
574 static_assert(!is_pair<T>::value);
575 using FT =
typename component_type_t<T>::TypeFull;
576 const auto& item = add<FT>();
577 if constexpr (supports_out_of_line_component<FT>()) {
578 if (out_of_line_mode(item.entity) != OutOfLineMode::None)
579 return sparse_component_store_mut<FT>(item.entity).mut(entity);
582 const auto& ec = m_recs.entities[entity.id()];
583 if constexpr (entity_kind_v<T> == EntityKind::EK_Gen)
584 return ec.pChunk->template set<T>(ec.row);
586 return ec.pChunk->template set<T>();
590 GAIA_NODISCARD
static bool raw_component_supported(
const ComponentCacheItem& item)
noexcept {
591 return item.comp.storage_type() == DataStorageType::Table && item.comp.soa() == 0;
595 GAIA_NODISCARD
static bool
596 raw_component_payload_args_valid(
const ComponentCacheItem& item,
const void* data, uint32_t size)
noexcept {
597 if (!raw_component_supported(item))
599 if (size != item.comp.size())
601 return size == 0 || data !=
nullptr;
604 template <
typename T>
605 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity, Entity
object) {
606 static_assert(!is_pair<T>::value);
607 using FT =
typename component_type_t<T>::TypeFull;
608 if constexpr (supports_out_of_line_component<FT>()) {
609 if (can_use_out_of_line_component<FT>(
object))
610 return sparse_component_store_mut<FT>(
object).mut(entity);
613 const auto& ec = m_recs.entities[entity.id()];
614 return ec.pChunk->template set<T>(ec.row,
object);
622 void finish_write(Entity entity, Entity term) {
623 if (tearing_down() || !valid(entity))
626 if (out_of_line_mode(term) != OutOfLineMode::None) {
627 world_notify_on_set_entity(*
this, term, entity);
631 auto compIdx = uint32_t(BadIndex);
633 const auto& ec = fetch(entity);
634 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
637 if (compIdx == BadIndex) {
638 (void)
override(entity, term);
639 const auto& ec = fetch(entity);
640 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
641 if (compIdx == BadIndex)
645 const auto& ec = fetch(entity);
646 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
647 (void)ec.pChunk->comp_ptr_mut_gen<
true>(compIdx, row);
648 world_notify_on_set_entity(*
this, term, entity);
651 template <
typename TApi,
typename TValue>
652 void write_back_set_typed(Entity entity, Entity term,
const TValue& value) {
653 using FT =
typename component_type_t<TApi>::TypeFull;
654 ::gaia::ecs::update_version(m_worldVersion);
656 if constexpr (supports_out_of_line_component<FT>()) {
657 if (out_of_line_mode(term) != OutOfLineMode::None) {
658 sparse_component_store_mut<FT>(term).add(entity) = value;
659 finish_write(entity, term);
664 const auto& ec = fetch(entity);
665 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
666 ComponentSetter{*
this, ec.pChunk, entity, row}.sset<TApi>(value);
667 finish_write(entity, term);
670 template <
typename TValue>
671 void write_back_set_object(Entity entity, Entity term,
const TValue& value) {
672 using FT =
typename component_type_t<TValue>::TypeFull;
673 ::gaia::ecs::update_version(m_worldVersion);
674 if constexpr (supports_out_of_line_component<FT>()) {
675 if (can_use_out_of_line_component<FT>(term)) {
676 sparse_component_store_mut<FT>(term).add(entity) = value;
677 finish_write(entity, term);
682 const auto& ec = fetch(entity);
683 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
684 ComponentSetter{*
this, ec.pChunk, entity, row}.template smut<TValue>(term) = value;
685 finish_write(entity, term);
693 ComponentCache m_compCache;
695 QueryCache m_queryCache;
698 cnt::darray<QueryMatchScratch*> m_queryMatchScratchStack;
700 uint32_t m_queryMatchScratchDepth = 0;
706 QuerySerMap m_querySerMap;
707 uint32_t m_nextQuerySerId = 0;
710 uint32_t m_emptySpace0 = 0;
713 EntityToArchetypeMap m_entityToArchetypeMap;
715 EntityToArchetypeVersionMap m_entityToArchetypeMapVersions;
724 PairMap m_entityToAsTargets;
727 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsTargetsTravCache;
735 PairMap m_entityToAsRelations;
738 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsRelationsTravCache;
741 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsTravCache;
744 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_srcBfsTravCache;
747 mutable cnt::map<EntityLookupKey, uint32_t> m_depthOrderCache;
750 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_sourcesAllCache;
753 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsAllCache;
759 cnt::map<EntityLookupKey, ExclusiveAdjunctStore> m_exclusiveAdjunctByRel;
761 EntityArrayMap m_srcToExclusiveAdjunctRel;
763 cnt::map<EntityLookupKey, SparseComponentStoreErased> m_sparseComponentsByComp;
765 cnt::map<EntityLookupKey, uint32_t> m_relationVersions;
768 mutable cnt::map<EntityLookupKey, uint32_t> m_srcEntityVersions;
770 enum class OutOfLineMode : uint8_t { None, Fragmenting, NonFragmenting };
773 ArchetypeDArray m_archetypes;
775 ArchetypeMapByHash m_archetypesByHash;
777 ArchetypeMapById m_archetypesById;
780 Archetype* m_pRootArchetype =
nullptr;
782 Archetype* m_pEntityArchetype =
nullptr;
784 Archetype* m_pCompArchetype =
nullptr;
786 ArchetypeId m_nextArchetypeId = 0;
789 uint32_t m_emptySpace1 = 0;
792 EntityContainers m_recs;
795 cnt::map<EntityNameLookupKey, Entity> m_nameToEntity;
797 cnt::map<EntityNameLookupKey, Entity> m_aliasToEntity;
799 Entity m_componentScope = EntityBad;
801 cnt::darray<Entity> m_componentLookupPath;
803 mutable util::str m_componentScopePathCache;
805 mutable Entity m_componentScopePathCacheEntity = EntityBad;
807 mutable bool m_componentScopePathCacheValid =
false;
810 cnt::set<ArchetypeLookupKey> m_reqArchetypesToDel;
812 cnt::set<EntityLookupKey> m_reqEntitiesToDel;
814#if GAIA_OBSERVERS_ENABLED
816 ObserverRegistry m_observers;
819#if GAIA_SYSTEMS_ENABLED
821 SystemRegistry m_systems;
825 CommandBufferST* m_pCmdBufferST;
827 CommandBufferMT* m_pCmdBufferMT;
829 bool m_teardownActive =
false;
831 Query m_systemsQuery;
833 detail::SystemScheduleScratch m_systemScheduleScratch;
837 mutable cnt::darray<uint64_t> m_entityVisitStamps;
839 mutable uint64_t m_entityVisitStamp = 0;
842 cnt::set<EntityLookupKey> m_entitiesToDel;
844 cnt::darray<ArchetypeChunkPair> m_chunksToDel;
846 ArchetypeDArray m_archetypesToDel;
848 uint32_t m_defragLastArchetypeIdx = 0;
850 uint32_t m_defragEntitiesPerTick = 100;
853 uint32_t m_worldVersion = 0;
855 uint32_t m_enabledHierarchyVersion = 0;
857 uint32_t m_structuralChangesLocked = 0;
862 m_pCmdBufferST(cmd_buffer_st_create(*this)),
864 m_pCmdBufferMT(cmd_buffer_mt_create(*this)) {
871 cmd_buffer_destroy(*m_pCmdBufferST);
872 cmd_buffer_destroy(*m_pCmdBufferMT);
875 World(World&&) =
delete;
876 World(
const World&) =
delete;
877 World& operator=(World&&) =
delete;
878 World& operator=(
const World&) =
delete;
887 *
const_cast<World*
>(
this), m_queryCache,
889 m_nextArchetypeId, m_worldVersion, m_entityToArchetypeMap, m_entityToArchetypeMapVersions, m_archetypes);
897 q.kind(QueryCacheKind::None);
901#if GAIA_ECS_TEST_HOOKS
903 GAIA_NODISCARD
bool verify_query_cache()
const {
904 return m_queryCache.verify_archetype_tracking();
909 GAIA_NODISCARD uint32_t test_query_cache_count()
const {
910 return m_queryCache.test_query_count();
930 return sched_resolve(m_sched);
939#if GAIA_ASSERT_ENABLED
941 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
942 if (!valid(entity)) {
946 const bool allowStaleExactPair = entity.pair() && m_recs.pairs.contains(
EntityLookupKey(entity));
947 GAIA_ASSERT(allowStaleExactPair);
950 return m_recs[entity];
957#if GAIA_ASSERT_ENABLED
959 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
960 if (!valid(entity)) {
964 const bool allowStaleExactPair = entity.pair() && m_recs.pairs.contains(
EntityLookupKey(entity));
965 GAIA_ASSERT(allowStaleExactPair);
968 return m_recs[entity];
978 if ((ec.
flags & EntityContainerFlags::DeleteRequested) != 0)
980 GAIA_ASSERT((ec.
flags & EntityContainerFlags::Load) == 0);
988 return (fetch(entity).flags & EntityContainerFlags::IsDontFragment) != 0;
995 return valid(relation) && !relation.pair() && is_dont_fragment(relation);
1003 if (!valid(relation) || relation.pair())
1006 const auto& ec = fetch(relation);
1007 return (ec.flags & EntityContainerFlags::IsExclusive) != 0 &&
1008 (ec.flags & EntityContainerFlags::IsDontFragment) != 0;
1014 if (!valid(relation) || relation.pair())
1017 return has(relation, Exclusive) && has(relation, Traversable);
1023 return valid(relation) && !relation.pair() && !is_dont_fragment(relation);
1029 return is_hierarchy_relation(relation) && is_fragmenting_relation(relation);
1037 return is_fragmenting_relation(relation);
1044 return is_fragmenting_hierarchy_relation(relation);
1052 if (!valid(component) || component.pair() || component.entity())
1055 const auto* pItem = comp_cache().
find(component);
1056 if (pItem ==
nullptr || component.kind() != EntityKind::EK_Gen || pItem->comp.soa() != 0)
1059 return component_has_out_of_line_data(pItem->comp);
1067 if (!is_out_of_line_component(component))
1070 return (fetch(component).flags & EntityContainerFlags::IsDontFragment) != 0;
1073 GAIA_NODISCARD OutOfLineMode out_of_line_mode(
Entity component)
const {
1074 if (!is_out_of_line_component(component))
1075 return OutOfLineMode::None;
1077 return is_non_fragmenting_out_of_line_component(component) ? OutOfLineMode::NonFragmenting
1078 : OutOfLineMode::Fragmenting;
1082 copies_sparse_payload_inter(Entity comp, Entity srcEntity,
const SparseComponentStoreErased& store)
const {
1083 return store.func_has(store.pStore, srcEntity) && out_of_line_mode(comp) != OutOfLineMode::None;
1086 GAIA_NODISCARD
bool copies_non_frag_sparse_payload_inter(
1087 Entity comp, Entity srcEntity,
const SparseComponentStoreErased& store)
const {
1088 return copies_sparse_payload_inter(comp, srcEntity, store) &&
1089 out_of_line_mode(comp) == OutOfLineMode::NonFragmenting;
1092 GAIA_NODISCARD
bool sparse_copy_adds_id_inter(Entity comp)
const {
1093 return out_of_line_mode(comp) == OutOfLineMode::NonFragmenting;
1102 auto& item = comp_cache_mut().
get(component);
1105 auto& ec = m_recs.entities[component.id()];
1106 if (ec.pArchetype ==
nullptr || ec.pChunk ==
nullptr)
1109 const auto compIdx = core::get_index(ec.pArchetype->ids_view(), GAIA_ID(
Component));
1113 auto* pComp =
reinterpret_cast<Component*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
1123 sync_component_record(item.
entity, item.
comp);
1125 if (addSparseTrait && item.
comp.storage_type() == DataStorageType::Sparse)
1126 add(item.
entity, Sparse);
1134 if (component.comp())
1135 set_component_sparse_storage(component);
1137 if ((ec.
flags & EntityContainerFlags::IsDontFragment) != 0)
1140 ec.
flags |= EntityContainerFlags::IsDontFragment;
1148 GAIA_ASSERT(valid(component));
1149 GAIA_ASSERT(component.comp());
1150 GAIA_ASSERT(!component.pair());
1151 GAIA_ASSERT(!component.entity());
1152 GAIA_ASSERT(component.kind() == EntityKind::EK_Gen);
1154 const auto& item = comp_cache().
get(component);
1155 GAIA_ASSERT(item.entity == component);
1157 if (item.comp.storage_type() == DataStorageType::Sparse)
1160 GAIA_ASSERT(item.comp.soa() == 0);
1161 if (item.comp.soa() != 0)
1164 const auto directTermEntityCnt = count_direct_term_entities_direct(component);
1165 GAIA_ASSERT(directTermEntityCnt == 0);
1166 if (directTermEntityCnt != 0)
1169 auto comp = item.comp;
1170 comp.data.storage = (IdentifierData)DataStorageType::Sparse;
1171 sync_component_record(component, comp);
1178 template <
typename T>
1180 using U =
typename actual_type_t<T>::Type;
1181 return !
is_pair<T>::value && entity_kind_v<T> == EntityKind::EK_Gen && !mem::is_soa_layout_v<U>;
1188 template <
typename T>
1190 if constexpr (!supports_out_of_line_component<T>())
1196 const auto* pItem = comp_cache().
find(
object);
1197 if (pItem ==
nullptr || pItem->entity !=
object || out_of_line_mode(
object) == OutOfLineMode::None)
1200 using U =
typename actual_type_t<T>::Type;
1201 return pItem->
comp.size() == (uint32_t)
sizeof(U) && pItem->comp.alig() == (uint32_t)
alignof(U) &&
1202 pItem->comp.soa() == 0 &&
object.kind() == entity_kind_v<T>;
1210 template <
typename T>
1212 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1213 if (it == m_sparseComponentsByComp.end())
1216 return static_cast<SparseComponentStore<T>*
>(it->second.pStore);
1223 template <
typename T>
1225 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1226 if (it == m_sparseComponentsByComp.end())
1229 return static_cast<const SparseComponentStore<T>*
>(it->second.pStore);
1236 template <
typename T>
1239 const auto it = m_sparseComponentsByComp.find(key);
1240 if (it != m_sparseComponentsByComp.end())
1241 return *
static_cast<SparseComponentStore<T>*
>(it->second.pStore);
1243 auto* pStore =
new SparseComponentStore<T>();
1244 m_sparseComponentsByComp.emplace(key, make_sparse_component_store_erased(pStore));
1254 GAIA_ASSERT(mode != OutOfLineMode::None);
1256#if GAIA_OBSERVERS_ENABLED
1258 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
1260 if (mode == OutOfLineMode::Fragmenting) {
1261 GAIA_ASSERT(!locked());
1263 eb.add_inter_init(
object);
1267 notify_add_single(entity,
object);
1268#if GAIA_OBSERVERS_ENABLED
1269 m_observers.finish_diff(*
this, GAIA_MOV(ctx));
1276 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
1278 store.func_del(store.pStore, entity);
1285 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1286 if (it == m_sparseComponentsByComp.end())
1289 it->second.func_clear_store(it->second.pStore);
1290 it->second.func_del_store(it->second.pStore);
1291 m_sparseComponentsByComp.erase(it);
1298 const auto it = m_exclusiveAdjunctByRel.find(
EntityLookupKey(relation));
1299 if (it == m_exclusiveAdjunctByRel.end())
1312 static void ensure_exclusive_adjunct_src_capacity(ExclusiveAdjunctStore& store,
Entity source) {
1313 const auto required = (uint32_t)source.id() + 1;
1314 if (store.srcToTgt.size() >= required)
1317 const auto oldSize = (uint32_t)store.srcToTgt.size();
1318 auto newSize = oldSize == 0 ? 16U : oldSize;
1319 while (newSize < required)
1322 store.srcToTgt.resize(newSize, EntityBad);
1323 store.srcToTgtIdx.resize(newSize, BadIndex);
1326 static void ensure_exclusive_adjunct_tgt_capacity(ExclusiveAdjunctStore& store, Entity target) {
1327 const auto required = target.id() + 1;
1328 if (store.tgtToSrc.size() >= required)
1331 const auto oldSize = (uint32_t)store.tgtToSrc.size();
1332 auto newSize = oldSize == 0 ? 16U : oldSize;
1333 while (newSize < required)
1336 store.tgtToSrc.resize(newSize);
1339 GAIA_NODISCARD
static Entity exclusive_adjunct_target(
const ExclusiveAdjunctStore& store, Entity source) {
1340 if (source.id() >= store.srcToTgt.size())
1343 return store.srcToTgt[source.id()];
1346 GAIA_NODISCARD
static const cnt::darray<Entity>*
1347 exclusive_adjunct_sources(
const ExclusiveAdjunctStore& store, Entity target) {
1348 if (target.id() >= store.tgtToSrc.size())
1351 const auto& sources = store.tgtToSrc[target.id()];
1352 return sources.empty() ? nullptr : &sources;
1355 static void del_exclusive_adjunct_target_source(ExclusiveAdjunctStore& store, Entity target, Entity source) {
1356 GAIA_ASSERT(target.id() < store.tgtToSrc.size());
1357 if (target.id() >= store.tgtToSrc.size())
1360 auto& sources = store.tgtToSrc[target.id()];
1361 const auto idx = source.id() < store.srcToTgtIdx.size() ? store.srcToTgtIdx[source.id()] :
BadIndex;
1362 GAIA_ASSERT(idx != BadIndex && idx < sources.size());
1363 if (idx == BadIndex || idx >= sources.size())
1366 const auto lastIdx = (uint32_t)sources.size() - 1;
1367 if (idx != lastIdx) {
1368 const auto movedSource = sources[lastIdx];
1369 sources[idx] = movedSource;
1370 GAIA_ASSERT(movedSource.id() < store.srcToTgtIdx.size());
1371 store.srcToTgtIdx[movedSource.id()] = idx;
1377 void exclusive_adjunct_track_src_relation(Entity source, Entity relation) {
1378 auto& rels = m_srcToExclusiveAdjunctRel[EntityLookupKey(source)];
1379 if (!core::has(rels, relation))
1380 rels.push_back(relation);
1383 void exclusive_adjunct_untrack_src_relation(Entity source, Entity relation) {
1384 const auto it = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
1385 if (it == m_srcToExclusiveAdjunctRel.end())
1388 auto& rels = it->second;
1389 const auto idx = core::get_index(rels, relation);
1390 if (idx != BadIndex)
1391 core::swap_erase_unsafe(rels, idx);
1393 m_srcToExclusiveAdjunctRel.erase(it);
1396 void exclusive_adjunct_set(Entity source, Entity relation, Entity target) {
1397 GAIA_ASSERT(is_exclusive_dont_fragment_relation(relation));
1399 auto& store = exclusive_adjunct_store_mut(relation);
1400 ensure_exclusive_adjunct_src_capacity(store, source);
1401 const auto oldTarget = store.srcToTgt[source.id()];
1402 if (oldTarget != EntityBad) {
1403 if (oldTarget == target)
1406 del_exclusive_adjunct_target_source(store, oldTarget, source);
1408 ++store.srcToTgtCnt;
1409 exclusive_adjunct_track_src_relation(source, relation);
1412 ensure_exclusive_adjunct_tgt_capacity(store, target);
1413 auto& sources = store.tgtToSrc[target.id()];
1414 store.srcToTgt[source.id()] = target;
1415 store.srcToTgtIdx[source.id()] = (uint32_t)sources.size();
1416 sources.push_back(source);
1418 touch_rel_version(relation);
1419 invalidate_queries_for_rel(relation);
1420 m_targetsTravCache = {};
1421 m_srcBfsTravCache = {};
1422 m_depthOrderCache = {};
1423 m_sourcesAllCache = {};
1424 m_targetsAllCache = {};
1427 bool exclusive_adjunct_del(Entity source, Entity relation, Entity target) {
1428 const auto itStore = m_exclusiveAdjunctByRel.find(EntityLookupKey(relation));
1429 if (itStore == m_exclusiveAdjunctByRel.end())
1432 auto& store = itStore->second;
1433 const auto oldTarget = exclusive_adjunct_target(store, source);
1434 if (oldTarget == EntityBad)
1436 if (target != EntityBad && oldTarget != target)
1439 del_exclusive_adjunct_target_source(store, oldTarget, source);
1440 store.srcToTgt[source.id()] = EntityBad;
1441 store.srcToTgtIdx[source.id()] =
BadIndex;
1442 GAIA_ASSERT(store.srcToTgtCnt > 0);
1443 --store.srcToTgtCnt;
1445 exclusive_adjunct_untrack_src_relation(source, relation);
1446 if (store.srcToTgtCnt == 0)
1447 m_exclusiveAdjunctByRel.erase(itStore);
1449 touch_rel_version(relation);
1450 invalidate_queries_for_rel(relation);
1451 m_targetsTravCache = {};
1452 m_srcBfsTravCache = {};
1453 m_depthOrderCache = {};
1454 m_sourcesAllCache = {};
1455 m_targetsAllCache = {};
1460 GAIA_NODISCARD
bool has_exclusive_adjunct_pair(Entity source, Entity
object)
const {
1464 const auto srcRelsIt = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
1465 if (srcRelsIt == m_srcToExclusiveAdjunctRel.end())
1468 const auto relId =
object.id();
1469 const auto tgtId =
object.gen();
1471 if (relId != All.id()) {
1472 const auto relation = get(relId);
1473 if (!is_exclusive_dont_fragment_relation(relation))
1476 const auto* pStore = exclusive_adjunct_store(relation);
1477 if (pStore ==
nullptr)
1480 const auto target = exclusive_adjunct_target(*pStore, source);
1481 if (target == EntityBad)
1484 return tgtId == All.id() || target.id() == tgtId;
1487 if (tgtId == All.id())
1488 return !srcRelsIt->second.empty();
1490 const auto target = get(tgtId);
1491 for (
auto relKey: srcRelsIt->second) {
1492 const auto relation = relKey;
1493 const auto* pStore = exclusive_adjunct_store(relation);
1494 if (pStore ==
nullptr)
1497 if (exclusive_adjunct_target(*pStore, source) == target)
1504 void del_exclusive_adjunct_source(Entity source) {
1505 const auto it = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
1506 if (it == m_srcToExclusiveAdjunctRel.end())
1509 cnt::darray<Entity> relations;
1510 for (
auto relKey: it->second)
1511 relations.push_back(relKey);
1513 for (
auto relation: relations) {
1514 touch_rel_version(relation);
1515 invalidate_queries_for_rel(relation);
1516 (void)exclusive_adjunct_del(source, relation, EntityBad);
1518 m_targetsTravCache = {};
1519 m_srcBfsTravCache = {};
1520 m_depthOrderCache = {};
1521 m_sourcesAllCache = {};
1522 m_targetsAllCache = {};
1525 void del_exclusive_adjunct_relation(Entity relation) {
1526 const auto itStore = m_exclusiveAdjunctByRel.find(EntityLookupKey(relation));
1527 if (itStore == m_exclusiveAdjunctByRel.end())
1530 cnt::darray<Entity> sources;
1531 const auto& srcToTgt = itStore->second.srcToTgt;
1532 GAIA_FOR((uint32_t)srcToTgt.size()) {
1533 if (srcToTgt[i] == EntityBad)
1536 sources.push_back(get((EntityId)i));
1539 touch_rel_version(relation);
1540 invalidate_queries_for_rel(relation);
1541 for (
auto source: sources)
1542 (void)exclusive_adjunct_del(source, relation, EntityBad);
1543 m_targetsTravCache = {};
1544 m_srcBfsTravCache = {};
1545 m_depthOrderCache = {};
1546 m_sourcesAllCache = {};
1547 m_targetsAllCache = {};
1552 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
1553 if (exclusive_adjunct_sources(store, target) ==
nullptr)
1556 if (has(relKey.entity(), cond))
1568 m_serializer = ser::make_serializer(m_stream);
1574 GAIA_ASSERT(serializer.valid());
1575 m_serializer = serializer;
1580 template <
typename TSerializer>
1582 set_serializer(ser::make_serializer(serializer));
1588 return m_serializer;
1602 uint32_t m_rowSrc = 0;
1614 uint8_t m_graphEdgeOpCount = 0;
1616 bool m_graphEdgeIsAdd =
false;
1628#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
1629 static constexpr uint32_t MAX_TERMS = 32;
1630 static_assert(MAX_TERMS <= ChunkHeader::MAX_COMPONENTS);
1637 m_world(world), m_pArchetypeSrc(ec.pArchetype), m_pChunkSrc(ec.pChunk), m_rowSrc(ec.row),
1638 m_pArchetype(ec.pArchetype), m_entity(entity) {
1640 GAIA_ASSERT(ec.
pChunk->entity_view()[ec.
row] == entity);
1644 const auto& ec = world.
fetch(entity);
1645 m_pArchetypeSrc = ec.pArchetype;
1646 m_pChunkSrc = ec.pChunk;
1649 m_pArchetype = ec.pArchetype;
1652 EntityBuilder(
const EntityBuilder&) =
default;
1653 EntityBuilder(EntityBuilder&&) =
delete;
1654 EntityBuilder& operator=(
const EntityBuilder&) =
delete;
1655 EntityBuilder& operator=(EntityBuilder&&) =
delete;
1665 if (m_pArchetype ==
nullptr) {
1666 reset_graph_edge_tracking();
1671 if (m_pArchetypeSrc != m_pArchetype) {
1672 auto& ec = m_world.fetch(m_entity);
1673 GAIA_ASSERT(ec.pArchetype == m_pArchetypeSrc);
1674#if GAIA_OBSERVERS_ENABLED
1675 auto delDiffCtx = tl_del_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
1676 : m_world.m_observers.prepare_diff(
1677 m_world, ObserverEvent::OnDel,
EntitySpan{tl_del_comps},
1679 auto addDiffCtx = tl_new_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
1680 : m_world.m_observers.prepare_diff(
1681 m_world, ObserverEvent::OnAdd,
EntitySpan{tl_new_comps},
1686 trigger_del_hooks(*m_pArchetype);
1689 m_world.move_entity_raw(m_entity, ec, *m_pArchetype);
1693 if (m_graphEdgeOpCount == 1 && m_graphEdgeEntity != EntityBad) {
1694 if (m_graphEdgeIsAdd)
1695 rebuild_graph_edge(m_pArchetypeSrc, m_pArchetype, m_graphEdgeEntity);
1697 rebuild_graph_edge(m_pArchetype, m_pArchetypeSrc, m_graphEdgeEntity);
1700 if (m_targetNameKey.str() !=
nullptr || m_targetAliasKey.str() !=
nullptr) {
1701 const auto compIdx = ec.pChunk->comp_idx(GAIA_ID(
EntityDesc));
1703 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut_gen<
false>(compIdx, ec.row));
1704 GAIA_ASSERT(core::check_alignment(pDesc));
1707 if (m_targetNameKey.str() !=
nullptr) {
1708 pDesc->name = m_targetNameKey.str();
1709 pDesc->name_len = m_targetNameKey.len();
1713 if (m_targetAliasKey.str() !=
nullptr) {
1714 pDesc->alias = m_targetAliasKey.str();
1715 pDesc->alias_len = m_targetAliasKey.len();
1720 trigger_add_hooks(*m_pArchetype);
1721#if GAIA_OBSERVERS_ENABLED
1722 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
1723 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
1725 cleanup_deleted_out_of_line_components();
1727 m_pArchetypeSrc = ec.pArchetype;
1728 m_pChunkSrc = ec.pChunk;
1733#if GAIA_ASSERT_ENABLED
1734 auto& ec = m_world.fetch(m_entity);
1735 GAIA_ASSERT(ec.pChunk == m_pChunkSrc);
1738#if GAIA_OBSERVERS_ENABLED
1739 auto delDiffCtx = tl_del_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
1740 : m_world.m_observers.prepare_diff(
1741 m_world, ObserverEvent::OnDel,
EntitySpan{tl_del_comps},
1743 auto addDiffCtx = tl_new_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
1744 : m_world.m_observers.prepare_diff(
1745 m_world, ObserverEvent::OnAdd,
EntitySpan{tl_new_comps},
1749 if (m_targetNameKey.str() !=
nullptr || m_targetAliasKey.str() !=
nullptr) {
1752 GAIA_ASSERT(core::check_alignment(pDesc));
1755 if (m_targetNameKey.str() !=
nullptr) {
1756 pDesc->name = m_targetNameKey.str();
1757 pDesc->name_len = m_targetNameKey.len();
1761 if (m_targetAliasKey.str() !=
nullptr) {
1762 pDesc->alias = m_targetAliasKey.str();
1763 pDesc->alias_len = m_targetAliasKey.len();
1767#if GAIA_OBSERVERS_ENABLED
1768 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
1769 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
1771 cleanup_deleted_out_of_line_components();
1775 m_pArchetype =
nullptr;
1776 m_targetNameKey = {};
1777 m_targetAliasKey = {};
1778 reset_graph_edge_tracking();
1788 void name(
const char* name, uint32_t len = 0) {
1789 name_inter<true>(name, len);
1804 name_inter<false>(name, len);
1811 void alias(
const char* alias, uint32_t len = 0) {
1812 alias_inter<true>(alias, len);
1820 alias_inter<false>(alias, len);
1825 if (m_entity.pair())
1830 const auto compIdx = core::get_index(m_pChunkSrc->ids_view(), GAIA_ID(
EntityDesc));
1835 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->comp_ptr(compIdx, m_rowSrc));
1836 GAIA_ASSERT(core::check_alignment(pDesc));
1837 if (pDesc->name ==
nullptr)
1846 m_world.invalidate_scope_path_cache();
1848 pDesc->name =
nullptr;
1849 pDesc->name_len = 0;
1850 if (pDesc->alias ==
nullptr)
1853 m_targetNameKey = {};
1858 if (m_entity.pair())
1863 const auto compIdx = core::get_index(m_pChunkSrc->ids_view(), GAIA_ID(
EntityDesc));
1868 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->comp_ptr(compIdx, m_rowSrc));
1869 GAIA_ASSERT(core::check_alignment(pDesc));
1870 if (pDesc->alias ==
nullptr)
1880 pDesc->alias =
nullptr;
1881 pDesc->alias_len = 0;
1882 if (pDesc->name ==
nullptr)
1885 m_targetAliasKey = {};
1891 GAIA_PROF_SCOPE(EntityBuilder::add);
1892 GAIA_ASSERT(m_world.valid(m_entity));
1893 GAIA_ASSERT(m_world.valid(entity));
1902 GAIA_PROF_SCOPE(EntityBuilder::add);
1903 GAIA_ASSERT(m_world.valid(m_entity));
1904 GAIA_ASSERT(m_world.valid(
pair.first()));
1905 GAIA_ASSERT(m_world.valid(
pair.second()));
1915 return add(
Pair(Is, entityBase));
1928 return static_cast<const World&
>(m_world).is(entity, entityBase);
1933 return add(
Pair(ChildOf, parent));
1938 template <
typename T>
1941 const auto rel = m_world.template reg_comp<typename T::rel>().entity;
1942 const auto tgt = m_world.template reg_comp<typename T::tgt>().entity;
1947 return m_world.template reg_comp<T>().entity;
1951 template <
typename T>
1954 add(register_component<T>());
1961 GAIA_PROF_SCOPE(EntityBuilder::del);
1962 GAIA_ASSERT(m_world.valid(m_entity));
1963 GAIA_ASSERT(m_world.valid(entity));
1971 GAIA_PROF_SCOPE(EntityBuilder::add);
1972 GAIA_ASSERT(m_world.valid(m_entity));
1973 GAIA_ASSERT(m_world.valid(
pair.first()));
1974 GAIA_ASSERT(m_world.valid(
pair.second()));
1979 template <
typename T>
1982 del(register_component<T>());
1989 void trigger_add_hooks(
const Archetype& newArchetype) {
1990#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
1991 if GAIA_UNLIKELY (m_world.tearing_down()) {
1992 tl_new_comps.clear();
1999 #if GAIA_ENABLE_ADD_DEL_HOOKS
2003 for (
auto entity: tl_new_comps) {
2007 const auto& item = m_world.comp_cache().get(entity);
2008 const auto& hooks = ComponentCache::hooks(item);
2009 if (hooks.func_add !=
nullptr)
2010 hooks.func_add(m_world, item, m_entity);
2015 #if GAIA_OBSERVERS_ENABLED
2017 m_world.m_observers.on_add(m_world, newArchetype,
std::span<Entity>{tl_new_comps}, {&m_entity, 1});
2022 tl_new_comps.clear();
2030 void trigger_del_hooks(
const Archetype& newArchetype) {
2031#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2032 if GAIA_UNLIKELY (m_world.tearing_down()) {
2033 tl_del_comps.clear();
2038 m_world.notify_inherited_del_dependents(m_entity,
std::span<Entity>{tl_del_comps});
2041 #if GAIA_OBSERVERS_ENABLED
2043 m_world.m_observers.on_del(m_world, newArchetype,
std::span<Entity>{tl_del_comps}, {&m_entity, 1});
2048 #if GAIA_ENABLE_ADD_DEL_HOOKS
2052 for (
auto entity: tl_del_comps) {
2056 const auto& item = m_world.comp_cache().get(entity);
2057 const auto& hooks = ComponentCache::hooks(item);
2058 if (hooks.func_del !=
nullptr)
2059 hooks.func_del(m_world, item, m_entity);
2068 void cleanup_deleted_out_of_line_components() {
2069 for (
auto entity: tl_del_comps) {
2070 if (entity.pair() || !m_world.is_out_of_line_component(entity) ||
2071 m_world.is_non_fragmenting_out_of_line_component(entity))
2074 const auto it = m_world.m_sparseComponentsByComp.find(EntityLookupKey(entity));
2075 if (it != m_world.m_sparseComponentsByComp.end())
2076 it->second.func_del(it->second.pStore, m_entity);
2079 tl_del_comps.clear();
2082 bool handle_add_entity(Entity entity) {
2083 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> targets;
2085 const auto& ecMain = m_world.fetch(entity);
2087 m_world.invalidate_scope_path_cache();
2090 if ((ecMain.flags & EntityContainerFlags::HasCantCombine) != 0) {
2091 m_world.targets(entity, CantCombine, [&targets](Entity target) {
2092 targets.push_back(target);
2094 for (
auto e: targets) {
2095 if (m_pArchetype->
has(e)) {
2096#if GAIA_ASSERT_ENABLED
2097 GAIA_ASSERT2(
false,
"Trying to add an entity which can't be combined with the source");
2098 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2106 if (entity.pair()) {
2108 const auto& ecRel = m_world.m_recs.entities[entity.id()];
2109 if ((ecRel.flags & EntityContainerFlags::IsExclusive) != 0) {
2111 entity.id(), ecRel.data.gen, (
bool)ecRel.data.ent, (
bool)ecRel.data.pair,
2112 (EntityKind)ecRel.data.kind);
2113 auto tgt = m_world.try_get(entity.gen());
2114 if (tgt == EntityBad)
2120 m_world.targets_if(m_entity, rel, [&targets](Entity target) {
2121 targets.push_back(target);
2124 return targets.size() < 2;
2127 const auto targetsCnt = targets.size();
2128 if (targetsCnt > 1) {
2129#if GAIA_ASSERT_ENABLED
2131 false,
"Trying to add a pair with exclusive relationship but there are multiple targets present. "
2132 "Make sure to add the Exclusive property before any relationships with it are created.");
2133 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2140 const auto tgtNew = *targets.begin();
2141 if (targetsCnt == 1 && tgt != tgtNew) {
2146 if (!can_del(entity)) {
2147#if GAIA_ASSERT_ENABLED
2149 false,
"Trying to replace an exclusive relationship but the entity which is getting removed has "
2151 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2156 handle_del(ecs::Pair(rel, tgtNew));
2164 m_world.targets(entity, Requires, [&targets](Entity target) {
2165 targets.push_back(target);
2168 for (
auto e: targets) {
2169 auto* pArchetype = m_pArchetype;
2170 handle_add<false>(e);
2171 if (m_pArchetype != pArchetype)
2172 handle_add_entity(e);
2179 GAIA_NODISCARD
bool has_Requires_tgt(Entity entity)
const {
2181 auto ids = m_pArchetype->ids_view();
2183 if (m_world.has(e, Pair(Requires, entity)))
2190 static void set_flag(EntityContainerFlagsType& flags, EntityContainerFlags flag,
bool enable) {
2197 void set_flag(Entity entity, EntityContainerFlags flag,
bool enable) {
2198 auto& ec = m_world.fetch(entity);
2199 set_flag(ec.flags, flag, enable);
2202 void try_set_flags(Entity entity,
bool enable) {
2203 auto& ecMain = m_world.fetch(m_entity);
2204 try_set_CantCombine(ecMain, entity, enable);
2206 auto& ec = m_world.fetch(entity);
2207 try_set_Is(ec, entity, enable);
2208 try_set_IsExclusive(ecMain, entity, enable);
2210 try_set_sticky_component_traits(ecMain, entity);
2211 try_set_IsSingleton(ecMain, entity, enable);
2212 try_set_OnDelete(ecMain, entity, enable);
2213 try_set_OnDeleteTarget(entity, enable);
2216 void try_set_Is(EntityContainer& ec, Entity entity,
bool enable) {
2217 if (!entity.pair() || entity.id() != Is.id())
2220 set_flag(ec.flags, EntityContainerFlags::HasAliasOf, enable);
2223 void try_set_CantCombine(EntityContainer& ec, Entity entity,
bool enable) {
2224 if (!entity.pair() || entity.id() != CantCombine.id())
2227 GAIA_ASSERT(entity != m_entity);
2236 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
true);
2237 else if ((ec.flags & EntityContainerFlags::HasCantCombine) != 0) {
2238 uint32_t targets = 0;
2239 m_world.targets(m_entity, CantCombine, [&targets]([[maybe_unused]] Entity entity) {
2243 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
false);
2247 void try_set_IsExclusive(EntityContainer& ec, Entity entity,
bool enable) {
2248 if (entity.pair() || entity.id() != Exclusive.id())
2251 set_flag(ec.flags, EntityContainerFlags::IsExclusive, enable);
2254 void try_set_sticky_component_traits(EntityContainer& ecMain, Entity entity) {
2258 if (entity.id() == DontFragment.id()) {
2259 m_world.set_component_dont_fragment(m_entity, ecMain);
2263 if (entity.id() == Sparse.id())
2264 m_world.set_component_sparse_storage(m_entity);
2270 GAIA_NODISCARD IdMode id_mode(Entity entity)
const noexcept {
2271 if (!entity.pair()) {
2272 if (m_entity.comp() && (entity.id() == DontFragment.id() || entity.id() == Sparse.id()))
2273 return IdMode::Sticky;
2275 return IdMode::Normal;
2278 return m_world.is_exclusive_dont_fragment_relation(m_world.get(entity.id())) ? IdMode::Adjunct
2286 GAIA_NODISCARD
bool has_id(IdMode mode, Entity entity)
const {
2287 return mode == IdMode::Adjunct ? m_world.has(m_entity, entity) : m_pArchetype->
has(entity);
2294 GAIA_NODISCARD
bool add_id(IdMode mode, Entity entity) {
2295 if (mode == IdMode::Adjunct) {
2296 const auto relation = m_world.try_get(entity.id());
2297 const auto target = m_world.try_get(entity.gen());
2298 if (relation == EntityBad || target == EntityBad)
2301 m_world.exclusive_adjunct_set(m_entity, relation, target);
2305 m_pArchetype = m_world.foc_archetype_add_no_graph(m_pArchetype, entity);
2306 note_graph_edge(entity,
true);
2313 void del_id(IdMode mode, Entity entity) {
2314 if (mode == IdMode::Adjunct) {
2315 const auto relation = m_world.try_get(entity.id());
2316 const auto target = m_world.try_get(entity.gen());
2317 if (relation != EntityBad && target != EntityBad)
2318 (void)m_world.exclusive_adjunct_del(m_entity, relation, target);
2322 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
2323 note_graph_edge(entity,
false);
2326 void try_set_OnDeleteTarget(Entity entity,
bool enable) {
2330 const auto rel = m_world.try_get(entity.id());
2331 const auto tgt = m_world.try_get(entity.gen());
2332 if (rel == EntityBad || tgt == EntityBad)
2337 if (m_world.has(rel, Pair(OnDeleteTarget, Delete)))
2338 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Delete, enable);
2339 else if (m_world.has(rel, Pair(OnDeleteTarget, Remove)))
2340 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Remove, enable);
2341 else if (m_world.has(rel, Pair(OnDeleteTarget, Error)))
2342 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Error, enable);
2345 void try_set_OnDelete(EntityContainer& ec, Entity entity,
bool enable) {
2346 if (entity == Pair(OnDelete, Delete))
2347 set_flag(ec.flags, EntityContainerFlags::OnDelete_Delete, enable);
2348 else if (entity == Pair(OnDelete, Remove))
2349 set_flag(ec.flags, EntityContainerFlags::OnDelete_Remove, enable);
2350 else if (entity == Pair(OnDelete, Error))
2351 set_flag(ec.flags, EntityContainerFlags::OnDelete_Error, enable);
2354 void try_set_IsSingleton(EntityContainer& ec, Entity entity,
bool enable) {
2355 const bool isSingleton = enable && m_entity == entity;
2356 set_flag(ec.flags, EntityContainerFlags::IsSingleton, isSingleton);
2359 void handle_DependsOn(Entity entity,
bool enable) {
2401 template <
bool IsBootstrap>
2402 bool handle_add(Entity entity) {
2403 const auto mode = id_mode(entity);
2404#if GAIA_ASSERT_ENABLED
2405 if (mode != IdMode::Adjunct)
2406 World::verify_add(m_world, *m_pArchetype, m_entity, entity);
2410 if (has_id(mode, entity))
2413 if (entity.pair()) {
2414 auto relation = m_world.get(entity.id());
2415 m_world.touch_rel_version(relation);
2416 m_world.invalidate_queries_for_rel(relation);
2417 m_world.m_targetsTravCache = {};
2418 m_world.m_srcBfsTravCache = {};
2419 m_world.m_depthOrderCache = {};
2420 m_world.m_sourcesAllCache = {};
2421 m_world.m_targetsAllCache = {};
2424 try_set_flags(entity,
true);
2427 if (entity.pair() && entity.id() == Is.id()) {
2428 auto e = m_world.try_get(entity.gen());
2432 EntityLookupKey entityKey(m_entity);
2433 EntityLookupKey eKey(e);
2436 auto& entity_to_e = m_world.m_entityToAsTargets[entityKey];
2437 entity_to_e.insert(eKey);
2438 m_world.m_entityToAsTargetsTravCache = {};
2440 auto& e_to_entity = m_world.m_entityToAsRelations[eKey];
2441 e_to_entity.insert(entityKey);
2442 m_world.m_entityToAsRelationsTravCache = {};
2449 m_world.invalidate_queries_for_entity({Is, e});
2452 if (!add_id(mode, entity))
2455 if constexpr (!IsBootstrap) {
2456 handle_DependsOn(entity,
true);
2458#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2459 tl_new_comps.push_back(entity);
2466 void handle_del(Entity entity) {
2467 if (entity.pair() && !m_world.valid(entity)) {
2468 if (m_pArchetype->
has(entity)) {
2469 const auto relation = m_world.try_get(entity.id());
2470 if (relation != EntityBad) {
2471 m_world.touch_rel_version(relation);
2472 m_world.invalidate_queries_for_rel(relation);
2473 m_world.m_targetsTravCache = {};
2474 m_world.m_srcBfsTravCache = {};
2475 m_world.m_depthOrderCache = {};
2476 m_world.m_sourcesAllCache = {};
2477 m_world.m_targetsAllCache = {};
2480 if (entity.id() == Is.id())
2481 m_world.unlink_stale_is_relations_by_target_id(m_entity, entity.gen());
2483 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
2484 note_graph_edge(entity,
false);
2489 const auto mode = id_mode(entity);
2491 m_world.invalidate_scope_path_cache();
2493#if GAIA_ASSERT_ENABLED
2494 if (mode != IdMode::Adjunct)
2495 World::verify_del(m_world, *m_pArchetype, m_entity, entity);
2499 if (!has_id(mode, entity))
2502 if (entity.pair()) {
2503 auto relation = m_world.get(entity.id());
2504 m_world.touch_rel_version(relation);
2505 m_world.invalidate_queries_for_rel(relation);
2506 m_world.m_targetsTravCache = {};
2507 m_world.m_srcBfsTravCache = {};
2508 m_world.m_depthOrderCache = {};
2509 m_world.m_sourcesAllCache = {};
2510 m_world.m_targetsAllCache = {};
2513 try_set_flags(entity,
false);
2514 handle_DependsOn(entity,
false);
2517 if (entity.pair() && entity.id() == Is.id()) {
2518 auto e = m_world.try_get(entity.gen());
2519 if (e != EntityBad) {
2520 m_world.unlink_live_is_relation(m_entity, e);
2524 del_id(mode, entity);
2526#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2527 tl_del_comps.push_back(entity);
2531 void add_inter(Entity entity) {
2532 GAIA_ASSERT(!is_wildcard(entity));
2534 if (entity.pair()) {
2536 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
2539 if (!handle_add_entity(entity))
2542 handle_add<false>(entity);
2545 void note_graph_edge(Entity entity,
bool isAdd) {
2546 ++m_graphEdgeOpCount;
2547 m_graphEdgeEntity = entity;
2548 m_graphEdgeIsAdd = isAdd;
2551 void reset_graph_edge_tracking() {
2552 m_graphEdgeEntity = EntityBad;
2553 m_graphEdgeOpCount = 0;
2554 m_graphEdgeIsAdd =
false;
2559 static void rebuild_graph_edge(Archetype* pArchetypeLeft, Archetype* pArchetypeRight, Entity entity) {
2560 pArchetypeLeft->del_graph_edge_right_local(entity);
2561 pArchetypeRight->del_graph_edge_left_local(entity);
2562 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
2565 void add_inter_init(Entity entity) {
2566 GAIA_ASSERT(!is_wildcard(entity));
2568 if (entity.pair()) {
2570 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
2573 if (!handle_add_entity(entity))
2576 handle_add<true>(entity);
2579 GAIA_NODISCARD
bool can_del(Entity entity)
const noexcept {
2580 if (has_Requires_tgt(entity))
2586 GAIA_NODISCARD
bool is_sticky_component_trait(Entity entity)
const noexcept {
2587 return id_mode(entity) == IdMode::Sticky;
2590 bool del_inter(Entity entity) {
2591 if (is_sticky_component_trait(entity))
2594 if (!can_del(entity))
2601 void del_name_inter(EntityNameLookupKey key) {
2602 const auto it = m_world.m_nameToEntity.find(key);
2606 GAIA_ASSERT(it != m_world.m_nameToEntity.end());
2607 if (it != m_world.m_nameToEntity.end()) {
2609 if (it->first.owned())
2610 mem::mem_free((
void*)key.str());
2612 m_world.m_nameToEntity.erase(it);
2616 void del_alias_inter(EntityNameLookupKey key) {
2617 const auto it = m_world.m_aliasToEntity.find(key);
2621 GAIA_ASSERT(it != m_world.m_aliasToEntity.end());
2622 if (it != m_world.m_aliasToEntity.end()) {
2624 if (it->first.owned())
2625 mem::mem_free((
void*)key.str());
2627 m_world.m_aliasToEntity.erase(it);
2631 template <
bool IsOwned>
2632 void name_inter(
const char* name, uint32_t len) {
2634 GAIA_ASSERT(!m_entity.pair());
2635 if (m_entity.pair())
2639 if (name ==
nullptr) {
2640 GAIA_ASSERT(len == 0);
2645 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
2650 const bool hasInvalidCharacter = name[i] ==
'.';
2651 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity names");
2652 if (hasInvalidCharacter)
2656 EntityNameLookupKey key(
2657 name, len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len, IsOwned);
2661 auto it = m_world.m_nameToEntity.find(key);
2662 if (it == m_world.m_nameToEntity.end()) {
2664 if (m_targetNameKey.str() !=
nullptr) {
2665 del_name_inter(m_targetNameKey);
2667 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(EntityDesc));
2668 if (compIdx != BadIndex) {
2669 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->comp_ptr_mut(compIdx, m_rowSrc));
2670 GAIA_ASSERT(core::check_alignment(pDesc));
2671 if (pDesc->name !=
nullptr) {
2672 del_name_inter(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0));
2673 pDesc->name =
nullptr;
2677 add_inter(GAIA_ID(EntityDesc));
2682 it = m_world.m_nameToEntity.emplace(key, m_entity).first;
2684#if GAIA_ASSERT_ENABLED
2685 if (it->second != m_entity && World::s_enableUniqueNameDuplicateAssert)
2686 GAIA_ASSERT(
false &&
"Trying to set non-unique name for an entity");
2693 if constexpr (IsOwned) {
2695 char* entityStr = (
char*)mem::mem_alloc(key.len() + 1);
2696 memcpy((
void*)entityStr, (
const void*)name, key.len());
2697 entityStr[key.len()] = 0;
2699 m_targetNameKey = EntityNameLookupKey(entityStr, key.len(), 1, {key.hash()});
2706 m_targetNameKey = key;
2713 m_world.invalidate_scope_path_cache();
2716 template <
bool IsOwned>
2717 void alias_inter(
const char* alias, uint32_t len) {
2719 GAIA_ASSERT(!m_entity.pair());
2720 if (m_entity.pair())
2724 if (alias ==
nullptr) {
2725 GAIA_ASSERT(len == 0);
2730 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
2735 const bool hasInvalidCharacter = alias[i] ==
'.';
2736 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity aliases");
2737 if (hasInvalidCharacter)
2741 EntityNameLookupKey key(
2742 alias, len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len, IsOwned);
2744 auto it = m_world.m_aliasToEntity.find(key);
2745 if (it == m_world.m_aliasToEntity.end()) {
2747 if (m_targetAliasKey.str() !=
nullptr) {
2748 del_alias_inter(m_targetAliasKey);
2750 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(EntityDesc));
2751 if (compIdx != BadIndex) {
2752 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->comp_ptr_mut(compIdx, m_rowSrc));
2753 GAIA_ASSERT(core::check_alignment(pDesc));
2754 if (pDesc->alias !=
nullptr) {
2755 del_alias_inter(EntityNameLookupKey(pDesc->alias, pDesc->alias_len, 0));
2756 pDesc->alias =
nullptr;
2760 add_inter(GAIA_ID(EntityDesc));
2764 it = m_world.m_aliasToEntity.emplace(key, m_entity).first;
2766#if GAIA_ASSERT_ENABLED
2767 if (it->second != m_entity && World::s_enableUniqueNameDuplicateAssert)
2768 GAIA_ASSERT(
false &&
"Trying to set non-unique alias for an entity");
2775 if constexpr (IsOwned) {
2777 char* aliasStr = (
char*)mem::mem_alloc(key.len() + 1);
2778 memcpy((
void*)aliasStr, (
const void*)alias, key.len());
2779 aliasStr[key.len()] = 0;
2781 m_targetAliasKey = EntityNameLookupKey(aliasStr, key.len(), 1, {key.hash()});
2788 m_targetAliasKey = key;
2818 if (symbol ==
nullptr || symbol[0] == 0)
2821 const auto* pItem = comp_cache().symbol(symbol, len);
2822 return pItem !=
nullptr ? pItem->
entity : EntityBad;
2829 const auto* pItem = comp_cache().
find(component);
2830 return pItem !=
nullptr ? comp_cache().symbol_name(*pItem) :
util::str_view{};
2837 GAIA_NODISCARD
Entity path(
const char* path, uint32_t len = 0)
const {
2838 if (path ==
nullptr || path[0] == 0)
2841 const auto* pItem = comp_cache().path(path, len);
2842 return pItem !=
nullptr ? pItem->entity : EntityBad;
2849 const auto* pItem = comp_cache().
find(component);
2850 return pItem !=
nullptr ? comp_cache().path_name(*pItem) :
util::str_view{};
2858 bool path(
Entity component,
const char* path, uint32_t len = 0) {
2859 auto* pItem = comp_cache_mut().
find(component);
2860 return pItem !=
nullptr ? comp_cache_mut().path(*pItem, path, len) :
false;
2867 GAIA_NODISCARD
Entity alias(
const char* alias, uint32_t len = 0)
const {
2868 if (alias ==
nullptr || alias[0] == 0)
2871 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
2872 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
2874 return it != m_aliasToEntity.end() ? it->second : EntityBad;
2884 const auto& ec = m_recs.entities[entity.id()];
2885 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
2889 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
2890 GAIA_ASSERT(core::check_alignment(pDesc));
2891 return {pDesc->alias, pDesc->alias_len};
2900 if (!valid(entity) || entity.pair())
2903 const auto before = this->alias(entity);
2905 const auto after = this->alias(entity);
2906 if (alias ==
nullptr)
2907 return !before.empty() && after.empty();
2909 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
2919 if (!valid(entity) || entity.pair())
2922 const auto before = this->alias(entity);
2924 const auto after = this->alias(entity);
2925 if (alias ==
nullptr)
2926 return !before.empty() && after.empty();
2928 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
2937 const auto* pItem = comp_cache().
find(entity);
2938 if (pItem ==
nullptr)
2941 const auto aliasValue = alias(entity);
2942 if (!aliasValue.empty())
2945 const auto pathValue = path(entity);
2946 if (!pathValue.empty()) {
2947 const auto symbolEntity = symbol(pathValue.data(), pathValue.size());
2948 if (symbolEntity == EntityBad || symbolEntity == entity)
2952 return symbol(entity);
2958 void invalidate_scope_path_cache()
const {
2959 m_componentScopePathCache.clear();
2960 m_componentScopePathCacheEntity = EntityBad;
2961 m_componentScopePathCacheValid =
false;
2968 GAIA_NODISCARD
bool build_scope_path(Entity scope,
util::str& out)
const {
2970 if (!valid(scope) || scope.pair())
2975 while (curr != EntityBad) {
2976 const auto currName = name(curr);
2977 if (currName.empty()) {
2982 segments.push_back(currName);
2983 curr = target(curr, ChildOf);
2986 if (segments.empty())
2989 uint32_t totalLen = 0;
2990 for (
auto segment: segments)
2991 totalLen += segment.size();
2992 totalLen += (uint32_t)segments.size() - 1;
2995 for (uint32_t i = (uint32_t)segments.size(); i > 0; --i) {
2998 out.
append(segments[i - 1]);
3007 GAIA_NODISCARD
bool current_scope_path(util::str& out)
const {
3008 if (m_componentScope == EntityBad) {
3009 invalidate_scope_path_cache();
3014 if (m_componentScopePathCacheValid && m_componentScopePathCacheEntity == m_componentScope) {
3015 out.assign(m_componentScopePathCache.view());
3019 if (!build_scope_path(m_componentScope, out)) {
3020 invalidate_scope_path_cache();
3024 m_componentScopePathCache.assign(out.view());
3025 m_componentScopePathCacheEntity = m_componentScope;
3026 m_componentScopePathCacheValid =
true;
3030 GAIA_NODISCARD
const ComponentCacheItem*
3031 find_comp_scope_chain_inter(Entity scopeEntity,
const char* name, uint32_t len)
const {
3032 if (scopeEntity == EntityBad)
3035 util::str scopePath;
3036 if (!build_scope_path(scopeEntity, scopePath))
3039 util::str scopedName;
3040 scopedName.reserve(scopePath.size() + 1 + len);
3042 while (!scopePath.empty()) {
3044 scopedName.append(scopePath.view());
3045 scopedName.append(
'.');
3046 scopedName.append(name, len);
3048 if (
const auto* pItem = m_compCache.path(scopedName.data(), (uint32_t)scopedName.size()); pItem !=
nullptr)
3051 const auto parentSepIdx = scopePath.view().find_last_of(
'.');
3052 if (parentSepIdx == BadIndex)
3055 scopePath.assign(util::str_view(scopePath.data(), parentSepIdx));
3061 void add_comp_scope_chain_hits_inter(
3062 cnt::darray<Entity>& out, Entity scopeEntity,
const char* name, uint32_t len)
const {
3063 if (scopeEntity == EntityBad)
3066 util::str scopePath;
3067 if (!build_scope_path(scopeEntity, scopePath))
3070 util::str scopedName;
3071 scopedName.reserve(scopePath.size() + 1 + len);
3073 while (!scopePath.empty()) {
3075 scopedName.append(scopePath.view());
3076 scopedName.append(
'.');
3077 scopedName.append(name, len);
3079 if (
const auto* pItem = m_compCache.path(scopedName.data(), (uint32_t)scopedName.size()); pItem !=
nullptr)
3080 ComponentCache::push_unique_entity(out, pItem->entity);
3082 const auto parentSepIdx = scopePath.view().find_last_of(
'.');
3083 if (parentSepIdx == BadIndex)
3086 scopePath.assign(util::str_view(scopePath.data(), parentSepIdx));
3090 GAIA_NODISCARD
const ComponentCacheItem* find_comp_lookup_inter(
const char* name, uint32_t len)
const {
3091 if (
const auto* pItem = find_comp_scope_chain_inter(m_componentScope, name, len); pItem !=
nullptr)
3094 for (
const auto scopeEntity: m_componentLookupPath) {
3095 if (scopeEntity == m_componentScope)
3098 if (
const auto* pItem = find_comp_scope_chain_inter(scopeEntity, name, len); pItem !=
nullptr)
3105 void add_comp_lookup_hits_inter(cnt::darray<Entity>& out,
const char* name, uint32_t len)
const {
3106 add_comp_scope_chain_hits_inter(out, m_componentScope, name, len);
3107 for (
const auto scopeEntity: m_componentLookupPath) {
3108 if (scopeEntity == m_componentScope)
3111 add_comp_scope_chain_hits_inter(out, scopeEntity, name, len);
3115 GAIA_NODISCARD
const ComponentCacheItem*
3116 find_comp_exact_inter(
const char* name, uint32_t len,
bool isPath,
bool isSymbol)
const {
3117 if (
const auto* pItem = m_compCache.symbol(name, len); pItem !=
nullptr)
3121 if (
const auto* pItem = m_compCache.path(name, len); pItem !=
nullptr)
3124 if (
const auto* pItem = m_compCache.short_symbol(name, len); pItem !=
nullptr)
3132 void add_comp_exact_hits_inter(
3133 cnt::darray<Entity>& out,
const char* name, uint32_t len,
bool isPath,
bool isSymbol)
const {
3134 if (
const auto* pItem = m_compCache.symbol(name, len); pItem !=
nullptr)
3135 ComponentCache::push_unique_entity(out, pItem->entity);
3137 m_compCache.add_path_matches(out, util::str_view(name, len));
3139 if (out.empty() && !isPath && !isSymbol) {
3140 if (
const auto* pItem = m_compCache.short_symbol(name, len); pItem !=
nullptr)
3141 ComponentCache::push_unique_entity(out, pItem->entity);
3145 GAIA_NODISCARD
bool has_comp_lookup_ctx_inter() const noexcept {
3146 return m_componentScope != EntityBad || !m_componentLookupPath.empty();
3149 GAIA_NODISCARD
static bool is_unqualified_comp_name_inter(
const char* name, uint32_t len)
noexcept {
3150 return memchr(name,
'.', len) ==
nullptr && memchr(name,
':', len) ==
nullptr;
3153 GAIA_NODISCARD Entity pick_name_or_comp_inter(Entity namedEntity,
const ComponentCacheItem* pCompItem)
const {
3154 if (pCompItem ==
nullptr)
3157 if (namedEntity == EntityBad)
3158 return pCompItem->entity;
3160 return m_compCache.find(namedEntity) !=
nullptr ? pCompItem->entity : namedEntity;
3170 GAIA_NODISCARD
const ComponentCacheItem* resolve_component_name_inter(
const char* name, uint32_t len = 0)
const {
3171 GAIA_ASSERT(name !=
nullptr);
3173 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
3174 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
3175 const bool isPath = memchr(name,
'.', l) !=
nullptr;
3176 const bool isSymbol = memchr(name,
':', l) !=
nullptr;
3179 if (
const auto* pItem = m_compCache.path(name, l); pItem !=
nullptr)
3183 if (!isPath && !isSymbol) {
3184 if (
const auto* pItem = find_comp_lookup_inter(name, l); pItem !=
nullptr)
3188 if (
const auto* pItem = find_comp_exact_inter(name, l, isPath, isSymbol); pItem !=
nullptr)
3191 const auto aliasEntity = alias(name, l);
3192 return aliasEntity != EntityBad ? m_compCache.find(aliasEntity) :
nullptr;
3202 return entity.pair()
3203 ? valid_pair(entity)
3204 : valid_entity(entity);
3215 if (!valid_entity_id(
id))
3218 const auto& ec = m_recs.entities[id];
3219 return Entity(
id, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind);
3224 return valid_entity_id(
id) ? get(
id) : EntityBad;
3230 template <
typename T>
3232 return comp_cache().
get<T>().entity;
3238 template <
typename T>
3240#if GAIA_ECS_AUTO_COMPONENT_REGISTRATION
3243 return comp_cache().template get<T>();
3260 GAIA_NODISCARD
Entity add(EntityKind kind = EntityKind::EK_Gen) {
3261 return add(*m_pEntityArchetype,
true,
false, kind);
3268 const auto entity = add(kind);
3269 add(entity, Prefab);
3276 template <
typename Func = TFunc_Vo
id_With_Entity>
3277 void add_n(uint32_t count, Func func = func_void_with_entity) {
3278 add_entity_n(*m_pEntityArchetype, count, func);
3286 template <
typename Func = TFunc_Vo
id_With_Entity>
3287 void add_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
3288 auto& ec = m_recs.entities[entity.id()];
3290 GAIA_ASSERT(ec.pArchetype !=
nullptr);
3291 GAIA_ASSERT(ec.pChunk !=
nullptr);
3293 add_entity_n(*ec.pArchetype, count, func);
3299 template <
typename T>
3303 using CT = component_type_t<T>;
3304 using FT =
typename CT::TypeFull;
3305 constexpr auto kind = CT::Kind;
3307 const auto* pItem = comp_cache().
find<FT>();
3308 if (pItem !=
nullptr)
3311 const auto entity = add(*m_pCompArchetype,
false,
false, kind);
3313 (void)current_scope_path(scopePath);
3315 const auto& item = comp_cache_mut().
add<FT>(entity, scopePath.
view());
3316 finalize_component_registration(item,
false);
3317#if GAIA_ECS_AUTO_COMPONENT_FIELDS
3318 auto_populate_component_fields<FT>(comp_cache_mut().get(item.entity));
3331 GAIA_ASSERT(item.
name.
size() < ComponentCacheItem::MaxNameLength);
3333 if (
const auto* pItem = comp_cache().symbol(item.
name); pItem !=
nullptr)
3336 const auto entity = add(*m_pCompArchetype,
false,
false, kind);
3338 (void)current_scope_path(scopePath);
3339 const auto& itemInfo = comp_cache_mut().
add(entity, item, scopePath.
view());
3340 finalize_component_registration(itemInfo,
true);
3349#if GAIA_ASSERT_ENABLED
3350 if (!
object.
pair()) {
3351 const auto* pItem = comp_cache().
find(
object);
3352 if (pItem !=
nullptr && pItem->entity ==
object && is_out_of_line_component(
object))
3354 false,
"Out-of-line runtime components require an explicit typed value when added by entity id");
3374 template <
typename T>
3376 using FT =
typename component_type_t<T>::TypeFull;
3377 const auto& item = add<FT>();
3378 if constexpr (supports_out_of_line_component<FT>()) {
3379 const auto mode = out_of_line_mode(item.entity);
3380 if (mode != OutOfLineMode::None) {
3381 (void)sparse_component_store_mut<FT>(item.entity).add(entity);
3382 finish_out_of_line_add_inter(entity, item.entity, mode);
3397 template <
typename T>
3399 static_assert(core::is_raw_v<T>);
3401 if constexpr (supports_out_of_line_component<typename component_type_t<T>::TypeFull>()) {
3402 using FT =
typename component_type_t<T>::TypeFull;
3403 if (can_use_out_of_line_component<FT>(
object)) {
3404 const auto mode = out_of_line_mode(
object);
3405 if (mode != OutOfLineMode::None) {
3406 auto& data = sparse_component_store_mut<FT>(
object).add(entity);
3407 data = GAIA_FWD(value);
3408 finish_out_of_line_add_inter(entity,
object, mode);
3415#if GAIA_OBSERVERS_ENABLED
3417 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
3419 eb.add_inter_init(
object);
3422 const auto& ec = fetch(entity);
3424 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
3425 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset(
object, GAIA_FWD(value));
3426 notify_add_single(entity,
object);
3427#if GAIA_OBSERVERS_ENABLED
3428 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3438 template <typename T, typename U = typename actual_type_t<T>::Type>
3440 using FT =
typename component_type_t<T>::TypeFull;
3442 const auto& item = add<FT>();
3443 const auto mode = out_of_line_mode(item.entity);
3444 if (mode != OutOfLineMode::None) {
3445 auto& data = sparse_component_store_mut<FT>(item.entity).add(entity);
3446 data = GAIA_FWD(value);
3447 finish_out_of_line_add_inter(entity, item.entity, mode);
3454#if GAIA_OBSERVERS_ENABLED
3456 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
3460 builder.add_inter_init(
object);
3463 const auto& ec = m_recs.entities[entity.id()];
3465 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
3466 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset<T>(GAIA_FWD(value));
3467 notify_add_single(entity,
object);
3468#if GAIA_OBSERVERS_ENABLED
3469 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3476 return override_inter(entity,
object);
3487 template <
typename T>
3488 GAIA_NODISCARD
bool override(
Entity entity) {
3490 using FT =
typename component_type_t<T>::TypeFull;
3491 const auto& item = add<FT>();
3493 if constexpr (supports_out_of_line_component<FT>()) {
3494 if (out_of_line_mode(item.entity) != OutOfLineMode::None)
3495 return override_out_of_line_inter(entity, item.entity);
3498 return override_inter(entity, item.entity);
3503 template <
typename T>
3506 using FT =
typename component_type_t<T>::TypeFull;
3508 if constexpr (supports_out_of_line_component<FT>()) {
3509 if (can_use_out_of_line_component<FT>(
object))
3510 return override_out_of_line_inter(entity,
object);
3513 return override_inter(entity,
object);
3523 GAIA_ASSERT(!entity.pair());
3524 GAIA_ASSERT(valid(entity));
3530 for (uint32_t i = (uint32_t)ids.size() - 1; i != (uint32_t)-1; --i)
3546 GAIA_ASSERT(!srcEntity.pair());
3547 GAIA_ASSERT(valid(srcEntity));
3549 auto& ec = m_recs.entities[srcEntity.id()];
3550 GAIA_ASSERT(ec.pArchetype !=
nullptr);
3551 GAIA_ASSERT(ec.pChunk !=
nullptr);
3553 auto* pDstArchetype = ec.pArchetype;
3559 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
3561 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
3562 auto& ecDst = m_recs.entities[dstEntity.id()];
3563 Chunk::copy_foreign_entity_data(ec.pChunk, ec.row, ecDst.pChunk, ecDst.row);
3566 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
3567 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
3570 copy_all_sparse_entity_data(srcEntity, dstEntity);
3584 template <
typename Func = TFunc_Vo
id_With_Entity>
3585 void copy_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
3586 copy_n_inter(entity, count, func,
EntitySpan{});
3589#if GAIA_OBSERVERS_ENABLED
3598 GAIA_ASSERT(!srcEntity.pair());
3599 GAIA_ASSERT(valid(srcEntity));
3601 auto& ec = m_recs.entities[srcEntity.id()];
3602 GAIA_ASSERT(ec.pArchetype !=
nullptr);
3603 GAIA_ASSERT(ec.pChunk !=
nullptr);
3605 auto* pDstArchetype = ec.pArchetype;
3608 const bool hasEntityDesc = pDstArchetype->has<
EntityDesc>();
3610 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
3612 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
3613 const auto sparseIdCount = copied_non_frag_sparse_id_count(srcEntity);
3614 const auto addedIdCount = archetypeIdCount + sparseIdCount;
3615 auto* pAddedIds = addedIdCount != 0U ? (
Entity*)alloca(
sizeof(
Entity) * addedIdCount) : nullptr;
3616 write_archetype_ids(*pDstArchetype, pAddedIds);
3617 write_copied_non_frag_sparse_ids(srcEntity, pAddedIds + archetypeIdCount);
3618 #if GAIA_OBSERVERS_ENABLED
3619 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this,
EntitySpan{pAddedIds, addedIdCount});
3623 if (hasEntityDesc) {
3624 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
3625 auto& ecDst = m_recs.entities[dstEntity.id()];
3626 Chunk::copy_foreign_entity_data(ec.pChunk, ec.row, ecDst.pChunk, ecDst.row);
3629 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
3630 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
3633 (void)copy_all_sparse_entity_data(srcEntity, dstEntity);
3634 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{&dstEntity, 1});
3636 m_observers.on_add(*
this, *pDstArchetype, EntitySpan{pAddedIds, addedIdCount}, EntitySpan{&dstEntity, 1});
3637 #if GAIA_OBSERVERS_ENABLED
3638 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3653 template <
typename Func = TFunc_Vo
id_With_Entity>
3654 void copy_ext_n(Entity entity, uint32_t count, Func func = func_void_with_entity) {
3655 auto& ec = m_recs.entities[entity.id()];
3656 auto* pDstArchetype = ec.pArchetype;
3657 if (pDstArchetype->has<EntityDesc>())
3658 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
3660 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
3661 const auto sparseIdCount = copied_non_frag_sparse_id_count(entity);
3662 const auto addedIdCount = archetypeIdCount + sparseIdCount;
3663 auto* pAddedIds = addedIdCount != 0U ? (Entity*)alloca(
sizeof(Entity) * addedIdCount) : nullptr;
3664 write_archetype_ids(*pDstArchetype, pAddedIds);
3665 write_copied_non_frag_sparse_ids(entity, pAddedIds + archetypeIdCount);
3666 #if GAIA_OBSERVERS_ENABLED
3667 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{pAddedIds, addedIdCount});
3670 entity, count, func, EntitySpan{pAddedIds, addedIdCount}, EntityBad
3671 #if GAIA_OBSERVERS_ENABLED
3676 #if GAIA_OBSERVERS_ENABLED
3677 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3683 struct CopyIterGroupState {
3684 Archetype* pArchetype =
nullptr;
3685 Chunk* pChunk =
nullptr;
3686 uint16_t startRow = 0;
3690 struct PrefabInstantiatePlanNode {
3691 Entity prefab = EntityBad;
3693 Archetype* pDstArchetype =
nullptr;
3694 cnt::darray_ext<Entity, 16> copiedSparseIds;
3695 cnt::darray_ext<Entity, 16> addedIds;
3696 cnt::darray_ext<Entity, 16> addHookIds;
3699 template <
typename Func>
3700 void invoke_copy_batch_callback(
3701 Func& func, Archetype* pDstArchetype, Chunk* pDstChunk, uint32_t originalChunkSize, uint32_t toCreate) {
3702 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
3705 it.set_archetype(pDstArchetype);
3706 it.set_chunk(pDstChunk);
3707 it.set_range((uint16_t)originalChunkSize, (uint16_t)toCreate);
3710 auto entities = pDstChunk->entity_view();
3711 GAIA_FOR2(originalChunkSize, pDstChunk->size()) func(entities[i]);
3715 template <typename Func>
3716 void flush_copy_iter_group(Func& func, CopyIterGroupState& group) {
3717 if (group.count == 0)
3722 it.set_archetype(group.pArchetype);
3723 it.set_chunk(group.pChunk);
3724 it.set_range(group.startRow, group.count);
3729 template <
typename Func>
3730 void push_copy_iter_group(Func& func, CopyIterGroupState& group, Entity instance) {
3731 const auto& ec = fetch(instance);
3733 if (group.count != 0 && ec.pArchetype == group.pArchetype && ec.pChunk == group.pChunk &&
3734 ec.row == uint16_t(group.startRow + group.count)) {
3739 flush_copy_iter_group(func, group);
3740 group.pArchetype = ec.pArchetype;
3741 group.pChunk = ec.pChunk;
3742 group.startRow = ec.row;
3746 void prepare_parent_batch(Entity parentEntity) {
3747 GAIA_ASSERT(valid(parentEntity));
3749 const auto parentPair = Pair(Parent, parentEntity);
3750 assign_pair(parentPair, *m_pEntityArchetype);
3752 touch_rel_version(Parent);
3753 invalidate_queries_for_rel(Parent);
3754 m_targetsTravCache = {};
3755 m_srcBfsTravCache = {};
3756 m_depthOrderCache = {};
3757 m_sourcesAllCache = {};
3758 m_targetsAllCache = {};
3760 auto& ecParent = fetch(parentEntity);
3761 EntityBuilder::set_flag(ecParent.flags, EntityContainerFlags::OnDeleteTarget_Delete,
true);
3765 Entity parentEntity, Archetype& archetype, Chunk& chunk, uint32_t originalChunkSize, uint32_t toCreate) {
3766 GAIA_ASSERT(valid(parentEntity));
3771 auto entities = chunk.entity_view();
3772#if GAIA_OBSERVERS_ENABLED
3773 const Entity parentPair = Pair(Parent, parentEntity);
3774 auto addDiffCtx = m_observers.prepare_diff(
3775 *
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1},
3776 EntitySpan{entities.data() + originalChunkSize, toCreate});
3778 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
3779 exclusive_adjunct_set(entities[rowIdx], Parent, parentEntity);
3782#if GAIA_OBSERVERS_ENABLED
3784 *
this, archetype, EntitySpan{&parentPair, 1}, EntitySpan{entities.data() + originalChunkSize, toCreate});
3785 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3789 void parent_direct(Entity entity, Entity parentEntity) {
3790 GAIA_ASSERT(valid(entity));
3791 GAIA_ASSERT(valid(parentEntity));
3793 prepare_parent_batch(parentEntity);
3794#if GAIA_OBSERVERS_ENABLED
3795 const Entity parentPair = Pair(Parent, parentEntity);
3797 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
3799 exclusive_adjunct_set(entity, Parent, parentEntity);
3801#if GAIA_OBSERVERS_ENABLED
3802 const auto& ec = fetch(entity);
3803 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
3804 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
3808 void notify_add_single(Entity entity, Entity
object) {
3809#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3810 if GAIA_UNLIKELY (tearing_down())
3813 const auto& ec = fetch(entity);
3817 #if GAIA_ENABLE_ADD_DEL_HOOKS
3818 if (
object.comp()) {
3819 const auto& item = comp_cache().
get(
object);
3820 const auto& hooks = ComponentCache::hooks(item);
3821 if (hooks.func_add !=
nullptr)
3822 hooks.func_add(*
this, item, entity);
3826 #if GAIA_OBSERVERS_ENABLED
3827 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
3837 void notify_del_single(Entity entity, Entity
object) {
3838#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3839 if GAIA_UNLIKELY (tearing_down())
3842 const auto& ec = fetch(entity);
3846 #if GAIA_OBSERVERS_ENABLED
3847 m_observers.on_del(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
3850 #if GAIA_ENABLE_ADD_DEL_HOOKS
3851 if (
object.comp()) {
3852 const auto& item = comp_cache().
get(
object);
3853 const auto& hooks = ComponentCache::hooks(item);
3854 if (hooks.func_del !=
nullptr)
3855 hooks.func_del(*
this, item, entity);
3866 GAIA_NODISCARD
bool has_semantic_match_without_source(
3867 Entity entity, Entity
object, Entity excludedSource, cnt::set<EntityLookupKey>& visited)
const {
3868 const auto inserted = visited.insert(EntityLookupKey(entity));
3869 if (!inserted.second)
3872 if (entity != excludedSource && has_direct(entity,
object))
3875 const auto it = m_entityToAsTargets.find(EntityLookupKey(entity));
3876 if (it == m_entityToAsTargets.end())
3879 for (
const auto baseKey: it->second) {
3880 if (has_semantic_match_without_source(baseKey.entity(),
object, excludedSource, visited))
3887 void notify_inherited_del_dependents(Entity source, Entity
object) {
3888#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3889 const auto& descendants = as_relations_trav_cache(source);
3890 for (
const auto descendant: descendants) {
3891 if (descendant == source)
3893 if (has_direct(descendant,
object) || !has(descendant,
object))
3896 cnt::set<EntityLookupKey> visited;
3897 if (has_semantic_match_without_source(descendant,
object, source, visited))
3900 notify_del_single(descendant,
object);
3908 void notify_inherited_del_dependents(Entity source, EntitySpan removedObjects) {
3909 for (
const auto object: removedObjects)
3910 notify_inherited_del_dependents(source, object);
3913 template <
typename Func>
3915 Entity entity, uint32_t count, Func& func, EntitySpan addedIds, Entity parentInstance = EntityBad
3916#
if GAIA_OBSERVERS_ENABLED
3918 ObserverRegistry::DiffDispatchCtx* pAddDiffCtx =
nullptr
3921 GAIA_ASSERT(!entity.pair());
3922 GAIA_ASSERT(valid(entity));
3923 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
3928#if GAIA_OBSERVERS_ENABLED
3929 const bool useLocalAddDiff = !addedIds.empty() && pAddDiffCtx ==
nullptr;
3930 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
3931 if (useLocalAddDiff)
3932 addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
3935 auto& ec = m_recs.entities[entity.id()];
3937 GAIA_ASSERT(ec.pChunk !=
nullptr);
3938 GAIA_ASSERT(ec.pArchetype !=
nullptr);
3940 auto* pSrcChunk = ec.pChunk;
3941 auto* pDstArchetype = ec.pArchetype;
3942 const auto hasEntityDesc = pDstArchetype->has<EntityDesc>();
3944 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
3946 if (parentInstance != EntityBad)
3947 prepare_parent_batch(parentInstance);
3952 const auto srcRow = ec.row;
3954 EntityContainerCtx ctx{
true,
false, EntityKind::EK_Gen};
3956 uint32_t left = count;
3958 auto* pDstChunk = pDstArchetype->foc_free_chunk();
3959 const uint32_t originalChunkSize = pDstChunk->size();
3960 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
3961 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
3963 GAIA_FOR(toCreate) {
3964 const auto entityNew = m_recs.entities.alloc(&ctx);
3965 auto& ecNew = m_recs.entities[entityNew.id()];
3966 store_entity(ecNew, entityNew, pDstArchetype, pDstChunk);
3968#if GAIA_ASSERT_ENABLED
3969 GAIA_ASSERT(ecNew.pChunk == pDstChunk);
3970 auto entityExpected = pDstChunk->entity_view()[ecNew.row];
3971 GAIA_ASSERT(entityExpected == entityNew);
3974 if (hasEntityDesc) {
3975 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, ecNew.row);
3978 copy_all_sparse_entity_data(entity, entityNew);
3981 pDstArchetype->try_update_free_chunk_idx();
3983 if (!hasEntityDesc) {
3984 pDstChunk->call_gen_ctors(originalChunkSize, toCreate);
3987 GAIA_PROF_SCOPE(World::copy_n_entity_data);
3988 Chunk::copy_entity_data_n_same_chunk(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
3992 pDstChunk->update_versions();
3994#if GAIA_OBSERVERS_ENABLED
3995 if (!addedIds.empty()) {
3996 auto entities = pDstChunk->entity_view();
3997 if (pAddDiffCtx !=
nullptr)
3998 m_observers.add_diff_targets(
3999 *
this, *pAddDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
4000 else if (useLocalAddDiff)
4001 m_observers.add_diff_targets(
4002 *
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
4004 *
this, *pDstArchetype, addedIds, EntitySpan{entities.data() + originalChunkSize, toCreate});
4008 if (parentInstance != EntityBad)
4009 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
4011 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
4015#if GAIA_OBSERVERS_ENABLED
4016 if (useLocalAddDiff)
4017 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4021 GAIA_NODISCARD
bool id_uses_inherit_policy(Entity
id)
const {
4022 return !is_wildcard(
id) && valid(
id) && target(
id, OnInstantiate) == Inherit;
4025 GAIA_NODISCARD Entity inherited_id_owner(Entity entity, Entity
id)
const {
4026 if (!id_uses_inherit_policy(
id))
4029 const auto& targets = as_targets_trav_cache(entity);
4030 for (
const auto target: targets) {
4031 if (has_inter(target,
id,
false))
4042 GAIA_NODISCARD
bool has_direct_out_of_line_inter(Entity entity, Entity
object)
const {
4043 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
4044 return itSparseStore != m_sparseComponentsByComp.end() &&
4045 itSparseStore->second.func_has(itSparseStore->second.pStore, entity);
4054 GAIA_NODISCARD Entity id_owner_inter(Entity entity, Entity
object)
const {
4055 GAIA_ASSERT(valid(entity));
4056 GAIA_ASSERT(
object != EntityBad);
4057 GAIA_ASSERT(!is_wildcard(
object));
4059 const auto& ec = fetch(entity);
4063 if (
object.pair()) {
4064 if (has_exclusive_adjunct_pair(entity,
object) || ec.pArchetype->has(
object))
4067 if (has_direct_out_of_line_inter(entity,
object) || ec.pArchetype->has(
object))
4071 return inherited_id_owner(entity,
object);
4074 GAIA_NODISCARD
bool instantiate_copies_id(Entity
id)
const {
4075 const auto policy = target(
id, OnInstantiate);
4076 if (policy == EntityBad || policy == Override)
4078 if (policy == DontInherit || policy == Inherit)
4083 template <
typename T>
4084 void gather_sorted_prefab_children(Entity prefabEntity, T& outChildren) {
4085 sources(Parent, prefabEntity, [&](Entity childPrefab) {
4086 if (!has_direct(childPrefab, Prefab))
4088 outChildren.push_back(childPrefab);
4091 core::sort(outChildren, [](Entity left, Entity right) {
4092 return left.id() < right.id();
4096 GAIA_NODISCARD
bool copy_sparse_store_inter(
4097 Entity srcEntity, Entity dstEntity, Entity comp,
const SparseComponentStoreErased& store) {
4098 if (!copies_sparse_payload_inter(comp, srcEntity, store))
4101 GAIA_ASSERT(store.func_copy_entity !=
nullptr);
4102 return store.func_copy_entity(store.pStore, dstEntity, srcEntity);
4105 uint32_t copy_all_sparse_entity_data(Entity srcEntity, Entity dstEntity, Entity* pCopiedIds =
nullptr) {
4106 uint32_t copiedCnt = 0;
4107 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
4108 const auto comp = compKey.entity();
4109 if (!copy_sparse_store_inter(srcEntity, dstEntity, comp, store))
4112 if (pCopiedIds !=
nullptr)
4113 pCopiedIds[copiedCnt] = comp;
4120 uint32_t copy_sparse_entity_data(
4121 Entity srcEntity, Entity dstEntity, EntitySpan copiedSparseIds, Entity* pCopiedIds =
nullptr) {
4122 uint32_t copiedCnt = 0;
4123 for (
const auto comp: copiedSparseIds) {
4124 auto it = m_sparseComponentsByComp.find(EntityLookupKey(comp));
4125 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
4127 auto& store = it->second;
4128 if (!copy_sparse_store_inter(srcEntity, dstEntity, comp, store))
4131 if (pCopiedIds !=
nullptr)
4132 pCopiedIds[copiedCnt] = comp;
4139 void write_archetype_ids(
const Archetype& dstArchetype, Entity* pDst)
const {
4140 for (
const auto id: dstArchetype.ids_view())
4144 GAIA_NODISCARD uint32_t copied_non_frag_sparse_id_count(Entity srcEntity)
const {
4146 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
4147 const auto comp = compKey.entity();
4148 if (!copies_non_frag_sparse_payload_inter(comp, srcEntity, store))
4156 void write_copied_non_frag_sparse_ids(Entity srcEntity, Entity* pDst)
const {
4157 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
4158 const auto comp = compKey.entity();
4159 if (!copies_non_frag_sparse_payload_inter(comp, srcEntity, store))
4165 GAIA_NODISCARD
bool copy_sparse_payload_inter(Entity dstEntity, Entity srcEntity, Entity
object) {
4166 const auto mode = out_of_line_mode(
object);
4167 if (mode == OutOfLineMode::None)
4170 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
4171 if (itSparseStore == m_sparseComponentsByComp.end())
4174 return copy_sparse_store_inter(srcEntity, dstEntity,
object, itSparseStore->second);
4181 GAIA_NODISCARD
bool override_out_of_line_inter(Entity entity, Entity
object) {
4182 GAIA_ASSERT(valid(entity));
4183 GAIA_ASSERT(valid(
object));
4184 GAIA_ASSERT(out_of_line_mode(
object) != OutOfLineMode::None);
4186 if (has_direct(entity,
object))
4189 const auto inheritedOwner = inherited_id_owner(entity,
object);
4190 if (inheritedOwner == EntityBad)
4193 if (!copy_sparse_payload_inter(entity, inheritedOwner,
object))
4196 if (out_of_line_mode(
object) == OutOfLineMode::Fragmenting)
4197 make_sparse_copy_direct_inter(entity,
object);
4207 GAIA_NODISCARD
bool copy_owned_out_of_line_inter(Entity srcEntity, Entity dstEntity, Entity
object) {
4208 GAIA_ASSERT(valid(srcEntity));
4209 GAIA_ASSERT(valid(dstEntity));
4210 GAIA_ASSERT(valid(
object));
4211 GAIA_ASSERT(out_of_line_mode(
object) != OutOfLineMode::None);
4213 const auto mode = out_of_line_mode(
object);
4214 if (mode == OutOfLineMode::Fragmenting) {
4215 if (!copy_sparse_payload_inter(dstEntity, srcEntity,
object))
4218 make_sparse_copy_direct_inter(dstEntity,
object);
4219 notify_add_single(dstEntity,
object);
4222#if GAIA_OBSERVERS_ENABLED
4224 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&dstEntity, 1});
4226 if (!copy_sparse_payload_inter(dstEntity, srcEntity,
object))
4228 notify_add_single(dstEntity,
object);
4229#if GAIA_OBSERVERS_ENABLED
4230 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4235 void make_sparse_copy_direct_inter(Entity entity, Entity
object) {
4236 GAIA_ASSERT(out_of_line_mode(
object) == OutOfLineMode::Fragmenting);
4237 EntityBuilder eb(*
this, entity);
4238 eb.add_inter_init(
object);
4247 void copy_direct_component_data_inter(
4248 Entity srcEntity, Entity dstEntity, Entity
object,
const ComponentCacheItem& item) {
4249 GAIA_ASSERT(valid(srcEntity));
4250 GAIA_ASSERT(valid(dstEntity));
4251 GAIA_ASSERT(valid(
object));
4252 GAIA_ASSERT(item.entity ==
object);
4253 GAIA_ASSERT(item.comp.size() != 0U);
4255 const auto& ecDst = fetch(dstEntity);
4256 const auto& ecSrc = fetch(srcEntity);
4257 const auto compIdxDst = ecDst.pChunk->comp_idx(
object);
4258 const auto compIdxSrc = ecSrc.pChunk->comp_idx(
object);
4259 GAIA_ASSERT(compIdxDst != BadIndex && compIdxSrc != BadIndex);
4261 const auto idxDst = uint16_t(ecDst.row * (1U - (uint32_t)
object.kind()));
4262 const auto idxSrc = uint16_t(ecSrc.row * (1U - (uint32_t)
object.kind()));
4263 void* pDst = ecDst.pChunk->comp_ptr_mut(compIdxDst);
4264 const void* pSrc = ecSrc.pChunk->comp_ptr(compIdxSrc);
4265 item.copy(pDst, pSrc, idxDst, idxSrc, ecDst.pChunk->capacity(), ecSrc.pChunk->capacity());
4268 GAIA_NODISCARD
bool override_inter(Entity entity, Entity
object) {
4269 GAIA_ASSERT(valid(entity));
4270 GAIA_ASSERT(
object.pair() || valid(
object));
4272 if (has_direct(entity,
object))
4275 const auto inheritedOwner = inherited_id_owner(entity,
object);
4276 if (inheritedOwner == EntityBad)
4279 if (!
object.pair()) {
4280 const auto* pItem = comp_cache().
find(
object);
4281 if (pItem !=
nullptr && pItem->entity ==
object) {
4282 const auto mode = out_of_line_mode(
object);
4283 if (mode != OutOfLineMode::None)
4284 return override_out_of_line_inter(entity,
object);
4286 if (pItem->comp.size() != 0U) {
4287 add(entity,
object);
4288 copy_direct_component_data_inter(inheritedOwner, entity,
object, *pItem);
4294 add(entity,
object);
4298 GAIA_NODISCARD
bool copy_owned_id_from_entity(Entity srcEntity, Entity dstEntity, Entity
object) {
4299 GAIA_ASSERT(valid(srcEntity));
4300 GAIA_ASSERT(valid(dstEntity));
4301 GAIA_ASSERT(
object.pair() || valid(
object));
4303 if (has_direct(dstEntity,
object))
4306 if (!
object.pair()) {
4307 const auto* pItem = comp_cache().
find(
object);
4308 if (pItem !=
nullptr && pItem->entity ==
object) {
4309 if (out_of_line_mode(
object) != OutOfLineMode::None)
4310 return copy_owned_out_of_line_inter(srcEntity, dstEntity,
object);
4312 if (pItem->comp.size() != 0U) {
4313 EntityBuilder eb(*
this, dstEntity);
4314 eb.add_inter_init(
object);
4316 copy_direct_component_data_inter(srcEntity, dstEntity,
object, *pItem);
4317 notify_add_single(dstEntity,
object);
4323 add(dstEntity,
object);
4327 GAIA_NODISCARD Archetype* instantiate_prefab_dst_archetype(Entity prefabEntity) {
4328 GAIA_ASSERT(!prefabEntity.pair());
4329 GAIA_ASSERT(valid(prefabEntity));
4330 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
4332 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
4335 auto& ecSrc = m_recs.entities[prefabEntity.id()];
4336 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
4338 auto* pDstArchetype = ecSrc.pArchetype;
4339 if (pDstArchetype->has<EntityDesc>())
4340 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
4341 if (pDstArchetype->has(Prefab))
4342 pDstArchetype = foc_archetype_del(pDstArchetype, Prefab);
4344 for (
const auto id: ecSrc.pArchetype->ids_view()) {
4345 if (
id.pair() &&
id.
id() == Is.id()) {
4346 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
4350 if (!instantiate_copies_id(
id))
4351 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
4354 const auto isPair = Pair(Is, prefabEntity);
4355 assign_pair(isPair, *m_pEntityArchetype);
4356 pDstArchetype = foc_archetype_add(pDstArchetype, isPair);
4358 return pDstArchetype;
4361 template <
typename T>
4362 void collect_prefab_copied_sparse_ids(Entity prefabEntity, T& outCopiedSparseIds) {
4363 outCopiedSparseIds.clear();
4364 if (m_sparseComponentsByComp.empty())
4367 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
4368 const auto comp = compKey.entity();
4369 if (!instantiate_copies_id(comp) || !copies_sparse_payload_inter(comp, prefabEntity, store))
4371 outCopiedSparseIds.push_back(comp);
4375 template <
typename T>
4376 void collect_prefab_added_ids(Archetype* pDstArchetype, EntitySpan copiedSparseIds, T& outAddedIds) {
4377 outAddedIds.clear();
4378 for (
const auto id: pDstArchetype->ids_view())
4379 outAddedIds.push_back(id);
4381 for (
const auto comp: copiedSparseIds) {
4382 if (sparse_copy_adds_id_inter(comp))
4383 outAddedIds.push_back(comp);
4387 template <
typename T>
4388 void collect_prefab_add_hook_ids(EntitySpan addedIds, T& outHookIds) {
4390 for (
const auto id: addedIds) {
4394 const auto& item = comp_cache().
get(
id);
4395 if (ComponentCache::hooks(item).func_add !=
nullptr)
4396 outHookIds.push_back(
id);
4400 GAIA_NODISCARD Entity instantiate_prefab_node_inter(
4401 Entity prefabEntity, Archetype* pDstArchetype, Entity parentInstance, EntitySpan copiedSparseIds,
4402 EntitySpan addedIds, EntitySpan addHookIds) {
4403 GAIA_ASSERT(!prefabEntity.pair());
4404 GAIA_ASSERT(valid(prefabEntity));
4405 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
4406 GAIA_ASSERT(pDstArchetype !=
nullptr);
4407#if GAIA_OBSERVERS_ENABLED
4408 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
4411 auto& ecSrc = m_recs.entities[prefabEntity.id()];
4412 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
4413 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
4415 EntityContainerCtx ctx{
true,
false, prefabEntity.kind()};
4416 const auto instance = m_recs.entities.alloc(&ctx);
4417 auto& ecDst = m_recs.entities[instance.id()];
4418 auto* pDstChunk = pDstArchetype->foc_free_chunk();
4419 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
4420 pDstArchetype->try_update_free_chunk_idx();
4421 Chunk::copy_foreign_entity_data(ecSrc.pChunk, ecSrc.row, pDstChunk, ecDst.row);
4422 pDstChunk->update_versions();
4424 ecDst.flags |= EntityContainerFlags::HasAliasOf;
4429 (void)copy_sparse_entity_data(prefabEntity, instance, copiedSparseIds);
4430#if GAIA_OBSERVERS_ENABLED
4431 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{&instance, 1});
4434 touch_rel_version(Is);
4435 invalidate_queries_for_rel(Is);
4436 m_targetsTravCache = {};
4437 m_srcBfsTravCache = {};
4438 m_depthOrderCache = {};
4439 m_sourcesAllCache = {};
4440 m_targetsAllCache = {};
4442 const auto instanceKey = EntityLookupKey(instance);
4443 const auto prefabKey = EntityLookupKey(prefabEntity);
4444 m_entityToAsTargets[instanceKey].insert(prefabKey);
4445 m_entityToAsTargetsTravCache = {};
4446 m_entityToAsRelations[prefabKey].insert(instanceKey);
4447 m_entityToAsRelationsTravCache = {};
4448 invalidate_queries_for_entity({Is, prefabEntity});
4450#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
4451 if GAIA_UNLIKELY (tearing_down()) {
4452 (void)pDstArchetype;
4457 #if GAIA_ENABLE_ADD_DEL_HOOKS
4458 for (
const auto id: addHookIds) {
4459 const auto& item = comp_cache().
get(
id);
4460 const auto& hooks = ComponentCache::hooks(item);
4461 GAIA_ASSERT(hooks.func_add !=
nullptr);
4462 hooks.func_add(*
this, item, instance);
4466 #if GAIA_OBSERVERS_ENABLED
4467 m_observers.on_add(*
this, *pDstArchetype, addedIds, EntitySpan{&instance, 1});
4474#if GAIA_OBSERVERS_ENABLED
4475 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4478 if (parentInstance != EntityBad)
4479 parent_direct(instance, parentInstance);
4484 GAIA_NODISCARD Entity instantiate_prefab_node_inter(Entity prefabEntity, Entity parentInstance) {
4485 auto* pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
4486 cnt::darray_ext<Entity, 16> copiedSparseIds;
4487 cnt::darray_ext<Entity, 16> addedIds;
4488 cnt::darray_ext<Entity, 16> addHookIds;
4489 collect_prefab_copied_sparse_ids(prefabEntity, copiedSparseIds);
4490 collect_prefab_added_ids(pDstArchetype, EntitySpan{copiedSparseIds}, addedIds);
4491 collect_prefab_add_hook_ids(EntitySpan{addedIds}, addHookIds);
4492 return instantiate_prefab_node_inter(
4493 prefabEntity, pDstArchetype, parentInstance, EntitySpan{copiedSparseIds}, EntitySpan{addedIds},
4494 EntitySpan{addHookIds});
4497 template <
typename Func>
4498 void instantiate_prefab_n_inter(
4499 const PrefabInstantiatePlanNode& node, Entity parentInstance, uint32_t count, Func& func) {
4500 GAIA_ASSERT(node.prefab != EntityBad);
4501 GAIA_ASSERT(node.pDstArchetype !=
nullptr);
4505#if GAIA_OBSERVERS_ENABLED
4506 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{node.addedIds});
4509 auto& ecSrc = m_recs.entities[node.prefab.id()];
4510 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
4512 if (parentInstance != EntityBad)
4513 prepare_parent_batch(parentInstance);
4515 const auto srcRow = ecSrc.row;
4516 auto* pSrcChunk = ecSrc.pChunk;
4517 auto* pDstArchetype = node.pDstArchetype;
4518 const auto prefabKey = EntityLookupKey(node.prefab);
4519 EntityContainerCtx ctx{
true,
false, node.prefab.kind()};
4521 uint32_t left = count;
4523 auto* pDstChunk = pDstArchetype->foc_free_chunk();
4524 const uint32_t originalChunkSize = pDstChunk->size();
4525 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
4526 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
4528 GAIA_FOR_(toCreate, rowOffset) {
4529 const auto instance = m_recs.entities.alloc(&ctx);
4530 auto& ecDst = m_recs.entities[instance.id()];
4531 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
4532 ecDst.flags |= EntityContainerFlags::HasAliasOf;
4534 (void)copy_sparse_entity_data(node.prefab, instance, EntitySpan{node.copiedSparseIds});
4537 pDstArchetype->try_update_free_chunk_idx();
4538 Chunk::copy_foreign_entity_data_n(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
4539 pDstChunk->update_versions();
4541 touch_rel_version(Is);
4542 invalidate_queries_for_rel(Is);
4543 m_targetsTravCache = {};
4544 m_srcBfsTravCache = {};
4545 m_depthOrderCache = {};
4546 m_sourcesAllCache = {};
4547 m_targetsAllCache = {};
4549 auto entities = pDstChunk->entity_view();
4550 auto& asRelations = m_entityToAsRelations[prefabKey];
4551 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
4552 const auto instance = entities[rowIdx];
4553 m_entityToAsTargets[EntityLookupKey(instance)].insert(prefabKey);
4554 asRelations.insert(EntityLookupKey(instance));
4556 m_entityToAsTargetsTravCache = {};
4557 m_entityToAsRelationsTravCache = {};
4558 invalidate_queries_for_entity({Is, node.prefab});
4560#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
4561 if GAIA_UNLIKELY (tearing_down()) {
4563 (void)originalChunkSize;
4568 #if GAIA_ENABLE_ADD_DEL_HOOKS
4569 for (
const auto id: node.addHookIds) {
4570 const auto& item = comp_cache().
get(
id);
4571 const auto& hooks = ComponentCache::hooks(item);
4572 GAIA_ASSERT(hooks.func_add !=
nullptr);
4574 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
4575 hooks.func_add(*
this, item, entities[rowIdx]);
4580 #if GAIA_OBSERVERS_ENABLED
4581 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
4583 *
this, *pDstArchetype, EntitySpan{node.addedIds},
4584 EntitySpan{entities.data() + originalChunkSize, toCreate});
4591 if (parentInstance != EntityBad)
4592 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
4594 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
4598#if GAIA_OBSERVERS_ENABLED
4599 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4603 template <
typename T>
4604 void build_prefab_instantiate_plan(Entity prefabEntity, uint32_t parentIdx, T& plan) {
4605 PrefabInstantiatePlanNode node{};
4606 node.prefab = prefabEntity;
4607 node.parentIdx = parentIdx;
4608 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
4609 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
4610 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
4611 collect_prefab_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
4613 const auto nodeIdx = (uint32_t)plan.size();
4614 plan.push_back(GAIA_MOV(node));
4616 cnt::darray_ext<Entity, 16> prefabChildren;
4617 gather_sorted_prefab_children(prefabEntity, prefabChildren);
4619 for (
const auto childPrefab: prefabChildren)
4620 build_prefab_instantiate_plan(childPrefab, nodeIdx, plan);
4623 GAIA_NODISCARD
bool instance_has_prefab_child(Entity parentInstance, Entity childPrefab)
const {
4625 sources(Parent, parentInstance, [&](Entity child) {
4628 if (has_direct(child, Pair(Is, childPrefab)))
4634 uint32_t sync_prefab_instance(
4635 Entity prefabEntity, Entity instance,
const PrefabInstantiatePlanNode& node, EntitySpan prefabChildren) {
4636 uint32_t changes = 0;
4638 const auto isPair = Pair(Is, prefabEntity);
4639 for (
const auto id: node.pDstArchetype->ids_view()) {
4640 if (
id == isPair || has_direct(instance,
id))
4642 if (copy_owned_id_from_entity(prefabEntity, instance,
id))
4646 for (
const auto comp: node.copiedSparseIds) {
4647 if (has_direct(instance, comp))
4649 if (copy_owned_id_from_entity(prefabEntity, instance, comp))
4653 for (
const auto childPrefab: prefabChildren) {
4654 if (instance_has_prefab_child(instance, childPrefab))
4656 (void)instantiate(childPrefab, instance);
4663 uint32_t sync_prefab_inter(Entity prefabEntity, cnt::set<EntityLookupKey>& visited) {
4664 GAIA_ASSERT(!prefabEntity.pair());
4665 GAIA_ASSERT(valid(prefabEntity));
4667 if (!has_direct(prefabEntity, Prefab))
4670 const auto ins = visited.insert(EntityLookupKey(prefabEntity));
4674 PrefabInstantiatePlanNode node{};
4675 node.prefab = prefabEntity;
4676 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
4677 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
4678 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
4679 collect_prefab_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
4681 cnt::darray_ext<Entity, 16> prefabChildren;
4682 gather_sorted_prefab_children(prefabEntity, prefabChildren);
4684 uint32_t changes = 0;
4685 const auto& descendants = as_relations_trav_cache(prefabEntity);
4686 for (
const auto entity: descendants) {
4687 if (has_direct(entity, Prefab))
4689 changes += sync_prefab_instance(prefabEntity, entity, node, EntitySpan{prefabChildren});
4692 for (
const auto childPrefab: prefabChildren)
4693 changes += sync_prefab_inter(childPrefab, visited);
4699 GAIA_NODISCARD Entity instantiate_inter(Entity prefabEntity, Entity parentInstance) {
4700 const auto instance = instantiate_prefab_node_inter(prefabEntity, parentInstance);
4702 cnt::darray_ext<Entity, 16> prefabChildren;
4703 gather_sorted_prefab_children(prefabEntity, prefabChildren);
4705 for (
const auto childPrefab: prefabChildren)
4706 (void)instantiate_inter(childPrefab, instance);
4716 GAIA_ASSERT(!prefabEntity.pair());
4717 GAIA_ASSERT(valid(prefabEntity));
4719 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
4720 return copy(prefabEntity);
4722 return instantiate_inter(prefabEntity, EntityBad);
4730 GAIA_ASSERT(!prefabEntity.pair());
4731 GAIA_ASSERT(valid(prefabEntity));
4732 GAIA_ASSERT(valid(parentInstance));
4734 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
4735 const auto instance = copy(prefabEntity);
4736 parent_direct(instance, parentInstance);
4740 return instantiate_inter(prefabEntity, parentInstance);
4755 template <
typename Func = TFunc_Vo
id_With_Entity>
4757 instantiate_n(prefabEntity, EntityBad, count, func);
4762 instantiate_n(prefabEntity, parentInstance, count, func_void_with_entity);
4767 template <
typename Func>
4769 GAIA_ASSERT(!prefabEntity.pair());
4770 GAIA_ASSERT(valid(prefabEntity));
4771 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
4776 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
4777 if (parentInstance == EntityBad) {
4778 copy_n(prefabEntity, count, func);
4782 copy_n_inter(prefabEntity, count, func,
EntitySpan{}, parentInstance);
4789 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
4790 build_prefab_instantiate_plan(prefabEntity,
BadIndex, plan);
4791 if (plan.size() == 1) {
4792 instantiate_prefab_n_inter(plan[0], parentInstance, count, func);
4796 CopyIterGroupState group;
4798 roots.reserve(count);
4799 auto collectRoot = [&](
Entity instance) {
4800 roots.push_back(instance);
4802 instantiate_prefab_n_inter(plan[0], parentInstance, count, collectRoot);
4804 spawned.resize((uint32_t)plan.size());
4806 GAIA_FOR_(count, rootIdx) {
4807 spawned[0] = roots[rootIdx];
4808 GAIA_FOR2_(1, (uint32_t)plan.size(), planIdx) {
4809 const auto parent = spawned[plan[planIdx].parentIdx];
4810 spawned[planIdx] = instantiate_prefab_node_inter(
4811 plan[planIdx].prefab, plan[planIdx].pDstArchetype, parent,
EntitySpan{plan[planIdx].copiedSparseIds},
4815 push_copy_iter_group(func, group, roots[rootIdx]);
4818 flush_copy_iter_group(func, group);
4820 build_prefab_instantiate_plan(prefabEntity,
BadIndex, plan);
4821 if (plan.size() == 1) {
4822 instantiate_prefab_n_inter(plan[0], parentInstance, count, func);
4827 roots.reserve(count);
4828 auto collectRoot = [&](
Entity instance) {
4829 roots.push_back(instance);
4831 instantiate_prefab_n_inter(plan[0], parentInstance, count, collectRoot);
4833 spawned.resize((uint32_t)plan.size());
4835 GAIA_FOR_(count, rootIdx) {
4836 spawned[0] = roots[rootIdx];
4837 GAIA_FOR2_(1, (uint32_t)plan.size(), planIdx) {
4838 const auto parent = spawned[plan[planIdx].parentIdx];
4839 spawned[planIdx] = instantiate_prefab_node_inter(
4840 plan[planIdx].prefab, plan[planIdx].pDstArchetype, parent,
EntitySpan{plan[planIdx].copiedSparseIds},
4844 func(roots[rootIdx]);
4853 GAIA_ASSERT(!prefabEntity.pair());
4854 GAIA_ASSERT(valid(prefabEntity));
4857 return sync_prefab_inter(prefabEntity, visited);
4865 if (!entity.pair()) {
4867 del_inter(
Pair(entity, All));
4868 del_inter(
Pair(All, entity));
4879 if (!
object.
pair()) {
4880 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
4881 if (itSparseStore != m_sparseComponentsByComp.end()) {
4882 if (!is_non_fragmenting_out_of_line_component(
object)) {
4887 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
4890#if GAIA_OBSERVERS_ENABLED
4892 m_observers.prepare_diff(*
this, ObserverEvent::OnDel,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
4894 notify_inherited_del_dependents(entity,
object);
4895 notify_del_single(entity,
object);
4896 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
4897#if GAIA_OBSERVERS_ENABLED
4898 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
4920 template <
typename T>
4922 using CT = component_type_t<T>;
4923 using FT =
typename CT::TypeFull;
4925 if constexpr (supports_out_of_line_component<FT>()) {
4926 if (
const auto* pItem = comp_cache().template find<FT>();
4927 pItem !=
nullptr && out_of_line_mode(pItem->entity) != OutOfLineMode::None) {
4928 if (sparse_component_store<FT>(pItem->entity) !=
nullptr)
4929 del(entity, pItem->entity);
4942 add(entity,
Pair(Is, entityBase));
4950 return is_inter<false>(entity, entityBase);
4960 return is_inter<true>(entity, entityBase);
4963 GAIA_NODISCARD
bool is_base(
Entity target)
const {
4964 GAIA_ASSERT(valid_entity(target));
4971 return it != m_entityToAsRelations.end();
4978 add(entity,
Pair(ChildOf, parent));
4986 return has(entity,
Pair(ChildOf, parent));
4991 add(entity,
Pair(Parent, parentEntity));
4999 return has(entity,
Pair(Parent, parentEntity));
5012#if GAIA_ENABLE_HOOKS
5014 bool TriggerSetEffects
5018 GAIA_ASSERT(valid(entity));
5020 if constexpr (supports_out_of_line_component<T>()) {
5021 const auto* pItem = comp_cache().template find<T>();
5022 if (pItem !=
nullptr && out_of_line_mode(pItem->entity) != OutOfLineMode::None) {
5023#if GAIA_ASSERT_ENABLED
5024 auto* pStore = sparse_component_store<typename component_type_t<T>::TypeFull>(pItem->entity);
5025 GAIA_ASSERT(pStore !=
nullptr);
5026 GAIA_ASSERT(has_direct_out_of_line_inter(entity, pItem->entity));
5029 ::gaia::ecs::update_version(m_worldVersion);
5031#if GAIA_OBSERVERS_ENABLED
5032 if constexpr (TriggerSetEffects)
5033 world_notify_on_set_entity(*
this, pItem->entity, entity);
5039 auto& ec = m_recs.entities[entity.id()];
5040 ec.pChunk->template modify<
5042#if GAIA_ENABLE_HOOKS
5048#if GAIA_OBSERVERS_ENABLED
5049 if constexpr (TriggerSetEffects) {
5052 const auto rel = comp_cache().template get<typename T::rel>().entity;
5053 const auto tgt = comp_cache().template get<typename T::tgt>().entity;
5056 term = comp_cache().template get<T>().entity;
5058 world_notify_on_set(*
this, term, *ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
5072#if GAIA_ENABLE_HOOKS
5074 bool TriggerSetEffects
5078 GAIA_ASSERT(valid(entity));
5079 GAIA_ASSERT(valid(
object));
5081 using FT =
typename component_type_t<T>::TypeFull;
5082 if constexpr (supports_out_of_line_component<FT>()) {
5083 if (can_use_out_of_line_component<FT>(
object)) {
5084#if GAIA_ASSERT_ENABLED
5085 auto* pStore = sparse_component_store<FT>(
object);
5086 GAIA_ASSERT(pStore !=
nullptr);
5087 GAIA_ASSERT(has_direct_out_of_line_inter(entity,
object));
5090 ::gaia::ecs::update_version(m_worldVersion);
5092#if GAIA_OBSERVERS_ENABLED
5093 if constexpr (TriggerSetEffects)
5094 world_notify_on_set_entity(*
this,
object, entity);
5100 auto& ec = m_recs.entities[entity.id()];
5101 const auto compIdx = ec.pChunk->comp_idx(
object);
5102 GAIA_ASSERT(compIdx != ComponentIndexBad);
5104 if constexpr (TriggerSetEffects)
5105 ec.pChunk->finish_write(compIdx, ec.row, (uint16_t)(ec.row + 1));
5107 ec.pChunk->update_world_version(compIdx);
5118 GAIA_ASSERT(valid(entity));
5120 const auto& ec = m_recs.entities[entity.id()];
5134 template <
typename T>
5137 using FT =
typename component_type_t<T>::TypeFull;
5138 using ValueType =
typename actual_type_t<T>::Type;
5139 const auto& item = add<FT>();
5140 return SetWriteProxyTyped<T, ValueType>{*
this, entity, item.entity, get<T>(entity)};
5154 template <
typename T>
5157 return SetWriteProxyObject<typename actual_type_t<T>::Type>{*
this, entity, object, get<T>(entity,
object)};
5167 template <
typename T>
5170 using FT =
typename component_type_t<T>::TypeFull;
5171 const auto& item = add<FT>();
5172 if constexpr (supports_out_of_line_component<FT>()) {
5173 if (out_of_line_mode(item.entity) != OutOfLineMode::None)
5174 return sparse_component_store_mut<FT>(item.entity).mut(entity);
5176 return acc_mut(entity).smut<T>();
5181 template <
typename T>
5184 using FT =
typename component_type_t<T>::TypeFull;
5185 if constexpr (supports_out_of_line_component<FT>()) {
5186 if (can_use_out_of_line_component<FT>(
object))
5187 return sparse_component_store_mut<FT>(
object).mut(entity);
5189 return acc_mut(entity).smut<T>(object);
5201 template <
typename T>
5204 return sset<T>(entity);
5209 template <
typename T>
5212 return sset<T>(entity,
object);
5221 if (component == EntityBad || component.pair() || !valid(entity))
5224 const auto* pItem = comp_cache().
find(component);
5225 if (pItem ==
nullptr || !raw_component_supported(*pItem))
5228 const auto owner = id_owner_inter(entity, component);
5229 if (owner == EntityBad)
5232 const auto& ec = fetch(owner);
5233 const auto compIdx = ec.pChunk->comp_idx(component);
5234 if (compIdx == ComponentIndexBad)
5237 const auto size = pItem->comp.size();
5239 return {
nullptr, 0, ComponentRawViewFlag_Valid};
5241 const auto row = uint32_t(ec.row * (1U - (uint32_t)component.kind()));
5242 return {ec.pChunk->comp_ptr(compIdx, row), size, ComponentRawViewFlag_Valid};
5251 if (component == EntityBad || component.pair() || !valid(entity))
5254 const auto* pItem = comp_cache().
find(component);
5255 if (pItem ==
nullptr || !raw_component_supported(*pItem))
5258 const auto& ec = fetch(entity);
5262 const auto compIdx = ec.pChunk->comp_idx(component);
5263 if (compIdx == ComponentIndexBad)
5266 const auto size = pItem->comp.size();
5268 return {
nullptr, 0, ComponentRawViewFlag_Valid};
5270 const auto row = uint32_t(ec.row * (1U - (uint32_t)component.kind()));
5271 return {ec.pChunk->comp_ptr_mut(compIdx, row), size, ComponentRawViewFlag_Valid};
5296 if (component == EntityBad || component.pair() || !valid(entity))
5299 const auto* pItem = comp_cache().
find(component);
5300 if (pItem ==
nullptr || !raw_component_payload_args_valid(*pItem, data, size))
5302 if (has_direct(entity, component))
5306#if GAIA_OBSERVERS_ENABLED
5308 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&component, 1},
EntitySpan{&entity, 1});
5310 eb.add_inter_init(component);
5313 const auto payload = mut_raw(entity, component);
5314 GAIA_ASSERT(payload.valid());
5315 if (payload.valid() && size != 0)
5316 memcpy(payload.data, data, size);
5318 notify_add_single(entity, component);
5319#if GAIA_OBSERVERS_ENABLED
5320 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5322 return payload.valid();
5332 if (component == EntityBad || component.pair())
5335 const auto* pItem = comp_cache().
find(component);
5336 if (pItem ==
nullptr || !raw_component_payload_args_valid(*pItem, data, size))
5339 const auto payload = mut_raw(entity, component);
5340 if (!payload.valid())
5343 memcpy(payload.data, data, size);
5345 modify_raw(entity, component);
5353 if (!mut_raw(entity, component).valid())
5355 finish_write(entity, component);
5366 GAIA_ASSERT(valid(entity));
5368 const auto& ec = m_recs.entities[entity.id()];
5379 template <
typename T>
5381 using FT =
typename component_type_t<T>::TypeFull;
5382 const auto compEntity = [&]() {
5384 const auto rel = comp_cache().template get<typename FT::rel>().entity;
5385 const auto tgt = comp_cache().template get<typename FT::tgt>().entity;
5388 return comp_cache().template get<FT>().entity;
5391 if constexpr (supports_out_of_line_component<FT>()) {
5392 const auto* pItem = comp_cache().template find<FT>();
5393 if (pItem !=
nullptr && out_of_line_mode(pItem->entity) != OutOfLineMode::None) {
5394 const auto* pStore = sparse_component_store<FT>(pItem->entity);
5395 GAIA_ASSERT(pStore !=
nullptr);
5396 const auto owner = id_owner_inter(entity, compEntity);
5397 GAIA_ASSERT(owner != EntityBad);
5398 return pStore->get(owner);
5402 const auto owner = id_owner_inter(entity, compEntity);
5403 GAIA_ASSERT(owner != EntityBad);
5404 return acc(owner).template get<T>();
5408 template <
typename T>
5410 using FT =
typename component_type_t<T>::TypeFull;
5411 if constexpr (supports_out_of_line_component<FT>()) {
5412 if (can_use_out_of_line_component<FT>(
object)) {
5413 const auto* pStore = sparse_component_store<FT>(
object);
5414 GAIA_ASSERT(pStore !=
nullptr);
5415 const auto owner = id_owner_inter(entity,
object);
5416 GAIA_ASSERT(owner != EntityBad);
5417 return pStore->get(owner);
5421 const auto owner = id_owner_inter(entity,
object);
5422 GAIA_ASSERT(owner != EntityBad);
5423 return acc(owner).template get<T>(
object);
5433 if (entity.pair()) {
5434 if (entity ==
Pair(All, All))
5437 if (is_wildcard(entity)) {
5442 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
5448 if (it == m_recs.pairs.end())
5451 const auto& ec = it->second;
5455#if GAIA_ASSERT_ENABLED
5457 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
5460 auto* pChunk = ec.pChunk;
5461 GAIA_ASSERT(pChunk !=
nullptr && ec.row < pChunk->size());
5470 if (entity.id() >= m_recs.entities.size() || !m_recs.entities.has(entity.id()))
5474 const auto& ec = m_recs.entities[entity.id()];
5478 auto* pChunk = ec.pChunk;
5479 return pChunk !=
nullptr && ec.row < pChunk->size();
5497 return has_inter(entity,
object,
true);
5505 return has_inter(entity,
object,
false);
5510 return has_inter(entity, (
Entity)
pair,
false);
5514 GAIA_NODISCARD
bool has_inter(
Entity entity,
Entity object,
bool allowSemanticIs)
const {
5515 const auto& ec = fetch(entity);
5519 if (
object.
pair() && has_exclusive_adjunct_pair(entity,
object))
5521 if (!
object.
pair()) {
5522 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
5523 if (itSparseStore != m_sparseComponentsByComp.end())
5524 return has_direct_out_of_line_inter(entity,
object) ||
5525 (allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad);
5528 const auto* pArchetype = ec.pArchetype;
5530 if (
object.pair()) {
5531 if (allowSemanticIs &&
object.
id() == Is.id() && !is_wildcard(
object.gen())) {
5532 const auto target = get(
object.gen());
5533 return valid(target) && is(entity, target);
5537 if (pArchetype->pairs() == 0)
5540 EntityId rel =
object.id();
5541 EntityId tgt =
object.gen();
5544 if (rel == All.id() && tgt == All.id())
5548 if (rel != All.id() && tgt == All.id()) {
5549 auto ids = pArchetype->ids_view();
5550 for (
auto id: ids) {
5561 if (rel == All.id() && tgt != All.id()) {
5562 auto ids = pArchetype->ids_view();
5563 for (
auto id: ids) {
5566 if (
id.gen() == tgt)
5574 if (pArchetype->has(
object))
5577 return allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad;
5585 return {m_componentLookupPath.data(), m_componentLookupPath.size()};
5592 m_componentLookupPath.clear();
5593 m_componentLookupPath.reserve((uint32_t)scopes.size());
5594 for (
const auto scopeEntity: scopes) {
5595 GAIA_ASSERT(scopeEntity != EntityBad && valid(scopeEntity) && !scopeEntity.pair());
5596 if (scopeEntity == EntityBad || !valid(scopeEntity) || scopeEntity.pair())
5599 m_componentLookupPath.push_back(scopeEntity);
5606 return m_componentScope;
5614 GAIA_ASSERT(scope == EntityBad || (valid(scope) && !scope.pair()));
5615 const auto prev = m_componentScope;
5616 if (scope == EntityBad || (valid(scope) && !scope.pair())) {
5617 m_componentScope = scope;
5618 invalidate_scope_path_cache();
5628 template <
typename Func>
5630 struct ComponentScopeRestore final {
5633 ~ComponentScopeRestore() {
5634 world.scope(prevScope);
5638 ComponentScopeRestore restore{*
this, scope(scopeEntity)};
5649 if (path ==
nullptr || path[0] == 0)
5652 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(path, ComponentCacheItem::MaxNameLength) : len;
5653 if (l == 0 || l >= ComponentCacheItem::MaxNameLength)
5655 if (path[l - 1] ==
'.')
5658 Entity parent = EntityBad;
5659 uint32_t partBeg = 0;
5660 while (partBeg < l) {
5661 uint32_t partEnd = partBeg;
5662 while (partEnd < l && path[partEnd] !=
'.')
5664 if (partEnd == partBeg)
5667 const auto partLen = partEnd - partBeg;
5669 const auto it = m_nameToEntity.find(key);
5672 if (it != m_nameToEntity.end()) {
5674 if (parent != EntityBad && !
static_cast<const World&
>(*this).
child(curr, parent)) {
5675 GAIA_ASSERT2(
false,
"Module path collides with an existing entity name outside the requested scope");
5680 name(curr, path + partBeg, partLen);
5681 if (parent != EntityBad)
5682 child(curr, parent);
5686 partBeg = partEnd + 1;
5708 template <
typename T>
5710 GAIA_ASSERT(valid(entity));
5712 using FT =
typename component_type_t<T>::TypeFull;
5713 const auto compEntity = [&]() {
5715 const auto* pRel = comp_cache().template find<typename FT::rel>();
5716 const auto* pTgt = comp_cache().template find<typename FT::tgt>();
5717 if (pRel ==
nullptr || pTgt ==
nullptr)
5720 const auto rel = pRel->entity;
5721 const auto tgt = pTgt->entity;
5724 const auto* pItem = comp_cache().template find<FT>();
5725 return pItem !=
nullptr ? pItem->entity : EntityBad;
5728 if (compEntity == EntityBad)
5731 if constexpr (supports_out_of_line_component<FT>()) {
5732 const auto* pItem = comp_cache().template find<FT>();
5733 if (pItem !=
nullptr && out_of_line_mode(pItem->entity) != OutOfLineMode::None) {
5734 return id_owner_inter(entity, compEntity) != EntityBad;
5738 return id_owner_inter(entity, compEntity) != EntityBad;
5781 const auto& ec = m_recs.entities[entity.id()];
5782 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
5786 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
5787 GAIA_ASSERT(core::check_alignment(pDesc));
5788 return {pDesc->name, pDesc->name_len};
5796 auto entity = get(entityId);
5797 return name(entity);
5810 if (name ==
nullptr || name[0] == 0)
5813 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
5814 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
5816 if (memchr(name,
'.', l) !=
nullptr) {
5817 const auto entity = get_entity_inter(name, l);
5818 if (entity != EntityBad)
5822 return get_inter(name, l);
5832 if (name ==
nullptr || name[0] == 0)
5835 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
5836 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
5838 auto push_unique = [&](
Entity entity) {
5839 if (entity == EntityBad)
5841 for (
const auto existing: out) {
5842 if (existing == entity)
5845 out.push_back(entity);
5848 push_unique(get_entity_inter(name, l));
5850 if (memchr(name,
'.', l) ==
nullptr && memchr(name,
':', l) ==
nullptr)
5851 add_comp_lookup_hits_inter(out, name, l);
5853 const bool isPath = memchr(name,
'.', l) !=
nullptr;
5854 const bool isSymbol = memchr(name,
':', l) !=
nullptr;
5855 add_comp_exact_hits_inter(out, name, l, isPath, isSymbol);
5857 push_unique(alias(name, l));
5865 GAIA_NODISCARD
Entity get(
const char* name, uint32_t len = 0)
const {
5866 return resolve(name, len);
5870 GAIA_NODISCARD
Entity find_named_entity_inter(
const char* name, uint32_t len = 0)
const {
5871 if (name ==
nullptr || name[0] == 0)
5874 const auto key = EntityNameLookupKey(name, len, 0);
5875 const auto it = m_nameToEntity.find(key);
5876 return it != m_nameToEntity.end() ? it->second : EntityBad;
5879 GAIA_NODISCARD Entity get_entity_inter(
const char* name, uint32_t len = 0)
const {
5880 if (name ==
nullptr || name[0] == 0)
5884 while (name[len] !=
'\0')
5888 Entity parent = EntityBad;
5889 Entity child = EntityBad;
5890 uint32_t posDot = 0;
5893 posDot = core::get_index(str,
'.');
5894 if (posDot == BadIndex)
5895 return find_named_entity_inter(str.data(), (uint32_t)str.size());
5900 parent = find_named_entity_inter(str.data(), posDot);
5901 if (parent == EntityBad)
5904 str = str.subspan(posDot + 1);
5905 while (!str.empty()) {
5906 posDot = core::get_index(str,
'.');
5908 if (posDot == BadIndex) {
5909 child = find_named_entity_inter(str.data(), (uint32_t)str.size());
5910 if (child == EntityBad || !this->child(child, parent))
5919 child = find_named_entity_inter(str.data(), posDot);
5920 if (child == EntityBad || !this->child(child, parent))
5924 str = str.subspan(posDot + 1);
5930 GAIA_NODISCARD Entity get_inter(
const char* name, uint32_t len = 0)
const {
5931 if (name ==
nullptr || name[0] == 0)
5934 auto key = EntityNameLookupKey(name, len, 0);
5935 const auto l = key.len();
5936 const bool isUnqualifiedCompName = is_unqualified_comp_name_inter(name, l);
5937 const auto namedEntity = find_named_entity_inter(name, l);
5939 if (has_comp_lookup_ctx_inter() && isUnqualifiedCompName) {
5940 if (
const auto* pScopedItem = resolve_component_name_inter(name, l); pScopedItem !=
nullptr)
5941 return pick_name_or_comp_inter(namedEntity, pScopedItem);
5944 if (namedEntity != EntityBad)
5947 if (
const auto* pItem = resolve_component_name_inter(name, l); pItem !=
nullptr)
5948 return pItem->entity;
5950 const auto aliasEntity = alias(name, l);
5951 if (aliasEntity != EntityBad)
5966 if (it == m_tgtToRel.end())
5978 GAIA_ASSERT(valid(entity));
5982 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
5983 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
5984 for (
auto relKey: itAdjunctRels->second) {
5985 const auto relation = relKey;
5986 const auto* pStore = exclusive_adjunct_store(relation);
5987 if (pStore ==
nullptr)
5990 if (exclusive_adjunct_target(*pStore, entity) == target)
5995 const auto& ec = fetch(entity);
5996 const auto* pArchetype = ec.pArchetype;
5999 if (pArchetype->pairs() == 0)
6002 const auto indices = pArchetype->pair_tgt_indices(target);
6003 if (indices.empty())
6006 const auto ids = pArchetype->ids_view();
6007 const auto e = ids[indices[0]];
6008 const auto& ecRel = m_recs.entities[e.id()];
6009 return *ecRel.pEntity;
6017 template <
typename Func>
6019 GAIA_ASSERT(valid(entity));
6023 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
6024 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
6025 for (
auto relKey: itAdjunctRels->second) {
6026 const auto relation = relKey;
6027 const auto* pStore = exclusive_adjunct_store(relation);
6028 if (pStore ==
nullptr)
6031 if (exclusive_adjunct_target(*pStore, entity) == target)
6036 const auto& ec = fetch(entity);
6037 const auto* pArchetype = ec.pArchetype;
6040 if (pArchetype->pairs() == 0)
6043 const auto ids = pArchetype->ids_view();
6044 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
6045 const auto e = ids[idsIdx];
6047 const auto& ecRel = m_recs.entities[e.id()];
6048 auto relation = *ecRel.pEntity;
6059 template <
typename Func>
6061 GAIA_ASSERT(valid(entity));
6065 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
6066 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
6067 for (
auto relKey: itAdjunctRels->second) {
6068 const auto relation = relKey;
6069 const auto* pStore = exclusive_adjunct_store(relation);
6070 if (pStore ==
nullptr)
6073 if (exclusive_adjunct_target(*pStore, entity) == target) {
6074 if (!func(relation))
6080 const auto& ec = fetch(entity);
6081 const auto* pArchetype = ec.pArchetype;
6084 if (pArchetype->pairs() == 0)
6087 const auto ids = pArchetype->ids_view();
6088 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
6089 const auto e = ids[idsIdx];
6091 const auto& ecRel = m_recs.entities[e.id()];
6092 auto relation = *ecRel.pEntity;
6093 if (!func(relation))
6102 const auto itCache = m_entityToAsRelationsTravCache.find(key);
6103 if (itCache != m_entityToAsRelationsTravCache.end())
6104 return itCache->second;
6106 auto& cache = m_entityToAsRelationsTravCache[key];
6107 const auto it = m_entityToAsRelations.find(key);
6108 if (it == m_entityToAsRelations.end())
6113 stack.reserve((uint32_t)it->second.size());
6114 for (
auto relation: it->second)
6115 stack.push_back(relation);
6117 while (!stack.empty()) {
6118 const auto relationKey = stack.back();
6121 const auto relation = relationKey.entity();
6122 cache.push_back(relation);
6124 const auto itChild = m_entityToAsRelations.find(relationKey);
6125 if (itChild == m_entityToAsRelations.end())
6128 for (
auto childRelation: itChild->second)
6129 stack.push_back(childRelation);
6139 const auto itCache = m_entityToAsTargetsTravCache.find(key);
6140 if (itCache != m_entityToAsTargetsTravCache.end())
6141 return itCache->second;
6143 auto& cache = m_entityToAsTargetsTravCache[key];
6144 const auto it = m_entityToAsTargets.find(key);
6145 if (it == m_entityToAsTargets.end())
6150 stack.reserve((uint32_t)it->second.size());
6151 for (
auto target: it->second)
6152 stack.push_back(target);
6154 while (!stack.empty()) {
6155 const auto targetKey = stack.back();
6158 const auto target = targetKey.entity();
6159 cache.push_back(target);
6161 const auto itChild = m_entityToAsTargets.find(targetKey);
6162 if (itChild == m_entityToAsTargets.end())
6165 for (
auto childTarget: itChild->second)
6166 stack.push_back(childTarget);
6176 const auto itCache = m_targetsTravCache.find(key);
6177 if (itCache != m_targetsTravCache.end())
6178 return itCache->second;
6180 auto& cache = m_targetsTravCache[key];
6181 if (!valid(relation) || !valid(source))
6185 GAIA_FOR(MAX_TRAV_DEPTH) {
6186 const auto next = target(curr, relation);
6187 if (next == EntityBad || next == curr)
6190 cache.push_back(next);
6201 const auto itCache = m_targetsAllCache.find(key);
6202 if (itCache != m_targetsAllCache.end())
6203 return itCache->second;
6205 auto& cache = m_targetsAllCache[key];
6209 const auto visitStamp = next_entity_visit_stamp();
6211 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(key);
6212 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
6213 for (
auto rel: itAdjunctRels->second) {
6214 const auto* pStore = exclusive_adjunct_store(rel);
6215 if (pStore ==
nullptr)
6218 const auto target = exclusive_adjunct_target(*pStore, source);
6219 if (target != EntityBad && try_mark_entity_visited(target, visitStamp))
6220 cache.push_back(target);
6224 const auto& ec = fetch(source);
6225 const auto* pArchetype = ec.pArchetype;
6226 if (pArchetype->pairs() == 0)
6229 const auto ids = pArchetype->ids_view();
6230 for (
auto idsIdx: pArchetype->pair_indices()) {
6231 const auto id = ids[idsIdx];
6232 const auto target = pair_target_if_alive(
id);
6233 if (target == EntityBad)
6235 if (try_mark_entity_visited(target, visitStamp))
6236 cache.push_back(target);
6246 const auto itCache = m_sourcesAllCache.find(key);
6247 if (itCache != m_sourcesAllCache.end())
6248 return itCache->second;
6250 auto& cache = m_sourcesAllCache[key];
6254 const auto visitStamp = next_entity_visit_stamp();
6256 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
6258 const auto* pSources = exclusive_adjunct_sources(store, target);
6259 if (pSources ==
nullptr)
6262 for (
auto source: *pSources) {
6263 if (valid(source) && try_mark_entity_visited(source, visitStamp))
6264 cache.push_back(source);
6268 const auto pair =
Pair(All, target);
6270 if (it == m_entityToArchetypeMap.end())
6273 for (
const auto& record: it->second) {
6274 const auto* pArchetype = record.pArchetype;
6275 if (pArchetype->is_req_del())
6278 for (
const auto* pChunk: pArchetype->chunks()) {
6279 const auto entities = pChunk->entity_view();
6280 GAIA_EACH(entities) {
6281 const auto source = entities[i];
6284 if (try_mark_entity_visited(source, visitStamp))
6285 cache.push_back(source);
6295 template <
typename Func>
6297 if (!valid(relation) || !valid(source))
6301 GAIA_FOR(MAX_TRAV_DEPTH) {
6302 const auto next = target(curr, relation);
6303 if (next == EntityBad || next == curr)
6316 template <
typename Func>
6318 if (!valid(relation) || !valid(source))
6322 GAIA_FOR(MAX_TRAV_DEPTH) {
6323 const auto next = target(curr, relation);
6324 if (next == EntityBad || next == curr)
6341 const auto itCache = m_srcBfsTravCache.find(key);
6342 if (itCache != m_srcBfsTravCache.end())
6343 return itCache->second;
6345 auto& cache = m_srcBfsTravCache[key];
6346 if (!valid(relation) || !valid(rootTarget))
6350 queue.push_back(rootTarget);
6355 for (uint32_t i = 0; i < queue.size(); ++i) {
6356 const auto currTarget = queue[i];
6359 sources(relation, currTarget, [&](
Entity source) {
6361 const auto ins = visited.
insert(keySource);
6365 children.push_back(source);
6369 return left.id() < right.id();
6372 for (
auto child: children) {
6373 cache.push_back(child);
6374 queue.push_back(child);
6386 const auto itCache = m_depthOrderCache.find(key);
6387 if (itCache != m_depthOrderCache.end()) {
6388 GAIA_ASSERT(itCache->second != GroupIdMax &&
"depth_order requires an acyclic relation graph");
6389 return itCache->second;
6392 if (!valid(relation) || !valid(sourceTarget))
6396 m_depthOrderCache[key] = GroupIdMax;
6399 targets(sourceTarget, relation, [&](
Entity next) {
6400 const auto nextDepth = depth_order_cache(relation, next);
6403 const auto candidate = nextDepth + 1;
6404 if (candidate > depth)
6408 m_depthOrderCache[key] = depth;
6416 template <
typename Func>
6421 const auto& relations = as_relations_trav_cache(target);
6422 for (
auto relation: relations)
6431 template <
typename Func>
6436 const auto& relations = as_relations_trav_cache(target);
6437 for (
auto relation: relations) {
6452 if (it == m_relToTgt.end())
6466 if (relation != All && !valid(relation))
6469 if (relation == All) {
6470 const auto& targets = targets_all_cache(entity);
6471 return targets.empty() ? EntityBad : targets[0];
6474 if (is_exclusive_dont_fragment_relation(relation)) {
6475 const auto* pStore = exclusive_adjunct_store(relation);
6476 if (pStore ==
nullptr)
6479 return exclusive_adjunct_target(*pStore, entity);
6482 const auto& ec = fetch(entity);
6483 const auto* pArchetype = ec.pArchetype;
6486 if (pArchetype->pairs() == 0)
6489 const auto ids = pArchetype->ids_view();
6490 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
6491 const auto e = ids[idsIdx];
6492 const auto target = pair_target_if_alive(e);
6493 if (target == EntityBad)
6506 template <
typename Func>
6510 if (relation != All && !valid(relation))
6513 if (relation == All) {
6514 for (
auto target: targets_all_cache(entity))
6519 if (is_exclusive_dont_fragment_relation(relation)) {
6520 const auto target = this->target(entity, relation);
6521 if (target != EntityBad)
6526 const auto& ec = fetch(entity);
6527 const auto* pArchetype = ec.pArchetype;
6530 if (pArchetype->pairs() == 0)
6533 const auto ids = pArchetype->ids_view();
6534 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
6535 const auto e = ids[idsIdx];
6536 const auto target = pair_target_if_alive(e);
6537 if (target == EntityBad)
6549 template <
typename Func>
6551 GAIA_ASSERT(valid(entity));
6552 if (relation != All && !valid(relation))
6555 if (relation == All) {
6556 for (
auto target: targets_all_cache(entity)) {
6563 if (is_exclusive_dont_fragment_relation(relation)) {
6564 const auto target = this->target(entity, relation);
6565 if (target != EntityBad)
6570 const auto& ec = fetch(entity);
6571 const auto* pArchetype = ec.pArchetype;
6574 if (pArchetype->pairs() == 0)
6577 const auto ids = pArchetype->ids_view();
6578 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
6579 const auto e = ids[idsIdx];
6580 const auto target = pair_target_if_alive(e);
6581 if (target == EntityBad)
6590 GAIA_ASSERT(
pair.pair());
6591 if (!valid_entity_id((EntityId)
pair.gen()))
6594 const auto& ecTarget = m_recs.entities[
pair.gen()];
6595 if (ecTarget.pEntity ==
nullptr)
6598 const auto target = *ecTarget.pEntity;
6599 return valid(target) ? target : EntityBad;
6607 template <
typename Func>
6609 if ((relation != All && !valid(relation)) || !valid(target))
6612 if (relation == All) {
6613 for (
auto source: sources_all_cache(target))
6618 if (is_exclusive_dont_fragment_relation(relation)) {
6619 const auto* pStore = exclusive_adjunct_store(relation);
6620 if (pStore ==
nullptr)
6623 const auto* pSources = exclusive_adjunct_sources(*pStore, target);
6624 if (pSources ==
nullptr)
6627 for (
auto source: *pSources) {
6636 const auto pair =
Pair(relation, target);
6638 if (it == m_entityToArchetypeMap.end())
6641 for (
const auto& record: it->second) {
6642 const auto* pArchetype = record.pArchetype;
6643 if (pArchetype->is_req_del())
6646 for (
const auto* pChunk: pArchetype->chunks()) {
6647 auto entities = pChunk->entity_view();
6648 GAIA_EACH(entities) {
6649 const auto source = entities[i];
6664 template <
typename Func>
6666 if ((relation != All && !valid(relation)) || !valid(target))
6669 if (relation == All) {
6670 for (
auto source: sources_all_cache(target)) {
6677 if (is_exclusive_dont_fragment_relation(relation)) {
6678 const auto* pStore = exclusive_adjunct_store(relation);
6679 if (pStore ==
nullptr)
6682 const auto* pSources = exclusive_adjunct_sources(*pStore, target);
6683 if (pSources ==
nullptr)
6686 for (
auto source: *pSources) {
6696 const auto pair =
Pair(relation, target);
6698 if (it == m_entityToArchetypeMap.end())
6701 for (
const auto& record: it->second) {
6702 const auto* pArchetype = record.pArchetype;
6703 if (pArchetype->is_req_del())
6706 for (
const auto* pChunk: pArchetype->chunks()) {
6707 auto entities = pChunk->entity_view();
6708 GAIA_EACH(entities) {
6709 const auto source = entities[i];
6720 GAIA_NODISCARD uint64_t next_entity_visit_stamp()
const {
6721 ++m_entityVisitStamp;
6722 if (m_entityVisitStamp != 0)
6723 return m_entityVisitStamp;
6725 const auto cnt = (uint32_t)m_entityVisitStamps.size();
6727 m_entityVisitStamps[i] = 0;
6730 m_entityVisitStamp = 1;
6731 return m_entityVisitStamp;
6734 GAIA_NODISCARD
bool try_mark_entity_visited(Entity entity, uint64_t stamp)
const {
6735 GAIA_ASSERT(!entity.pair());
6736 if (entity.id() >= m_entityVisitStamps.size())
6737 m_entityVisitStamps.resize(m_recs.entities.size(), 0);
6739 auto& slot = m_entityVisitStamps[entity.id()];
6747 template <
typename Func>
6748 GAIA_NODISCARD
bool for_each_inherited_term_entity(Entity term, Func&& func)
const {
6749 cnt::set<EntityLookupKey> seen;
6750 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
6751 if (it == m_entityToArchetypeMap.end())
6754 for (
const auto& record: it->second) {
6755 const auto* pArchetype = record.pArchetype;
6756 if (pArchetype->is_req_del())
6759 for (
const auto* pChunk: pArchetype->chunks()) {
6760 const auto entities = pChunk->entity_view();
6761 GAIA_EACH(entities) {
6762 const auto entity = entities[i];
6763 GAIA_ASSERT(valid(entity));
6764 const auto entityKey = EntityLookupKey(entity);
6765 if (seen.contains(entityKey))
6767 seen.insert(entityKey);
6772 const auto& descendants = as_relations_trav_cache(entity);
6773 for (
const auto descendant: descendants) {
6774 GAIA_ASSERT(valid(descendant));
6775 const auto descendantKey = EntityLookupKey(descendant);
6776 if (seen.contains(descendantKey))
6778 seen.insert(descendantKey);
6780 if (!func(descendant))
6792 GAIA_NODISCARD uint32_t count_direct_term_entities_inter(Entity term,
bool allowSemanticIs)
const {
6793 if (term == EntityBad)
6796 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
6797 const auto target = get(term.gen());
6801 return (uint32_t)as_relations_trav_cache(target).size() + 1;
6804 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
6806 (void)for_each_inherited_term_entity(term, [&](Entity) {
6813 if (term.pair() && is_exclusive_dont_fragment_relation(pair_rel(*
this, term))) {
6814 const auto relation = pair_rel(*
this, term);
6815 const auto* pStore = exclusive_adjunct_store(relation);
6816 if (pStore ==
nullptr)
6819 if (is_wildcard(term.gen()))
6820 return pStore->srcToTgtCnt;
6822 const auto* pSources = exclusive_adjunct_sources(*pStore, pair_tgt(*
this, term));
6823 return pSources !=
nullptr ? (uint32_t)pSources->size() : 0;
6826 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
6827 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
6828 return it != m_sparseComponentsByComp.end() ? it->second.func_count(it->second.pStore) : 0;
6831 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
6832 if (it == m_entityToArchetypeMap.end())
6836 for (
const auto& record: it->second) {
6837 const auto* pArchetype = record.pArchetype;
6838 if (pArchetype->is_req_del())
6840 for (
const auto* pChunk: pArchetype->chunks())
6841 cnt += pChunk->size();
6848 void collect_direct_term_entities_inter(Entity term, cnt::darray<Entity>& out,
bool allowSemanticIs)
const {
6849 if (term == EntityBad)
6852 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
6853 const auto target = get(term.gen());
6857 out.push_back(target);
6858 const auto& relations = as_relations_trav_cache(target);
6859 out.reserve(out.size() + (uint32_t)relations.size());
6860 for (
auto relation: relations)
6861 out.push_back(relation);
6865 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
6866 (void)for_each_inherited_term_entity(term, [&](Entity entity) {
6867 out.push_back(entity);
6873 if (term.pair() && is_exclusive_dont_fragment_relation(pair_rel(*
this, term))) {
6874 const auto relation = pair_rel(*
this, term);
6875 const auto* pStore = exclusive_adjunct_store(relation);
6876 if (pStore ==
nullptr)
6879 if (is_wildcard(term.gen())) {
6880 out.reserve(out.size() + pStore->srcToTgtCnt);
6881 const auto cnt = (uint32_t)pStore->srcToTgt.size();
6883 const auto target = pStore->srcToTgt[i];
6884 if (target == EntityBad)
6886 if (!m_recs.entities.has(i))
6888 out.push_back(EntityContainer::handle(m_recs.entities[i]));
6893 const auto* pSources = exclusive_adjunct_sources(*pStore, pair_tgt(*
this, term));
6894 if (pSources ==
nullptr)
6897 out.reserve(out.size() + (uint32_t)pSources->size());
6898 for (
auto source: *pSources)
6899 out.push_back(source);
6903 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
6904 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
6905 if (it != m_sparseComponentsByComp.end())
6906 it->second.func_collect_entities(it->second.pStore, out);
6910 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
6911 if (it == m_entityToArchetypeMap.end())
6914 for (
const auto& record: it->second) {
6915 const auto* pArchetype = record.pArchetype;
6916 if (pArchetype->is_req_del())
6919 for (
const auto* pChunk: pArchetype->chunks()) {
6920 const auto entities = pChunk->entity_view();
6921 out.reserve(out.size() + (uint32_t)entities.size());
6923 out.push_back(entities[i]);
6929 GAIA_NODISCARD
bool for_each_direct_term_entity_inter(
6930 Entity term,
void* ctx,
bool (*func)(
void*, Entity),
bool allowSemanticIs)
const {
6931 if (term == EntityBad)
6934 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
6935 const auto target = get(term.gen());
6939 if (!func(ctx, target))
6942 const auto& relations = as_relations_trav_cache(target);
6943 for (
auto relation: relations) {
6944 if (!func(ctx, relation))
6950 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
6951 return for_each_inherited_term_entity(term, [&](Entity entity) {
6952 return func(ctx, entity);
6956 if (term.pair() && is_exclusive_dont_fragment_relation(pair_rel(*
this, term))) {
6957 const auto relation = pair_rel(*
this, term);
6958 const auto* pStore = exclusive_adjunct_store(relation);
6959 if (pStore ==
nullptr)
6962 if (is_wildcard(term.gen())) {
6963 const auto cnt = (uint32_t)pStore->srcToTgt.size();
6965 const auto target = pStore->srcToTgt[i];
6966 if (target == EntityBad)
6968 if (!m_recs.entities.has(i))
6970 if (!func(ctx, EntityContainer::handle(m_recs.entities[i])))
6976 const auto* pSources = exclusive_adjunct_sources(*pStore, pair_tgt(*
this, term));
6977 if (pSources ==
nullptr)
6980 for (
auto source: *pSources) {
6981 if (!func(ctx, source))
6987 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
6988 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
6989 if (it == m_sparseComponentsByComp.end())
6991 return it->second.func_for_each_entity(it->second.pStore, ctx, func);
6994 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
6995 if (it == m_entityToArchetypeMap.end())
6998 for (
const auto& record: it->second) {
6999 const auto* pArchetype = record.pArchetype;
7000 if (pArchetype->is_req_del())
7003 for (
const auto* pChunk: pArchetype->chunks()) {
7004 const auto entities = pChunk->entity_view();
7005 GAIA_EACH(entities) {
7006 if (!func(ctx, entities[i]))
7020 return count_direct_term_entities_inter(term,
true);
7027 return count_direct_term_entities_inter(term,
false);
7034 collect_direct_term_entities_inter(term, out,
true);
7041 collect_direct_term_entities_inter(term, out,
false);
7050 return for_each_direct_term_entity_inter(term, ctx, func,
true);
7060 return for_each_direct_term_entity_inter(term, ctx, func,
false);
7068 template <
typename Func>
7070 if (!valid(relation) || !valid(rootTarget))
7074 queue.push_back(rootTarget);
7079 for (uint32_t i = 0; i < queue.size(); ++i) {
7080 const auto currTarget = queue[i];
7083 sources(relation, currTarget, [&](
Entity source) {
7085 const auto ins = visited.
insert(key);
7089 children.push_back(source);
7093 return left.id() < right.id();
7096 for (
auto child: children) {
7097 if (!enabled(child))
7100 queue.push_back(child);
7112 template <
typename Func>
7114 if (!valid(relation) || !valid(rootTarget))
7118 queue.push_back(rootTarget);
7123 for (uint32_t i = 0; i < queue.size(); ++i) {
7124 const auto currTarget = queue[i];
7127 sources(relation, currTarget, [&](
Entity source) {
7129 const auto ins = visited.
insert(key);
7133 children.push_back(source);
7137 return left.id() < right.id();
7140 for (
auto child: children) {
7141 if (!enabled(child))
7146 queue.push_back(child);
7156 template <
typename Func>
7158 sources(ChildOf, parent, func);
7165 template <
typename Func>
7167 sources_if(ChildOf, parent, func);
7173 template <
typename Func>
7175 sources_bfs(ChildOf, root, func);
7183 template <
typename Func>
7185 return sources_bfs_if(ChildOf, root, func);
7192 template <
typename Func>
7194 GAIA_ASSERT(valid(relation));
7195 if (!valid(relation))
7198 const auto& targets = as_targets_trav_cache(relation);
7199 for (
auto target: targets)
7208 template <
typename Func>
7210 GAIA_ASSERT(valid(relation));
7211 if (!valid(relation))
7214 const auto& targets = as_targets_trav_cache(relation);
7215 for (
auto target: targets)
7227 return *m_pCmdBufferST;
7233 return *m_pCmdBufferMT;
7238#if GAIA_SYSTEMS_ENABLED
7241 void systems_init();
7264 SystemBuilder system();
7267 SystemRegistry& systems() {
7272 const SystemRegistry& systems()
const {
7278#if GAIA_OBSERVERS_ENABLED
7282 ObserverBuilder observer();
7285 ObserverRegistry& observers() {
7290 const ObserverRegistry& observers()
const {
7303 GAIA_ASSERT(valid(entity));
7305 auto& ec = m_recs.entities[entity.id()];
7306 auto& archetype = *ec.pArchetype;
7307 auto* pChunk = ec.pChunk;
7308 const bool wasEnabled = !ec.data.dis;
7309#if GAIA_ASSERT_ENABLED
7310 verify_enable(*
this, archetype, entity);
7312 archetype.enable_entity(ec.pChunk, ec.row, enable, m_recs);
7314 if (wasEnabled != enable) {
7315 pChunk->update_world_version();
7316 pChunk->update_entity_order_version();
7317 update_version(m_enabledHierarchyVersion);
7318 update_version(m_worldVersion);
7326 const bool entityStateInContainer = !ec.data.
dis;
7327#if GAIA_ASSERT_ENABLED
7329 GAIA_ASSERT(entityStateInChunk == entityStateInContainer);
7331 return entityStateInContainer;
7339 GAIA_ASSERT(valid(entity));
7341 const auto& ec = m_recs.entities[entity.id()];
7348 GAIA_ASSERT(valid(entity));
7349 GAIA_ASSERT(valid(relation));
7350 if (!valid(entity) || !valid(relation))
7352 if (!enabled(entity))
7356 GAIA_FOR(MAX_TRAV_DEPTH) {
7357 const auto next = target(curr, relation);
7358 if (next == EntityBad || next == curr)
7374 GAIA_ASSERT(entity.id() < m_recs.entities.size());
7375 const auto& ec = m_recs.entities[entity.id()];
7385 GAIA_ASSERT(entity.id() < m_recs.entities.size());
7386 const auto& ec = m_recs.entities[entity.id()];
7393 GAIA_NODISCARD uint32_t
size()
const {
7394 return m_recs.entities.item_count();
7399 uint32_t& outArchetypes, uint32_t& outChunks, uint32_t& outEntitiesTotal, uint32_t& outEntitiesActive)
const {
7400 outArchetypes = (uint32_t)m_archetypes.size();
7402 outEntitiesTotal = 0;
7403 outEntitiesActive = 0;
7405 for (
const auto* pArchetype: m_archetypes) {
7406 if (pArchetype ==
nullptr)
7408 const auto& chunks = pArchetype->chunks();
7409 outChunks += (uint32_t)chunks.size();
7410 for (
const auto* pChunk: chunks) {
7411 if (pChunk ==
nullptr)
7413 outEntitiesTotal += pChunk->size();
7414 outEntitiesActive += pChunk->size_enabled();
7422 return m_worldVersion;
7429 return it != m_relationVersions.end() ? it->second : 0;
7436 return m_enabledHierarchyVersion;
7440 friend uint32_t world_version(
const World& world);
7446 const auto it = m_srcEntityVersions.find(key);
7447 if (it == m_srcEntityVersions.end())
7450 update_version(it->second);
7466 auto& ec = fetch(entity);
7467 const auto prevLifespan = ec.pArchetype->max_lifespan();
7468 ec.pArchetype->set_max_lifespan(lifespan);
7470 if (prevLifespan == 0) {
7472 try_enqueue_archetype_for_deletion(*ec.pArchetype);
7529 if GAIA_UNLIKELY (m_teardownActive)
7531 m_teardownActive =
true;
7533 GAIA_PROF_SCOPE(World::teardown);
7535#if GAIA_SYSTEMS_ENABLED
7537 m_systems.teardown();
7540#if GAIA_OBSERVERS_ENABLED
7541 m_observers.teardown();
7545 const auto prevReqArchetypes = m_reqArchetypesToDel.size();
7546 const auto prevReqEntities = m_reqEntitiesToDel.size();
7547 const auto prevChunks = m_chunksToDel.size();
7548 const auto prevArchetypes = m_archetypesToDel.size();
7553 if (m_reqArchetypesToDel.empty() && m_reqEntitiesToDel.empty() && m_chunksToDel.empty() &&
7554 m_archetypesToDel.empty())
7557 const bool madeProgress = m_reqArchetypesToDel.size() != prevReqArchetypes ||
7558 m_reqEntitiesToDel.size() != prevReqEntities ||
7559 m_chunksToDel.size() != prevChunks || m_archetypesToDel.size() != prevArchetypes;
7572 m_pRootArchetype =
nullptr;
7573 m_pEntityArchetype =
nullptr;
7574 m_pCompArchetype =
nullptr;
7575 m_nextArchetypeId = 0;
7576 m_defragLastArchetypeIdx = 0;
7578 m_enabledHierarchyVersion = 0;
7585 m_defragEntitiesPerTick = value;
7592 GAIA_LOG_N(
"Archetypes:%u", (uint32_t)m_archetypes.size());
7593 for (
auto* pArchetype: m_archetypes)
7594 Archetype::diag(*
this, *pArchetype);
7600 comp_cache().diag();
7606 validate_entities();
7608 GAIA_LOG_N(
"Deleted entities: %u", (uint32_t)m_recs.entities.get_free_items());
7609 if (m_recs.entities.get_free_items() != 0U) {
7610 GAIA_LOG_N(
" --> %u", (uint32_t)m_recs.entities.get_next_free_item());
7613 auto fe = m_recs.entities.next_free(m_recs.entities.get_next_free_item());
7614 while (fe != IdentifierIdBad) {
7615 GAIA_LOG_N(
" --> %u", m_recs.entities.next_free(fe));
7616 fe = m_recs.entities.next_free(fe);
7618 if (iters > m_recs.entities.get_free_items())
7622 if ((iters == 0U) || iters > m_recs.entities.get_free_items())
7623 GAIA_LOG_E(
" Entities recycle list contains inconsistent data!");
7636 void cleanup_inter() {
7637 GAIA_PROF_SCOPE(World::cleanup_inter);
7643 m_queryCache.clear_archetype_tracking();
7644 m_reqArchetypesToDel = {};
7645 m_reqEntitiesToDel = {};
7646 m_entitiesToDel = {};
7648 m_archetypesToDel = {};
7652 m_recs.entities = {};
7658 for (
auto* pArchetype: m_archetypes)
7659 Archetype::destroy(pArchetype);
7661 m_entityToAsRelations = {};
7662 m_entityToAsRelationsTravCache = {};
7663 m_entityToAsTargets = {};
7664 m_entityToAsTargetsTravCache = {};
7665 m_targetsTravCache = {};
7666 m_srcBfsTravCache = {};
7667 m_depthOrderCache = {};
7668 m_sourcesAllCache = {};
7669 m_targetsAllCache = {};
7672 m_exclusiveAdjunctByRel = {};
7673 m_srcToExclusiveAdjunctRel = {};
7674 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
7676 store.func_clear_store(store.pStore);
7677 store.func_del_store(store.pStore);
7679 m_sparseComponentsByComp = {};
7680 m_relationVersions = {};
7681 m_srcEntityVersions = {};
7684 m_archetypesById = {};
7685 m_archetypesByHash = {};
7690 m_entityToArchetypeMap = {};
7691 m_entityToArchetypeMapVersions = {};
7692 m_queryCache.clear();
7693 for (
auto* pScratch: m_queryMatchScratchStack)
7695 m_queryMatchScratchStack = {};
7696 m_queryMatchScratchDepth = 0;
7698 m_nextQuerySerId = 0;
7703 for (
auto& pair: m_aliasToEntity) {
7704 if (!pair.first.owned())
7707 mem::mem_free((
void*)pair.first.str());
7709 m_aliasToEntity = {};
7714 for (
auto& pair: m_nameToEntity) {
7715 if (!pair.first.owned())
7718 mem::mem_free((
void*)pair.first.str());
7720 m_nameToEntity = {};
7724 m_compCache.clear();
7727 GAIA_NODISCARD
static bool valid(
const EntityContainer& ec, [[maybe_unused]] Entity entityExpected) {
7728 if ((ec.flags & EntityContainerFlags::Load) != 0) {
7729 return entityExpected.id() == ec.idx && entityExpected.gen() == ec.data.gen &&
7730 entityExpected.entity() == (bool)ec.data.ent && entityExpected.pair() == (bool)ec.data.pair &&
7731 entityExpected.kind() == (EntityKind)ec.data.kind;
7738 const auto* pChunk = ec.pChunk;
7739 if (pChunk ==
nullptr || ec.row >= pChunk->size())
7742 const auto entityPresent = pChunk->entity_view()[ec.row];
7745 return entityExpected == entityPresent;
7751 GAIA_NODISCARD
bool valid_pair(Entity entity)
const {
7752 if (entity == EntityBad)
7755 GAIA_ASSERT(entity.pair());
7760 if (is_wildcard(entity))
7763 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
7764 if (it == m_recs.pairs.end())
7767 const auto& ec = it->second;
7768 return valid(ec, entity);
7774 GAIA_NODISCARD
bool valid_entity(Entity entity)
const {
7775 if (entity == EntityBad)
7778 GAIA_ASSERT(!entity.pair());
7783 if (entity.id() >= m_recs.entities.size())
7786 const auto* pEc = m_recs.entities.try_get(entity.id());
7790 return valid(*pEc, entity);
7797 GAIA_NODISCARD
bool valid_entity_id(EntityId entityId)
const {
7798 if (entityId == EntityBad.id())
7802 if (entityId >= m_recs.entities.size())
7805 const auto* pEc = m_recs.entities.try_get(entityId);
7809 const auto& ec = *pEc;
7810 if (ec.data.pair != 0)
7814 ec, Entity(entityId, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind));
7821 GAIA_ASSERT(m_structuralChangesLocked != (uint32_t)-1);
7822 ++m_structuralChangesLocked;
7829 GAIA_ASSERT(m_structuralChangesLocked > 0);
7830 --m_structuralChangesLocked;
7833#if GAIA_SYSTEMS_ENABLED
7834 void systems_done();
7840 return m_structuralChangesLocked != 0;
7846 return m_teardownActive;
7850 static constexpr uint32_t WorldSerializerVersion = 3;
7851 static constexpr uint32_t WorldSerializerJSONVersion = 1;
7854 GAIA_ASSERT(s.valid());
7857 s.save((uint32_t)WorldSerializerVersion);
7861 const auto lastCoreComponentId = GAIA_ID(LastCoreComponent).id();
7862 s.save(lastCoreComponentId);
7870 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) == 0);
7873#if GAIA_USE_SAFE_ENTITY
7876 s.save((uint32_t)0);
7879 uint32_t archetypeIdx = ec.pArchetype->list_idx();
7880 s.save(archetypeIdx);
7881 uint32_t chunkIdx = ec.pChunk->idx();
7885 const auto recEntities = (uint32_t)m_recs.entities.size();
7886 const auto newEntities = recEntities - lastCoreComponentId;
7887 s.save(newEntities);
7888 GAIA_FOR2(lastCoreComponentId, recEntities) {
7889 const bool isAlive = m_recs.entities.has(i);
7892 saveEntityContainer(m_recs.entities[i]);
7894 s.save(m_recs.entities.handle(i).val);
7895 s.save(m_recs.entities.next_free(i));
7900 uint32_t pairsCnt = 0;
7901 for (
const auto& pair: m_recs.pairs) {
7903 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
7911 for (
const auto& pair: m_recs.pairs) {
7913 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
7916 saveEntityContainer(pair.second);
7920 s.save(m_recs.entities.m_nextFreeIdx);
7921 s.save(m_recs.entities.m_freeItems);
7926 s.save((uint32_t)m_archetypes.size());
7927 for (
auto* pArchetype: m_archetypes) {
7928 s.save((uint32_t)pArchetype->ids_view().size());
7929 for (
auto e: pArchetype->ids_view())
7932 pArchetype->save(s);
7935 s.save(m_worldVersion);
7940 s.save((uint32_t)m_nameToEntity.size());
7941 for (
const auto& pair: m_nameToEntity) {
7942 s.save(pair.second);
7943 const bool isOwnedStr = pair.first.owned();
7948 const auto* str = pair.first.str();
7949 const uint32_t len = pair.first.len();
7951 s.save_raw(str, len, ser::serialization_type_id::c8);
7956 else if (!pair.second.comp()) {
7957 const auto* str = pair.first.str();
7958 const uint32_t len = pair.first.len();
7960 const auto ptr_val = (uint64_t)str;
7961 s.save_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
7968 uint32_t aliasCnt = 0;
7969 GAIA_FOR((uint32_t)m_recs.entities.size()) {
7970 const auto entity = get((EntityId)i);
7971 if (!valid(entity) || entity.pair())
7974 const auto& ec = m_recs.entities[i];
7975 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
7976 if (compIdx == BadIndex)
7979 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
7980 if (pDesc->alias !=
nullptr)
7985 GAIA_FOR((uint32_t)m_recs.entities.size()) {
7986 const auto entity = get((EntityId)i);
7987 if (!valid(entity) || entity.pair())
7990 const auto& ec = m_recs.entities[i];
7991 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
7992 if (compIdx == BadIndex)
7995 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
7996 if (pDesc->alias ==
nullptr)
8000 s.save(pDesc->alias_len);
8001 s.save_raw(pDesc->alias, pDesc->alias_len, ser::serialization_type_id::c8);
8014 auto s = m_serializer;
8015 GAIA_ASSERT(s.valid());
8025 bool save_json(
ser::ser_json& writer, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default)
const;
8028 ser::json_str save_json(
bool& ok, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default)
const;
8039 bool load_json(
const char* json, uint32_t len);
8059 auto s = inputSerializer.valid() ? inputSerializer : m_serializer;
8060 GAIA_ASSERT(s.valid());
8066 uint32_t version = 0;
8068 if (version < 2 || version > WorldSerializerVersion) {
8069 GAIA_LOG_E(
"Unsupported world version %u. Expected 2..%u.", version, WorldSerializerVersion);
8074 uint32_t lastCoreComponentId = 0;
8075 s.load(lastCoreComponentId);
8079 const auto currLastCoreComponentId = GAIA_ID(LastCoreComponent).id();
8080 if (lastCoreComponentId > currLastCoreComponentId) {
8082 "Unsupported world core boundary %u. Current runtime supports up to %u.", lastCoreComponentId,
8083 currLastCoreComponentId);
8089 const detail::EntityLoadRemapGuard entityLoadRemapGuard(
8090 lastCoreComponentId, currLastCoreComponentId, version >= WorldSerializerVersion);
8091 auto remapLoadedEntityId = [&](uint32_t id) {
8092 return detail::remap_loaded_entity_id(
id, lastCoreComponentId, currLastCoreComponentId);
8097 auto loadEntityContainer = [&](EntityContainer& ec) {
8102 ec.flags |= EntityContainerFlags::Load;
8104 ec.idx = remapLoadedEntityId(ec.idx);
8105 if (ec.data.pair != 0)
8106 ec.data.gen = remapLoadedEntityId(ec.data.gen);
8108#if GAIA_USE_SAFE_ENTITY
8115 GAIA_ASSERT(ec.unused == 0);
8118 uint32_t archetypeIdx = 0;
8119 s.load(archetypeIdx);
8120 ec.pArchetype = (Archetype*)((uintptr_t)archetypeIdx);
8122 uint32_t chunkIdx = 0;
8124 ec.pChunk = (Chunk*)((uintptr_t)chunkIdx);
8127 uint32_t newEntities = 0;
8128 s.load(newEntities);
8129 GAIA_FOR(newEntities) {
8130 bool isAlive =
false;
8133 EntityContainer ec{};
8134 loadEntityContainer(ec);
8135 m_recs.entities.add_live(GAIA_MOV(ec));
8137 Identifier
id = IdentifierBad;
8138 uint32_t nextFreeIdx = Entity::IdMask;
8140 s.load(nextFreeIdx);
8141 auto entity = Entity(
id);
8142 entity = detail::remap_loaded_entity(entity, lastCoreComponentId, currLastCoreComponentId);
8143 nextFreeIdx = remapLoadedEntityId(nextFreeIdx);
8144 GAIA_ASSERT(entity.id() == remapLoadedEntityId(lastCoreComponentId + i));
8145 m_recs.entities.add_free(entity, nextFreeIdx);
8149 uint32_t pairsCnt = 0;
8151 GAIA_FOR(pairsCnt) {
8152 EntityContainer ec{};
8153 loadEntityContainer(ec);
8154 Entity pair(ec.idx, ec.data.gen);
8155 m_recs.pairs.emplace(EntityLookupKey(pair), GAIA_MOV(ec));
8158 s.load(m_recs.entities.m_nextFreeIdx);
8159 m_recs.entities.m_nextFreeIdx = remapLoadedEntityId(m_recs.entities.m_nextFreeIdx);
8160 s.load(m_recs.entities.m_freeItems);
8165 uint32_t archetypesSize = 0;
8166 s.load(archetypesSize);
8167 m_archetypes.reserve(archetypesSize);
8168 GAIA_FOR(archetypesSize) {
8169 uint32_t idsSize = 0;
8171 Entity ids[ChunkHeader::MAX_COMPONENTS];
8172 GAIA_FOR_(idsSize, j) {
8177 const auto hashLookup = calc_lookup_hash({&ids[0], idsSize}).hash;
8179 auto* pArchetype = find_archetype({hashLookup}, {&ids[0], idsSize});
8180 if (pArchetype ==
nullptr) {
8182 pArchetype = create_archetype({&ids[0], idsSize});
8183 pArchetype->set_hashes({hashLookup});
8189 reg_archetype(pArchetype);
8193 pArchetype->load(s);
8196 s.load(m_worldVersion);
8203 for (
auto& ec: m_recs.entities) {
8204 if ((ec.flags & EntityContainerFlags::Load) == 0)
8206 ec.flags &= ~EntityContainerFlags::Load;
8208 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
8209 ec.pArchetype = m_archetypes[archetypeIdx];
8210 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
8211 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
8212 ec.pEntity = &ec.pChunk->entity_view()[ec.row];
8215 for (
auto& pair: m_recs.pairs) {
8216 auto& ec = pair.second;
8219 if ((ec.flags & EntityContainerFlags::Load) == 0)
8222 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) != 0);
8223 ec.flags &= ~EntityContainerFlags::Load;
8225 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
8226 ec.pArchetype = m_archetypes[archetypeIdx];
8227 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
8228 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
8229 ec.pEntity = &ec.pChunk->entity_view()[ec.row];
8233 if (version < WorldSerializerVersion) {
8234 for (
const auto& [entityId, pItem]: m_compCache.m_compByEntityId) {
8236 GAIA_ASSERT(pItem !=
nullptr);
8237 auto comp = pItem->comp;
8238 comp.data.id = pItem->entity.id();
8239 sync_component_record(pItem->entity, comp);
8243#if GAIA_ASSERT_ENABLED
8244 for (
const auto& ec: m_recs.entities) {
8245 GAIA_ASSERT(ec.idx < m_recs.entities.size());
8246 GAIA_ASSERT(m_recs.entities.handle(ec.idx) == EntityContainer::handle(ec));
8247 GAIA_ASSERT(ec.pArchetype !=
nullptr);
8248 GAIA_ASSERT(ec.pChunk !=
nullptr);
8249 GAIA_ASSERT(ec.pEntity !=
nullptr);
8254 m_nameToEntity = {};
8262 const auto& ec = fetch(entity);
8263 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
8264 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
8265 GAIA_ASSERT(core::check_alignment(pDesc));
8267 bool isOwned =
false;
8270 if (entity.comp()) {
8273 const auto& ci = comp_cache().
get(entity);
8274 pDesc->
name = ci.name.str();
8276 GAIA_ASSERT(pDesc->name_len == ci.name.len());
8277 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
8281 uint64_t ptr_val = 0;
8282 s.load_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
8285 pDesc->name = (
const char*)ptr_val;
8286 pDesc->name_len = len;
8287 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
8297 const char* entityStr = (
const char*)(s.data() + s.tell());
8298 s.seek(s.tell() + len);
8302 pDesc->name =
nullptr;
8303 pDesc->name_len = 0;
8307 name(entity, entityStr, len);
8313 m_aliasToEntity = {};
8314 for (
auto& ec: m_recs.entities) {
8315 const auto entity = EntityContainer::handle(ec);
8319 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
8320 if (compIdx == BadIndex)
8323 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
8324 GAIA_ASSERT(core::check_alignment(pDesc));
8325 pDesc->alias =
nullptr;
8326 pDesc->alias_len = 0;
8335 const auto& ec = fetch(entity);
8336 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
8337 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
8338 GAIA_ASSERT(core::check_alignment(pDesc));
8344 const char* aliasStr = (
const char*)(s.data() + s.tell());
8345 s.seek(s.tell() + len);
8347 pDesc->alias =
nullptr;
8348 pDesc->alias_len = 0;
8349 alias(entity, aliasStr, len);
8359 template <
typename TSerializer>
8360 bool load(TSerializer& inputSerializer) {
8361 return load(ser::make_serializer(inputSerializer));
8366 void sort_archetypes() {
8369 return a->id() < b->id();
8373 core::sort(m_archetypes, sort_cond{}, [&](uint32_t left, uint32_t right) {
8374 Archetype* tmp = m_archetypes[left];
8376 m_archetypes[right]->list_idx(left);
8377 m_archetypes[left]->list_idx(right);
8379 m_archetypes.data()[left] = (Archetype*)m_archetypes[right];
8380 m_archetypes.data()[right] = tmp;
8387 void remove_chunk(Archetype& archetype, Chunk& chunk) {
8388 archetype.del(&chunk);
8389 try_enqueue_archetype_for_deletion(archetype);
8393 void remove_chunk_from_delete_queue(uint32_t idx) {
8394 GAIA_ASSERT(idx < m_chunksToDel.size());
8396 auto* pChunk = m_chunksToDel[idx].pChunk;
8397 pChunk->clear_delete_queue_index();
8399 const auto lastIdx = (uint32_t)m_chunksToDel.size() - 1;
8400 if (idx != lastIdx) {
8401 auto* pMovedChunk = m_chunksToDel[lastIdx].pChunk;
8402 pMovedChunk->delete_queue_index(idx);
8405 core::swap_erase(m_chunksToDel, idx);
8412 void remove_entity(Archetype& archetype, Chunk& chunk, uint16_t row) {
8413 archetype.remove_entity(chunk, row, m_recs);
8414 try_enqueue_chunk_for_deletion(archetype, chunk);
8418 void del_empty_chunks() {
8419 GAIA_PROF_SCOPE(World::del_empty_chunks);
8421 for (uint32_t i = 0; i < m_chunksToDel.size();) {
8422 auto* pArchetype = m_chunksToDel[i].pArchetype;
8423 auto* pChunk = m_chunksToDel[i].pChunk;
8426 if (!pChunk->empty()) {
8428 revive_archetype(*pArchetype);
8429 remove_chunk_from_delete_queue(i);
8434 if (pChunk->progress_death()) {
8440 remove_chunk(*pArchetype, *pChunk);
8441 remove_chunk_from_delete_queue(i);
8446 void del_empty_archetype(Archetype* pArchetype) {
8447 GAIA_PROF_SCOPE(World::del_empty_archetype);
8449 GAIA_ASSERT(pArchetype !=
nullptr);
8450 GAIA_ASSERT(pArchetype->empty() || pArchetype->is_req_del());
8451 GAIA_ASSERT(!pArchetype->dying() || pArchetype->is_req_del());
8453 unreg_archetype(pArchetype);
8454 for (
auto& ec: m_recs.entities) {
8455 if (ec.pArchetype != pArchetype)
8458 ec.pArchetype =
nullptr;
8459 ec.pChunk =
nullptr;
8460 ec.pEntity =
nullptr;
8462 for (
auto& [_, ec]: m_recs.pairs) {
8463 if (ec.pArchetype != pArchetype)
8466 ec.pArchetype =
nullptr;
8467 ec.pChunk =
nullptr;
8468 ec.pEntity =
nullptr;
8470 Archetype::destroy(pArchetype);
8474 void del_empty_archetypes() {
8475 GAIA_PROF_SCOPE(World::del_empty_archetypes);
8477 cnt::sarray_ext<Archetype*, 512> tmp;
8485 auto remove_from_queries = [&]() {
8489 for (
auto* pArchetype: tmp) {
8490 m_queryCache.remove_archetype_from_queries(pArchetype);
8491 del_empty_archetype(pArchetype);
8496 for (uint32_t i = 0; i < m_archetypesToDel.size();) {
8497 auto* pArchetype = m_archetypesToDel[i];
8500 if (!pArchetype->empty() || pArchetype->max_lifespan() == 0) {
8501 revive_archetype(*pArchetype);
8502 core::swap_erase(m_archetypesToDel, i);
8508 if (!pArchetype->is_req_del() && pArchetype->progress_death()) {
8513 tmp.push_back(pArchetype);
8516 core::swap_erase(m_archetypesToDel, i);
8519 if (tmp.size() == tmp.max_size())
8520 remove_from_queries();
8523 remove_from_queries();
8526 void revive_archetype(Archetype& archetype) {
8528 m_reqArchetypesToDel.erase(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
8531 void try_enqueue_chunk_for_deletion(Archetype& archetype, Chunk& chunk) {
8532 if (chunk.dying() || !chunk.empty())
8547 chunk.start_dying();
8549 m_chunksToDel.push_back({&archetype, &chunk});
8550 chunk.delete_queue_index((uint32_t)m_chunksToDel.size() - 1);
8553 void try_enqueue_archetype_for_deletion(Archetype& archetype) {
8554 if (!archetype.ready_to_die())
8569 archetype.start_dying();
8571 m_archetypesToDel.push_back(&archetype);
8576 void defrag_chunks(uint32_t maxEntities) {
8577 GAIA_PROF_SCOPE(World::defrag_chunks);
8579 const auto maxIters = m_archetypes.size();
8581 GAIA_ASSERT(maxIters > 0);
8583 GAIA_FOR(maxIters) {
8584 const auto idx = (m_defragLastArchetypeIdx + 1) % maxIters;
8585 auto* pArchetype = m_archetypes[idx];
8586 defrag_archetype(*pArchetype, maxEntities);
8587 if (maxEntities == 0)
8590 m_defragLastArchetypeIdx = idx;
8597 void defrag_archetype(Archetype& archetype, uint32_t& maxEntities) {
8621 if (maxEntities == 0)
8624 const auto& chunks = archetype.chunks();
8625 if (chunks.size() < 2)
8629 uint32_t back = chunks.size() - 1;
8631 auto* pDstChunk = chunks[front];
8632 auto* pSrcChunk = chunks[back];
8635 while (front < back && (pDstChunk->full() || !pDstChunk->is_semi()))
8636 pDstChunk = chunks[++front];
8638 while (front < back && (pSrcChunk->empty() || !pSrcChunk->is_semi()))
8639 pSrcChunk = chunks[--back];
8641 const auto& props = archetype.props();
8642 const bool hasUniEnts =
8643 props.cntEntities > 0 && archetype.ids_view()[props.cntEntities - 1].kind() == EntityKind::EK_Uni;
8646 while (front < back) {
8647 pDstChunk = chunks[front];
8648 pSrcChunk = chunks[back];
8650 const uint32_t entitiesInSrcChunk = pSrcChunk->size();
8651 const uint32_t spaceInDstChunk = pDstChunk->capacity() - pDstChunk->size();
8652 const uint32_t entitiesToMoveSrc = core::get_min(entitiesInSrcChunk, maxEntities);
8653 const uint32_t entitiesToMove = core::get_min(entitiesToMoveSrc, spaceInDstChunk);
8657 auto rec = pSrcChunk->comp_rec_view();
8659 GAIA_FOR2(props.genEntities, props.cntEntities) {
8660 const auto* pSrcVal = (
const void*)pSrcChunk->comp_ptr(i, 0);
8661 const auto* pDstVal = (
const void*)pDstChunk->comp_ptr(i, 0);
8662 if (rec[i].pItem->cmp(pSrcVal, pDstVal)) {
8670 pDstChunk = chunks[++front];
8671 goto next_iteration;
8675 GAIA_FOR(entitiesToMove) {
8676 const auto lastSrcEntityIdx = entitiesInSrcChunk - i - 1;
8677 const auto entity = pSrcChunk->entity_view()[lastSrcEntityIdx];
8679 auto& ec = m_recs[entity];
8681 const auto srcRow = ec.row;
8682 const auto dstRow = pDstChunk->add_entity(entity);
8683 const bool wasEnabled = !ec.data.dis;
8686 archetype.enable_entity(pSrcChunk, srcRow,
true, m_recs);
8688 GAIA_ASSERT(srcRow == ec.row);
8691 pDstChunk->move_entity_data(entity, dstRow, m_recs);
8698 archetype.remove_entity_raw(*pSrcChunk, srcRow, m_recs);
8699 try_enqueue_chunk_for_deletion(archetype, *pSrcChunk);
8702 ec.pChunk = pDstChunk;
8703 ec.row = (uint16_t)dstRow;
8704 ec.pEntity = &pDstChunk->entity_view()[dstRow];
8707 archetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
8711 if (entitiesToMove > 0) {
8712 pSrcChunk->update_world_version();
8713 pDstChunk->update_world_version();
8714 pSrcChunk->update_entity_order_version();
8715 pDstChunk->update_entity_order_version();
8716 update_version(m_worldVersion);
8719 maxEntities -= entitiesToMove;
8720 if (maxEntities == 0)
8724 if (pSrcChunk->empty()) {
8725 while (front < back) {
8726 if (chunks[--back]->is_semi())
8734 while (front < back && pDstChunk->full())
8735 pDstChunk = chunks[++front];
8743 GAIA_NODISCARD Archetype* find_archetype(Archetype::LookupHash hashLookup, EntitySpan ids) {
8744 auto tmpArchetype = ArchetypeLookupChecker(ids);
8745 ArchetypeLookupKey key(hashLookup, &tmpArchetype);
8748 const auto it = m_archetypesByHash.find(key);
8749 if (it == m_archetypesByHash.end())
8752 auto* pArchetype = it->second;
8756 GAIA_NODISCARD
static auto
8757 find_component_index_record(ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
8758 return core::get_index_if(records, [&](
const auto& record) {
8759 return record.matches(pArchetype);
8763 GAIA_NODISCARD
static auto
8764 find_component_index_record(
const ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
8765 return core::get_index_if(records, [&](
const auto& record) {
8766 return record.matches(pArchetype);
8772 void update_entity_archetype_lookup_revision(EntityLookupKey entityKey) {
8773 auto [it, _] = m_entityToArchetypeMapVersions.try_emplace(entityKey, 0);
8776 if (it->second == 0)
8782 void add_entity_archetype_pair(
8783 Entity entity, Archetype* pArchetype, uint16_t compIdx = ComponentIndexBad, uint16_t matchCount = 1) {
8784 GAIA_ASSERT(pArchetype !=
nullptr);
8785 GAIA_ASSERT(matchCount > 0);
8787 EntityLookupKey entityKey(entity);
8788 const auto it = m_entityToArchetypeMap.find(entityKey);
8789 if (it == m_entityToArchetypeMap.end()) {
8790 ComponentIndexEntryArray records;
8791 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
8792 m_entityToArchetypeMap.try_emplace(entityKey, GAIA_MOV(records));
8796 auto& records = it->second;
8797 const auto idx = find_component_index_record(records, pArchetype);
8798 if (idx == BadIndex) {
8799 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
8803 auto& record = records[idx];
8804 record.matchCount = (uint16_t)(record.matchCount + matchCount);
8805 if (compIdx != ComponentIndexBad)
8806 record.compIdx = compIdx;
8809 void add_pair_archetype_query_pairs(Entity pair, Archetype* pArchetype, uint16_t matchCount = 1) {
8810 GAIA_ASSERT(pair.pair());
8811 GAIA_ASSERT(pArchetype !=
nullptr);
8812 GAIA_ASSERT(matchCount > 0);
8814 const auto first = get(pair.id());
8815 const auto second = get(pair.gen());
8817 add_entity_archetype_pair(Pair(All, second), pArchetype, ComponentIndexBad, matchCount);
8818 add_entity_archetype_pair(Pair(first, All), pArchetype, ComponentIndexBad, matchCount);
8819 add_entity_archetype_pair(Pair(All, All), pArchetype, ComponentIndexBad, matchCount);
8825 void del_entity_query_pair(Pair pair, Entity entityToRemove) {
8826 const auto entityKey = EntityLookupKey(pair);
8827 auto it = m_entityToArchetypeMap.find(entityKey);
8828 if (it == m_entityToArchetypeMap.end())
8830 auto& records = it->second;
8831 bool changed =
false;
8835 for (uint32_t i = records.size() - 1; i != (uint32_t)-1; --i) {
8836 auto& record = records[i];
8837 const auto* pArchetype = record.pArchetype;
8838 if (!pArchetype->has(entityToRemove))
8841 if ((!is_wildcard(pair.first()) && !is_wildcard(pair.second())) || record.matchCount <= 1)
8842 core::swap_erase_unsafe(records, i);
8844 --record.matchCount;
8849 update_entity_archetype_lookup_revision(entityKey);
8851 if (records.empty())
8852 m_entityToArchetypeMap.erase(it);
8857 void del_entity_query_pair(Pair pair, Archetype* pArchetypeToRemove) {
8858 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
8860 const auto entityKey = EntityLookupKey(pair);
8861 auto it = m_entityToArchetypeMap.find(entityKey);
8862 if (it == m_entityToArchetypeMap.end())
8865 auto& records = it->second;
8866 const auto idx = find_component_index_record(records, pArchetypeToRemove);
8867 if (idx != BadIndex) {
8868 core::swap_erase_unsafe(records, idx);
8869 update_entity_archetype_lookup_revision(entityKey);
8872 if (records.empty())
8873 m_entityToArchetypeMap.erase(it);
8876 void del_pair_archetype_query_pairs(Entity pair, Archetype* pArchetypeToRemove) {
8877 GAIA_ASSERT(pair.pair());
8878 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
8880 GAIA_ASSERT(pair.id() < m_recs.entities.size());
8881 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
8882 const auto first = m_recs.entities.handle(pair.id());
8883 const auto second = m_recs.entities.handle(pair.gen());
8885 del_entity_query_pair(Pair(All, second), pArchetypeToRemove);
8886 del_entity_query_pair(Pair(first, All), pArchetypeToRemove);
8887 del_entity_query_pair(Pair(All, All), pArchetypeToRemove);
8890 void del_pair_archetype_query_pairs(Entity pair, Entity entityToRemove) {
8891 GAIA_ASSERT(pair.pair());
8893 GAIA_ASSERT(pair.id() < m_recs.entities.size());
8894 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
8895 const auto first = m_recs.entities.handle(pair.id());
8896 const auto second = m_recs.entities.handle(pair.gen());
8898 del_entity_query_pair(Pair(All, second), entityToRemove);
8899 del_entity_query_pair(Pair(first, All), entityToRemove);
8900 del_entity_query_pair(Pair(All, All), entityToRemove);
8905 void del_entity_archetype_pair(Entity entity, Archetype* pArchetypeToRemove) {
8906 GAIA_ASSERT(entity != Pair(All, All));
8907 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
8909 const auto entityKey = EntityLookupKey(entity);
8910 auto it = m_entityToArchetypeMap.find(entityKey);
8911 if (it == m_entityToArchetypeMap.end())
8914 auto& records = it->second;
8915 const auto idx = find_component_index_record(records, pArchetypeToRemove);
8916 if (idx != BadIndex) {
8917 core::swap_erase_unsafe(records, idx);
8918 update_entity_archetype_lookup_revision(entityKey);
8921 if (records.empty())
8922 m_entityToArchetypeMap.erase(it);
8926 void del_archetype_entity_pairs(Archetype* pArchetype) {
8927 GAIA_ASSERT(pArchetype !=
nullptr);
8929 for (
const auto entity: pArchetype->ids_view()) {
8930 del_entity_archetype_pair(entity, pArchetype);
8938 GAIA_ASSERT(entity.id() < m_recs.entities.size());
8939 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
8940 del_pair_archetype_query_pairs(entity, pArchetype);
8947 void del_entity_archetype_pairs(Entity entity, Archetype* pArchetype) {
8948 GAIA_ASSERT(entity != Pair(All, All));
8950 const auto entityKey = EntityLookupKey(entity);
8951 if (m_entityToArchetypeMap.erase(entityKey) != 0)
8952 update_entity_archetype_lookup_revision(entityKey);
8954 if (entity.pair()) {
8955 if (pArchetype !=
nullptr) {
8956 del_pair_archetype_query_pairs(entity, pArchetype);
8958 del_pair_archetype_query_pairs(entity, entity);
8966 GAIA_NODISCARD Archetype* create_archetype(EntitySpan entities) {
8967 GAIA_ASSERT(m_nextArchetypeId < (
decltype(m_nextArchetypeId))-1);
8968 auto* pArchetype = Archetype::create(*
this, m_nextArchetypeId++, m_worldVersion, entities);
8970 const auto entityCnt = (uint32_t)entities.size();
8971 GAIA_FOR(entityCnt) {
8972 auto entity = entities[i];
8973 add_entity_archetype_pair(entity, pArchetype, (uint16_t)i);
8975#if GAIA_OBSERVERS_ENABLED
8976 auto& ec = fetch(entity);
8977 if ((ec.flags & EntityContainerFlags::IsObserved) != 0 || m_observers.has_observers(entity)) {
8978 ec.flags |= EntityContainerFlags::IsObserved;
8979 pArchetype->observed_terms_inc();
8985 if (entity.pair()) {
8986 add_pair_archetype_query_pairs(entity, pArchetype);
8995 void reg_archetype(Archetype* pArchetype) {
8996 GAIA_ASSERT(pArchetype !=
nullptr);
9003 GAIA_ASSERT(pArchetype->list_idx() == BadIndex);
9006 [[maybe_unused]]
const auto it0 =
9007 m_archetypesById.emplace(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()), pArchetype);
9008 [[maybe_unused]]
const auto it1 =
9009 m_archetypesByHash.emplace(ArchetypeLookupKey(pArchetype->lookup_hash(), pArchetype), pArchetype);
9011 GAIA_ASSERT(it0.second);
9012 GAIA_ASSERT(it1.second);
9014 pArchetype->list_idx(m_archetypes.size());
9015 m_archetypes.emplace_back(pArchetype);
9017 m_queryCache.register_archetype_with_queries(pArchetype);
9022 void unreg_archetype(Archetype* pArchetype) {
9023 GAIA_ASSERT(pArchetype !=
nullptr);
9027 (m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));
9030 GAIA_ASSERT(pArchetype->list_idx() != BadIndex);
9035 del_archetype_entity_pairs(pArchetype);
9039 auto& edgeLefts = pArchetype->left_edges();
9040 for (
auto& itLeft: edgeLefts)
9041 remove_edge_from_archetype(pArchetype, itLeft.second, itLeft.first.entity());
9044 auto tmpArchetype = ArchetypeLookupChecker(pArchetype->ids_view());
9045 [[maybe_unused]]
const auto res0 =
9046 m_archetypesById.erase(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()));
9047 [[maybe_unused]]
const auto res1 =
9048 m_archetypesByHash.erase(ArchetypeLookupKey(pArchetype->lookup_hash(), &tmpArchetype));
9049 GAIA_ASSERT(res0 != 0);
9050 GAIA_ASSERT(res1 != 0);
9052 const auto idx = pArchetype->list_idx();
9053 GAIA_ASSERT(idx == core::get_index(m_archetypes, pArchetype));
9054 core::swap_erase(m_archetypes, idx);
9055 update_entity_archetype_lookup_revision(EntityBadLookupKey);
9056 if (!m_archetypes.empty() && idx != m_archetypes.size())
9057 m_archetypes[idx]->list_idx(idx);
9060#if GAIA_ASSERT_ENABLED
9061 static void print_archetype_entities(
const World& world,
const Archetype& archetype, Entity entity,
bool adding) {
9062 auto ids = archetype.ids_view();
9064 GAIA_LOG_W(
"Currently present:");
9066 const auto name = entity_name(world, ids[i]);
9068 "> [%u] %.*s [%s]", i, (
int)name.size(), name.empty() ?
"" : name.data(),
9069 EntityKindString[(uint32_t)ids[i].kind()]);
9072 GAIA_LOG_W(
"Trying to %s:", adding ?
"add" :
"del");
9073 const auto name = entity_name(world, entity);
9075 "> %.*s [%s]", (
int)name.size(), name.empty() ?
"" : name.data(),
9076 EntityKindString[(uint32_t)entity.kind()]);
9079 static void verify_add(
const World& world, Archetype& archetype, Entity entity, Entity addEntity) {
9081 if (world.locked()) {
9082 GAIA_ASSERT2(
false,
"Trying to add an entity while the world is locked");
9083 GAIA_LOG_W(
"Trying to add an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
9084 print_archetype_entities(world, archetype, entity,
false);
9089 if (is_wildcard(addEntity)) {
9090 GAIA_ASSERT2(
false,
"Adding wildcard pairs is not supported");
9091 print_archetype_entities(world, archetype, addEntity,
true);
9096 auto ids = archetype.ids_view();
9097 if GAIA_UNLIKELY (ids.size() + 1 >= ChunkHeader::MAX_COMPONENTS) {
9098 GAIA_ASSERT2(
false,
"Trying to add too many entities to entity!");
9099 GAIA_LOG_W(
"Trying to add an entity to entity [%u:%u] but there's no space left!", entity.id(), entity.gen());
9100 print_archetype_entities(world, archetype, addEntity,
true);
9105 static void verify_del(
const World& world, Archetype& archetype, Entity entity, Entity func_del) {
9107 if (world.locked()) {
9108 GAIA_ASSERT2(
false,
"Trying to delete an entity while the world is locked");
9109 GAIA_LOG_W(
"Trying to delete an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
9110 print_archetype_entities(world, archetype, entity,
false);
9115 if GAIA_UNLIKELY (!archetype.has(func_del)) {
9116 GAIA_ASSERT2(
false,
"Trying to remove an entity which wasn't added");
9117 GAIA_LOG_W(
"Trying to del an entity from entity [%u:%u] but it was never added", entity.id(), entity.gen());
9118 print_archetype_entities(world, archetype, func_del,
false);
9123 static void verify_enable(
const World& world, Archetype& archetype, Entity entity) {
9124 if (world.locked()) {
9125 GAIA_ASSERT2(
false,
"Trying to enable/disable an entity while the world is locked");
9126 GAIA_LOG_W(
"Trying to enable/disable an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
9127 print_archetype_entities(world, archetype, entity,
false);
9131 static void verify_move(
const World& world, Archetype& archetype, Entity entity) {
9132 if (world.locked()) {
9133 GAIA_ASSERT2(
false,
"Trying to move an entity while the world is locked");
9134 GAIA_LOG_W(
"Trying to move an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
9135 print_archetype_entities(world, archetype, entity,
false);
9145 GAIA_NODISCARD Archetype* foc_archetype_add(Archetype* pArchetypeLeft, Entity entity) {
9147 bool edgeNeedsRebuild =
false;
9149 const auto edge = pArchetypeLeft->find_edge_right(entity);
9150 if (edge != ArchetypeIdHashPairBad) {
9151 auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
9152 if (it != m_archetypesById.end() && it->second !=
nullptr)
9156 pArchetypeLeft->del_graph_edge_right_local(entity);
9157 edgeNeedsRebuild =
true;
9162 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
9164 auto entsOld = pArchetypeLeft->ids_view();
9165 const auto entsOldCnt = entsOld.size();
9166 entsNew.resize((uint32_t)entsOld.size() + 1);
9167 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
9168 entsNew[(uint32_t)entsOld.size()] = entity;
9173 sort(entsNew, SortComponentCond{});
9176 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
9177 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
9178 if (pArchetypeRight ==
nullptr) {
9179 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
9180 pArchetypeRight->set_hashes({hashLookup});
9181 reg_archetype(pArchetypeRight);
9182 edgeNeedsRebuild =
true;
9185 if (edgeNeedsRebuild)
9186 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
9188 return pArchetypeRight;
9193 GAIA_NODISCARD Archetype* foc_archetype_add_no_graph(Archetype* pArchetypeLeft, Entity entity) {
9194 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
9196 auto entsOld = pArchetypeLeft->ids_view();
9197 const auto entsOldCnt = entsOld.size();
9198 entsNew.resize((uint32_t)entsOld.size() + 1);
9199 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
9200 entsNew[(uint32_t)entsOld.size()] = entity;
9203 sort(entsNew, SortComponentCond{});
9205 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
9206 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
9207 if (pArchetypeRight !=
nullptr)
9208 return pArchetypeRight;
9210 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
9211 pArchetypeRight->set_hashes({hashLookup});
9212 reg_archetype(pArchetypeRight);
9213 return pArchetypeRight;
9221 GAIA_NODISCARD Archetype* foc_archetype_del(Archetype* pArchetypeRight, Entity entity) {
9223 bool edgeNeedsRebuild =
false;
9225 const auto edge = pArchetypeRight->find_edge_left(entity);
9226 if (edge != ArchetypeIdHashPairBad) {
9227 const auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
9228 if (it != m_archetypesById.end()) {
9229 auto* pArchetypeLeft = it->second;
9230 if (pArchetypeLeft !=
nullptr)
9231 return pArchetypeLeft;
9235 pArchetypeRight->del_graph_edge_left_local(entity);
9236 edgeNeedsRebuild =
true;
9240 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
9241 auto entsOld = pArchetypeRight->ids_view();
9244 for (
const auto e: entsOld) {
9248 entsNew.push_back(e);
9252 GAIA_ASSERT(entsNew.size() != entsOld.size());
9255 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
9256 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
9257 if (pArchetype ==
nullptr) {
9258 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
9259 pArchetype->set_hashes({hashLookup});
9260 reg_archetype(pArchetype);
9261 edgeNeedsRebuild =
true;
9264 if (edgeNeedsRebuild)
9265 pArchetype->build_graph_edges(pArchetypeRight, entity);
9272 GAIA_NODISCARD Archetype* foc_archetype_del_no_graph(Archetype* pArchetypeRight, Entity entity) {
9273 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
9274 auto entsOld = pArchetypeRight->ids_view();
9276 for (
const auto e: entsOld) {
9280 entsNew.push_back(e);
9283 GAIA_ASSERT(entsNew.size() != entsOld.size());
9285 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
9286 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
9287 if (pArchetype !=
nullptr)
9290 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
9291 pArchetype->set_hashes({hashLookup});
9292 reg_archetype(pArchetype);
9298 GAIA_NODISCARD
const auto& archetypes()
const {
9299 return m_archetypes;
9305 GAIA_NODISCARD Archetype& archetype(Entity entity) {
9306 const auto& ec = fetch(entity);
9307 return *ec.pArchetype;
9313 void del_name(EntityContainer& ec, Entity entity) {
9314 EntityBuilder(*
this, entity, ec).
del_name();
9319 void del_name(Entity entity) {
9320 EntityBuilder(*
this, entity).
del_name();
9327 void del_entity(Entity entity,
bool invalidate) {
9328 if (entity.pair() || entity == EntityBad)
9331 auto& ec = fetch(entity);
9332 del_entity_inter(ec, entity, invalidate);
9340 void del_entity(EntityContainer& ec, Entity entity,
bool invalidate) {
9341 if (entity.pair() || entity == EntityBad)
9344 del_entity_inter(ec, entity, invalidate);
9351 void del_entity_inter(EntityContainer& ec, Entity entity,
bool invalidate) {
9352 GAIA_ASSERT(entity.id() > GAIA_ID(LastCoreComponent).
id());
9356 if (m_recs.entities.item_count() == 0)
9359#if GAIA_ASSERT_ENABLED
9360 auto* pChunk = ec.pChunk;
9361 GAIA_ASSERT(pChunk !=
nullptr);
9368 del_name(ec, entity);
9369 remove_entity(*ec.pArchetype, *ec.pChunk, ec.row);
9370 remove_src_entity_version(entity);
9376 invalidate_entity(entity);
9384 void del_entities(Archetype& archetype) {
9385 for (
auto* pChunk: archetype.chunks()) {
9386 auto ids = pChunk->entity_view();
9391#if GAIA_ASSERT_ENABLED
9392 const auto& ec = fetch(e);
9395 GAIA_ASSERT((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) == 0);
9398 del_entity(e,
true);
9401 validate_chunk(pChunk);
9405 if (pChunk->queued_for_deletion())
9406 remove_chunk_from_delete_queue(pChunk->delete_queue_index());
9408 remove_chunk(archetype, *pChunk);
9411 validate_entities();
9416 void del_inter(Entity entity) {
9417 auto on_delete = [
this](Entity entityToDel) {
9418 auto& ec = fetch(entityToDel);
9419 handle_del_entity(ec, entityToDel);
9422 if (is_wildcard(entity)) {
9423 const auto rel = get(entity.id());
9424 const auto tgt = get(entity.gen());
9427 if (rel == All && tgt == All) {
9428 GAIA_ASSERT2(
false,
"Not supported yet");
9431 else if (rel == All) {
9432 if (
const auto* pTargets = relations(tgt)) {
9435 cnt::darray_ext<Entity, 64> tmp;
9436 for (
auto key: *pTargets)
9437 tmp.push_back(key.entity());
9439 on_delete(Pair(e, tgt));
9443 else if (tgt == All) {
9444 if (
const auto* pRelations = targets(rel)) {
9447 cnt::darray_ext<Entity, 64> tmp;
9448 for (
auto key: *pRelations)
9449 tmp.push_back(key.entity());
9451 on_delete(Pair(rel, e));
9460 void del_finalize_archetypes() {
9461 GAIA_PROF_SCOPE(World::del_finalize_archetypes);
9463 for (
auto& key: m_reqArchetypesToDel) {
9464 auto* pArchetype = key.archetype();
9465 if (pArchetype ==
nullptr)
9468 del_entities(*pArchetype);
9475 m_reqArchetypesToDel.clear();
9479 void del_finalize_entities() {
9480 GAIA_PROF_SCOPE(World::del_finalize_entities);
9482 for (
auto it = m_reqEntitiesToDel.begin(); it != m_reqEntitiesToDel.end();) {
9483 const auto e = it->entity();
9486 if (m_entityToArchetypeMap.contains(*it)) {
9492 invalidate_entity(e);
9494 it = m_reqEntitiesToDel.erase(it);
9499 void del_finalize() {
9500 GAIA_PROF_SCOPE(World::del_finalize);
9502 del_finalize_archetypes();
9503 del_finalize_entities();
9506 GAIA_NODISCARD
bool archetype_cond_match(Archetype& archetype, Pair cond, Entity target)
const {
9511 auto ids = archetype.ids_view();
9513 if (target.pair()) {
9518 if (e.gen() != target.gen())
9521 const auto& ec = m_recs.entities[e.id()];
9522 const auto entity = ec.pChunk->entity_view()[ec.row];
9523 if (!has(entity, cond))
9545 void move_to_archetype(Archetype& srcArchetype, Archetype& dstArchetype) {
9546 GAIA_ASSERT(&srcArchetype != &dstArchetype);
9548 bool updated =
false;
9550 for (
auto* pSrcChunk: srcArchetype.chunks()) {
9551 auto srcEnts = pSrcChunk->entity_view();
9552 if (srcEnts.empty())
9565 uint32_t i = (uint32_t)srcEnts.size();
9567 auto* pDstChunk = dstArchetype.foc_free_chunk();
9568 const uint32_t dstSpaceLeft = pDstChunk->capacity() - pDstChunk->size();
9569 const uint32_t cnt = core::get_min(dstSpaceLeft, i);
9570 for (uint32_t j = 0; j < cnt; ++j) {
9571 auto e = srcEnts[i - j - 1];
9572 move_entity(e, fetch(e), dstArchetype, *pDstChunk);
9575 pDstChunk->update_world_version();
9576 pDstChunk->update_entity_order_version();
9578 GAIA_ASSERT(cnt <= i);
9582 pSrcChunk->update_world_version();
9583 pSrcChunk->update_entity_order_version();
9588 update_version(m_worldVersion);
9595 GAIA_NODISCARD Archetype* calc_dst_archetype_ent(Archetype* pArchetype, Entity entity) {
9596 GAIA_ASSERT(!is_wildcard(entity));
9598 auto ids = pArchetype->ids_view();
9599 for (
auto id: ids) {
9603 return foc_archetype_del(pArchetype,
id);
9613 GAIA_NODISCARD Archetype* calc_dst_archetype_all_ent(Archetype* pArchetype, Entity entity) {
9614 GAIA_ASSERT(is_wildcard(entity));
9616 Archetype* pDstArchetype = pArchetype;
9618 auto ids = pArchetype->ids_view();
9619 for (
auto id: ids) {
9620 if (!
id.pair() ||
id.gen() != entity.gen())
9623 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
9626 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
9633 GAIA_NODISCARD Archetype* calc_dst_archetype_ent_all(Archetype* pArchetype, Entity entity) {
9634 GAIA_ASSERT(is_wildcard(entity));
9636 Archetype* pDstArchetype = pArchetype;
9638 auto ids = pArchetype->ids_view();
9639 for (
auto id: ids) {
9640 if (!
id.pair() ||
id.
id() != entity.id())
9643 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
9646 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
9653 GAIA_NODISCARD Archetype* calc_dst_archetype_all_all(Archetype* pArchetype, [[maybe_unused]] Entity entity) {
9654 GAIA_ASSERT(is_wildcard(entity));
9656 Archetype* pDstArchetype = pArchetype;
9659 auto ids = pArchetype->ids_view();
9660 for (
auto id: ids) {
9664 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
9668 return found ? pDstArchetype :
nullptr;
9676 GAIA_NODISCARD Archetype* calc_dst_archetype(Archetype* pArchetype, Entity entity) {
9677 if (entity.pair()) {
9678 auto rel = entity.id();
9679 auto tgt = entity.gen();
9682 if (rel == All.id() || tgt == All.id()) {
9684 if (rel != All.id() && tgt == All.id())
9685 return calc_dst_archetype_ent_all(pArchetype, entity);
9688 if (rel == All.id() && tgt != All.id())
9689 return calc_dst_archetype_all_ent(pArchetype, entity);
9692 return calc_dst_archetype_all_all(pArchetype, EntityBad);
9697 return calc_dst_archetype_ent(pArchetype, entity);
9700 void req_del(Archetype& archetype) {
9701 if (archetype.is_req_del())
9704 archetype.req_del();
9705 m_reqArchetypesToDel.insert(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
9708 void req_del(EntityContainer& ec, Entity entity) {
9712#if GAIA_OBSERVERS_ENABLED
9714 m_observers.prepare_diff(*
this, ObserverEvent::OnDel, EntitySpan{&entity, 1}, EntitySpan{&entity, 1});
9716 del_entity(ec, entity,
false);
9717#if GAIA_OBSERVERS_ENABLED
9718 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
9722 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
9725 void invalidate_pair_removal_caches(Entity entity) {
9729 auto invalidate_relation = [
this](Entity relation) {
9730 if (relation == EntityBad)
9732 touch_rel_version(relation);
9733 invalidate_queries_for_rel(relation);
9736 if (entity.id() != All.id()) {
9737 invalidate_relation(try_get(entity.id()));
9738 }
else if (entity.gen() != All.id()) {
9739 const auto target = try_get(entity.gen());
9740 if (target != EntityBad) {
9741 if (
const auto* pRelations = relations(target)) {
9742 for (
auto relationKey: *pRelations)
9743 invalidate_relation(relationKey.entity());
9747 for (
const auto& [relationKey, _]: m_relToTgt)
9748 invalidate_relation(relationKey.entity());
9751 m_targetsTravCache = {};
9752 m_srcBfsTravCache = {};
9753 m_depthOrderCache = {};
9754 m_sourcesAllCache = {};
9755 m_targetsAllCache = {};
9758 void unlink_live_is_relation(Entity source, Entity target) {
9759 const auto sourceKey = EntityLookupKey(source);
9760 const auto targetKey = EntityLookupKey(target);
9762 invalidate_queries_for_entity({Is, target});
9764 if (
const auto itTargets = m_entityToAsTargets.find(sourceKey); itTargets != m_entityToAsTargets.end()) {
9765 itTargets->second.erase(targetKey);
9766 if (itTargets->second.empty())
9767 m_entityToAsTargets.erase(itTargets);
9769 m_entityToAsTargetsTravCache = {};
9771 if (
const auto itRelations = m_entityToAsRelations.find(targetKey);
9772 itRelations != m_entityToAsRelations.end()) {
9773 itRelations->second.erase(sourceKey);
9774 if (itRelations->second.empty())
9775 m_entityToAsRelations.erase(itRelations);
9777 m_entityToAsRelationsTravCache = {};
9780 void unlink_stale_is_relations_by_target_id(Entity source, EntityId targetId) {
9781 const auto sourceKey = EntityLookupKey(source);
9782 const auto itTargets = m_entityToAsTargets.find(sourceKey);
9783 if (itTargets == m_entityToAsTargets.end())
9786 cnt::darray_ext<EntityLookupKey, 4> removedTargets;
9787 for (
auto targetKey: itTargets->second) {
9788 if (targetKey.entity().id() == targetId)
9789 removedTargets.push_back(targetKey);
9792 for (
auto targetKey: removedTargets) {
9793 invalidate_queries_for_structural_entity(EntityLookupKey(Pair{Is, targetKey.entity()}));
9794 itTargets->second.erase(targetKey);
9796 const auto itRelations = m_entityToAsRelations.find(targetKey);
9797 if (itRelations != m_entityToAsRelations.end()) {
9798 itRelations->second.erase(sourceKey);
9799 if (itRelations->second.empty())
9800 m_entityToAsRelations.erase(itRelations);
9804 if (itTargets->second.empty())
9805 m_entityToAsTargets.erase(itTargets);
9807 m_entityToAsTargetsTravCache = {};
9808 m_entityToAsRelationsTravCache = {};
9811 template <
typename Func>
9812 void each_delete_cascade_direct_source(Entity target, Pair cond, Func&& func) {
9813 GAIA_ASSERT(!target.pair());
9815 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
9816 const auto relation = relKey.entity();
9817 if (!has(relation, cond))
9820 const auto* pSources = exclusive_adjunct_sources(store, target);
9821 if (pSources ==
nullptr)
9824 for (
auto source: *pSources)
9828 const auto pairEntity = Pair(All, target);
9829 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(pairEntity));
9830 if (it != m_entityToArchetypeMap.end()) {
9831 for (
const auto& record: it->second) {
9832 auto* pArchetype = record.pArchetype;
9833 if (pArchetype ==
nullptr || pArchetype->is_req_del())
9835 if (!archetype_cond_match(*pArchetype, cond, pairEntity))
9838 for (
const auto* pChunk: pArchetype->chunks()) {
9839 const auto entities = pChunk->entity_view();
9847 void collect_delete_cascade_direct_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
9848 GAIA_ASSERT(!target.pair());
9849 const auto visitStamp = next_entity_visit_stamp();
9850 each_delete_cascade_direct_source(target, cond, [&](Entity source) {
9853 if (!try_mark_entity_visited(source, visitStamp))
9855 out.push_back(source);
9859 void collect_delete_cascade_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
9860 GAIA_ASSERT(!target.pair());
9861 const auto visitStamp = next_entity_visit_stamp();
9862 cnt::darray_ext<Entity, 32> targetsToVisit;
9863 (void)try_mark_entity_visited(target, visitStamp);
9864 targetsToVisit.push_back(target);
9866 for (uint32_t i = 0; i < targetsToVisit.size(); ++i) {
9867 const auto currTarget = targetsToVisit[i];
9868 each_delete_cascade_direct_source(currTarget, cond, [&](Entity source) {
9871 if (!try_mark_entity_visited(source, visitStamp))
9874 out.push_back(source);
9875 targetsToVisit.push_back(source);
9882 void req_del_entities_with(Entity entity) {
9883 GAIA_PROF_SCOPE(World::req_del_entities_with);
9885 GAIA_ASSERT(entity != Pair(All, All));
9887 auto req_del_adjunct_pair = [&](Entity relation, Entity target) {
9888 cnt::darray<Entity> sourcesToDel;
9889 sources(relation, target, [&](Entity source) {
9890 sourcesToDel.push_back(source);
9893 for (
auto source: sourcesToDel)
9894 req_del(fetch(source), source);
9897 if (entity.pair()) {
9898 if (entity.id() != All.id()) {
9899 const auto relation = try_get(entity.id());
9900 const auto target = try_get(entity.gen());
9901 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
9902 req_del_adjunct_pair(relation, target);
9904 const auto target = try_get(entity.gen());
9905 if (target == EntityBad)
9906 goto skip_req_del_all_target;
9907 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
9908 if (exclusive_adjunct_sources(store, target) ==
nullptr)
9911 req_del_adjunct_pair(relKey.entity(), target);
9913 skip_req_del_all_target:;
9915 }
else if (is_exclusive_dont_fragment_relation(entity)) {
9916 if (
const auto* pTargets = targets(entity)) {
9917 for (
auto targetKey: *pTargets)
9918 req_del_adjunct_pair(entity, targetKey.entity());
9922 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
9923 if (it == m_entityToArchetypeMap.end())
9926 for (
const auto& record: it->second)
9927 req_del(*record.pArchetype);
9934 void req_del_entities_with(Entity entity, Pair cond) {
9935 cnt::set<EntityLookupKey> visited;
9936 req_del_entities_with(entity, cond, visited);
9939 void req_del_entities_with(Entity entity, Pair cond, cnt::set<EntityLookupKey>& visited) {
9940 GAIA_PROF_SCOPE(World::req_del_entities_with);
9942 GAIA_ASSERT(entity != Pair(All, All));
9943 if (!visited.insert(EntityLookupKey(entity)).second)
9946 auto req_del_adjunct_pair = [&](Entity relation, Entity target) {
9947 if (!has(relation, cond))
9950 cnt::darray<Entity> sourcesToDel;
9951 sources(relation, target, [&](Entity source) {
9952 sourcesToDel.push_back(source);
9955 for (
auto source: sourcesToDel)
9956 req_del(fetch(source), source);
9959 if (entity.pair()) {
9960 if (entity.id() != All.id()) {
9961 const auto relation = try_get(entity.id());
9962 const auto target = try_get(entity.gen());
9963 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
9964 req_del_adjunct_pair(relation, target);
9966 const auto target = try_get(entity.gen());
9967 if (target == EntityBad)
9968 goto skip_req_del_all_target_cond;
9969 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
9970 if (exclusive_adjunct_sources(store, target) ==
nullptr)
9973 req_del_adjunct_pair(relKey.entity(), target);
9975 skip_req_del_all_target_cond:;
9977 }
else if (is_exclusive_dont_fragment_relation(entity)) {
9978 if (
const auto* pTargets = targets(entity)) {
9979 for (
auto targetKey: *pTargets)
9980 req_del_adjunct_pair(entity, targetKey.entity());
9984 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
9985 if (it == m_entityToArchetypeMap.end())
9988 cnt::darray<Entity> cascadeTargets;
9989 if (entity.pair() && entity.id() == All.id()) {
9990 const auto target = try_get(entity.gen());
9991 if (target != EntityBad)
9992 collect_delete_cascade_direct_sources(target, cond, cascadeTargets);
9995 for (
auto source: cascadeTargets)
9996 req_del_entities_with(Pair(All, source), cond, visited);
9998 for (
const auto& record: it->second) {
9999 auto* pArchetype = record.pArchetype;
10001 if (!archetype_cond_match(*pArchetype, cond, entity))
10004 req_del(*pArchetype);
10010 void rem_from_entities(Entity entity) {
10011 GAIA_PROF_SCOPE(World::rem_from_entities);
10013 invalidate_pair_removal_caches(entity);
10015 auto rem_adjunct_pair = [&](Entity relation, Entity target) {
10016 cnt::darray<Entity> sourcesToRem;
10017 sources(relation, target, [&](Entity source) {
10018 sourcesToRem.push_back(source);
10021 for (
auto source: sourcesToRem)
10022 del(source, Pair(relation, target));
10025 if (entity.pair()) {
10026 if (entity.id() != All.id()) {
10027 const auto relation = try_get(entity.id());
10028 const auto target = try_get(entity.gen());
10029 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
10030 rem_adjunct_pair(relation, target);
10032 const auto target = try_get(entity.gen());
10033 if (target == EntityBad)
10034 goto skip_rem_all_target;
10035 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
10036 if (exclusive_adjunct_sources(store, target) ==
nullptr)
10039 rem_adjunct_pair(relKey.entity(), target);
10041 skip_rem_all_target:;
10043 }
else if (is_exclusive_dont_fragment_relation(entity)) {
10044 if (
const auto* pTargets = targets(entity)) {
10045 for (
auto targetKey: *pTargets)
10046 rem_adjunct_pair(entity, targetKey.entity());
10050 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
10051 if (it == m_entityToArchetypeMap.end())
10055 if (!entity.pair()) {
10056 auto& ec = fetch(entity);
10057 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
10058 auto ids = ec.pArchetype->ids_view();
10059 const auto idx = core::get_index(ids, entity);
10060 if (idx != BadIndex)
10061 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
10065#if GAIA_OBSERVERS_ENABLED
10066 cnt::set<EntityLookupKey> diffTermSet;
10067 cnt::darray<Entity> diffTerms;
10068 cnt::darray<Entity> diffTargets;
10069 for (
const auto& record: it->second) {
10070 auto* pArchetype = record.pArchetype;
10071 if (pArchetype->is_req_del())
10074 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
10075 if (pDstArchetype ==
nullptr)
10078 for (
auto id: pArchetype->ids_view()) {
10079 bool matches =
false;
10080 if (entity.pair()) {
10081 if (entity.id() == All.id() && entity.gen() == All.id())
10082 matches =
id.pair();
10083 else if (entity.id() == All.id())
10084 matches =
id.pair() &&
id.gen() == entity.gen();
10085 else if (entity.gen() == All.id())
10086 matches =
id.pair() &&
id.id() == entity.id();
10088 matches =
id == entity;
10090 matches =
id == entity;
10095 if (diffTermSet.insert(EntityLookupKey(
id)).second)
10096 diffTerms.push_back(
id);
10099 for (
const auto* pChunk: pArchetype->chunks()) {
10100 const auto entities = pChunk->entity_view();
10101 GAIA_EACH(entities)
10102 diffTargets.push_back(entities[i]);
10105 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
10106 ? ObserverRegistry::DiffDispatchCtx{}
10107 : m_observers.prepare_diff(
10108 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
10109 EntitySpan{diffTargets.data(), diffTargets.size()});
10113 for (
const auto& record: it->second) {
10114 auto* pArchetype = record.pArchetype;
10115 if (pArchetype->is_req_del())
10118 if (entity.pair()) {
10119 cnt::darray_ext<Entity, 16> removedIsTargets;
10120 for (
auto id: pArchetype->ids_view()) {
10121 bool matches =
false;
10122 if (entity.id() == All.id() && entity.gen() == All.id())
10123 matches =
id.pair();
10124 else if (entity.id() == All.id())
10125 matches =
id.pair() &&
id.gen() == entity.gen();
10126 else if (entity.gen() == All.id())
10127 matches =
id.pair() &&
id.id() == entity.id();
10129 matches =
id == entity;
10131 if (!matches || !
id.pair() ||
id.
id() != Is.id())
10134 const auto target = try_get(
id.gen());
10135 if (target != EntityBad)
10136 removedIsTargets.push_back(target);
10139 if (!removedIsTargets.empty()) {
10140 for (
const auto* pChunk: pArchetype->chunks()) {
10141 auto entities = pChunk->entity_view();
10142 GAIA_EACH(entities) {
10143 for (
const auto target: removedIsTargets)
10144 unlink_live_is_relation(entities[i], target);
10150 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
10151 if (pDstArchetype !=
nullptr)
10152 move_to_archetype(*pArchetype, *pDstArchetype);
10155#if GAIA_OBSERVERS_ENABLED
10156 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
10164 void rem_from_entities(Entity entity, Pair cond) {
10165 GAIA_PROF_SCOPE(World::rem_from_entities);
10167 invalidate_pair_removal_caches(entity);
10169 auto rem_adjunct_pair = [&](Entity relation, Entity target) {
10170 if (!has(relation, cond))
10173 cnt::darray<Entity> sourcesToRem;
10174 sources(relation, target, [&](Entity source) {
10175 sourcesToRem.push_back(source);
10178 for (
auto source: sourcesToRem)
10179 del(source, Pair(relation, target));
10182 if (entity.pair()) {
10183 if (entity.id() != All.id()) {
10184 const auto relation = try_get(entity.id());
10185 const auto target = try_get(entity.gen());
10186 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
10187 rem_adjunct_pair(relation, target);
10189 const auto target = try_get(entity.gen());
10190 if (target == EntityBad)
10191 goto skip_rem_all_target_cond;
10192 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
10193 if (exclusive_adjunct_sources(store, target) ==
nullptr)
10196 rem_adjunct_pair(relKey.entity(), target);
10198 skip_rem_all_target_cond:;
10200 }
else if (is_exclusive_dont_fragment_relation(entity)) {
10201 if (
const auto* pTargets = targets(entity)) {
10202 for (
auto targetKey: *pTargets)
10203 rem_adjunct_pair(entity, targetKey.entity());
10207 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
10208 if (it == m_entityToArchetypeMap.end())
10212 if (!entity.pair()) {
10213 auto& ec = fetch(entity);
10214 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
10215 auto ids = ec.pArchetype->ids_view();
10216 const auto idx = core::get_index(ids, entity);
10217 if (idx != BadIndex)
10218 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
10222#if GAIA_OBSERVERS_ENABLED
10223 cnt::set<EntityLookupKey> diffTermSet;
10224 cnt::darray<Entity> diffTerms;
10225 cnt::darray<Entity> diffTargets;
10226 for (
const auto& record: it->second) {
10227 auto* pArchetype = record.pArchetype;
10228 if (pArchetype->is_req_del())
10231 if (!archetype_cond_match(*pArchetype, cond, entity))
10234 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
10235 if (pDstArchetype ==
nullptr)
10238 for (
auto id: pArchetype->ids_view()) {
10239 bool matches =
false;
10240 if (entity.pair()) {
10241 if (entity.id() == All.id() && entity.gen() == All.id())
10242 matches =
id.pair();
10243 else if (entity.id() == All.id())
10244 matches =
id.pair() &&
id.gen() == entity.gen();
10245 else if (entity.gen() == All.id())
10246 matches =
id.pair() &&
id.id() == entity.id();
10248 matches =
id == entity;
10250 matches =
id == entity;
10255 if (diffTermSet.insert(EntityLookupKey(
id)).second)
10256 diffTerms.push_back(
id);
10259 for (
const auto* pChunk: pArchetype->chunks()) {
10260 const auto entities = pChunk->entity_view();
10261 GAIA_EACH(entities)
10262 diffTargets.push_back(entities[i]);
10265 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
10266 ? ObserverRegistry::DiffDispatchCtx{}
10267 : m_observers.prepare_diff(
10268 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
10269 EntitySpan{diffTargets.data(), diffTargets.size()});
10272 for (
const auto& record: it->second) {
10273 auto* pArchetype = record.pArchetype;
10274 if (pArchetype->is_req_del())
10278 if (!archetype_cond_match(*pArchetype, cond, entity))
10281 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
10282 if (pDstArchetype !=
nullptr)
10283 move_to_archetype(*pArchetype, *pDstArchetype);
10286#if GAIA_OBSERVERS_ENABLED
10287 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
10303 void handle_del_entity(EntityContainer& ec, Entity entity) {
10304 GAIA_PROF_SCOPE(World::handle_del_entity);
10306 GAIA_ASSERT(!is_wildcard(entity));
10308 if (entity.pair()) {
10309 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
10310 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
10312 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
10313 entity.gen(), name(entity), EntityKindString[entity.kind()]);
10317 const auto tgt = try_get(entity.gen());
10318 const bool hasLiveTarget = tgt != EntityBad;
10319 if (hasLiveTarget) {
10320 const auto& ecTgt = fetch(tgt);
10321 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
10322 has_exclusive_adjunct_target_cond(tgt, Pair(OnDeleteTarget, Error))) {
10324 false,
"Trying to delete an entity that is forbidden from being deleted (target restriction)");
10326 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted (target restriction)",
10327 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
10332#if GAIA_USE_SAFE_ENTITY
10334 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
10336 ec.flags |= EntityContainerFlags::RefDecreased;
10340 if (ec.refCnt != 0)
10344 if (hasLiveTarget) {
10345 const auto& ecTgt = fetch(tgt);
10346 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0 ||
10347 has_exclusive_adjunct_target_cond(tgt, Pair(OnDeleteTarget, Delete))) {
10348#if GAIA_OBSERVERS_ENABLED
10349 cnt::darray<Entity> cascadeTargets;
10350 collect_delete_cascade_sources(tgt, Pair(OnDeleteTarget, Delete), cascadeTargets);
10351 auto cascadeDelDiffCtx =
10352 cascadeTargets.empty()
10353 ? ObserverRegistry::DiffDispatchCtx{}
10354 : m_observers.prepare_diff(
10355 *
this, ObserverEvent::OnDel, EntitySpan{cascadeTargets.data(), cascadeTargets.size()},
10356 EntitySpan{cascadeTargets.data(), cascadeTargets.size()});
10359 req_del_entities_with(Pair(All, tgt), Pair(OnDeleteTarget, Delete));
10360#if GAIA_OBSERVERS_ENABLED
10361 m_observers.finish_diff(*
this, GAIA_MOV(cascadeDelDiffCtx));
10365 rem_from_entities(Pair(All, tgt));
10370 if (is_req_del(ec))
10373#if GAIA_OBSERVERS_ENABLED
10374 observers().del(*
this, entity);
10376#if GAIA_SYSTEMS_ENABLED
10377 systems().del(entity);
10380 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
10382 req_del_entities_with(entity);
10385 rem_from_entities(entity);
10388 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
10389 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
10391 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
10392 entity.gen(), name(entity), EntityKindString[entity.kind()]);
10396 if ((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
10397 has_exclusive_adjunct_target_cond(entity, Pair(OnDeleteTarget, Error))) {
10398 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted (a pair's target)");
10400 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted (a pair's target)",
10401 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
10405#if GAIA_USE_SAFE_ENTITY
10407 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
10409 ec.flags |= EntityContainerFlags::RefDecreased;
10413 if (ec.refCnt != 0)
10417 const bool deleteTargets = (ec.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0 ||
10418 has_exclusive_adjunct_target_cond(entity, Pair(OnDeleteTarget, Delete));
10419 cnt::darray<Entity> cascadeTargets;
10421 collect_delete_cascade_sources(entity, Pair(OnDeleteTarget, Delete), cascadeTargets);
10422#if GAIA_OBSERVERS_ENABLED
10423 auto cascadeDelDiffCtx = ObserverRegistry::DiffDispatchCtx{};
10424 if (!cascadeTargets.empty()) {
10425 cascadeDelDiffCtx = m_observers.prepare_diff(
10426 *
this, ObserverEvent::OnDel, EntitySpan{cascadeTargets.data(), cascadeTargets.size()},
10427 EntitySpan{cascadeTargets.data(), cascadeTargets.size()});
10431 if (deleteTargets) {
10433 req_del_entities_with(Pair(All, entity), Pair(OnDeleteTarget, Delete));
10436 rem_from_entities(Pair(All, entity));
10439#if GAIA_OBSERVERS_ENABLED
10440 m_observers.finish_diff(*
this, GAIA_MOV(cascadeDelDiffCtx));
10444 if (is_req_del(ec))
10447#if GAIA_OBSERVERS_ENABLED
10448 observers().del(*
this, entity);
10450#if GAIA_SYSTEMS_ENABLED
10451 systems().del(entity);
10454 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
10456 req_del_entities_with(entity);
10459 rem_from_entities(entity);
10464 req_del(ec, entity);
10466#if GAIA_USE_WEAK_ENTITY
10468 while (ec.pWeakTracker !=
nullptr) {
10469 auto* pTracker = ec.pWeakTracker;
10470 ec.pWeakTracker = pTracker->next;
10471 if (ec.pWeakTracker !=
nullptr)
10472 ec.pWeakTracker->prev =
nullptr;
10474 auto* pWeakEntity = pTracker->pWeakEntity;
10475 GAIA_ASSERT(pWeakEntity !=
nullptr);
10476 GAIA_ASSERT(pWeakEntity->m_pTracker == pTracker);
10477 pWeakEntity->m_pTracker =
nullptr;
10478 pWeakEntity->m_entity = EntityBad;
10488 void remove_edge_from_archetype(Archetype* pArchetype, ArchetypeGraphEdge edgeLeft, Entity edgeEntity) {
10489 GAIA_ASSERT(pArchetype !=
nullptr);
10491 const auto edgeLeftIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
10492 if (edgeLeftIt == m_archetypesById.end())
10495 auto* pArchetypeLeft = edgeLeftIt->second;
10496 GAIA_ASSERT(pArchetypeLeft !=
nullptr);
10499 pArchetypeLeft->del_graph_edges(pArchetype, edgeEntity);
10502 auto& archetypesRight = pArchetype->right_edges();
10503 for (
auto& it: archetypesRight) {
10504 const auto& edgeRight = it.second;
10505 const auto edgeRightIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
10506 if (edgeRightIt == m_archetypesById.end())
10509 auto* pArchetypeRight = edgeRightIt->second;
10512 pArchetype->del_graph_edges(pArchetypeRight, it.first.entity());
10516 void remove_edges(Entity entityToRemove) {
10517 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entityToRemove));
10518 if (it == m_entityToArchetypeMap.end())
10521 for (
const auto& record: it->second) {
10522 auto* pArchetype = record.pArchetype;
10523 remove_edge_from_archetype(pArchetype, pArchetype->find_edge_left(entityToRemove), entityToRemove);
10527 void remove_edges_from_pairs(Entity entity) {
10533 const auto* tgts = targets(entity);
10534 if (tgts !=
nullptr) {
10535 for (
auto target: *tgts)
10536 remove_edges(Pair(entity, target.entity()));
10539 const auto* rels = relations(entity);
10540 if (rels !=
nullptr) {
10541 for (
auto relation: *rels)
10542 remove_edges(Pair(relation.entity(), entity));
10548 void del_graph_edges(Entity entity) {
10549 remove_edges(entity);
10550 remove_edges_from_pairs(entity);
10553 void touch_rel_version(Entity relation) {
10554 const EntityLookupKey key(relation);
10555 auto it = m_relationVersions.find(key);
10556 if (it == m_relationVersions.end())
10557 m_relationVersions.emplace(key, 1);
10560 if (it->second == 0)
10565 void del_reltgt_tgtrel_pairs(Entity entity) {
10566 auto delPair = [](PairMap& map, Entity source, Entity remove) {
10567 auto itTargets = map.find(EntityLookupKey(source));
10568 if (itTargets != map.end()) {
10569 auto& targets = itTargets->second;
10570 targets.erase(EntityLookupKey(remove));
10574 Archetype* pArchetype =
nullptr;
10576 if (entity.pair()) {
10577 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
10578 if (it != m_recs.pairs.end()) {
10579 pArchetype = it->second.pArchetype;
10581 m_recs.pairs.erase(it);
10586 GAIA_ASSERT(entity.id() < m_recs.entities.size());
10587 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
10588 auto rel = m_recs.entities.handle(entity.id());
10589 auto tgt = m_recs.entities.handle(entity.gen());
10591 delPair(m_relToTgt, rel, tgt);
10592 delPair(m_relToTgt, All, tgt);
10593 delPair(m_tgtToRel, tgt, rel);
10594 delPair(m_tgtToRel, All, rel);
10598 auto ec = m_recs.entities[entity.id()];
10599 m_recs.entities.free(entity);
10602 del_sparse_components(entity);
10604 del_exclusive_adjunct_source(entity);
10606 del_exclusive_adjunct_relation(entity);
10608 del_sparse_component_store(entity);
10611 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0)
10612 req_del(*ec.pArchetype);
10614 ec.pArchetype =
nullptr;
10615 ec.pChunk =
nullptr;
10616 ec.pEntity =
nullptr;
10617 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::DeleteRequested,
false);
10620 delPair(m_relToTgt, All, entity);
10621 delPair(m_tgtToRel, All, entity);
10622 m_relToTgt.erase(EntityLookupKey(entity));
10623 m_tgtToRel.erase(EntityLookupKey(entity));
10626 del_entity_archetype_pairs(entity, pArchetype);
10631 void invalidate_entity(Entity entity) {
10632 del_graph_edges(entity);
10633 del_reltgt_tgtrel_pairs(entity);
10641 void store_entity(EntityContainer& ec, Entity entity, Archetype* pArchetype, Chunk* pChunk) {
10642 GAIA_ASSERT(pArchetype !=
nullptr);
10643 GAIA_ASSERT(pChunk !=
nullptr);
10645 !locked() &&
"Entities can't be stored while the world is locked "
10646 "(structural changes are forbidden during this time!)");
10648 ec.pArchetype = pArchetype;
10649 ec.pChunk = pChunk;
10650 ec.row = pChunk->add_entity(entity);
10651 ec.pEntity = &pChunk->entity_view()[ec.row];
10652 GAIA_ASSERT(entity.pair() || ec.data.gen == entity.gen());
10661 void move_entity(Entity entity, EntityContainer& ec, Archetype& dstArchetype, Chunk& dstChunk) {
10662 GAIA_PROF_SCOPE(World::move_entity);
10664 auto* pDstChunk = &dstChunk;
10665 auto* pSrcChunk = ec.pChunk;
10667 GAIA_ASSERT(pDstChunk != pSrcChunk);
10669 const auto srcRow0 = ec.row;
10670 const auto dstRow = pDstChunk->add_entity(entity);
10671 const bool wasEnabled = !ec.data.dis;
10673 auto& srcArchetype = *ec.pArchetype;
10674 const bool archetypeChanged = srcArchetype.id() != dstArchetype.id();
10675#if GAIA_ASSERT_ENABLED
10676 verify_move(*
this, srcArchetype, entity);
10680 srcArchetype.enable_entity(pSrcChunk, srcRow0,
true, m_recs);
10682 const auto srcRow = ec.row;
10685 if (dstArchetype.id() == srcArchetype.id()) {
10686 pDstChunk->move_entity_data(entity, dstRow, m_recs);
10688 pDstChunk->move_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, dstRow);
10692 remove_entity(srcArchetype, *pSrcChunk, srcRow);
10695 dstArchetype.try_update_free_chunk_idx();
10698 ec.pArchetype = &dstArchetype;
10699 ec.pChunk = pDstChunk;
10700 ec.row = (uint16_t)dstRow;
10701 ec.pEntity = &pDstChunk->entity_view()[dstRow];
10702 if (archetypeChanged)
10703 update_src_entity_version(entity);
10706 dstArchetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
10709 GAIA_ASSERT(valid(entity));
10710 validate_chunk(pSrcChunk);
10711 validate_chunk(pDstChunk);
10712 validate_entities();
10719 void move_entity_raw(Entity entity, EntityContainer& ec, Archetype& dstArchetype) {
10721 ec.pChunk->update_world_version();
10722 ec.pChunk->update_entity_order_version();
10724 auto* pDstChunk = dstArchetype.foc_free_chunk();
10725 move_entity(entity, ec, dstArchetype, *pDstChunk);
10728 pDstChunk->update_world_version();
10729 pDstChunk->update_entity_order_version();
10730 update_version(m_worldVersion);
10736 Chunk* move_entity(Entity entity, Archetype& dstArchetype) {
10738 auto& ec = fetch(entity);
10739 if (ec.pArchetype == &dstArchetype)
10743 ec.pChunk->update_world_version();
10744 ec.pChunk->update_entity_order_version();
10746 auto* pDstChunk = dstArchetype.foc_free_chunk();
10747 move_entity(entity, ec, dstArchetype, *pDstChunk);
10750 pDstChunk->update_world_version();
10751 pDstChunk->update_entity_order_version();
10752 update_version(m_worldVersion);
10757 void validate_archetype_edges([[maybe_unused]]
const Archetype* pArchetype)
const {
10758#if GAIA_ECS_VALIDATE_ARCHETYPE_GRAPH && GAIA_ASSERT_ENABLED
10759 GAIA_ASSERT(pArchetype !=
nullptr);
10762 const auto& archetypesLeft = pArchetype->left_edges();
10763 for (
const auto& it: archetypesLeft) {
10764 const auto& edge = it.second;
10765 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
10766 if (edgeIt == m_archetypesById.end())
10769 const auto entity = it.first.entity();
10770 const auto* pArchetypeRight = edgeIt->second;
10773 const auto edgeRight = pArchetypeRight->find_edge_right(entity);
10774 GAIA_ASSERT(edgeRight != ArchetypeIdHashPairBad);
10777 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
10778 GAIA_ASSERT(it2 != m_archetypesById.end());
10779 const auto* pArchetype2 = it2->second;
10780 GAIA_ASSERT(pArchetype2 == pArchetype);
10784 const auto& archetypesRight = pArchetype->right_edges();
10785 for (
const auto& it: archetypesRight) {
10786 const auto& edge = it.second;
10787 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
10788 if (edgeIt == m_archetypesById.end())
10791 const auto entity = it.first.entity();
10792 const auto* pArchetypeRight = edgeIt->second;
10795 const auto edgeLeft = pArchetypeRight->find_edge_left(entity);
10796 GAIA_ASSERT(edgeLeft != ArchetypeIdHashPairBad);
10799 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
10800 GAIA_ASSERT(it2 != m_archetypesById.end());
10801 const auto* pArchetype2 = it2->second;
10802 GAIA_ASSERT(pArchetype2 == pArchetype);
10808 void validate_entities()
const {
10809#if GAIA_ECS_VALIDATE_ENTITY_LIST
10810 m_recs.entities.validate();
10815 void validate_chunk([[maybe_unused]] Chunk* pChunk)
const {
10816#if GAIA_ECS_VALIDATE_CHUNKS && GAIA_ASSERT_ENABLED
10817 GAIA_ASSERT(pChunk !=
nullptr);
10819 if (!pChunk->empty()) {
10822 for (
const auto& ec: m_recs.entities) {
10823 if (ec.pChunk != pChunk)
10827 for (
const auto& pair: m_recs.pairs) {
10828 if (pair.second.pChunk != pChunk)
10832 GAIA_ASSERT(cnt == pChunk->size());
10835 for (
const auto& ec: m_recs.entities) {
10836 GAIA_ASSERT(ec.pChunk != pChunk);
10838 for (
const auto& pair: m_recs.pairs) {
10839 GAIA_ASSERT(pair.second.pChunk != pChunk);
10851 template <
bool CheckIn>
10852 GAIA_NODISCARD
bool is_inter(Entity entity, Entity entityBase)
const {
10853 GAIA_ASSERT(valid_entity(entity));
10854 GAIA_ASSERT(valid_entity(entityBase));
10857 if (entity.pair() || entityBase.pair())
10860 if constexpr (!CheckIn) {
10861 if (entity == entityBase)
10865 const auto& targets = as_targets_trav_cache(entity);
10866 for (
auto target: targets) {
10867 if (target == entityBase)
10875 template <
bool CheckIn,
typename Func>
10876 void as_up_trav(Entity entity, Func func) {
10877 GAIA_ASSERT(valid_entity(entity));
10883 if constexpr (!CheckIn) {
10887 const auto& ec = m_recs.entities[entity.id()];
10888 const auto* pArchetype = ec.pArchetype;
10891 if (pArchetype->pairs_is() == 0)
10894 for (uint32_t i = 0; i < pArchetype->pairs_is(); ++i) {
10895 auto e = pArchetype->entity_from_pairs_as_idx(i);
10896 const auto& ecTarget = m_recs.entities[e.gen()];
10897 auto target = *ecTarget.pEntity;
10900 as_up_trav<CheckIn>(target, func);
10904 template <
typename T>
10905 const ComponentCacheItem& reg_core_entity(Entity
id, Archetype* pArchetype) {
10906 auto comp = add(*pArchetype,
id.entity(),
id.pair(),
id.kind());
10907 const auto& ci = comp_cache_mut().
add<T>(id);
10908 GAIA_ASSERT(ci.entity ==
id);
10909 GAIA_ASSERT(comp ==
id);
10914 template <
typename T>
10915 const ComponentCacheItem& reg_core_entity(Entity
id) {
10916 return reg_core_entity<T>(
id, m_pRootArchetype);
10919 static ComponentDesc
10920 primitive_type_desc(
const char* name, uint32_t nameLen, uint32_t size, RuntimePrimitiveKind primitiveKind) {
10921 ComponentDesc desc{};
10922 desc.name = util::str_view(name, nameLen);
10925 desc.storageType = DataStorageType::Table;
10926 desc.typeKind = RuntimeTypeKind::Primitive;
10927 desc.primitiveKind = primitiveKind;
10931 const ComponentCacheItem& reg_core_primitive_type(
10932 Entity
id,
const char* name, uint32_t nameLen, uint32_t size, RuntimePrimitiveKind primitiveKind) {
10933 auto comp = add(*m_pCompArchetype,
id.entity(),
id.pair(),
id.kind());
10934 const auto desc = primitive_type_desc(name, nameLen, size, primitiveKind);
10935 const auto& ci = comp_cache_mut().
add(
id, desc);
10936 GAIA_ASSERT(ci.entity ==
id);
10937 GAIA_ASSERT(comp ==
id);
10939 finalize_component_registration(ci,
false);
10943#if GAIA_ECS_AUTO_COMPONENT_FIELDS
10944 template <
typename T>
10945 static Entity auto_component_field_type() noexcept {
10946 using U = core::raw_t<T>;
10947 if constexpr (std::is_same_v<U, bool>)
10949 else if constexpr (std::is_same_v<U, char>)
10951 else if constexpr (std::is_arithmetic_v<U>)
10952 return runtime_primitive_type_entity(ser::type_id<U>());
10957 template <
typename T>
10958 static void auto_populate_component_fields(ComponentCacheItem& item) {
10959 if (!item.fields_empty())
10962 using U = core::raw_t<T>;
10963 if constexpr (std::is_empty_v<U>)
10965 if constexpr (mem::is_soa_layout_v<U>)
10968 if constexpr (!std::is_class_v<U>) {
10969 const auto fieldType = auto_component_field_type<U>();
10970 if (fieldType != EntityBad)
10971 (void)item.add_field({util::str_view(
"value", 5), fieldType, 0, 0});
10973 }
else if constexpr (!std::is_aggregate_v<U>) {
10979 const auto* pBase =
reinterpret_cast<const uint8_t*
>(&tmp);
10980 uint32_t fieldIdx = 0;
10981 meta::each_member(tmp, [&](
auto&... fields) {
10982 auto add_field = [&](
auto& field) {
10983 using F = core::raw_t<
decltype(field)>;
10984 char fieldName[24]{};
10985 (void)GAIA_STRFMT(fieldName,
sizeof(fieldName),
"f%u", fieldIdx++);
10986 const auto* pField =
reinterpret_cast<const uint8_t*
>(&field);
10987 const auto offset = (uint32_t)(pField - pBase);
10988 const auto fieldType = auto_component_field_type<F>();
10989 if (fieldType != EntityBad)
10990 (void)item.add_field({util::str_view(fieldName), fieldType, offset, 0});
10992 (add(fields), ...);
11003#if GAIA_ECS_CHUNK_ALLOCATOR
11004 ChunkAllocator::get().flush();
11011 void assign_entity(Entity entity, Archetype& archetype) {
11012 GAIA_ASSERT(!entity.pair());
11014 auto* pChunk = archetype.foc_free_chunk();
11015 store_entity(m_recs.entities[entity.id()], entity, &archetype, pChunk);
11016 pChunk->update_versions();
11017 archetype.try_update_free_chunk_idx();
11020 pChunk->call_gen_ctors(pChunk->size() - 1, 1);
11022#if GAIA_ASSERT_ENABLED
11023 const auto& ec = m_recs.entities[entity.id()];
11024 GAIA_ASSERT(ec.pChunk == pChunk);
11025 auto entityExpected = pChunk->entity_view()[ec.row];
11026 GAIA_ASSERT(entityExpected == entity);
11033 void assign_pair(Entity entity, Archetype& archetype) {
11034 GAIA_ASSERT(entity.pair());
11037 GAIA_ASSERT(&archetype == m_pEntityArchetype);
11039 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
11040 if (it != m_recs.pairs.end())
11044 EntityContainer ec{};
11045 ec.idx = entity.id();
11046 ec.data.gen = entity.gen();
11049 ec.data.kind = EntityKind::EK_Gen;
11051 auto* pChunk = archetype.foc_free_chunk();
11052 store_entity(ec, entity, &archetype, pChunk);
11053 pChunk->update_versions();
11054 archetype.try_update_free_chunk_idx();
11056 m_recs.pairs.emplace(EntityLookupKey(entity), GAIA_MOV(ec));
11059 const auto rel = get(entity.id());
11060 const auto tgt = get(entity.gen());
11062 auto addPair = [](PairMap& map, Entity source, Entity add) {
11063 auto& ents = map[EntityLookupKey(source)];
11064 ents.insert(EntityLookupKey(add));
11067 addPair(m_relToTgt, rel, tgt);
11068 addPair(m_relToTgt, All, tgt);
11069 addPair(m_tgtToRel, tgt, rel);
11070 addPair(m_tgtToRel, All, rel);
11072#if GAIA_OBSERVERS_ENABLED
11073 m_observers.try_mark_term_observed(*
this, entity);
11083 GAIA_NODISCARD Entity add(Archetype& archetype,
bool isEntity,
bool isPair, EntityKind kind) {
11084 EntityContainerCtx ctx{isEntity, isPair, kind};
11085 const auto entity = m_recs.entities.alloc(&ctx);
11086 assign_entity(entity, archetype);
11095 template <
typename Func>
11096 void add_entity_n(Archetype& archetype, uint32_t count, Func func) {
11097 EntityContainerCtx ctx{
true,
false, EntityKind::EK_Gen};
11098#if GAIA_OBSERVERS_ENABLED
11099 const auto addedIds = EntitySpan{archetype.ids_view()};
11100 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
11101 if (!addedIds.empty())
11102 addDiffCtx = m_observers.prepare_diff_add_new(*
this, addedIds);
11105 uint32_t left = count;
11107 auto* pChunk = archetype.foc_free_chunk();
11108 const uint32_t originalChunkSize = pChunk->size();
11109 const uint32_t freeSlotsInChunk = pChunk->capacity() - originalChunkSize;
11110 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
11112 GAIA_FOR(toCreate) {
11113 const auto entityNew = m_recs.entities.alloc(&ctx);
11114 auto& ecNew = m_recs.entities[entityNew.id()];
11115 store_entity(ecNew, entityNew, &archetype, pChunk);
11117#if GAIA_ASSERT_ENABLED
11118 GAIA_ASSERT(ecNew.pChunk == pChunk);
11119 auto entityExpected = pChunk->entity_view()[ecNew.row];
11120 GAIA_ASSERT(entityExpected == entityNew);
11125 archetype.try_update_free_chunk_idx();
11128 pChunk->call_gen_ctors(originalChunkSize, toCreate);
11132 auto entities = pChunk->entity_view();
11133 GAIA_FOR2(originalChunkSize, pChunk->size()) func(entities[i]);
11136 pChunk->update_versions();
11138#if GAIA_OBSERVERS_ENABLED
11139 if (!addedIds.empty()) {
11140 auto entities = pChunk->entity_view();
11141 const auto targets = EntitySpan{entities.data() + originalChunkSize, toCreate};
11142 m_observers.add_diff_targets(*
this, addDiffCtx, targets);
11143 m_observers.on_add(*
this, archetype, addedIds, targets);
11148 }
while (left > 0);
11150#if GAIA_OBSERVERS_ENABLED
11151 if (!addedIds.empty())
11152 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
11158 GAIA_PROF_SCOPE(World::gc);
11160 del_empty_chunks();
11161 defrag_chunks(m_defragEntitiesPerTick);
11162 del_empty_archetypes();
11172 if GAIA_UNLIKELY (serId == QueryIdBad) {
11173#if GAIA_ASSERT_ENABLED
11174 uint32_t safetyCounter = 0;
11178#if GAIA_ASSERT_ENABLED
11181 GAIA_ASSERT(safetyCounter < 100000);
11184 serId = ++m_nextQuerySerId;
11186 GAIA_ASSERT(serId != 0);
11191 auto ret = m_querySerMap.try_emplace(serId);
11195 return ret.first->second;
11199 return m_querySerMap[serId];
11205 auto it = m_querySerMap.find(serId);
11206 if (it == m_querySerMap.end())
11209 m_querySerMap.erase(it);
11210 serId = QueryIdBad;
11216 m_queryCache.invalidate_queries_for_entity(entityKey, QueryCache::ChangeKind::Structural);
11222 m_queryCache.invalidate_queries_for_rel(relation, QueryCache::ChangeKind::DynamicResult);
11228 m_queryCache.invalidate_sorted_queries_for_entity(entity);
11233 m_queryCache.invalidate_sorted_queries();
11239 GAIA_ASSERT(
is_pair.first() == Is);
11253 as_up_trav<false>(e, [&](
Entity target) {
11264 auto expr = util::trim(exprRaw);
11266 if (expr[0] ==
'(') {
11267 if (expr.back() !=
')') {
11268 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
11272 const auto idStr = expr.subspan(1, expr.size() - 2);
11273 const auto commaIdx = core::get_index(idStr,
',');
11275 const auto first = name_to_entity(idStr.subspan(0, commaIdx));
11276 if (first == EntityBad)
11278 const auto second = name_to_entity(idStr.subspan(commaIdx + 1));
11279 if (second == EntityBad)
11286 auto idStr = util::trim(expr);
11289 if (idStr.size() == 1 && idStr[0] ==
'*')
11292 return get_inter(idStr.data(), (uint32_t)idStr.size());
11302 auto expr = util::trim(exprRaw);
11304 if (expr[0] ==
'%') {
11305 if (expr[1] !=
'e') {
11306 GAIA_ASSERT2(
false,
"Expression '%' not terminated");
11310 auto id = (Identifier)va_arg(args,
unsigned long long);
11314 if (expr[0] ==
'(') {
11315 if (expr.back() !=
')') {
11316 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
11320 const auto idStr = expr.subspan(1, expr.size() - 2);
11321 const auto commaIdx = core::get_index(idStr,
',');
11323 const auto first = expr_to_entity(args, idStr.subspan(0, commaIdx));
11324 if (first == EntityBad)
11326 const auto second = expr_to_entity(args, idStr.subspan(commaIdx + 1));
11327 if (second == EntityBad)
11334 auto idStr = util::trim(expr);
11337 if (idStr.size() == 1 && idStr[0] ==
'*')
11341 const auto* pItem = resolve_component_name_inter(idStr.data(), (uint32_t)idStr.size());
11342 if (pItem ==
nullptr) {
11343 GAIA_ASSERT2(
false,
"Component not found");
11344 GAIA_LOG_W(
"Component '%.*s' not found", (uint32_t)idStr.size(), idStr.data());
11348 return pItem->entity;