83 inline static bool s_enableUniqueNameDuplicateAssert =
true;
90#if GAIA_OBSERVERS_ENABLED
91 friend class ObserverRegistry;
92 friend struct ObserverRuntimeData;
93 friend struct ObserverRegistry::DiffDispatcher;
94 friend struct ObserverRegistry::DirectDispatcher;
95 friend struct ObserverRegistry::SharedDispatch;
99 friend void lock(
World&);
100 friend void unlock(
World&);
101 friend EntityContainer* try_fetch_mut(
World&,
Entity);
102#if GAIA_USE_SAFE_ENTITY
107 friend void query_match_scratch_release(
World&,
bool);
108 friend uint32_t world_component_index_bucket_size(
const World&,
Entity);
111 template <
typename T>
112 friend decltype(
auto) world_direct_entity_arg(
World& world,
Entity entity);
113 template <
typename T>
114 friend decltype(
auto) world_direct_entity_arg_raw(
World& world,
Entity entity);
115 template <
typename T>
116 friend decltype(
auto) world_query_entity_arg_by_id(
World& world,
Entity entity,
Entity id);
117 template <
typename T>
118 friend decltype(
auto) world_query_entity_arg_by_id_raw(
World& world,
Entity entity,
Entity id);
124 using TFunc_Void_With_Entity = void(
Entity);
127 static void func_void_with_entity([[maybe_unused]]
Entity entity) {}
133 using NonFragmentingRelationStore = detail::NonFragmentingRelationStore;
134 using SparseComponentStoreErased = detail::SparseComponentStoreErased;
135 using RuntimeSparseComponentStore = detail::RuntimeSparseComponentStore;
136 template <
typename T>
137 using SparseComponentStore = detail::SparseComponentStore<T>;
138 using CopyIterGroupState = detail::CopyIterGroupState;
139 using PrefabChildEdge = detail::PrefabChildEdge;
140 using PrefabInstantiatePlanNode = detail::PrefabInstantiatePlanNode;
143 typename TApi,
typename TValue,
bool DeriveFromValue = std::is_class_v<TValue> && !std::is_final_v<TValue>>
144 class SetWriteProxyTyped;
146 template <
typename TApi,
typename TValue>
147 class SetWriteProxyTyped<TApi, TValue, true>:
public TValue {
148 World* m_pWorld =
nullptr;
149 Entity m_entity = EntityBad;
150 Entity m_term = EntityBad;
154 if (m_pWorld ==
nullptr)
157 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
167 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
168 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
175 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
176 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
178 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
179 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
181 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
182 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
183 m_term(other.m_term) {
184 other.m_pWorld =
nullptr;
187 ~SetWriteProxyTyped() {
191 SetWriteProxyTyped& operator=(
const TValue& value) {
192 static_cast<TValue&
>(*this) = value;
196 SetWriteProxyTyped& operator=(TValue&& value) {
197 static_cast<TValue&
>(*this) = GAIA_MOV(value);
203 GAIA_NODISCARD
operator TValue&() {
209 GAIA_NODISCARD
operator const TValue&()
const {
214 template <
typename TApi,
typename TValue>
215 class SetWriteProxyTyped<TApi, TValue, false> {
216 World* m_pWorld =
nullptr;
217 Entity m_entity = EntityBad;
218 Entity m_term = EntityBad;
223 if (m_pWorld ==
nullptr)
226 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term, m_value);
236 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
237 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
244 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
245 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
247 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
248 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
250 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
251 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
252 other.m_pWorld =
nullptr;
255 ~SetWriteProxyTyped() {
259 SetWriteProxyTyped& operator=(
const TValue& value) {
264 SetWriteProxyTyped& operator=(TValue&& value) {
265 m_value = GAIA_MOV(value);
271 GAIA_NODISCARD
operator TValue&() {
277 GAIA_NODISCARD
operator const TValue&()
const {
283 GAIA_NODISCARD TValue* operator->() {
289 GAIA_NODISCARD
const TValue* operator->()
const {
294 template <
typename TValue,
bool DeriveFromValue = std::is_
class_v<TValue> && !std::is_final_v<TValue>>
295 class SetWriteProxyObject;
297 template <
typename TValue>
298 class SetWriteProxyObject<TValue, true>:
public TValue {
299 World* m_pWorld =
nullptr;
300 Entity m_entity = EntityBad;
301 Entity m_term = EntityBad;
305 if (m_pWorld ==
nullptr)
308 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
318 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
319 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
326 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
327 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
329 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
330 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
332 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
333 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
334 m_term(other.m_term) {
335 other.m_pWorld =
nullptr;
338 ~SetWriteProxyObject() {
342 SetWriteProxyObject& operator=(
const TValue& value) {
343 static_cast<TValue&
>(*this) = value;
347 SetWriteProxyObject& operator=(TValue&& value) {
348 static_cast<TValue&
>(*this) = GAIA_MOV(value);
354 GAIA_NODISCARD
operator TValue&() {
360 GAIA_NODISCARD
operator const TValue&()
const {
365 template <
typename TValue>
366 class SetWriteProxyObject<TValue, false> {
367 World* m_pWorld =
nullptr;
368 Entity m_entity = EntityBad;
369 Entity m_term = EntityBad;
374 if (m_pWorld ==
nullptr)
377 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term, m_value);
387 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
388 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
395 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
396 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
398 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
399 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
401 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
402 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
403 other.m_pWorld =
nullptr;
406 ~SetWriteProxyObject() {
410 SetWriteProxyObject& operator=(
const TValue& value) {
415 SetWriteProxyObject& operator=(TValue&& value) {
416 m_value = GAIA_MOV(value);
422 GAIA_NODISCARD
operator TValue&() {
428 GAIA_NODISCARD
operator const TValue&()
const {
434 GAIA_NODISCARD TValue* operator->() {
440 GAIA_NODISCARD
const TValue* operator->()
const {
449 GAIA_NODISCARD EntityContainer& cont(Entity entity) {
451 return fetch(entity);
452 return m_recs.entities[entity.id()];
459 GAIA_NODISCARD
const EntityContainer& cont(Entity entity)
const {
461 return fetch(entity);
462 return m_recs.entities[entity.id()];
469 template <
typename T>
470 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity) {
471 static_assert(!is_pair<T>::value);
472 using FT =
typename component_type_t<T>::TypeFull;
473 const auto& item = add<FT>();
474 if constexpr (uses_ct_sparse_storage<FT>())
475 return sparse_component_store_mut<FT>(item.entity).mut(entity);
477 const auto& ec = cont(entity);
478 return ec.pChunk->template set<T>(ec.row);
486 GAIA_NODISCARD
const ComponentCacheItem* component_item(Entity entity, Entity component)
const {
487 if (!component.pair())
488 return comp_cache().find(component);
490 const auto& ec = fetch(entity);
491 const auto compIdx = core::get_index(ec.pChunk->ids_view(), component);
492 return compIdx != BadIndex ? ec.pChunk->comp_rec_view()[compIdx].pItem
493 : comp_cache().find_pair_payload(component);
499 GAIA_NODISCARD
static bool raw_component_supported(
const ComponentCacheItem& item)
noexcept {
500 return item.comp.soa() == 0;
506 GAIA_NODISCARD
static bool soa_field_supported(
const ComponentCacheItem& item)
noexcept {
507 return item.comp.soa() != 0 && item.comp.storage_type() == DataStorageType::Table;
515 GAIA_NODISCARD
static bool
516 raw_component_payload_args_valid(
const ComponentCacheItem& item,
const void* data, uint32_t size)
noexcept {
517 if (!raw_component_supported(item))
519 if (size != item.comp.size())
521 return size == 0 || data !=
nullptr;
529 template <
typename T>
530 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity, Entity
object) {
531 static_assert(!is_pair<T>::value);
532 using FT =
typename component_type_t<T>::TypeFull;
533 if constexpr (supports_sparse_component_storage<FT>()) {
534 if (can_use_sparse_component_storage<FT>(
object))
535 return sparse_component_mut_value<FT>(
object, entity);
538 const auto& ec = cont(entity);
539 return ec.pChunk->template set<T>(ec.row,
object);
547 template <
typename T,
bool TriggerSetEffects>
548 void modify_table_inter(Entity entity, EntityContainer& ec) {
551 ec.pChunk->template modify<T, TriggerSetEffects>();
553#if GAIA_OBSERVERS_ENABLED
554 if constexpr (TriggerSetEffects) {
555 Entity term = EntityBad;
556 if constexpr (is_pair<T>::value) {
557 const auto rel = comp_cache().template get<typename T::rel>().entity;
558 const auto tgt = comp_cache().template get<typename T::tgt>().entity;
559 term = (Entity)Pair(rel, tgt);
561 term = comp_cache().template get<T>().entity;
563 world_notify_on_set(*
this, term, *ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
574 template <
bool TriggerSetEffects>
575 void modify_table_inter(Entity
object, EntityContainer& ec) {
576 const auto compIdx = ec.pChunk->comp_idx(
object);
577 GAIA_ASSERT(compIdx != ComponentIndexBad);
579 if constexpr (TriggerSetEffects)
580 ec.pChunk->finish_write(compIdx, ec.row, (uint16_t)(ec.row + 1));
582 ec.pChunk->update_world_version(compIdx);
591 void mark_sparse_component_write(Entity entity, Entity term) {
592 const auto& ec = fetch(entity);
593 const auto compIdx = core::get_index(ec.pChunk->ids_view(), term);
594 if (compIdx != BadIndex)
595 ec.pChunk->update_world_version(compIdx);
597 ec.pChunk->update_entity_order_version();
605 void mark_non_fragmenting_membership_changed(Entity entity, Entity term) {
609 ::gaia::ecs::update_version(m_worldVersion);
610 mark_sparse_component_write(entity, term);
618 void finish_write(Entity entity, Entity term) {
619 if (tearing_down() || !valid(entity))
622 ::gaia::ecs::update_version(m_worldVersion);
624 if (sparse_storage_mode(term) != SparseStorageMode::None) {
625 mark_sparse_component_write(entity, term);
626 world_notify_on_set_entity(*
this, term, entity);
630 auto compIdx = uint32_t(BadIndex);
632 const auto& ec = fetch(entity);
633 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
636 if (compIdx == BadIndex) {
637 (void)
override(entity, term);
638 const auto& ec = fetch(entity);
639 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
640 if (compIdx == BadIndex)
644 const auto& ec = fetch(entity);
645 const auto row = ec.row;
646 (void)ec.pChunk->comp_ptr_mut_gen<
true>(compIdx, row);
647 world_notify_on_set_entity(*
this, term, entity);
656 template <
typename TApi,
typename TValue>
657 void write_back_set_typed(Entity entity, Entity term,
const TValue& value) {
658 using FT =
typename component_type_t<TApi>::TypeFull;
660 if constexpr (uses_ct_sparse_storage<FT>()) {
661 sparse_component_store_mut<FT>(term).add(entity) = value;
662 finish_write(entity, term);
664 const auto& ec = fetch(entity);
665 const auto row = ec.row;
666 ComponentSetter{*
this, ec.pChunk, entity, row}.sset<TApi>(value);
667 finish_write(entity, term);
676 template <
typename TValue>
677 void write_back_set_object(Entity entity, Entity term,
const TValue& value) {
678 using FT =
typename component_type_t<TValue>::TypeFull;
679 if constexpr (supports_sparse_component_storage<FT>()) {
680 if (can_use_sparse_component_storage<FT>(term)) {
681 sparse_component_add_value<FT>(term, entity) = value;
682 finish_write(entity, term);
687 const auto& ec = fetch(entity);
688 const auto row = ec.row;
689 ComponentSetter{*
this, ec.pChunk, entity, row}.template smut<TValue>(term) = value;
690 finish_write(entity, term);
698 ComponentCache m_compCache;
700 QueryCache m_queryCache;
703 cnt::darray<QueryMatchScratch*> m_queryMatchScratchStack;
705 uint32_t m_queryMatchScratchDepth = 0;
711 QuerySerMap m_querySerMap;
712 uint32_t m_nextQuerySerId = 0;
714#if GAIA_OBSERVERS_ENABLED && GAIA_ASSERT_ENABLED
716 uint32_t m_observerCallbackDepth = 0;
720 EntityToArchetypeMap m_entityToArchetypeMap;
722 EntityToArchetypeVersionMap m_entityToArchetypeMapVersions;
731 PairMap m_entityToAsTargets;
734 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsTargetsTravCache;
742 PairMap m_entityToAsRelations;
745 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsRelationsTravCache;
748 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsTravCache;
751 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_srcBfsTravCache;
754 mutable cnt::map<EntityLookupKey, uint32_t> m_depthOrderCache;
757 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_sourcesAllCache;
760 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsAllCache;
762 mutable bool m_relationCachesPopulated =
false;
764 bool m_hasOnDeleteTargetPolicy =
false;
766 bool m_hasCantCombinePolicy =
false;
768 bool m_hasRequiresPolicy =
false;
770 PairLookup m_pairLookup;
772 cnt::map<EntityLookupKey, NonFragmentingRelationStore> m_nonFragmentingRelationsByRel;
774 cnt::map<EntityLookupKey, SparseComponentStoreErased> m_sparseComponentsByComp;
776 cnt::map<EntityLookupKey, uint32_t> m_relationVersions;
778 Entity m_lastRelationVersionRelation = EntityBad;
780 uint32_t* m_pLastRelationVersion =
nullptr;
783 mutable cnt::map<EntityLookupKey, uint32_t> m_srcEntityVersions;
785 enum class SparseStorageMode : uint8_t { None, Fragmenting, NonFragmenting };
788 ArchetypeDArray m_archetypes;
790 ArchetypeMapByHash m_archetypesByHash;
792 ArchetypeMapById m_archetypesById;
795 Archetype* m_pRootArchetype =
nullptr;
797 Archetype* m_pEntityArchetype =
nullptr;
799 Archetype* m_pCompArchetype =
nullptr;
801 ArchetypeId m_nextArchetypeId = 0;
804 uint32_t m_defaultMaxLifespan = Archetype::MAX_ARCHETYPE_LIFESPAN;
807 EntityContainers m_recs;
809 cnt::map<EntityNameLookupKey, Entity> m_nameToEntity;
811 cnt::map<EntityNameLookupKey, Entity> m_aliasToEntity;
813 Entity m_componentScope = EntityBad;
815 cnt::darray<Entity> m_componentLookupPath;
817 mutable util::str m_componentScopePathCache;
819 mutable Entity m_componentScopePathCacheEntity = EntityBad;
821 mutable bool m_componentScopePathCacheValid =
false;
824 cnt::set<ArchetypeLookupKey> m_reqArchetypesToDel;
826 cnt::set<EntityLookupKey> m_reqEntitiesToDel;
828 cnt::darray<Entity> m_archetypeRowsToDel;
830 uint32_t m_archetypeRowDelDepth = 0;
832 const EntityContainer* m_pArchetypeRowInRules =
nullptr;
834#if GAIA_OBSERVERS_ENABLED
836 ObserverRegistry m_observers;
839 struct DeferredOnSet {
848 cnt::darray<cnt::darray<DeferredOnSet>> m_deferredOnSet;
850 uint32_t m_deferOnSetDepth = 0;
860 struct DeferredSortInv {
867 cnt::darray<cnt::darray<DeferredSortInv>> m_deferredSortInv;
869 uint32_t m_deferSortInvDepth = 0;
871#if GAIA_SYSTEMS_ENABLED
873 SystemRegistry m_systems;
877 CommandBufferST* m_pCmdBufferST;
879 CommandBufferMT* m_pCmdBufferMT;
881 bool m_teardownActive =
false;
883 Query m_systemsQuery;
885 detail::SystemScheduleScratch m_systemScheduleScratch;
889 mutable cnt::darray<uint64_t> m_entityVisitStamps;
891 mutable uint64_t m_entityVisitStamp = 0;
893#if GAIA_USE_SAFE_ENTITY
895 uint32_t m_compDtorDepth = 0;
898 cnt::darray<Entity> m_safeEntityDels;
901 static constexpr uint32_t EntitiesDeletingScanMax = 16;
903 cnt::darray_ext<Entity, EntitiesDeletingScanMax> m_entitiesDeleting;
907 cnt::set<EntityLookupKey> m_entitiesDeletingSet;
909 cnt::darray<ArchetypeChunkPair> m_chunksToDel;
911 ArchetypeDArray m_archetypesToDel;
913 uint32_t m_defragLastArchetypeIdx = 0;
915 uint32_t m_defragEntitiesPerTick = 100;
918 uint32_t m_worldVersion = 0;
920 uint32_t m_enabledHierarchyVersion = 0;
922 uint32_t m_archetypeDeleteVersion = 0;
924 uint32_t m_structuralChangesLocked = 0;
930 GAIA_NODISCARD
const EntityContainer* try_entity_record(Entity entity)
const {
934 const auto* pEc = m_recs.entities.try_get(entity.id());
935 if (pEc ==
nullptr || pEc->data.gen != entity.gen())
945 GAIA_NODISCARD
const EntityContainer* try_fetch_record(Entity entity)
const {
946 if (entity == EntityBad)
950 if (is_wildcard(entity))
952 return m_recs.pair_record_find(entity);
955 return try_entity_record(entity);
961 GAIA_NODISCARD EntityContainer* try_fetch_record(Entity entity) {
962 return const_cast<EntityContainer*
>(
static_cast<const World*
>(
this)->try_fetch_record(entity));
968 GAIA_NODISCARD
static bool record_uses_non_fragmenting_relation_storage(
const EntityContainer& ec) {
969 return (ec.flags & EntityContainerFlags::IsExclusive) != 0 &&
970 (ec.flags & EntityContainerFlags::IsDontFragment) != 0;
976 m_pCmdBufferST(cmd_buffer_st_create(*this)),
978 m_pCmdBufferMT(cmd_buffer_mt_create(*this)) {
985 cmd_buffer_destroy(*m_pCmdBufferST);
986 cmd_buffer_destroy(*m_pCmdBufferMT);
989 World(World&&) =
delete;
990 World(
const World&) =
delete;
991 World& operator=(World&&) =
delete;
992 World& operator=(
const World&) =
delete;
1001 *
const_cast<World*
>(
this), m_queryCache,
1003 m_nextArchetypeId, m_worldVersion, m_entityToArchetypeMap, m_entityToArchetypeMapVersions, m_archetypes);
1011 q.kind(QueryCacheKind::None);
1015#if GAIA_ECS_TEST_HOOKS
1017 GAIA_NODISCARD
bool verify_query_cache()
const {
1018 return m_queryCache.verify_archetype_tracking();
1023 GAIA_NODISCARD uint32_t test_query_cache_count()
const {
1024 return m_queryCache.test_query_count();
1044 return sched_resolve(m_sched);
1053#if GAIA_ASSERT_ENABLED
1055 GAIA_ASSERT(!entity.
pair() || !is_wildcard(entity));
1056 if (!valid(entity)) {
1061 const bool allowStaleExactPair = entity.
pair() && m_recs.pair_record_contains(entity);
1062 const bool allowDeleteRequestedEntity =
1063 !entity.
pair() && entity.
id() < m_recs.entities.size() &&
1064 m_recs.entities[entity.
id()].data.gen == entity.
gen() &&
1065 (m_recs.entities[entity.
id()].flags & EntityContainerFlags::DeleteRequested) != 0;
1066 const bool allowQueuedDeleteEntity = !entity.
pair() && m_reqEntitiesToDel.contains(
EntityLookupKey(entity));
1067 const bool allowStaleEntityRecord = !entity.
pair() && entity.
id() < m_recs.entities.size() &&
1068 m_recs.entities[entity.
id()].data.gen == entity.
gen() &&
1069 m_recs.entities[entity.
id()].pArchetype !=
nullptr &&
1070 m_recs.entities[entity.
id()].pChunk !=
nullptr;
1072 allowStaleExactPair || allowDeleteRequestedEntity || allowQueuedDeleteEntity || allowStaleEntityRecord);
1075 return m_recs[entity];
1082#if GAIA_ASSERT_ENABLED
1084 GAIA_ASSERT(!entity.
pair() || !is_wildcard(entity));
1085 if (!valid(entity)) {
1090 const bool allowStaleExactPair = entity.
pair() && m_recs.pair_record_contains(entity);
1091 const bool allowDeleteRequestedEntity =
1092 !entity.
pair() && entity.
id() < m_recs.entities.size() &&
1093 m_recs.entities[entity.
id()].data.gen == entity.
gen() &&
1094 (m_recs.entities[entity.
id()].flags & EntityContainerFlags::DeleteRequested) != 0;
1095 const bool allowQueuedDeleteEntity = !entity.
pair() && m_reqEntitiesToDel.contains(
EntityLookupKey(entity));
1096 const bool allowStaleEntityRecord = !entity.
pair() && entity.
id() < m_recs.entities.size() &&
1097 m_recs.entities[entity.
id()].data.gen == entity.
gen() &&
1098 m_recs.entities[entity.
id()].pArchetype !=
nullptr &&
1099 m_recs.entities[entity.
id()].pChunk !=
nullptr;
1101 allowStaleExactPair || allowDeleteRequestedEntity || allowQueuedDeleteEntity || allowStaleEntityRecord);
1104 return m_recs[entity];
1115 GAIA_NODISCARD
bool is_req_del(
const EntityContainer& ec)
const {
1116 if ((ec.flags & EntityContainerFlags::DeleteRequested) != 0)
1118 return is_req_del_or_queued(ec) && &ec != m_pArchetypeRowInRules;
1126 if ((ec.flags & EntityContainerFlags::DeleteRequested) != 0)
1128 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) == 0);
1129 return ec.pArchetype !=
nullptr && ec.pArchetype->is_req_del();
1137 const auto* pEc = try_fetch_record(entity);
1138 if (pEc ==
nullptr || !valid(*pEc, entity))
1147 return const_cast<EntityContainer*
>(
static_cast<const World*
>(
this)->try_live_record(entity));
1150#if GAIA_OBSERVERS_ENABLED
1151 #if GAIA_ASSERT_ENABLED
1153 void observer_callback_enter() {
1154 ++m_observerCallbackDepth;
1158 void observer_callback_leave() {
1159 GAIA_ASSERT(m_observerCallbackDepth != 0);
1160 --m_observerCallbackDepth;
1164 GAIA_NODISCARD
bool observer_callback_active()
const {
1165 return m_observerCallbackDepth != 0;
1174 if GAIA_UNLIKELY (m_entitiesDeleting.size() > EntitiesDeletingScanMax)
1177 for (
auto deleting: m_entitiesDeleting) {
1178 if (deleting == entity)
1187 GAIA_ASSERT(!entity_deletion_active(entity));
1188 m_entitiesDeleting.push_back(entity);
1189 if GAIA_UNLIKELY (m_entitiesDeleting.size() > EntitiesDeletingScanMax)
1190 entity_deletion_set_enter();
1196 GAIA_ASSERT(!m_entitiesDeleting.empty());
1197 GAIA_ASSERT(m_entitiesDeleting.back() == entity);
1198 if GAIA_UNLIKELY (m_entitiesDeleting.size() > EntitiesDeletingScanMax)
1199 entity_deletion_set_leave();
1200 m_entitiesDeleting.pop_back();
1206 if (m_entitiesDeleting.size() == EntitiesDeletingScanMax + 1) {
1207 for (
auto deleting: m_entitiesDeleting)
1210 m_entitiesDeletingSet.insert(
EntityLookupKey(m_entitiesDeleting.back()));
1217 if (m_entitiesDeleting.size() == EntitiesDeletingScanMax + 1) {
1218 for (
auto deleting: m_entitiesDeleting)
1221 m_entitiesDeletingSet.erase(
EntityLookupKey(m_entitiesDeleting.back()));
1228 const auto* pEc = try_entity_record(entity);
1229 return pEc !=
nullptr && (pEc->flags & EntityContainerFlags::IsDontFragment) != 0;
1236 return is_dont_fragment(relation);
1244 const auto* pEc = try_entity_record(relation);
1245 return pEc !=
nullptr && record_uses_non_fragmenting_relation_storage(*pEc);
1253 const auto* pEc = try_entity_record(relation);
1254 if (pEc ==
nullptr || (pEc->flags & EntityContainerFlags::IsExclusive) == 0 || pEc->pArchetype ==
nullptr)
1257 return pEc->pArchetype->has(Traversable);
1265 const auto* pEc = try_entity_record(relation);
1269 return (pEc->flags & EntityContainerFlags::IsDontFragment) == 0;
1277 return relation_is_hierarchy(relation) && relation_is_fragmenting(relation);
1287 return relation_is_fragmenting(relation);
1296 return relation_is_fragmenting_hierarchy(relation);
1304 if (!component.
pair()) {
1305 const auto* pEc = try_entity_record(component);
1306 if (pEc ==
nullptr || pEc->data.ent != 0)
1309 const auto* pItem = comp_cache().find(component);
1310 if (pItem ==
nullptr || pItem->comp.soa() != 0)
1313 return gaia::ecs::component_uses_sparse_storage(pItem->comp);
1316 const auto* pItem = comp_cache().find_pair_payload(component);
1317 return pItem !=
nullptr && pItem->comp.soa() == 0 && gaia::ecs::component_uses_sparse_storage(pItem->comp);
1326 if (component.
pair())
1327 return sparse_storage_mode(component) == SparseStorageMode::NonFragmenting;
1329 const auto* pEc = try_entity_record(component);
1330 if (pEc ==
nullptr || pEc->data.ent != 0)
1333 const auto flags = pEc->flags;
1334 return (flags & EntityContainerFlags::IsDontFragment) != 0 && (flags & EntityContainerFlags::IsExclusive) == 0;
1341 if (!component.
pair()) {
1342 const auto* pEc = try_entity_record(component);
1343 if (pEc ==
nullptr || pEc->data.ent != 0)
1344 return SparseStorageMode::None;
1346 const auto* pItem = comp_cache().find(component);
1347 if (pItem ==
nullptr || pItem->comp.soa() != 0 || !gaia::ecs::component_uses_sparse_storage(pItem->comp))
1348 return SparseStorageMode::None;
1350 return (pEc->flags & EntityContainerFlags::IsDontFragment) != 0 ? SparseStorageMode::NonFragmenting
1351 : SparseStorageMode::Fragmenting;
1354 const auto* pItem = comp_cache().find_pair_payload(component);
1355 if (pItem ==
nullptr || pItem->comp.soa() != 0 || !gaia::ecs::component_uses_sparse_storage(pItem->comp))
1356 return SparseStorageMode::None;
1358 return is_dont_fragment(pair_rel(*
this, component)) ? SparseStorageMode::NonFragmenting
1359 : SparseStorageMode::Fragmenting;
1369 return store.func_has(store.pStore, srcEntity) &&
1370 (component_is_non_fragmenting(comp) || sparse_storage_mode(comp) != SparseStorageMode::None);
1379 Entity comp,
Entity srcEntity,
const SparseComponentStoreErased& store)
const {
1380 return store.func_has(store.pStore, srcEntity) && component_is_non_fragmenting(comp);
1387 return component_is_non_fragmenting(comp);
1396 auto& item = comp_cache_mut().get(component);
1399 auto& ec = m_recs.entities[component.
id()];
1400 if (ec.pArchetype ==
nullptr || ec.pChunk ==
nullptr)
1403 const auto compIdx = core::get_index(ec.pArchetype->ids_view(), GAIA_ID(
Component));
1404 if (compIdx == BadIndex)
1407 auto* pComp =
reinterpret_cast<Component*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
1416#if GAIA_ASSERT_ENABLED
1417 auto validate = [&](
Entity semantic) {
1418 if (semantic == EntityBad)
1421 GAIA_ASSERT(build_scope_path(semantic, path));
1431#if GAIA_JSON_ENABLED
1437 bool write_runtime_schema_json(ser::ser_json& writer,
const char* schemaHash,
bool includeRuntimeEntities)
const;
1446 sync_component_record(item.entity, item.comp);
1447 const auto symbol = item.symbol_name();
1448 name_raw(item.entity, symbol.data(), symbol.size());
1449 if (addSparseTrait && item.comp.storage_type() == DataStorageType::Sparse)
1450 add(item.entity, Sparse);
1458 if (component.
comp()) {
1459 const auto& item = comp_cache().get(component);
1460 if (item.comp.size() != 0)
1461 set_component_sparse_storage(component);
1464 if ((ec.flags & EntityContainerFlags::IsDontFragment) != 0)
1467 ec.flags |= EntityContainerFlags::IsDontFragment;
1476 GAIA_ASSERT(valid(component));
1477 GAIA_ASSERT(component.
comp());
1478 GAIA_ASSERT(!component.
pair());
1479 GAIA_ASSERT(!component.
entity());
1481 const auto& item = comp_cache().get(component);
1482 GAIA_ASSERT(item.entity == component);
1484 if (item.comp.storage_type() == DataStorageType::Sparse)
1487 GAIA_ASSERT(item.comp.size() != 0);
1488 if (item.comp.size() == 0)
1491 GAIA_ASSERT(item.comp.soa() == 0);
1492 if (item.comp.soa() != 0)
1495 const auto directTermEntityCnt = count_direct_term_entities_direct(component);
1496 GAIA_ASSERT(directTermEntityCnt == 0);
1497 if (directTermEntityCnt != 0)
1500 auto comp = item.comp;
1501 comp.data.storage = (IdentifierData)DataStorageType::Sparse;
1502 sync_component_record(component, comp);
1512 if (!component.
comp())
1515 const auto& item = comp_cache().get(component);
1516 if (item.func_create_sparse_store ==
nullptr || item.comp.storage_type() == DataStorageType::Sparse)
1519 if (trait.
id() != DontFragment.
id() || item.comp.size() != 0)
1522 return count_direct_term_entities_direct(component) == 0;
1529 template <
typename T>
1531 using U =
typename actual_type_t<T>::Type;
1532 return !mem::is_soa_layout_v<U>;
1538 template <
typename T>
1540 return uses_ct_sparse_storage_v<T>;
1546 template <
typename T>
1549 const auto relation = reg_comp<typename T::rel>().
entity;
1550 const auto target = reg_comp<typename T::tgt>().
entity;
1551 return Pair(relation, target);
1553 return reg_comp<T>().entity;
1561 template <
typename T>
1563 const auto object = register_component_id<T>();
1564 auto storageTraitOwner = object;
1566 storageTraitOwner = m_recs.entities.handle(
object.id());
1568 mode = is_dont_fragment(storageTraitOwner) ? SparseStorageMode::NonFragmenting : SparseStorageMode::Fragmenting;
1576 template <
typename T>
1578 if constexpr (!supports_sparse_component_storage<T>())
1581 if (try_live_record(
object) ==
nullptr)
1584 const auto* pItem =
object.pair() ? comp_cache().find_pair_payload(
object) : comp_cache().find(
object);
1585 if (pItem ==
nullptr || (!
object.
pair() && pItem->entity !=
object) ||
1586 !gaia::ecs::component_uses_sparse_storage(pItem->comp))
1589 using U =
typename actual_type_t<T>::Type;
1590 return pItem->comp.size() == (uint32_t)
sizeof(U) && pItem->comp.alig() == (uint32_t)
alignof(U) &&
1591 pItem->comp.soa() == 0;
1599 template <
typename T>
1601 using U =
typename actual_type_t<T>::Type;
1602 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1603 if (it == m_sparseComponentsByComp.end())
1604 return (SparseComponentStore<U>*)
nullptr;
1606 return static_cast<SparseComponentStore<U>*
>(it->second.pStore);
1613 template <
typename T>
1615 using U =
typename actual_type_t<T>::Type;
1616 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1617 if (it == m_sparseComponentsByComp.end())
1618 return (
const SparseComponentStore<U>*)
nullptr;
1620 return static_cast<const SparseComponentStore<U>*
>(it->second.pStore);
1627 template <
typename T>
1629 using U =
typename actual_type_t<T>::Type;
1631 const auto it = m_sparseComponentsByComp.find(key);
1632 if (it != m_sparseComponentsByComp.end())
1633 return *
static_cast<SparseComponentStore<U>*
>(it->second.pStore);
1635 auto* pStore =
new SparseComponentStore<U>();
1636 m_sparseComponentsByComp.emplace(key, make_sparse_component_store_erased(pStore));
1645 template <
typename T>
1647 using U =
typename actual_type_t<T>::Type;
1648 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1649 if (it == m_sparseComponentsByComp.end()) {
1651 component.
pair() ? comp_cache().find_pair_payload(component) : comp_cache().find(component);
1652 GAIA_ASSERT(pItem !=
nullptr);
1653 auto& store = sparse_component_store_erased_mut(component, *pItem);
1654 return *(U*)store.func_add(store.pStore, entity);
1656 return *(U*)it->second.func_add(it->second.pStore, entity);
1664 template <
typename T>
1666 using U =
typename actual_type_t<T>::Type;
1667 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1668 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
1669 return *(U*)it->second.func_mut(it->second.pStore, entity);
1677 template <
typename T>
1679 using U =
typename actual_type_t<T>::Type;
1680 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1681 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
1682 return *(
const U*)it->second.func_get(it->second.pStore, entity);
1690 template <
typename T>
1692 using U =
typename actual_type_t<T>::Type;
1693 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1694 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
1695 return *(
const U*)it->second.func_get_entity(it->second.pStore, entity);
1702 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1703 return it != m_sparseComponentsByComp.end() ? &it->second :
nullptr;
1710 GAIA_NODISCARD SparseComponentStoreErased&
1713 const auto it = m_sparseComponentsByComp.find(key);
1714 if (it != m_sparseComponentsByComp.end())
1716 if (item.func_create_sparse_store !=
nullptr) {
1717 item.func_create_sparse_store(*
this, component);
1718 return m_sparseComponentsByComp.find(key)->second;
1721 auto* pStore =
new RuntimeSparseComponentStore(item);
1722 return m_sparseComponentsByComp.emplace(key, make_sparse_component_store_erased(pStore)).first->second;
1731 GAIA_ASSERT(mode != SparseStorageMode::None);
1733#if GAIA_OBSERVERS_ENABLED
1735 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
1737 if (
object.
pair() || mode == SparseStorageMode::Fragmenting) {
1738 GAIA_ASSERT(!locked());
1740 eb.add_inter_init(
object,
false);
1744 notify_add_single(entity,
object);
1745#if GAIA_OBSERVERS_ENABLED
1746 m_observers.finish_diff(*
this, GAIA_MOV(ctx));
1753 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
1755 store.func_del(store.pStore, entity);
1762 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
1763 if (it == m_sparseComponentsByComp.end())
1766 it->second.func_clear_store(it->second.pStore);
1767 it->second.func_del_store(it->second.pStore);
1768 m_sparseComponentsByComp.erase(it);
1775 const auto it = m_nonFragmentingRelationsByRel.find(
EntityLookupKey(relation));
1776 if (it == m_nonFragmentingRelationsByRel.end())
1796 GAIA_ASSERT(relation_uses_non_fragmenting_storage(relation));
1797 if (store.set(source, target))
1798 invalidate_relation_caches(relation);
1806 auto& store = nonfragmenting_relation_store_mut(relation);
1807 nonfragmenting_relation_set(store, source, relation, target);
1816 const auto itStore = m_nonFragmentingRelationsByRel.find(
EntityLookupKey(relation));
1817 if (itStore == m_nonFragmentingRelationsByRel.end())
1820 auto& store = itStore->second;
1821 if (!store.remove(source, target))
1825 m_nonFragmentingRelationsByRel.erase(itStore);
1827 invalidate_relation_caches(relation);
1840 const auto relId =
object.id();
1841 const auto tgtId =
object.gen();
1843 if (relId != All.
id()) {
1844 const auto relation = get(relId);
1845 if (!relation_uses_non_fragmenting_storage(relation))
1848 const auto* pStore = nonfragmenting_relation_store(relation);
1849 if (pStore ==
nullptr)
1852 const auto target = pStore->target(source);
1853 if (target == EntityBad)
1856 return tgtId == All.
id() || target.
id() == tgtId;
1859 if (tgtId == All.
id()) {
1860 for (
const auto& it: m_nonFragmentingRelationsByRel) {
1861 if (it.second.target(source) != EntityBad)
1867 const auto target = get(tgtId);
1868 for (
const auto& it: m_nonFragmentingRelationsByRel) {
1869 if (it.second.target(source) == target)
1880 for (
const auto& it: m_nonFragmentingRelationsByRel) {
1881 const auto relation = it.first.entity();
1882 if (it.second.target(source) != EntityBad)
1885 if (relations.
empty())
1888 for (
auto relation: relations) {
1889 touch_rel_version(relation);
1890 invalidate_queries_for_rel(relation);
1891 (void)nonfragmenting_relation_del(source, relation, EntityBad);
1893 clear_relation_caches();
1896#if GAIA_OBSERVERS_ENABLED
1899 void del_nonfragmenting_relation_source_observed(
Entity source) {
1900 if (!m_observers.has_on_del_observers())
1904 for (
const auto& it: m_nonFragmentingRelationsByRel) {
1905 const auto relation = it.first.entity();
1906 const auto target = it.second.target(source);
1907 if (target != EntityBad)
1913 const auto& ec = fetch(source);
1914 for (
auto pair: pairs) {
1915 const auto relation = try_get(pair.id());
1916 const auto target = try_get(pair.gen());
1917 if (relation == EntityBad || target == EntityBad)
1920 const Entity
object = Pair(relation, target);
1922 m_observers.prepare_diff(*
this, ObserverEvent::OnDel, EntitySpan{&object, 1}, EntitySpan{&source, 1});
1923 if (nonfragmenting_relation_del(source, relation, target))
1924 m_observers.on_del(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&source, 1});
1925 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
1933 const auto itStore = m_nonFragmentingRelationsByRel.find(
EntityLookupKey(relation));
1934 if (itStore == m_nonFragmentingRelationsByRel.end())
1939 itStore->second.collect_source_ids(sourceIds);
1940 itStore->second.collect_pair_sources(pairSources);
1942 touch_rel_version(relation);
1943 invalidate_queries_for_rel(relation);
1944 for (
auto sourceId: sourceIds)
1945 (
void)nonfragmenting_relation_del(get(sourceId), relation, EntityBad);
1946 for (
auto source: pairSources)
1947 (
void)nonfragmenting_relation_del(source, relation, EntityBad);
1948 clear_relation_caches();
1956 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
1957 if (store.sources(target) ==
nullptr)
1960 if (has(relKey.entity(), cond))
1972 m_serializer = ser::make_serializer(m_stream);
1978 GAIA_ASSERT(serializer.
valid());
1979 m_serializer = serializer;
1984 template <
typename TSerializer>
1986 set_serializer(ser::make_serializer(serializer));
1992 return m_serializer;
2012 uint32_t m_rowSrc = 0;
2022 using RelationMutationPath = detail::RelationMutationPath;
2024#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2025 static constexpr uint32_t MAX_TERMS = 32;
2026 static_assert(MAX_TERMS <= ChunkHeader::MAX_COMPONENTS);
2034#if GAIA_OBSERVERS_ENABLED
2048 m_world(world), m_pArchetypeSrc(ec.pArchetype), m_pChunkSrc(ec.pChunk), m_rowSrc(ec.row),
2049 m_pArchetype(ec.pArchetype), m_entity(entity) {
2051 GAIA_ASSERT(ec.pChunk->entity_view()[ec.row] == entity);
2058 const auto& ec = world.
fetch(entity);
2059 m_pArchetypeSrc = ec.pArchetype;
2060 m_pChunkSrc = ec.pChunk;
2063 m_pArchetype = ec.pArchetype;
2072 m_world(other.m_world), m_pArchetypeSrc(other.m_pArchetypeSrc), m_pChunkSrc(other.m_pChunkSrc),
2073 m_rowSrc(other.m_rowSrc), m_pArchetype(other.m_pArchetype), m_targetNameKey(other.m_targetNameKey),
2074 m_targetAliasKey(other.m_targetAliasKey), m_entity(other.m_entity)
2075#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2077 tl_new_comps(GAIA_MOV(other.tl_new_comps)), tl_del_comps(GAIA_MOV(other.tl_del_comps))
2079#if GAIA_OBSERVERS_ENABLED
2081 tl_del_nonfragmenting_relations(GAIA_MOV(other.tl_del_nonfragmenting_relations)),
2082 tl_add_relation_diff_contexts(GAIA_MOV(other.tl_add_relation_diff_contexts)),
2083 tl_del_relation_diff_contexts(GAIA_MOV(other.tl_del_relation_diff_contexts))
2086 other.m_pArchetype =
nullptr;
2100 if (m_pArchetype ==
nullptr) {
2105 if (m_pArchetypeSrc != m_pArchetype) {
2106 auto& ec = m_world.
fetch(m_entity);
2107 GAIA_ASSERT(ec.pArchetype == m_pArchetypeSrc);
2108#if GAIA_OBSERVERS_ENABLED
2109 const bool hasOnDelObservers = !tl_del_comps.empty() && m_world.m_observers.has_on_del_observers();
2110 const bool hasOnAddObservers = !tl_new_comps.
empty() && m_world.m_observers.has_on_add_observers();
2111 const bool hasNonfragmentingAdds = hasOnAddObservers && has_nonfragmenting_adds();
2112 auto delDiffCtx = !hasOnDelObservers ? ObserverRegistry::DiffDispatchCtx{}
2113 : m_world.m_observers.prepare_diff(
2114 m_world, ObserverEvent::OnDel, EntitySpan{tl_del_comps},
2115 EntitySpan{&m_entity, 1});
2117 !hasOnAddObservers ? ObserverRegistry::DiffDispatchCtx{}
2118 : hasNonfragmentingAdds
2119 ? m_world.m_observers.prepare_diff_add_new(m_world, EntitySpan{tl_new_comps})
2120 : m_world.m_observers.prepare_diff(
2121 m_world, ObserverEvent::OnAdd, EntitySpan{tl_new_comps}, EntitySpan{&m_entity, 1});
2122 if (hasNonfragmentingAdds)
2123 m_world.m_observers.add_diff_targets(m_world, addDiffCtx, EntitySpan{&m_entity, 1});
2126#if GAIA_OBSERVERS_ENABLED
2127 if (hasOnDelObservers)
2128 flush_del_nonfragmenting_relations();
2132 trigger_del_hooks(*m_pArchetype);
2135 m_world.move_entity_raw(m_entity, ec, *m_pArchetype);
2137 if (m_targetNameKey.
str() !=
nullptr || m_targetAliasKey.
str() !=
nullptr) {
2138 const auto compIdx = ec.pChunk->comp_idx(GAIA_ID(
EntityDesc));
2140 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut_gen<
false>(compIdx, ec.row));
2141 GAIA_ASSERT(core::check_alignment(pDesc));
2144 if (m_targetNameKey.
str() !=
nullptr) {
2145 pDesc->name = m_targetNameKey.
str();
2146 pDesc->name_len = m_targetNameKey.len();
2150 if (m_targetAliasKey.
str() !=
nullptr) {
2151 pDesc->alias = m_targetAliasKey.
str();
2152 pDesc->alias_len = m_targetAliasKey.len();
2157 trigger_add_hooks(*m_pArchetype);
2158#if GAIA_OBSERVERS_ENABLED
2159 if (hasOnDelObservers)
2160 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
2161 if (hasOnAddObservers)
2162 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
2164 cleanup_deleted_sparse_components();
2166 m_pArchetypeSrc = ec.pArchetype;
2167 m_pChunkSrc = ec.pChunk;
2172#if GAIA_ASSERT_ENABLED
2173 auto& ec = m_world.
fetch(m_entity);
2174 GAIA_ASSERT(ec.pChunk == m_pChunkSrc);
2177#if GAIA_OBSERVERS_ENABLED
2178 const bool hasOnDelObservers = !tl_del_comps.empty() && m_world.m_observers.has_on_del_observers();
2179 const bool hasOnAddObservers = !tl_new_comps.
empty() && m_world.m_observers.has_on_add_observers();
2180 const bool hasNonfragmentingAdds = hasOnAddObservers && has_nonfragmenting_adds();
2181 auto delDiffCtx = !hasOnDelObservers ? ObserverRegistry::DiffDispatchCtx{}
2182 : m_world.m_observers.prepare_diff(
2183 m_world, ObserverEvent::OnDel, EntitySpan{tl_del_comps},
2184 EntitySpan{&m_entity, 1});
2186 !hasOnAddObservers ? ObserverRegistry::DiffDispatchCtx{}
2187 : hasNonfragmentingAdds
2188 ? m_world.m_observers.prepare_diff_add_new(m_world, EntitySpan{tl_new_comps})
2189 : m_world.m_observers.prepare_diff(
2190 m_world, ObserverEvent::OnAdd, EntitySpan{tl_new_comps}, EntitySpan{&m_entity, 1});
2191 if (hasNonfragmentingAdds)
2192 m_world.m_observers.add_diff_targets(m_world, addDiffCtx, EntitySpan{&m_entity, 1});
2195#if GAIA_OBSERVERS_ENABLED
2196 if (hasOnDelObservers)
2197 flush_del_nonfragmenting_relations();
2200 if (m_targetNameKey.
str() !=
nullptr || m_targetAliasKey.
str() !=
nullptr) {
2203 GAIA_ASSERT(core::check_alignment(pDesc));
2206 if (m_targetNameKey.
str() !=
nullptr) {
2207 pDesc->name = m_targetNameKey.
str();
2208 pDesc->name_len = m_targetNameKey.len();
2212 if (m_targetAliasKey.
str() !=
nullptr) {
2213 pDesc->alias = m_targetAliasKey.
str();
2214 pDesc->alias_len = m_targetAliasKey.len();
2218#if GAIA_OBSERVERS_ENABLED
2219 if (hasOnDelObservers)
2220 m_world.m_observers.on_del(m_world, *m_pArchetypeSrc, EntitySpan{tl_del_comps}, EntitySpan{&m_entity, 1});
2221 if (hasOnAddObservers)
2222 m_world.m_observers.on_add(m_world, *m_pArchetypeSrc, EntitySpan{tl_new_comps}, EntitySpan{&m_entity, 1});
2223 if (hasOnDelObservers)
2224 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
2225 if (hasOnAddObservers)
2226 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
2228 cleanup_deleted_sparse_components();
2231#if GAIA_OBSERVERS_ENABLED
2232 finish_relation_diff_contexts();
2236 m_pArchetype =
nullptr;
2237 m_targetNameKey = {};
2238 m_targetAliasKey = {};
2248 void name(
const char* name, uint32_t len = 0) {
2249 name_inter<true>(name, len);
2264 name_inter<false>(name, len);
2271 void alias(
const char* alias, uint32_t len = 0) {
2272 alias_inter<true>(alias, len);
2280 alias_inter<false>(alias, len);
2285 if (m_entity.
pair())
2291 if (compIdx == BadIndex)
2295 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->
comp_ptr(compIdx, m_rowSrc));
2296 GAIA_ASSERT(core::check_alignment(pDesc));
2297 if (pDesc->name ==
nullptr)
2306 m_world.invalidate_scope_path_cache();
2308 pDesc->name =
nullptr;
2309 pDesc->name_len = 0;
2310 if (pDesc->alias ==
nullptr)
2313 m_targetNameKey = {};
2318 if (m_entity.
pair())
2324 if (compIdx == BadIndex)
2328 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->
comp_ptr(compIdx, m_rowSrc));
2329 GAIA_ASSERT(core::check_alignment(pDesc));
2330 if (pDesc->alias ==
nullptr)
2340 pDesc->alias =
nullptr;
2341 pDesc->alias_len = 0;
2342 if (pDesc->name ==
nullptr)
2345 m_targetAliasKey = {};
2352 GAIA_PROF_SCOPE(EntityBuilder::add);
2353 GAIA_ASSERT(m_world.
valid(m_entity));
2354 GAIA_ASSERT(m_world.
valid(entity));
2365 GAIA_PROF_SCOPE(EntityBuilder::add);
2366 GAIA_ASSERT(m_world.
valid(m_entity));
2367 GAIA_ASSERT(m_world.
valid(
pair.first()));
2368 GAIA_ASSERT(m_world.
valid(
pair.second()));
2369 GAIA_ASSERT(!
pair.first().pair());
2370 GAIA_ASSERT(!
pair.second().pair());
2371 GAIA_ASSERT(
pair.first() != Is || !m_entity.
pair());
2382 GAIA_ASSERT(!m_entity.
pair());
2383 GAIA_ASSERT(!entityBase.
pair());
2384 return add(
Pair(Is, entityBase));
2398 return static_cast<const World&
>(m_world).is(entity, entityBase);
2405 return add(
Pair(ChildOf, parent));
2411 template <
typename T>
2413 return m_world.template register_component_id<T>();
2419 template <
typename T>
2422 add(register_component<T>());
2430 GAIA_PROF_SCOPE(EntityBuilder::del);
2431 GAIA_ASSERT(m_world.
valid(m_entity));
2432 GAIA_ASSERT(m_world.
valid(entity));
2441 GAIA_PROF_SCOPE(EntityBuilder::add);
2442 GAIA_ASSERT(m_world.
valid(m_entity));
2443 GAIA_ASSERT(m_world.
valid(
pair.first()));
2444 GAIA_ASSERT(m_world.
valid(
pair.second()));
2452 template <
typename T>
2455 del(register_component<T>());
2462 void trigger_add_hooks(
const Archetype& newArchetype) {
2463#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2465 tl_new_comps.
clear();
2472 #if GAIA_ENABLE_ADD_DEL_HOOKS
2476 for (
auto entity: tl_new_comps) {
2477 const auto* pItem = m_world.component_item(m_entity, entity);
2478 if (pItem ==
nullptr)
2480 const auto& hooks = pItem->hooks();
2481 if (hooks.func_add !=
nullptr)
2482 hooks.func_add(m_world, *pItem, m_entity);
2487 #if GAIA_OBSERVERS_ENABLED
2489 if (m_world.m_observers.has_on_add_observers())
2490 m_world.m_observers.on_add(m_world, newArchetype, std::span<Entity>{tl_new_comps}, {&m_entity, 1});
2495 tl_new_comps.
clear();
2503 void trigger_del_hooks(
const Archetype& newArchetype) {
2504#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2506 tl_del_comps.clear();
2511 m_world.notify_inherited_del_dependents(m_entity, std::span<Entity>{tl_del_comps});
2514 #if GAIA_OBSERVERS_ENABLED
2516 if (m_world.m_observers.has_on_del_observers())
2517 m_world.m_observers.on_del(m_world, newArchetype, std::span<Entity>{tl_del_comps}, {&m_entity, 1});
2522 #if GAIA_ENABLE_ADD_DEL_HOOKS
2526 for (
auto entity: tl_del_comps) {
2527 const auto* pItem = m_world.component_item(m_entity, entity);
2528 if (pItem ==
nullptr)
2530 const auto& hooks = pItem->hooks();
2531 if (hooks.func_del !=
nullptr)
2532 hooks.func_del(m_world, *pItem, m_entity);
2542 void cleanup_deleted_sparse_components() {
2543 for (
auto entity: tl_del_comps) {
2544 if (entity.pair() || m_world.
sparse_storage_mode(entity) != SparseStorageMode::Fragmenting)
2547 const auto it = m_world.m_sparseComponentsByComp.find(EntityLookupKey(entity));
2548 if (it != m_world.m_sparseComponentsByComp.
end())
2549 it->second.func_del(it->second.pStore, m_entity);
2552 tl_del_comps.
clear();
2555#if GAIA_OBSERVERS_ENABLED
2557 void finish_relation_diff_contexts() {
2558 for (
auto& ctx: tl_del_relation_diff_contexts)
2559 m_world.m_observers.finish_diff(m_world, GAIA_MOV(ctx));
2560 tl_del_relation_diff_contexts.
clear();
2561 for (
auto& ctx: tl_add_relation_diff_contexts)
2562 m_world.m_observers.finish_diff(m_world, GAIA_MOV(ctx));
2563 tl_add_relation_diff_contexts.
clear();
2568 GAIA_NODISCARD
bool has_nonfragmenting_adds()
const {
2569 for (
auto entity: tl_new_comps) {
2570 if (entity.pair() && relation_mutation_path(entity) == RelationMutationPath::NonFragmentingExclusive)
2577 void flush_del_nonfragmenting_relations() {
2578 for (
auto entity: tl_del_nonfragmenting_relations)
2579 del_nonfragmenting_relation_id(entity);
2580 tl_del_nonfragmenting_relations.
clear();
2588 bool handle_add_entity(Entity entity, RelationMutationPath relationPath) {
2589 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> targets;
2590 const bool isPair = entity.pair();
2591 GAIA_ASSERT(isPair || relationPath == RelationMutationPath::Fragmenting);
2594 m_world.invalidate_scope_path_cache();
2597 if (m_world.m_hasCantCombinePolicy) {
2598 const auto& ecMain = m_world.
fetch(entity);
2599 if ((ecMain.flags & EntityContainerFlags::HasCantCombine) != 0) {
2600 m_world.
targets(entity, CantCombine, [&targets](Entity target) {
2601 targets.push_back(target);
2603 for (
auto e: targets) {
2604 if (m_pArchetype->
has(e)) {
2605#if GAIA_ASSERT_ENABLED
2606 GAIA_ASSERT2(
false,
"Trying to add an entity which can't be combined with the source");
2607 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2617 if (isPair && relationPath != RelationMutationPath::NonFragmentingExclusive) {
2619 const auto& ecRel = m_world.m_recs.entities[entity.id()];
2621 if ((ecRel.flags & EntityContainerFlags::IsExclusive) != 0 && m_pArchetype->
pairs() != 0) {
2622 auto rel = Entity(entity.id(), ecRel.data.gen, (
bool)ecRel.data.ent, (
bool)ecRel.data.pair);
2623 auto tgt = m_world.
try_get(entity.gen());
2624 if (tgt == EntityBad)
2627 const auto targetsCntMax = m_pArchetype->
pair_matches(Pair(rel, All));
2628 if (targetsCntMax > 1) {
2629#if GAIA_ASSERT_ENABLED
2631 false,
"Trying to add a pair with exclusive relationship but there are multiple targets present. "
2632 "Make sure to add the Exclusive property before any relationships with it are created.");
2633 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2638 if (targetsCntMax == 1) {
2641 m_world.
targets_if(m_entity, rel, [&targets](Entity target) {
2642 targets.push_back(target);
2646 GAIA_ASSERT(targets.size() <= 1);
2647 if (!targets.empty() && tgt != targets[0]) {
2652 if (!can_del(entity)) {
2653#if GAIA_ASSERT_ENABLED
2656 "Trying to replace an exclusive relationship but the entity which is getting removed has "
2658 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
2663 handle_del(ecs::Pair(rel, targets[0]));
2670 if (m_world.m_hasRequiresPolicy) {
2672 m_world.
targets(entity, Requires, [&targets](Entity target) {
2673 targets.push_back(target);
2676 for (
auto e: targets) {
2677 auto* pArchetype = m_pArchetype;
2678 handle_add<false>(e);
2679 if (m_pArchetype != pArchetype) {
2680 const auto requiredPath = e.pair() ? relation_mutation_path_add(e) : RelationMutationPath::Fragmenting;
2681 handle_add_entity(e, requiredPath);
2692 GAIA_NODISCARD
bool has_Requires_tgt(Entity entity)
const {
2693 if (!m_world.m_hasRequiresPolicy)
2697 auto ids = m_pArchetype->
ids_view();
2699 if (m_world.
has(e, Pair(Requires, entity)))
2710 static void set_flag(EntityContainerFlagsType& flags, EntityContainerFlags flag,
bool enable) {
2721 void set_flag(Entity entity, EntityContainerFlags flag,
bool enable) {
2722 auto& ec = m_world.
fetch(entity);
2723 set_flag(ec.flags, flag, enable);
2729 void try_set_flags(Entity entity,
bool enable) {
2730 if (entity.pair()) {
2731 if (entity.id() == Is.
id()) {
2732 auto& ec = m_world.
fetch(entity);
2733 try_set_Is(ec, entity, enable);
2736 if (entity.id() == CantCombine.
id() || entity.id() == OnDelete.
id() || entity.id() == OnDeleteTarget.
id()) {
2737 auto& ecMain = m_world.
fetch(m_entity);
2738 try_set_CantCombine(ecMain, entity, enable);
2739 try_set_OnDelete(ecMain, entity, enable);
2740 try_set_OnDeleteTargetPolicy(ecMain, entity, enable);
2742 if (enable && entity.id() == Requires.
id())
2743 m_world.m_hasRequiresPolicy =
true;
2745 if (m_world.m_hasOnDeleteTargetPolicy)
2746 try_set_OnDeleteTarget(entity, enable);
2750 auto& ecMain = m_world.
fetch(m_entity);
2751 try_set_CantCombine(ecMain, entity, enable);
2753 auto& ec = m_world.
fetch(entity);
2754 try_set_Is(ec, entity, enable);
2755 try_set_IsExclusive(ecMain, entity, enable);
2757 try_set_sticky_component_traits(ecMain, entity);
2758 try_set_IsSingleton(ecMain, entity, enable);
2759 try_set_OnDelete(ecMain, entity, enable);
2760 try_set_OnDeleteTargetPolicy(ecMain, entity, enable);
2767 void try_set_Is(EntityContainer& ec, Entity entity,
bool enable) {
2768 if (!entity.pair() || entity.id() != Is.
id())
2771 set_flag(ec.flags, EntityContainerFlags::HasAliasOf, enable);
2778 void try_set_CantCombine(EntityContainer& ec, Entity entity,
bool enable) {
2779 if (!entity.pair() || entity.id() != CantCombine.
id())
2782 GAIA_ASSERT(entity != m_entity);
2791 m_world.m_hasCantCombinePolicy =
true;
2792 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
true);
2793 }
else if ((ec.flags & EntityContainerFlags::HasCantCombine) != 0) {
2794 uint32_t targets = 0;
2795 m_world.
targets(m_entity, CantCombine, [&targets]([[maybe_unused]] Entity entity) {
2799 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
false);
2807 void try_set_IsExclusive(EntityContainer& ec, Entity entity,
bool enable) {
2808 if (entity.pair() || entity.id() != Exclusive.
id())
2811 set_flag(ec.flags, EntityContainerFlags::IsExclusive, enable);
2817 void try_set_sticky_component_traits(EntityContainer& ecMain, Entity entity) {
2821 if (entity.id() == DontFragment.
id()) {
2826 if (entity.id() == Sparse.
id())
2833 GAIA_NODISCARD RelationMutationPath relation_mutation_path(Entity entity)
const noexcept {
2834 GAIA_ASSERT(entity.pair());
2836 return relation_mutation_path_from_flags(m_world.
fetch(m_world.
get(entity.id())).flags);
2842 GAIA_NODISCARD RelationMutationPath relation_mutation_path_add(Entity entity)
const noexcept {
2843 GAIA_ASSERT(entity.pair());
2845 const auto& ecRel = m_world.m_recs.entities[entity.id()];
2846 return relation_mutation_path_from_flags(ecRel.flags);
2852 GAIA_NODISCARD
static RelationMutationPath
2853 relation_mutation_path_from_flags(EntityContainerFlagsType flags)
noexcept {
2854 const auto nonFragmentingExclusiveFlags =
2855 EntityContainerFlags::IsExclusive | EntityContainerFlags::IsDontFragment;
2856 if ((flags & nonFragmentingExclusiveFlags) == nonFragmentingExclusiveFlags)
2857 return RelationMutationPath::NonFragmentingExclusive;
2859 if ((flags & EntityContainerFlags::IsDontFragment) == 0)
2860 return RelationMutationPath::Fragmenting;
2862 return RelationMutationPath::NonFragmentingArchetypePair;
2868 GAIA_NODISCARD
bool has_archetype_id(Entity entity)
const {
2869 return m_pArchetype->
has(entity);
2875 GAIA_NODISCARD
bool has_nonfragmenting_relation_id(Entity entity)
const {
2876 GAIA_ASSERT(entity.pair());
2883 GAIA_NODISCARD
bool has_nonfragmenting_archetype_pair_id(Entity entity)
const {
2884 GAIA_ASSERT(entity.pair());
2885 return has_archetype_id(entity);
2890 void add_archetype_id(Entity entity) {
2891 m_pArchetype = m_world.foc_archetype_add_no_graph(m_pArchetype, entity);
2897 GAIA_NODISCARD
bool add_id(Entity entity) {
2898 add_archetype_id(entity);
2904 void del_nonfragmenting_relation_id(Entity entity) {
2905 GAIA_ASSERT(entity.pair());
2907 const auto relation = m_world.
try_get(entity.id());
2908 const auto target = m_world.
try_get(entity.gen());
2909 if (relation != EntityBad && target != EntityBad)
2915 void del_archetype_id(Entity entity) {
2916 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
2921 void del_id(Entity entity) {
2922 del_archetype_id(entity);
2929 void try_set_OnDeleteTargetPolicy(EntityContainer& ec, Entity entity,
bool enable) {
2930 if (entity == Pair(OnDeleteTarget, Delete))
2931 set_flag(ec.flags, EntityContainerFlags::OnDeleteTarget_Delete, enable);
2932 else if (entity == Pair(OnDeleteTarget, Remove))
2933 set_flag(ec.flags, EntityContainerFlags::OnDeleteTarget_Remove, enable);
2934 else if (entity == Pair(OnDeleteTarget, Error))
2935 set_flag(ec.flags, EntityContainerFlags::OnDeleteTarget_Error, enable);
2940 m_world.m_hasOnDeleteTargetPolicy =
true;
2946 void try_set_OnDeleteTarget(Entity entity,
bool enable) {
2951 GAIA_ASSERT(m_world.
valid(m_world.
get(entity.id())));
2952 GAIA_ASSERT(m_world.
valid(m_world.
get(entity.gen())));
2954 const auto& ecRel = m_world.m_recs.entities[entity.id()];
2955 const auto policyFlags = ecRel.flags & (EntityContainerFlags::OnDeleteTarget_Delete |
2956 EntityContainerFlags::OnDeleteTarget_Remove |
2957 EntityContainerFlags::OnDeleteTarget_Error);
2958 if (policyFlags == 0)
2961 auto& ecTgt = m_world.m_recs.entities[entity.gen()];
2962 if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Delete) != 0)
2963 set_flag(ecTgt.flags, EntityContainerFlags::OnDeleteTarget_Delete,
true);
2964 else if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Remove) != 0)
2965 set_flag(ecTgt.flags, EntityContainerFlags::OnDeleteTarget_Remove,
true);
2966 else if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Error) != 0)
2967 set_flag(ecTgt.flags, EntityContainerFlags::OnDeleteTarget_Error,
true);
2971 const auto rel = m_world.
try_get(entity.id());
2972 if (rel == EntityBad)
2975 const auto& ecRel = m_world.
fetch(rel);
2976 const auto policyFlags =
2977 ecRel.flags & (EntityContainerFlags::OnDeleteTarget_Delete | EntityContainerFlags::OnDeleteTarget_Remove |
2978 EntityContainerFlags::OnDeleteTarget_Error);
2979 if (policyFlags == 0)
2982 const auto tgt = m_world.
try_get(entity.gen());
2983 if (tgt == EntityBad)
2988 if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Delete) != 0)
2989 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Delete, enable);
2990 else if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Remove) != 0)
2991 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Remove, enable);
2992 else if ((policyFlags & EntityContainerFlags::OnDeleteTarget_Error) != 0)
2993 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Error, enable);
3000 void try_set_OnDelete(EntityContainer& ec, Entity entity,
bool enable) {
3001 if (entity == Pair(OnDelete, Delete))
3002 set_flag(ec.flags, EntityContainerFlags::OnDelete_Delete, enable);
3003 else if (entity == Pair(OnDelete, Remove))
3004 set_flag(ec.flags, EntityContainerFlags::OnDelete_Remove, enable);
3005 else if (entity == Pair(OnDelete, Error))
3006 set_flag(ec.flags, EntityContainerFlags::OnDelete_Error, enable);
3013 void try_set_IsSingleton(EntityContainer& ec, Entity entity,
bool enable) {
3014 const bool isSingleton = enable && m_entity == entity;
3015 set_flag(ec.flags, EntityContainerFlags::IsSingleton, isSingleton);
3021 void handle_DependsOn(Entity entity,
bool enable) {
3065 void invalidate_relation_change(Entity entity) {
3066 GAIA_ASSERT(entity.pair());
3068 auto relation = m_world.m_recs.entities.handle(entity.id());
3069 m_world.invalidate_relation_caches(relation);
3075 GAIA_NODISCARD
bool link_is_relation(Entity entity) {
3076 GAIA_ASSERT(entity.pair());
3078 if (entity.id() != Is.
id())
3081 GAIA_ASSERT(!m_entity.
pair());
3083 auto e = m_world.
try_get(entity.gen());
3087 EntityLookupKey entityKey(m_entity);
3088 EntityLookupKey eKey(e);
3091 auto& entity_to_e = m_world.m_entityToAsTargets[entityKey];
3092 entity_to_e.
insert(eKey);
3093 m_world.m_entityToAsTargetsTravCache = {};
3095 auto& e_to_entity = m_world.m_entityToAsRelations[eKey];
3096 e_to_entity.
insert(entityKey);
3097 m_world.m_entityToAsRelationsTravCache = {};
3110 void unlink_is_relation(Entity entity) {
3111 GAIA_ASSERT(entity.pair());
3113 if (entity.id() != Is.
id())
3116 auto e = m_world.
try_get(entity.gen());
3118 m_world.unlink_live_is_relation(m_entity, e);
3124 template <
bool IsBootstrap>
3125 void finish_add_id(Entity entity) {
3126 if constexpr (!IsBootstrap) {
3127 handle_DependsOn(entity,
true);
3129#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3137 void finish_del_id(Entity entity) {
3138#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3145 void add_sparse_pair_payload(Entity pair) {
3149 const auto* pItem = m_world.
comp_cache().find_pair_payload(pair);
3150 GAIA_ASSERT(pItem !=
nullptr);
3152 (void)store.func_add(store.pStore, m_entity);
3157 void add_nonfragmenting_component_id(Entity component) {
3158 const auto* pItem = m_world.
comp_cache().find(component);
3159 GAIA_ASSERT(pItem !=
nullptr);
3160 if (pItem ==
nullptr)
3164 (void)store.func_add(store.pStore, m_entity);
3169 void del_nonfragmenting_component_id(Entity component) {
3170 const auto it = m_world.m_sparseComponentsByComp.find(EntityLookupKey(component));
3171 if (it != m_world.m_sparseComponentsByComp.
end())
3172 it->second.func_del(it->second.pStore, m_entity);
3177 void del_sparse_pair_payload(Entity pair) {
3178 const auto it = m_world.m_sparseComponentsByComp.find(EntityLookupKey(pair));
3179 if (it != m_world.m_sparseComponentsByComp.
end())
3180 it->second.func_del(it->second.pStore, m_entity);
3187 template <
bool IsBootstrap>
3188 bool handle_add(Entity entity) {
3189 if (entity.pair()) {
3190 const auto relationPath = relation_mutation_path_add(entity);
3191 if (relationPath == RelationMutationPath::NonFragmentingExclusive)
3192 return handle_add_nonfragmenting_relation<IsBootstrap>(entity);
3194 return handle_add_archetype_relation<IsBootstrap>(entity);
3197#if GAIA_ASSERT_ENABLED
3198 World::verify_add(m_world, *m_pArchetype, m_entity, entity);
3202 if (has_archetype_id(entity))
3206 if (m_world.has_direct_sparse_component_inter(m_entity, entity))
3209 try_set_flags(entity,
true);
3210 if constexpr (!IsBootstrap) {
3211#if GAIA_OBSERVERS_ENABLED
3212 auto addDiffCtx = m_world.m_observers.prepare_diff(
3213 m_world, ObserverEvent::OnAdd, EntitySpan{&entity, 1}, EntitySpan{&m_entity, 1});
3215 add_nonfragmenting_component_id(entity);
3216 handle_DependsOn(entity,
true);
3217 m_world.mark_non_fragmenting_membership_changed(m_entity, entity);
3218 m_world.notify_add_single(m_entity, entity);
3219#if GAIA_OBSERVERS_ENABLED
3220 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
3223 add_nonfragmenting_component_id(entity);
3231 try_set_flags(entity,
true);
3233 if (!add_id(entity))
3236 finish_add_id<IsBootstrap>(entity);
3246 template <
bool IsBootstrap>
3247 bool handle_add_archetype_relation(Entity entity,
bool addSparsePayload =
true) {
3248 GAIA_ASSERT(entity.pair());
3250#if GAIA_ASSERT_ENABLED
3251 World::verify_add(m_world, *m_pArchetype, m_entity, entity);
3255 if (has_archetype_id(entity))
3258#if GAIA_OBSERVERS_ENABLED
3259 ObserverRegistry::DiffDispatchCtx relationDiffCtx{};
3260 if constexpr (!IsBootstrap) {
3261 if (entity.id() == Is.
id())
3262 relationDiffCtx = m_world.m_observers.prepare_diff(
3263 m_world, ObserverEvent::OnAdd, EntitySpan{&entity, 1}, EntitySpan{&m_entity, 1});
3267 invalidate_relation_change(entity);
3269 try_set_flags(entity,
true);
3270 if (!link_is_relation(entity))
3273#if GAIA_OBSERVERS_ENABLED
3274 if (relationDiffCtx.active)
3275 tl_add_relation_diff_contexts.
push_back(GAIA_MOV(relationDiffCtx));
3278 add_archetype_id(entity);
3279 if (addSparsePayload)
3280 add_sparse_pair_payload(entity);
3281 finish_add_id<IsBootstrap>(entity);
3291 template <
bool IsBootstrap>
3292 bool handle_add_nonfragmenting_relation(
3293 Entity entity,
bool addSparsePayload =
true,
bool pairAlreadyPrepared =
false) {
3294 GAIA_ASSERT(entity.pair());
3297 if (!pairAlreadyPrepared && has_nonfragmenting_relation_id(entity))
3300 const auto relation = m_world.
try_get(entity.id());
3301 const auto target = m_world.
try_get(entity.gen());
3302 if (relation == EntityBad || target == EntityBad)
3306 const auto oldTarget = relationStore.
target(m_entity);
3307 if (oldTarget != EntityBad && oldTarget != target) {
3308 const Entity oldPair = Pair(relation, oldTarget);
3309#if GAIA_OBSERVERS_ENABLED
3310 if constexpr (!IsBootstrap) {
3311 auto delDiffCtx = m_world.m_observers.prepare_diff(
3312 m_world, ObserverEvent::OnDel, EntitySpan{&oldPair, 1}, EntitySpan{&m_entity, 1});
3313 if (delDiffCtx.active) {
3314 delDiffCtx.targetsRemovedAfterPrepare =
true;
3315 tl_del_relation_diff_contexts.
push_back(GAIA_MOV(delDiffCtx));
3319 try_set_flags(oldPair,
false);
3320 handle_DependsOn(oldPair,
false);
3321 unlink_is_relation(oldPair);
3322 del_sparse_pair_payload(oldPair);
3323 finish_del_id(oldPair);
3326 try_set_flags(entity,
true);
3327 if (!link_is_relation(entity))
3330#if GAIA_OBSERVERS_ENABLED
3331 if constexpr (!IsBootstrap) {
3332 auto addDiffCtx = m_world.m_observers.prepare_diff_add_new(m_world, EntitySpan{&entity, 1});
3333 m_world.m_observers.add_diff_targets(m_world, addDiffCtx, EntitySpan{&m_entity, 1});
3334 if (addDiffCtx.active && !addDiffCtx.targetsAddedAfterPrepare)
3335 tl_add_relation_diff_contexts.
push_back(GAIA_MOV(addDiffCtx));
3340 if (addSparsePayload)
3341 add_sparse_pair_payload(entity);
3343 finish_add_id<IsBootstrap>(entity);
3350 void handle_del(Entity entity) {
3351 if (entity.pair() && !m_world.
valid(entity)) {
3352 del_sparse_pair_payload(entity);
3354 if (m_pArchetype->
has(entity)) {
3355 const auto relation = m_world.
try_get(entity.id());
3356 if (relation != EntityBad) {
3357 m_world.invalidate_relation_caches(relation);
3360 if (entity.id() == Is.
id())
3361 m_world.unlink_stale_is_relations_by_target_id(m_entity, entity.gen());
3363 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
3368 if (entity.pair()) {
3369 m_world.invalidate_scope_path_cache();
3370 if (is_wildcard(entity)) {
3371 handle_del_nonfragmenting_wildcard(entity);
3372 handle_del_archetype_wildcard(entity);
3376 const auto relationPath = relation_mutation_path(entity);
3377 if (relationPath == RelationMutationPath::NonFragmentingExclusive)
3378 handle_del_nonfragmenting_relation(entity);
3380 handle_del_archetype_relation(entity);
3384#if GAIA_ASSERT_ENABLED
3385 World::verify_del(m_world, *m_pArchetype, m_entity, entity);
3389 if (!has_archetype_id(entity)) {
3391 m_world.has_direct_sparse_component_inter(m_entity, entity)) {
3392 try_set_flags(entity,
false);
3393 handle_DependsOn(entity,
false);
3394#if GAIA_OBSERVERS_ENABLED
3395 auto delDiffCtx = m_world.m_observers.prepare_diff(
3396 m_world, ObserverEvent::OnDel, EntitySpan{&entity, 1}, EntitySpan{&m_entity, 1});
3398 del_nonfragmenting_component_id(entity);
3399 m_world.mark_non_fragmenting_membership_changed(m_entity, entity);
3400 m_world.notify_del_single(m_entity, entity);
3401#if GAIA_OBSERVERS_ENABLED
3402 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
3408 try_set_flags(entity,
false);
3409 handle_DependsOn(entity,
false);
3412 finish_del_id(entity);
3417 void handle_del_archetype_relation(Entity entity) {
3418 GAIA_ASSERT(entity.pair());
3420#if GAIA_ASSERT_ENABLED
3421 World::verify_del(m_world, *m_pArchetype, m_entity, entity);
3425 if (!has_archetype_id(entity))
3428 invalidate_relation_change(entity);
3430 try_set_flags(entity,
false);
3431 handle_DependsOn(entity,
false);
3432 unlink_is_relation(entity);
3434 del_sparse_pair_payload(entity);
3436 del_archetype_id(entity);
3437 finish_del_id(entity);
3442 void handle_del_archetype_wildcard(Entity entity) {
3443 GAIA_ASSERT(entity.pair());
3444 GAIA_ASSERT(is_wildcard(entity));
3446 const auto relationFilter = entity.id();
3447 const auto targetFilter = entity.gen();
3448 cnt::darray_ext<Entity, 16> pairs;
3449 for (
auto pair: m_pArchetype->ids_view()) {
3452 if (relationFilter != All.
id() && pair.id() != relationFilter)
3454 if (targetFilter != All.
id() && pair.gen() != targetFilter)
3460 for (
auto pair: pairs) {
3462 handle_del_archetype_relation(pair);
3468 void handle_del_nonfragmenting_relation(Entity entity) {
3469 GAIA_ASSERT(entity.pair());
3472 if (!has_nonfragmenting_relation_id(entity))
3475 try_set_flags(entity,
false);
3476 handle_DependsOn(entity,
false);
3477 unlink_is_relation(entity);
3478 del_sparse_pair_payload(entity);
3480#if GAIA_OBSERVERS_ENABLED
3481 if (m_world.m_observers.has_on_del_observers())
3482 tl_del_nonfragmenting_relations.
push_back(entity);
3485 del_nonfragmenting_relation_id(entity);
3486 finish_del_id(entity);
3491 void handle_del_nonfragmenting_wildcard(Entity entity) {
3492 GAIA_ASSERT(entity.pair());
3493 GAIA_ASSERT(is_wildcard(entity));
3495 const auto relationFilter = entity.id();
3496 const auto targetFilter = entity.gen();
3497 cnt::darray_ext<Entity, 16> pairs;
3498 for (
const auto& item: m_world.m_nonFragmentingRelationsByRel) {
3499 const auto relation = item.first.entity();
3500 if (relationFilter != All.
id() && relation.id() != relationFilter)
3503 const auto target = item.second.target(m_entity);
3504 if (target == EntityBad || (targetFilter != All.
id() && target.
id() != targetFilter))
3507 pairs.push_back(Pair(relation, target));
3510 core::sort(pairs.begin(), pairs.end(), core::is_smaller<Entity>{});
3511 for (
auto pair: pairs) {
3513 handle_del_nonfragmenting_relation(pair);
3521 GAIA_NODISCARD
bool prepare_pair_add(Entity entity, RelationMutationPath relationPath) {
3522 GAIA_ASSERT(entity.pair());
3523 GAIA_ASSERT(entity.id() != Is.
id() || !m_entity.
pair());
3525 if (relationPath == RelationMutationPath::NonFragmentingExclusive) {
3526 if (has_nonfragmenting_relation_id(entity))
3528 }
else if (relationPath == RelationMutationPath::NonFragmentingArchetypePair) {
3529 if (has_nonfragmenting_archetype_pair_id(entity))
3534 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
3540 void add_inter(Entity entity) {
3541 GAIA_ASSERT(!is_wildcard(entity));
3542 const bool isPair = entity.pair();
3543 const auto relationPath = isPair ? relation_mutation_path_add(entity) : RelationMutationPath::Fragmenting;
3545 if (isPair && !prepare_pair_add(entity, relationPath))
3548 if (!handle_add_entity(entity, relationPath))
3552 if (relationPath == RelationMutationPath::NonFragmentingExclusive)
3553 handle_add_nonfragmenting_relation<false>(entity,
true,
true);
3555 handle_add_archetype_relation<false>(entity);
3559 handle_add<false>(entity);
3567 static void rebuild_graph_edge(Archetype* pArchetypeLeft, Archetype* pArchetypeRight, Entity entity) {
3568 pArchetypeLeft->del_graph_edge_right_local(entity);
3569 pArchetypeRight->del_graph_edge_left_local(entity);
3570 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
3579 static void ensure_graph_edge(Archetype* pArchetypeLeft, Archetype* pArchetypeRight, Entity entity) {
3580 const auto right = pArchetypeLeft->find_edge_right(entity);
3581 const auto left = pArchetypeRight->find_edge_left(entity);
3582 if (right.id == pArchetypeRight->id() && left.id == pArchetypeLeft->id())
3585 rebuild_graph_edge(pArchetypeLeft, pArchetypeRight, entity);
3591 void add_inter_init(Entity entity,
bool addSparsePayload =
true) {
3592 GAIA_ASSERT(!is_wildcard(entity));
3593 const bool isPair = entity.pair();
3594 const auto relationPath = isPair ? relation_mutation_path_add(entity) : RelationMutationPath::Fragmenting;
3596 if (isPair && !prepare_pair_add(entity, relationPath))
3599 if (!handle_add_entity(entity, relationPath))
3603 if (relationPath == RelationMutationPath::NonFragmentingExclusive)
3604 handle_add_nonfragmenting_relation<true>(entity, addSparsePayload,
true);
3606 handle_add_archetype_relation<true>(entity, addSparsePayload);
3610 handle_add<true>(entity);
3616 GAIA_NODISCARD
static bool is_component_storage_trait(Entity entity)
noexcept {
3617 return !entity.pair() && (entity.id() == DontFragment.
id() || entity.id() == Sparse.
id());
3625 GAIA_NODISCARD
bool can_del(Entity entity)
const noexcept {
3626 if (!entity.pair() && m_world.
has_direct(entity, Requires))
3628 if (has_Requires_tgt(entity))
3637 bool del_inter(Entity entity) {
3638 if (!can_del(entity))
3647 void del_name_inter(EntityNameLookupKey key) {
3648 World::del_name_lookup(m_world.m_nameToEntity, key);
3653 void del_alias_inter(EntityNameLookupKey key) {
3654 World::del_name_lookup(m_world.m_aliasToEntity, key);
3661 template <
bool IsOwned>
3662 void name_inter(
const char* name, uint32_t len) {
3664 GAIA_ASSERT(!m_entity.
pair());
3665 if (m_entity.
pair())
3669 if (name ==
nullptr) {
3670 GAIA_ASSERT(len == 0);
3675 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
3680 const bool hasInvalidCharacter = name[i] ==
'.';
3681 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity names");
3682 if (hasInvalidCharacter)
3686 EntityNameLookupKey key(
3687 name, len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len, IsOwned);
3691 auto it = m_world.m_nameToEntity.find(key);
3692 if (it == m_world.m_nameToEntity.
end()) {
3694 if (m_targetNameKey.
str() !=
nullptr) {
3695 del_name_inter(m_targetNameKey);
3697 const auto compIdx = core::get_index(m_pArchetypeSrc->
ids_view(), GAIA_ID(EntityDesc));
3698 if (compIdx != BadIndex) {
3699 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->
comp_ptr_mut(compIdx, m_rowSrc));
3700 GAIA_ASSERT(core::check_alignment(pDesc));
3701 if (pDesc->name !=
nullptr) {
3702 del_name_inter(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0));
3703 pDesc->name =
nullptr;
3707 add_inter(GAIA_ID(EntityDesc));
3712 it = m_world.m_nameToEntity.emplace(key, m_entity).first;
3714#if GAIA_ASSERT_ENABLED
3716 GAIA_ASSERT(
false &&
"Trying to set non-unique name for an entity");
3723 if constexpr (IsOwned) {
3725 char* entityStr = (
char*)mem::mem_alloc(key.len() + 1);
3726 memcpy((
void*)entityStr, (
const void*)name, key.len());
3727 entityStr[key.len()] = 0;
3729 m_targetNameKey = EntityNameLookupKey(entityStr, key.len(), 1, {key.hash()});
3733 auto p = robin_hood::pair(std::make_pair(m_targetNameKey, m_entity));
3736 m_targetNameKey = key;
3739 auto p = robin_hood::pair(std::make_pair(key, m_entity));
3743 m_world.invalidate_scope_path_cache();
3750 template <
bool IsOwned>
3751 void alias_inter(
const char* alias, uint32_t len) {
3753 GAIA_ASSERT(!m_entity.
pair());
3754 if (m_entity.
pair())
3758 if (alias ==
nullptr) {
3759 GAIA_ASSERT(len == 0);
3764 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
3769 const bool hasInvalidCharacter = alias[i] ==
'.';
3770 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity aliases");
3771 if (hasInvalidCharacter)
3775 EntityNameLookupKey key(
3776 alias, len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len, IsOwned);
3778 auto it = m_world.m_aliasToEntity.find(key);
3779 if (it == m_world.m_aliasToEntity.
end()) {
3781 if (m_targetAliasKey.
str() !=
nullptr) {
3782 del_alias_inter(m_targetAliasKey);
3784 const auto compIdx = core::get_index(m_pArchetypeSrc->
ids_view(), GAIA_ID(EntityDesc));
3785 if (compIdx != BadIndex) {
3786 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->
comp_ptr_mut(compIdx, m_rowSrc));
3787 GAIA_ASSERT(core::check_alignment(pDesc));
3788 if (pDesc->alias !=
nullptr) {
3789 del_alias_inter(EntityNameLookupKey(pDesc->alias, pDesc->alias_len, 0));
3790 pDesc->alias =
nullptr;
3794 add_inter(GAIA_ID(EntityDesc));
3798 it = m_world.m_aliasToEntity.emplace(key, m_entity).first;
3800#if GAIA_ASSERT_ENABLED
3802 GAIA_ASSERT(
false &&
"Trying to set non-unique alias for an entity");
3809 if constexpr (IsOwned) {
3811 char* aliasStr = (
char*)mem::mem_alloc(key.len() + 1);
3812 memcpy((
void*)aliasStr, (
const void*)alias, key.len());
3813 aliasStr[key.len()] = 0;
3815 m_targetAliasKey = EntityNameLookupKey(aliasStr, key.len(), 1, {key.hash()});
3819 auto p = robin_hood::pair(std::make_pair(m_targetAliasKey, m_entity));
3822 m_targetAliasKey = key;
3825 auto p = robin_hood::pair(std::make_pair(key, m_entity));
3852 if (symbol ==
nullptr || symbol[0] == 0)
3855 const auto* pItem = comp_cache().symbol(symbol, len);
3856 return pItem !=
nullptr ? pItem->entity : EntityBad;
3863 const auto* pItem = comp_cache().find(component);
3864 return pItem !=
nullptr ? comp_cache().symbol_name(*pItem) :
util::str_view{};
3871 GAIA_NODISCARD
Entity path(
const char* path, uint32_t len = 0)
const {
3872 if (path ==
nullptr || path[0] == 0)
3875 const auto* pItem = comp_cache().path(path, len);
3876 return pItem !=
nullptr ? pItem->entity : EntityBad;
3883 const auto* pItem = comp_cache().find(component);
3884 return pItem !=
nullptr ? comp_cache().path_name(*pItem) :
util::str_view{};
3892 bool path(
Entity component,
const char* path, uint32_t len = 0) {
3893 auto* pItem = comp_cache_mut().find(component);
3894 return pItem !=
nullptr ? comp_cache_mut().path(*pItem, path, len) :
false;
3901 GAIA_NODISCARD
Entity alias(
const char* alias, uint32_t len = 0)
const {
3902 if (alias ==
nullptr || alias[0] == 0)
3905 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
3906 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
3908 return it != m_aliasToEntity.end() ? it->second : EntityBad;
3918 const auto& ec = m_recs.entities[entity.
id()];
3919 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
3920 if (compIdx == BadIndex)
3923 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
3924 GAIA_ASSERT(core::check_alignment(pDesc));
3925 return {pDesc->alias, pDesc->alias_len};
3934 if (!valid(entity) || entity.
pair())
3937 const auto before = this->alias(entity);
3939 const auto after = this->alias(entity);
3940 if (alias ==
nullptr)
3941 return !before.empty() && after.empty();
3943 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
3953 if (!valid(entity) || entity.
pair())
3956 const auto before = this->alias(entity);
3958 const auto after = this->alias(entity);
3959 if (alias ==
nullptr)
3960 return !before.empty() && after.empty();
3962 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
3971 const auto* pItem = comp_cache().find(entity);
3972 if (pItem ==
nullptr)
3975 const auto aliasValue = alias(entity);
3976 if (!aliasValue.empty())
3979 const auto pathValue = path(entity);
3980 if (!pathValue.empty()) {
3981 const auto symbolEntity = symbol(pathValue.data(), pathValue.size());
3982 if (symbolEntity == EntityBad || symbolEntity == entity)
3986 return symbol(entity);
3993 void invalidate_scope_path_cache()
const {
3994 m_componentScopePathCache.clear();
3995 m_componentScopePathCacheEntity = EntityBad;
3996 m_componentScopePathCacheValid =
false;
4003 GAIA_NODISCARD
bool build_scope_path(Entity scope,
util::str& out)
const {
4005 if (!valid(scope) || scope.pair())
4010 while (curr != EntityBad) {
4011 const auto currName = name(curr);
4012 if (currName.empty()) {
4018 curr = target(curr, ChildOf);
4021 if (segments.
empty())
4024 uint32_t totalLen = 0;
4025 for (
auto segment: segments)
4026 totalLen += segment.size();
4027 totalLen += (uint32_t)segments.
size() - 1;
4030 for (uint32_t i = (uint32_t)segments.
size(); i > 0; --i) {
4033 out.
append(segments[i - 1]);
4042 GAIA_NODISCARD
bool current_scope_path(util::str& out)
const {
4043 if (m_componentScope == EntityBad) {
4044 invalidate_scope_path_cache();
4049 if (m_componentScopePathCacheValid && m_componentScopePathCacheEntity == m_componentScope) {
4050 out.assign(m_componentScopePathCache.view());
4054 if (!build_scope_path(m_componentScope, out)) {
4055 invalidate_scope_path_cache();
4059 m_componentScopePathCache.assign(out.view());
4060 m_componentScopePathCacheEntity = m_componentScope;
4061 m_componentScopePathCacheValid =
true;
4070 GAIA_NODISCARD
const ComponentCacheItem*
4071 find_comp_scope_chain_inter(Entity scopeEntity,
const char* name, uint32_t len)
const {
4072 if (scopeEntity == EntityBad)
4075 util::str scopePath;
4076 if (!build_scope_path(scopeEntity, scopePath))
4079 util::str scopedName;
4080 scopedName.reserve(scopePath.size() + 1 + len);
4082 while (!scopePath.empty()) {
4084 scopedName.append(scopePath.view());
4085 scopedName.append(
'.');
4086 scopedName.append(name, len);
4088 if (
const auto* pItem = m_compCache.path(scopedName.data(), (uint32_t)scopedName.size()); pItem !=
nullptr)
4091 const auto parentSepIdx = scopePath.view().find_last_of(
'.');
4092 if (parentSepIdx == BadIndex)
4095 scopePath.assign(util::str_view(scopePath.data(), parentSepIdx));
4106 void add_comp_scope_chain_hits_inter(
4107 cnt::darray<Entity>& out, Entity scopeEntity,
const char* name, uint32_t len)
const {
4108 if (scopeEntity == EntityBad)
4111 util::str scopePath;
4112 if (!build_scope_path(scopeEntity, scopePath))
4115 util::str scopedName;
4116 scopedName.reserve(scopePath.size() + 1 + len);
4118 while (!scopePath.empty()) {
4120 scopedName.append(scopePath.view());
4121 scopedName.append(
'.');
4122 scopedName.append(name, len);
4124 if (
const auto* pItem = m_compCache.path(scopedName.data(), (uint32_t)scopedName.size()); pItem !=
nullptr)
4125 ComponentCache::push_unique_entity(out, pItem->entity);
4127 const auto parentSepIdx = scopePath.view().find_last_of(
'.');
4128 if (parentSepIdx == BadIndex)
4131 scopePath.assign(util::str_view(scopePath.data(), parentSepIdx));
4139 GAIA_NODISCARD
const ComponentCacheItem* find_comp_lookup_inter(
const char* name, uint32_t len)
const {
4140 if (
const auto* pItem = find_comp_scope_chain_inter(m_componentScope, name, len); pItem !=
nullptr)
4143 for (
const auto scopeEntity: m_componentLookupPath) {
4144 if (scopeEntity == m_componentScope)
4147 if (
const auto* pItem = find_comp_scope_chain_inter(scopeEntity, name, len); pItem !=
nullptr)
4158 void add_comp_lookup_hits_inter(cnt::darray<Entity>& out,
const char* name, uint32_t len)
const {
4159 add_comp_scope_chain_hits_inter(out, m_componentScope, name, len);
4160 for (
const auto scopeEntity: m_componentLookupPath) {
4161 if (scopeEntity == m_componentScope)
4164 add_comp_scope_chain_hits_inter(out, scopeEntity, name, len);
4174 GAIA_NODISCARD
const ComponentCacheItem*
4175 find_comp_exact_inter(
const char* name, uint32_t len,
bool isPath,
bool isSymbol)
const {
4176 if (
const auto* pItem = m_compCache.symbol(name, len); pItem !=
nullptr)
4180 if (
const auto* pItem = m_compCache.path(name, len); pItem !=
nullptr)
4183 if (
const auto* pItem = m_compCache.short_symbol(name, len); pItem !=
nullptr)
4197 void add_comp_exact_hits_inter(
4198 cnt::darray<Entity>& out,
const char* name, uint32_t len,
bool isPath,
bool isSymbol)
const {
4199 if (
const auto* pItem = m_compCache.symbol(name, len); pItem !=
nullptr)
4200 ComponentCache::push_unique_entity(out, pItem->entity);
4202 m_compCache.add_path_matches(out, util::str_view(name, len));
4204 if (out.empty() && !isPath && !isSymbol) {
4205 if (
const auto* pItem = m_compCache.short_symbol(name, len); pItem !=
nullptr)
4206 ComponentCache::push_unique_entity(out, pItem->entity);
4212 GAIA_NODISCARD
bool has_comp_lookup_ctx_inter() const noexcept {
4213 return m_componentScope != EntityBad || !m_componentLookupPath.empty();
4220 GAIA_NODISCARD
static bool is_unqualified_comp_name_inter(
const char* name, uint32_t len)
noexcept {
4221 return memchr(name,
'.', len) ==
nullptr && memchr(name,
':', len) ==
nullptr;
4228 GAIA_NODISCARD Entity pick_name_or_comp_inter(Entity namedEntity,
const ComponentCacheItem* pCompItem)
const {
4229 if (pCompItem ==
nullptr)
4232 if (namedEntity == EntityBad)
4233 return pCompItem->entity;
4235 return m_compCache.find(namedEntity) !=
nullptr ? pCompItem->entity : namedEntity;
4245 GAIA_NODISCARD
const ComponentCacheItem* resolve_component_name_inter(
const char* name, uint32_t len = 0)
const {
4246 GAIA_ASSERT(name !=
nullptr);
4248 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
4249 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
4250 const bool isPath = memchr(name,
'.', l) !=
nullptr;
4251 const bool isSymbol = memchr(name,
':', l) !=
nullptr;
4254 if (
const auto* pItem = m_compCache.path(name, l); pItem !=
nullptr)
4258 if (!isPath && !isSymbol) {
4259 if (
const auto* pItem = find_comp_lookup_inter(name, l); pItem !=
nullptr)
4263 if (
const auto* pItem = find_comp_exact_inter(name, l, isPath, isSymbol); pItem !=
nullptr)
4266 const auto aliasEntity = alias(name, l);
4267 return aliasEntity != EntityBad ? m_compCache.find(aliasEntity) :
nullptr;
4277 return entity.
pair()
4278 ? valid_pair(entity)
4279 : valid_entity(entity);
4290 if (!valid_entity_id(
id))
4293 const auto& ec = m_recs.entities[id];
4294 return Entity(
id, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair);
4301 return valid_entity_id(
id) ? get(
id) : EntityBad;
4307 template <
typename T>
4309 return comp_cache().get<T>().entity;
4315 template <
typename T>
4317#if GAIA_ECS_AUTO_COMPONENT_REGISTRATION
4320 return comp_cache().template get<T>();
4337 return add(*m_pEntityArchetype,
true,
false);
4343 const auto entity = add();
4344 add(entity, Prefab);
4351 template <
typename Func = TFunc_Vo
id_With_Entity>
4352 void add_n(uint32_t count, Func func = func_void_with_entity) {
4353 add_entity_n(*m_pEntityArchetype, count, func);
4367 template <
typename Func = TFunc_Vo
id_With_Entity>
4368 void add_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
4369 auto& ec = cont(entity);
4371 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4372 GAIA_ASSERT(ec.pChunk !=
nullptr);
4374 auto* pDstArchetype = ec.pArchetype;
4376 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4379 collect_nonfragmenting_relation_pairs(entity, nonfragmentingPairs);
4382 collect_add_n_sparse_ids(entity, nonFragSparseIds, fragSparseIds);
4384 if (nonfragmentingPairs.
empty() && nonFragSparseIds.
empty() && fragSparseIds.
empty()) {
4385 add_entity_n(*pDstArchetype, count, func);
4389 add_entity_n(*pDstArchetype, count, [&](
Entity dstEntity) {
4390 apply_add_n_non_table_type(
4391 entity, dstEntity, EntitySpan{nonfragmentingPairs}, EntitySpan{nonFragSparseIds},
4392 EntitySpan{fragSparseIds});
4404 template <
typename Func = TFunc_Vo
id_With_Entity>
4405 void add_n(
Pair entity, uint32_t count, Func func = func_void_with_entity) {
4406 add_n((
Entity)entity, count, func);
4412 template <
typename T>
4413 GAIA_NODISCARD
const ComponentCacheItem&
add() {
4416 using CT = component_type_t<T>;
4417 using FT =
typename CT::TypeFull;
4418 const auto* pItem = comp_cache().find<FT>();
4419 if (pItem !=
nullptr)
4422 const auto entity = add(*m_pCompArchetype,
false,
false);
4424 (void)current_scope_path(scopePath);
4426 const auto& item = comp_cache_mut().add<FT>(entity, scopePath.
view());
4427 item.func_create_sparse_store = [](
World& world,
Entity component) {
4430 finalize_component_registration(item, item.comp.storage_type() == DataStorageType::Sparse);
4431 if constexpr (uses_ct_dont_fragment_v<FT>)
4432 add(item.entity, DontFragment);
4433 if constexpr (supports_sparse_component_storage<FT>()) {
4434 if (item.comp.storage_type() == DataStorageType::Sparse)
4435 (
void)sparse_component_store_mut<FT>(item.entity);
4447 template <
typename T>
4451 using CT = component_type_t<T>;
4452 using FT =
typename CT::TypeFull;
4453 const auto* pItem = comp_cache().find<FT>();
4454 if (pItem !=
nullptr)
4457 validate_runtime_semantics(runtimeType);
4458 const auto entity = add(*m_pCompArchetype,
false,
false);
4460 (void)current_scope_path(scopePath);
4462 const auto& item = comp_cache_mut().add<FT>(entity, runtimeType, scopePath.
view());
4463 item.func_create_sparse_store = [](
World& world,
Entity component) {
4466 finalize_component_registration(item, item.comp.storage_type() == DataStorageType::Sparse);
4467 if constexpr (uses_ct_dont_fragment_v<FT>)
4468 add(item.entity, DontFragment);
4469 if constexpr (supports_sparse_component_storage<FT>()) {
4470 if (item.comp.storage_type() == DataStorageType::Sparse)
4471 (
void)sparse_component_store_mut<FT>(item.entity);
4482 GAIA_ASSERT(desc.
name.
size() < ComponentCacheItem::MaxNameLength);
4484 if (
const auto* pItem = comp_cache().symbol(desc.
name); pItem !=
nullptr)
4485 return *comp_cache_mut().find(pItem->entity);
4488 const auto entity = add(*m_pCompArchetype,
false,
false);
4490 (void)current_scope_path(scopePath);
4491 auto& itemInfo = comp_cache_mut().add(entity, desc, scopePath.
view());
4492 finalize_component_registration(itemInfo,
true);
4501#if GAIA_ASSERT_ENABLED
4502 if (!
object.
pair()) {
4503 const auto* pItem = comp_cache().find(
object);
4504 if (pItem !=
nullptr && pItem->entity ==
object && component_uses_sparse_storage(
object))
4505 GAIA_ASSERT2(
false,
"Sparse runtime components require an explicit typed value when added by entity id");
4518 auto& ec = cont(entity);
4531 const auto source = (
Entity)entity;
4532 auto& ec = fetch(source);
4543 template <
typename T>
4545 using FT =
typename component_type_t<T>::TypeFull;
4546 if constexpr (uses_ct_sparse_storage<FT>()) {
4547 SparseStorageMode mode;
4548 const auto object = register_sparse_component_id<FT>(mode);
4549 (void)sparse_component_store_mut<FT>(
object).add(entity);
4550 finish_sparse_component_add_inter(entity,
object, mode);
4561 template <
typename T>
4573 template <
typename T>
4575 static_assert(core::is_raw_v<T>);
4577 if constexpr (supports_sparse_component_storage<typename component_type_t<T>::TypeFull>()) {
4578 using FT =
typename component_type_t<T>::TypeFull;
4579 if (can_use_sparse_component_storage<FT>(
object)) {
4580 const auto mode = sparse_storage_mode(
object);
4581 if (mode != SparseStorageMode::None) {
4582 auto& data = sparse_component_add_value<FT>(
object, entity);
4583 data = GAIA_FWD(value);
4584 finish_sparse_component_add_inter(entity,
object, mode);
4591#if GAIA_OBSERVERS_ENABLED
4593 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
4595 eb.add_inter_init(
object);
4598 const auto& ec = fetch(entity);
4599 const auto idx = ec.row;
4600 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset(
object, GAIA_FWD(value));
4601 notify_add_single(entity,
object);
4602#if GAIA_OBSERVERS_ENABLED
4603 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4615 template <
typename T>
4626 template <typename T, typename U = typename actual_type_t<T>::Type>
4628 using FT =
typename component_type_t<T>::TypeFull;
4629 if constexpr (uses_ct_sparse_storage<FT>()) {
4630 SparseStorageMode mode;
4631 const auto object = register_sparse_component_id<FT>(mode);
4632 auto& data = sparse_component_store_mut<FT>(
object).add(entity);
4633 data = GAIA_FWD(value);
4634 finish_sparse_component_add_inter(entity,
object, mode);
4638#if GAIA_OBSERVERS_ENABLED
4640 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
4644 builder.add_inter_init(
object);
4647 const auto& ec = cont(entity);
4648 const auto idx = ec.row;
4649 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset<T>(GAIA_FWD(value));
4650 notify_add_single(entity,
object);
4651#if GAIA_OBSERVERS_ENABLED
4652 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4664 template <typename T, typename U = typename actual_type_t<T>::Type>
4666 using FT =
typename component_type_t<T>::TypeFull;
4667 const auto object = register_component_id<FT>();
4669 using ValueType =
typename actual_type_t<T>::Type;
4670 add<ValueType>((
Entity)entity,
object, GAIA_FWD(value));
4678 return override_inter(entity,
object);
4693 template <
typename T>
4694 GAIA_NODISCARD
bool override(
Entity entity) {
4696 using FT =
typename component_type_t<T>::TypeFull;
4697 const auto& item = add<FT>();
4699 if constexpr (uses_ct_sparse_storage<FT>())
4700 return override_sparse_component_inter(entity, item.entity);
4702 return override_inter(entity, item.entity);
4710 template <
typename T>
4713 using FT =
typename component_type_t<T>::TypeFull;
4715 if constexpr (supports_sparse_component_storage<FT>()) {
4716 if (can_use_sparse_component_storage<FT>(
object))
4717 return override_sparse_component_inter(entity,
object);
4720 return override_inter(entity,
object);
4730 GAIA_ASSERT(!entity.
pair());
4731 GAIA_ASSERT(valid(entity));
4737 for (uint32_t i = (uint32_t)ids.size() - 1; i != (uint32_t)-1; --i)
4743 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
4744 const auto component = compKey.entity();
4745 if (component_is_non_fragmenting(component) && store.func_has(store.pStore, entity))
4748 for (
const auto component: sparseIds)
4749 del(entity, component);
4752 for (
const auto& [relationKey, store]: m_nonFragmentingRelationsByRel) {
4753 if (store.target(entity) != EntityBad)
4754 relationIds.
push_back(relationKey.entity());
4756 for (
const auto relation: relationIds) {
4757 const auto targetEntity = target(entity, relation);
4758 if (targetEntity != EntityBad)
4759 del(entity,
Pair(relation, targetEntity));
4774 GAIA_ASSERT(valid(srcEntity));
4776 auto& ec = cont(srcEntity);
4777 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4778 GAIA_ASSERT(ec.pChunk !=
nullptr);
4781 collect_nonfragmenting_relation_pairs(srcEntity, nonfragmentingPairs);
4783 auto* pSrcChunk = ec.pChunk;
4784 const auto srcRow = ec.row;
4785 auto* pDstArchetype = ec.pArchetype;
4790 const bool hasEntityDesc = pDstArchetype->has<
EntityDesc>();
4792 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4795 collect_copy_add_hook_ids(srcEntity, *pDstArchetype, EntitySpan{nonfragmentingPairs}, addHookIds);
4797 dstEntity = add(*pDstArchetype,
true,
false);
4798 if (hasEntityDesc) {
4799 auto& ecDst = m_recs.entities[dstEntity.
id()];
4800 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, ecDst.pChunk, ecDst.row);
4802 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
4804 copy_all_sparse_entity_data(srcEntity, dstEntity);
4805 copy_nonfragmenting_relation_pairs(dstEntity, EntitySpan{nonfragmentingPairs});
4806 trigger_copy_add_hooks(EntitySpan{&dstEntity, 1}, EntitySpan{addHookIds});
4821 template <
typename Func = TFunc_Vo
id_With_Entity>
4822 void copy_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
4824 collect_nonfragmenting_relation_pairs(entity, nonfragmentingPairs);
4825 auto* pDstArchetype = cont(entity).pArchetype;
4827 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4829 collect_copy_add_hook_ids(entity, *pDstArchetype, EntitySpan{nonfragmentingPairs}, addHookIds);
4830 copy_n_inter(entity, count, func, EntitySpan{}, EntitySpan{nonfragmentingPairs}, EntitySpan{addHookIds});
4833#if GAIA_OBSERVERS_ENABLED
4843 GAIA_ASSERT(valid(srcEntity));
4845 auto& ec = cont(srcEntity);
4846 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4847 GAIA_ASSERT(ec.pChunk !=
nullptr);
4850 collect_nonfragmenting_relation_pairs(srcEntity, nonfragmentingPairs);
4852 auto* pSrcChunk = ec.pChunk;
4853 const auto srcRow = ec.row;
4854 auto* pDstArchetype = ec.pArchetype;
4857 const bool hasEntityDesc = pDstArchetype->has<
EntityDesc>();
4859 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4861 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
4862 const auto sparseIdCount = copied_non_frag_sparse_id_count(srcEntity);
4863 const auto nonfragmentingPairCount = (uint32_t)nonfragmentingPairs.
size();
4864 const auto addedIdCount = archetypeIdCount + sparseIdCount + nonfragmentingPairCount;
4865 auto* pAddedIds = addedIdCount != 0U ? (
Entity*)alloca(
sizeof(
Entity) * addedIdCount) : nullptr;
4866 write_archetype_ids(*pDstArchetype, pAddedIds);
4867 write_copied_non_frag_sparse_ids(srcEntity, pAddedIds + archetypeIdCount);
4868 GAIA_FOR(nonfragmentingPairCount)
4869 pAddedIds[archetypeIdCount + sparseIdCount + i] = nonfragmentingPairs[i];
4871 collect_add_hook_ids(EntitySpan{pAddedIds, addedIdCount}, addHookIds);
4872 #if GAIA_OBSERVERS_ENABLED
4873 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{pAddedIds, addedIdCount});
4876 const auto dstEntity = add(*pDstArchetype,
true,
false);
4877 if (hasEntityDesc) {
4878 auto& ecDst = m_recs.entities[dstEntity.id()];
4879 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, ecDst.pChunk, ecDst.row);
4881 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
4883 (void)copy_all_sparse_entity_data(srcEntity, dstEntity);
4884 copy_nonfragmenting_relation_pairs(dstEntity, EntitySpan{nonfragmentingPairs});
4885 trigger_copy_add_hooks(EntitySpan{&dstEntity, 1}, EntitySpan{addHookIds});
4886 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{&dstEntity, 1});
4888 m_observers.on_add(*
this, *pDstArchetype, EntitySpan{pAddedIds, addedIdCount}, EntitySpan{&dstEntity, 1});
4889 #if GAIA_OBSERVERS_ENABLED
4890 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4906 template <
typename Func = TFunc_Vo
id_With_Entity>
4907 void copy_ext_n(Entity entity, uint32_t count, Func func = func_void_with_entity) {
4908 cnt::darray_ext<Entity, 8> nonfragmentingPairs;
4909 collect_nonfragmenting_relation_pairs(entity, nonfragmentingPairs);
4911 auto& ec = cont(entity);
4912 auto* pDstArchetype = ec.pArchetype;
4913 if (pDstArchetype->has<EntityDesc>())
4914 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
4916 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
4917 const auto sparseIdCount = copied_non_frag_sparse_id_count(entity);
4918 const auto nonfragmentingPairCount = (uint32_t)nonfragmentingPairs.size();
4919 const auto addedIdCount = archetypeIdCount + sparseIdCount + nonfragmentingPairCount;
4920 auto* pAddedIds = addedIdCount != 0U ? (Entity*)alloca(
sizeof(Entity) * addedIdCount) : nullptr;
4921 write_archetype_ids(*pDstArchetype, pAddedIds);
4922 write_copied_non_frag_sparse_ids(entity, pAddedIds + archetypeIdCount);
4923 GAIA_FOR(nonfragmentingPairCount)
4924 pAddedIds[archetypeIdCount + sparseIdCount + i] = nonfragmentingPairs[i];
4925 cnt::darray_ext<Entity, 16> addHookIds;
4926 collect_add_hook_ids(EntitySpan{pAddedIds, addedIdCount}, addHookIds);
4927 #if GAIA_OBSERVERS_ENABLED
4928 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{pAddedIds, addedIdCount});
4931 entity, count, func, EntitySpan{pAddedIds, addedIdCount}, EntitySpan{nonfragmentingPairs},
4932 EntitySpan{addHookIds}, EntityBad
4933 #if GAIA_OBSERVERS_ENABLED
4938 #if GAIA_OBSERVERS_ENABLED
4939 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4952 template <
typename Func>
4953 void invoke_copy_batch_callback(
4954 Func& func, Archetype* pDstArchetype, Chunk* pDstChunk, uint32_t originalChunkSize, uint32_t toCreate) {
4955 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
4958 it.set_archetype(pDstArchetype);
4959 it.set_chunk(pDstChunk);
4960 it.set_range((uint16_t)originalChunkSize, (uint16_t)toCreate);
4963 auto entities = pDstChunk->entity_view();
4964 GAIA_FOR2(originalChunkSize, pDstChunk->size()) func(entities[i]);
4972 template <typename Func>
4973 void flush_copy_iter_group(Func& func, CopyIterGroupState& group) {
4974 if (group.count == 0)
4979 it.set_archetype(group.pArchetype);
4980 it.set_chunk(group.pChunk);
4981 it.set_range(group.startRow, group.count);
4991 template <
typename Func>
4992 void push_copy_iter_group(Func& func, CopyIterGroupState& group, Entity instance) {
4993 const auto& ec = fetch(instance);
4995 if (group.count != 0 && ec.pArchetype == group.pArchetype && ec.pChunk == group.pChunk &&
4996 ec.row == uint16_t(group.startRow + group.count)) {
5001 flush_copy_iter_group(func, group);
5002 group.pArchetype = ec.pArchetype;
5003 group.pChunk = ec.pChunk;
5004 group.startRow = ec.row;
5011 void prepare_parent_batch(Entity parentEntity,
const NonFragmentingRelationStore& parentStore) {
5012 GAIA_ASSERT(valid(parentEntity));
5013 if (parentStore.sources(parentEntity) !=
nullptr)
5016 const auto parentPair = Pair(Parent, parentEntity);
5017 assign_pair(parentPair, *m_pEntityArchetype);
5019 auto& ecParent = fetch(parentEntity);
5020 EntityBuilder::set_flag(ecParent.flags, EntityContainerFlags::OnDeleteTarget_Delete,
true);
5025 void prepare_parent_batch(Entity parentEntity) {
5026 const auto* pStore = nonfragmenting_relation_store(Parent);
5027 if (pStore !=
nullptr) {
5028 prepare_parent_batch(parentEntity, *pStore);
5032 GAIA_ASSERT(valid(parentEntity));
5033 const auto parentPair = Pair(Parent, parentEntity);
5034 assign_pair(parentPair, *m_pEntityArchetype);
5036 auto& ecParent = fetch(parentEntity);
5037 EntityBuilder::set_flag(ecParent.flags, EntityContainerFlags::OnDeleteTarget_Delete,
true);
5047 Entity parentEntity, Archetype& archetype, Chunk& chunk, uint32_t originalChunkSize, uint32_t toCreate) {
5048 GAIA_ASSERT(valid(parentEntity));
5049#if !GAIA_OBSERVERS_ENABLED
5056 auto& parentStore = nonfragmenting_relation_store_mut(Parent);
5057 prepare_parent_batch(parentEntity, parentStore);
5059 auto entities = chunk.entity_view();
5060#if GAIA_OBSERVERS_ENABLED
5061 if (!m_observers.has_on_add_observers()) {
5062 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
5063 nonfragmenting_relation_set(parentStore, entities[rowIdx], Parent, parentEntity);
5068 const Entity parentPair = Pair(Parent, parentEntity);
5069 auto addDiffCtx = m_observers.prepare_diff(
5070 *
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1},
5071 EntitySpan{entities.data() + originalChunkSize, toCreate});
5073 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
5074 nonfragmenting_relation_set(parentStore, entities[rowIdx], Parent, parentEntity);
5077#if GAIA_OBSERVERS_ENABLED
5079 *
this, archetype, EntitySpan{&parentPair, 1}, EntitySpan{entities.data() + originalChunkSize, toCreate});
5080 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5087 void parent_direct(Entity entity, Entity parentEntity) {
5088 GAIA_ASSERT(valid(entity));
5089 GAIA_ASSERT(valid(parentEntity));
5090 auto& parentStore = nonfragmenting_relation_store_mut(Parent);
5091 const auto oldParentEntity = parentStore.target(entity);
5093 if (oldParentEntity == parentEntity)
5096#if GAIA_OBSERVERS_ENABLED
5097 const bool hasOnDelObservers = oldParentEntity != EntityBad && m_observers.has_on_del_observers();
5098 const bool hasOnAddObservers = m_observers.has_on_add_observers();
5099 if (!hasOnDelObservers && !hasOnAddObservers) {
5100 prepare_parent_batch(parentEntity, parentStore);
5101 nonfragmenting_relation_set(parentStore, entity, Parent, parentEntity);
5105 const Entity oldParentPair = hasOnDelObservers ? Pair(Parent, oldParentEntity) : EntityBad;
5106 const Entity parentPair = Pair(Parent, parentEntity);
5107 auto delDiffCtx = !hasOnDelObservers
5108 ? ObserverRegistry::DiffDispatchCtx{}
5109 : m_observers.prepare_diff(
5110 *
this, ObserverEvent::OnDel, EntitySpan{&oldParentPair, 1}, EntitySpan{&entity, 1});
5111 auto addDiffCtx = !hasOnAddObservers
5112 ? ObserverRegistry::DiffDispatchCtx{}
5113 : m_observers.prepare_diff(
5114 *
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
5115 prepare_parent_batch(parentEntity, parentStore);
5117 prepare_parent_batch(parentEntity, parentStore);
5119 nonfragmenting_relation_set(parentStore, entity, Parent, parentEntity);
5121#if GAIA_OBSERVERS_ENABLED
5122 const auto& ec = fetch(entity);
5123 if (hasOnDelObservers)
5124 m_observers.on_del(*
this, *ec.pArchetype, EntitySpan{&oldParentPair, 1}, EntitySpan{&entity, 1});
5125 if (hasOnAddObservers)
5126 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
5127 if (hasOnDelObservers)
5128 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
5129 if (hasOnAddObservers)
5130 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5137 void notify_add_single(Entity entity, Entity
object) {
5138#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5139 if GAIA_UNLIKELY (tearing_down())
5142 #if GAIA_OBSERVERS_ENABLED
5143 const auto& ec = fetch(entity);
5148 #if GAIA_ENABLE_ADD_DEL_HOOKS
5149 const auto* pItem = component_item(entity,
object);
5150 if (pItem !=
nullptr) {
5151 const auto& hooks = pItem->hooks();
5152 if (hooks.func_add !=
nullptr)
5153 hooks.func_add(*
this, *pItem, entity);
5157 #if GAIA_OBSERVERS_ENABLED
5158 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
5171 void notify_del_single(Entity entity, Entity
object) {
5172#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5173 if GAIA_UNLIKELY (tearing_down())
5176 #if GAIA_OBSERVERS_ENABLED
5177 const auto& ec = fetch(entity);
5182 #if GAIA_OBSERVERS_ENABLED
5183 m_observers.on_del(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
5186 #if GAIA_ENABLE_ADD_DEL_HOOKS
5187 const auto* pItem = component_item(entity,
object);
5188 if (pItem !=
nullptr) {
5189 const auto& hooks = pItem->hooks();
5190 if (hooks.func_del !=
nullptr)
5191 hooks.func_del(*
this, *pItem, entity);
5205 GAIA_NODISCARD
bool may_notify_del_entity_components(
const Archetype& archetype)
const {
5206#if GAIA_ENABLE_ADD_DEL_HOOKS
5207 if (m_compCache.hooks_accessed())
5210#if GAIA_OBSERVERS_ENABLED
5211 return m_observers.has_on_del_observers() &&
5212 (archetype.has_observed_terms() || !m_sparseComponentsByComp.empty());
5222 void notify_del_entity_components(
const EntityContainer& ec, Entity entity) {
5223#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5224 const auto& archetype = *ec.pArchetype;
5225 if (!may_notify_del_entity_components(archetype))
5227 if GAIA_UNLIKELY (tearing_down())
5230 #if GAIA_OBSERVERS_ENABLED
5231 const bool inspectObserverTerms =
5232 m_observers.has_on_del_observers() && (archetype.has_observed_terms() || !m_sparseComponentsByComp.empty());
5234 #if GAIA_ENABLE_ADD_DEL_HOOKS
5235 const bool inspectHookTerms = m_compCache.hooks_accessed();
5237 cnt::darray_ext<Entity, 32> observerTerms;
5238 cnt::darray_ext<Entity, 32> hookTerms;
5240 #if GAIA_OBSERVERS_ENABLED
5241 if (inspectObserverTerms && archetype.has_observed_terms()) {
5242 for (
const auto term: archetype.ids_view())
5243 observerTerms.push_back(term);
5247 #if GAIA_ENABLE_ADD_DEL_HOOKS
5248 if (inspectHookTerms) {
5249 for (
const auto term: archetype.ids_view()) {
5250 const auto* pItem = component_item(entity, term);
5251 if (pItem !=
nullptr && pItem->hooks().func_del !=
nullptr)
5252 hookTerms.push_back(term);
5257 for (
const auto& [componentKey, store]: m_sparseComponentsByComp) {
5258 const auto component = componentKey.entity();
5259 if (!component_is_non_fragmenting(component) || !store.func_has(store.pStore, entity))
5262 #if GAIA_OBSERVERS_ENABLED
5263 if ((fetch(component).flags & EntityContainerFlags::IsObserved) != 0)
5264 observerTerms.push_back(component);
5266 #if GAIA_ENABLE_ADD_DEL_HOOKS
5267 const auto* pItem = component_item(entity, component);
5268 if (inspectHookTerms && pItem !=
nullptr && pItem->hooks().func_del !=
nullptr)
5269 hookTerms.push_back(component);
5273 if (observerTerms.empty() && hookTerms.empty())
5278 #if GAIA_OBSERVERS_ENABLED
5279 if (!observerTerms.empty())
5280 m_observers.on_del(*
this, archetype, EntitySpan{observerTerms}, EntitySpan{&entity, 1});
5283 #if GAIA_ENABLE_ADD_DEL_HOOKS
5284 for (
const auto term: hookTerms) {
5285 const auto* pItem = component_item(entity, term);
5286 if (pItem ==
nullptr)
5288 const auto& hooks = pItem->hooks();
5289 if (hooks.func_del !=
nullptr)
5290 hooks.func_del(*
this, *pItem, entity);
5307 GAIA_NODISCARD
bool has_semantic_match_without_source(
5308 Entity entity, Entity
object, Entity excludedSource, cnt::set<EntityLookupKey>& visited)
const {
5309 const auto inserted = visited.insert(EntityLookupKey(entity));
5310 if (!inserted.second)
5313 if (entity != excludedSource && has_direct(entity,
object))
5316 const auto it = m_entityToAsTargets.find(EntityLookupKey(entity));
5317 if (it == m_entityToAsTargets.end())
5320 for (
const auto baseKey: it->second) {
5321 if (has_semantic_match_without_source(baseKey.entity(),
object, excludedSource, visited))
5331 void notify_inherited_del_dependents(Entity source, Entity
object) {
5332#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5333 const auto& descendants = as_relations_trav_cache(source);
5334 for (
const auto descendant: descendants) {
5335 if (descendant == source)
5337 if (has_direct(descendant,
object) || !has(descendant,
object))
5340 cnt::set<EntityLookupKey> visited;
5341 if (has_semantic_match_without_source(descendant,
object, source, visited))
5344 notify_del_single(descendant,
object);
5355 void notify_inherited_del_dependents(Entity source, EntitySpan removedObjects) {
5356 for (
const auto object: removedObjects)
5357 notify_inherited_del_dependents(source, object);
5369#if GAIA_OBSERVERS_ENABLED
5372 template <
typename Func>
5374 Entity entity, uint32_t count, Func& func, EntitySpan addedIds, EntitySpan nonfragmentingPairs,
5375 EntitySpan addHookIds, Entity parentInstance = EntityBad
5376#
if GAIA_OBSERVERS_ENABLED
5378 ObserverRegistry::DiffDispatchCtx* pAddDiffCtx =
nullptr
5381 GAIA_ASSERT(valid(entity));
5382 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
5383#if !GAIA_OBSERVERS_ENABLED
5390#if GAIA_OBSERVERS_ENABLED
5391 const bool useLocalAddDiff = !addedIds.empty() && pAddDiffCtx ==
nullptr;
5392 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
5393 if (useLocalAddDiff)
5394 addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
5397 auto& ec = cont(entity);
5399 GAIA_ASSERT(ec.pChunk !=
nullptr);
5400 GAIA_ASSERT(ec.pArchetype !=
nullptr);
5402 auto* pSrcChunk = ec.pChunk;
5403 auto* pDstArchetype = ec.pArchetype;
5404 const auto hasEntityDesc = pDstArchetype->has<EntityDesc>();
5406 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
5408 if (parentInstance != EntityBad)
5409 prepare_parent_batch(parentInstance);
5414 const auto srcRow = ec.row;
5416 EntityContainerCtx ctx{
true,
false};
5418 uint32_t left = count;
5420 auto* pDstChunk = pDstArchetype->foc_free_chunk();
5421 const uint32_t originalChunkSize = pDstChunk->size();
5422 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
5423 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
5425 GAIA_FOR(toCreate) {
5426 const auto entityNew = m_recs.entities.alloc(&ctx);
5427 auto& ecNew = m_recs.entities[entityNew.id()];
5428 store_entity(ecNew, entityNew, pDstArchetype, pDstChunk);
5430#if GAIA_ASSERT_ENABLED
5431 GAIA_ASSERT(ecNew.pChunk == pDstChunk);
5432 auto entityExpected = pDstChunk->entity_view()[ecNew.row];
5433 GAIA_ASSERT(entityExpected == entityNew);
5436 if (hasEntityDesc) {
5437 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, ecNew.row);
5440 copy_all_sparse_entity_data(entity, entityNew);
5441 copy_nonfragmenting_relation_pairs(entityNew, nonfragmentingPairs);
5444 pDstArchetype->try_update_free_chunk_idx();
5446 if (!hasEntityDesc) {
5447 pDstChunk->call_ctors(originalChunkSize, toCreate);
5450 GAIA_PROF_SCOPE(World::copy_n_entity_data);
5451 Chunk::copy_entity_data_n_same_chunk(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
5455 pDstChunk->update_versions();
5456 auto entities = pDstChunk->entity_view();
5457 trigger_copy_add_hooks(EntitySpan{entities.data() + originalChunkSize, toCreate}, EntitySpan{addHookIds});
5459#if GAIA_OBSERVERS_ENABLED
5460 if (!addedIds.empty()) {
5461 if (pAddDiffCtx !=
nullptr)
5462 m_observers.add_diff_targets(
5463 *
this, *pAddDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
5464 else if (useLocalAddDiff)
5465 m_observers.add_diff_targets(
5466 *
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
5468 *
this, *pDstArchetype, addedIds, EntitySpan{entities.data() + originalChunkSize, toCreate});
5472 if (parentInstance != EntityBad)
5473 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
5475 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
5479#if GAIA_OBSERVERS_ENABLED
5480 if (useLocalAddDiff)
5481 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5488 GAIA_NODISCARD
bool id_uses_inherit_policy(Entity
id)
const {
5489 return !is_wildcard(
id) && target(
id, OnInstantiate) == Inherit;
5496 GAIA_NODISCARD Entity inherited_id_owner(Entity entity, Entity
id)
const {
5497 if (!id_uses_inherit_policy(
id))
5500 const auto& targets = as_targets_trav_cache(entity);
5501 for (
const auto target: targets) {
5502 if (has_inter(target,
id,
false))
5513 GAIA_NODISCARD
bool has_direct_sparse_component_inter(Entity entity, Entity
object)
const {
5514 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
5515 return itSparseStore != m_sparseComponentsByComp.end() &&
5516 itSparseStore->second.func_has(itSparseStore->second.pStore, entity);
5525 GAIA_NODISCARD Entity id_owner_inter(Entity entity, Entity
object)
const {
5526 GAIA_ASSERT(
object != EntityBad);
5527 GAIA_ASSERT(!is_wildcard(
object));
5529 const auto* pEc = try_live_record(entity);
5533 const auto& ec = *pEc;
5535 if (
object.pair()) {
5536 if (has_nonfragmenting_relation_pair(entity,
object) || ec.pArchetype->has(
object))
5539 if (has_direct_sparse_component_inter(entity,
object) || ec.pArchetype->has(
object))
5543 return inherited_id_owner(entity,
object);
5549 GAIA_NODISCARD
bool instantiate_copies_id(Entity
id)
const {
5550 const auto policy = target(
id, OnInstantiate);
5551 if (policy == EntityBad || policy == Override)
5553 if (policy == DontInherit || policy == Inherit)
5563 template <
typename T>
5564 GAIA_NODISCARD
bool prefab_child_edge_exists(
const T& children, Entity childPrefab)
const {
5565 for (
const auto& child: children) {
5566 if (child.prefab == childPrefab)
5579 template <
typename T>
5580 void gather_prefab_children_for_relation(Entity prefabEntity, Entity relation, T& outChildren) {
5581 sources(relation, prefabEntity, [&](Entity childPrefab) {
5582 if (!has_direct(childPrefab, Prefab) || prefab_child_edge_exists(outChildren, childPrefab))
5585 PrefabChildEdge edge{};
5586 edge.prefab = childPrefab;
5587 edge.relation = relation;
5588 outChildren.push_back(edge);
5597 template <
typename T>
5598 void gather_sorted_prefab_children(Entity prefabEntity, T& outChildren) {
5599 gather_prefab_children_for_relation(prefabEntity, Parent, outChildren);
5600 gather_prefab_children_for_relation(prefabEntity, ChildOf, outChildren);
5602 core::sort(outChildren, [](
const PrefabChildEdge& left,
const PrefabChildEdge& right) {
5603 if (left.prefab.id() != right.prefab.id())
5604 return left.prefab.id() < right.prefab.id();
5606 return left.relation.id() < right.relation.id();
5613 GAIA_NODISCARD
bool prefab_hierarchy_parent_id(Entity
id)
const {
5614 if (!
id.pair() ||
id.
id() != ChildOf.
id())
5617 const auto parentPrefab = pair_tgt(*
this,
id);
5618 return valid(parentPrefab) && has_direct(parentPrefab, Prefab);
5625 void attach_prefab_child_instance(Entity instance, Entity relation, Entity parentInstance) {
5626 GAIA_ASSERT(valid(instance));
5627 GAIA_ASSERT(valid(relation));
5628 GAIA_ASSERT(valid(parentInstance));
5630 if (relation == Parent) {
5631 parent_direct(instance, parentInstance);
5635 add(instance, Pair(relation, parentInstance));
5641 void collect_nonfragmenting_relation_pairs(Entity srcEntity, cnt::darray_ext<Entity, 8>& out)
const {
5642 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
5643 const auto target = store.target(srcEntity);
5644 if (target != EntityBad)
5645 out.push_back(Pair(relKey.entity(), target));
5648 core::sort(out.begin(), out.end(), core::is_smaller<Entity>{});
5654 void copy_nonfragmenting_relation_pairs(Entity dstEntity, EntitySpan pairs) {
5658 EntityBuilder builder(*
this, dstEntity);
5659 for (
const auto pair: pairs)
5660 builder.add_inter_init(pair);
5671 template <
typename TNonFrag,
typename TFrag>
5672 void collect_add_n_sparse_ids(Entity srcEntity, TNonFrag& nonFragOut, TFrag& fragOut)
const {
5675 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5676 const auto comp = compKey.entity();
5677 if (!store.func_has(store.pStore, srcEntity))
5680 if (component_is_non_fragmenting(comp)) {
5681 nonFragOut.push_back(comp);
5685 if (sparse_storage_mode(comp) != SparseStorageMode::None)
5686 fragOut.push_back(comp);
5693 void add_default_sparse_ids(Entity dstEntity, EntitySpan sparseIds) {
5694 for (
const auto comp: sparseIds) {
5695 auto it = m_sparseComponentsByComp.find(EntityLookupKey(comp));
5696 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
5697 GAIA_ASSERT(it->second.func_add !=
nullptr);
5698 (void)it->second.func_add(it->second.pStore, dstEntity);
5708 void apply_add_n_non_table_type(
5709 Entity srcEntity, Entity dstEntity, EntitySpan nonfragmentingPairs, EntitySpan nonFragSparseIds,
5710 EntitySpan fragSparseIds) {
5711 copy_nonfragmenting_relation_pairs(dstEntity, nonfragmentingPairs);
5712 (void)copy_sparse_entity_data(srcEntity, dstEntity, nonFragSparseIds);
5713 add_default_sparse_ids(dstEntity, fragSparseIds);
5722 GAIA_NODISCARD
bool copy_sparse_store_inter(
5723 Entity srcEntity, Entity dstEntity, Entity comp,
const SparseComponentStoreErased& store) {
5724 if (!copies_sparse_payload_inter(comp, srcEntity, store))
5727 GAIA_ASSERT(store.func_copy_entity !=
nullptr);
5728 return store.func_copy_entity(store.pStore, dstEntity, srcEntity);
5736 uint32_t copy_all_sparse_entity_data(Entity srcEntity, Entity dstEntity, Entity* pCopiedIds =
nullptr) {
5737 uint32_t copiedCnt = 0;
5738 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
5739 const auto comp = compKey.entity();
5740 if (!copy_sparse_store_inter(srcEntity, dstEntity, comp, store))
5743 if (pCopiedIds !=
nullptr)
5744 pCopiedIds[copiedCnt] = comp;
5755 template <
typename T>
5756 void collect_add_hook_ids(EntitySpan addedIds, T& outHookIds)
const {
5758#if GAIA_ENABLE_ADD_DEL_HOOKS
5759 if (!m_compCache.hooks_accessed())
5762 for (
const auto id: addedIds) {
5763 const auto* pItem =
id.pair() ? comp_cache().find_pair_payload(
id) : comp_cache().find(
id);
5764 if (pItem !=
nullptr && pItem->hooks().func_add !=
nullptr)
5765 outHookIds.push_back(
id);
5778 template <
typename T>
5779 void collect_copy_add_hook_ids(
5780 Entity srcEntity,
const Archetype& dstArchetype, EntitySpan nonfragmentingPairs, T& outHookIds)
const {
5782#if GAIA_ENABLE_ADD_DEL_HOOKS
5783 if (!m_compCache.hooks_accessed())
5786 auto appendHook = [&](Entity id) {
5787 const auto* pItem =
id.pair() ? comp_cache().find_pair_payload(
id) : comp_cache().find(
id);
5788 if (pItem !=
nullptr && pItem->hooks().func_add !=
nullptr)
5789 outHookIds.push_back(
id);
5792 for (
const auto id: dstArchetype.ids_view())
5794 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5795 const auto component = compKey.entity();
5796 if (copies_non_frag_sparse_payload_inter(component, srcEntity, store))
5797 appendHook(component);
5799 for (
const auto pair: nonfragmentingPairs)
5804 (void)nonfragmentingPairs;
5811 void trigger_copy_add_hooks(EntitySpan entities, EntitySpan hookIds) {
5812#if GAIA_ENABLE_ADD_DEL_HOOKS
5813 if (hookIds.empty() || tearing_down())
5817 for (
const auto id: hookIds) {
5818 for (
const auto entity: entities) {
5819 const auto* pItem = component_item(entity,
id);
5820 if (pItem ==
nullptr)
5822 const auto& hooks = pItem->hooks();
5823 if (hooks.func_add !=
nullptr)
5824 hooks.func_add(*
this, *pItem, entity);
5840 uint32_t copy_sparse_entity_data(
5841 Entity srcEntity, Entity dstEntity, EntitySpan copiedSparseIds, Entity* pCopiedIds =
nullptr) {
5842 uint32_t copiedCnt = 0;
5843 for (
const auto comp: copiedSparseIds) {
5844 auto it = m_sparseComponentsByComp.find(EntityLookupKey(comp));
5845 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
5847 auto& store = it->second;
5848 if (!copy_sparse_store_inter(srcEntity, dstEntity, comp, store))
5851 if (pCopiedIds !=
nullptr)
5852 pCopiedIds[copiedCnt] = comp;
5862 void write_archetype_ids(
const Archetype& dstArchetype, Entity* pDst)
const {
5863 for (
const auto id: dstArchetype.ids_view())
5870 GAIA_NODISCARD uint32_t copied_non_frag_sparse_id_count(Entity srcEntity)
const {
5872 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5873 const auto comp = compKey.entity();
5874 if (!copies_non_frag_sparse_payload_inter(comp, srcEntity, store))
5885 void write_copied_non_frag_sparse_ids(Entity srcEntity, Entity* pDst)
const {
5886 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5887 const auto comp = compKey.entity();
5888 if (!copies_non_frag_sparse_payload_inter(comp, srcEntity, store))
5899 GAIA_NODISCARD
bool copy_sparse_payload_inter(Entity dstEntity, Entity srcEntity, Entity
object) {
5900 if (!component_is_non_fragmenting(
object) && sparse_storage_mode(
object) == SparseStorageMode::None)
5903 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
5904 if (itSparseStore == m_sparseComponentsByComp.end())
5907 return copy_sparse_store_inter(srcEntity, dstEntity,
object, itSparseStore->second);
5914 GAIA_NODISCARD
bool override_sparse_component_inter(Entity entity, Entity
object) {
5915 GAIA_ASSERT(valid(entity));
5916 GAIA_ASSERT(valid(
object));
5917 GAIA_ASSERT(sparse_storage_mode(
object) != SparseStorageMode::None);
5919 if (has_direct(entity,
object))
5922 const auto inheritedOwner = inherited_id_owner(entity,
object);
5923 if (inheritedOwner == EntityBad)
5926 if (!copy_sparse_payload_inter(entity, inheritedOwner,
object))
5929 if (sparse_storage_mode(
object) == SparseStorageMode::Fragmenting)
5930 make_sparse_copy_direct_inter(entity,
object);
5940 GAIA_NODISCARD
bool copy_owned_sparse_component_inter(Entity srcEntity, Entity dstEntity, Entity
object) {
5941 GAIA_ASSERT(valid(srcEntity));
5942 GAIA_ASSERT(valid(dstEntity));
5943 GAIA_ASSERT(valid(
object));
5944 GAIA_ASSERT(sparse_storage_mode(
object) != SparseStorageMode::None);
5946 const auto mode = sparse_storage_mode(
object);
5947 if (mode == SparseStorageMode::Fragmenting) {
5948 if (!copy_sparse_payload_inter(dstEntity, srcEntity,
object))
5951 make_sparse_copy_direct_inter(dstEntity,
object);
5952 notify_add_single(dstEntity,
object);
5955#if GAIA_OBSERVERS_ENABLED
5957 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&dstEntity, 1});
5959 if (!copy_sparse_payload_inter(dstEntity, srcEntity,
object))
5961 notify_add_single(dstEntity,
object);
5962#if GAIA_OBSERVERS_ENABLED
5963 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5971 void make_sparse_copy_direct_inter(Entity entity, Entity
object) {
5972 GAIA_ASSERT(sparse_storage_mode(
object) == SparseStorageMode::Fragmenting);
5973 EntityBuilder eb(*
this, entity);
5974 eb.add_inter_init(
object);
5983 void copy_direct_component_data_inter(
5984 Entity srcEntity, Entity dstEntity, Entity
object,
const ComponentCacheItem& item) {
5985 GAIA_ASSERT(valid(srcEntity));
5986 GAIA_ASSERT(valid(dstEntity));
5987 GAIA_ASSERT(
object.pair() || valid(
object));
5988 GAIA_ASSERT(
object.pair() || item.entity ==
object);
5989 GAIA_ASSERT(item.comp.size() != 0U);
5991 const auto& ecDst = fetch(dstEntity);
5992 const auto& ecSrc = fetch(srcEntity);
5993 const auto compIdxDst = ecDst.pChunk->comp_idx(
object);
5994 const auto compIdxSrc = ecSrc.pChunk->comp_idx(
object);
5995 GAIA_ASSERT(compIdxDst != BadIndex && compIdxSrc != BadIndex);
5997 const auto idxDst = ecDst.row;
5998 const auto idxSrc = ecSrc.row;
5999 void* pDst = ecDst.pChunk->comp_ptr_mut(compIdxDst);
6000 const void* pSrc = ecSrc.pChunk->comp_ptr(compIdxSrc);
6001 item.copy(pDst, pSrc, idxDst, idxSrc, ecDst.pChunk->capacity(), ecSrc.pChunk->capacity());
6008 GAIA_NODISCARD
bool override_inter(Entity entity, Entity
object) {
6009 GAIA_ASSERT(valid(entity));
6010 GAIA_ASSERT(
object.pair() || valid(
object));
6012 if (has_direct(entity,
object))
6015 const auto inheritedOwner = inherited_id_owner(entity,
object);
6016 if (inheritedOwner == EntityBad)
6019 const auto* pItem = component_item(inheritedOwner,
object);
6020 if (pItem !=
nullptr && sparse_storage_mode(
object) != SparseStorageMode::None)
6021 return override_sparse_component_inter(entity,
object);
6023 if (pItem !=
nullptr && pItem->comp.size() != 0U) {
6024 EntityBuilder eb(*
this, entity);
6025 eb.add_inter_init(
object);
6027 copy_direct_component_data_inter(inheritedOwner, entity,
object, *pItem);
6028 notify_add_single(entity,
object);
6032 add(entity,
object);
6041 GAIA_NODISCARD
bool copy_owned_id_from_entity(Entity srcEntity, Entity dstEntity, Entity
object) {
6042 GAIA_ASSERT(valid(srcEntity));
6043 GAIA_ASSERT(valid(dstEntity));
6044 GAIA_ASSERT(
object.pair() || valid(
object));
6046 if (has_direct(dstEntity,
object))
6049 const auto* pItem = component_item(srcEntity,
object);
6050 if (pItem !=
nullptr && sparse_storage_mode(
object) != SparseStorageMode::None)
6051 return copy_owned_sparse_component_inter(srcEntity, dstEntity,
object);
6053 if (pItem !=
nullptr && pItem->comp.size() != 0U) {
6054 EntityBuilder eb(*
this, dstEntity);
6055 eb.add_inter_init(
object);
6057 copy_direct_component_data_inter(srcEntity, dstEntity,
object, *pItem);
6058 notify_add_single(dstEntity,
object);
6062 add(dstEntity,
object);
6069 GAIA_NODISCARD Archetype* instantiate_prefab_dst_archetype(Entity prefabEntity) {
6070 GAIA_ASSERT(valid(prefabEntity));
6071 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
6073 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
6074 return fetch(prefabEntity).pArchetype;
6076 auto& ecSrc = cont(prefabEntity);
6077 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
6079 auto* pDstArchetype = ecSrc.pArchetype;
6080 if (pDstArchetype->has<EntityDesc>())
6081 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
6082 if (pDstArchetype->has(Prefab))
6083 pDstArchetype = foc_archetype_del(pDstArchetype, Prefab);
6085 for (
const auto id: ecSrc.pArchetype->ids_view()) {
6086 if (
id.pair() &&
id.
id() == Is.
id()) {
6087 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
6090 if (prefab_hierarchy_parent_id(
id)) {
6091 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
6095 if (!instantiate_copies_id(
id))
6096 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
6100 if (!prefabEntity.pair()) {
6101 const auto isPair = Pair(Is, prefabEntity);
6102 assign_pair(isPair, *m_pEntityArchetype);
6103 pDstArchetype = foc_archetype_add(pDstArchetype, isPair);
6106 return pDstArchetype;
6113 template <
typename T>
6114 void collect_prefab_copied_sparse_ids(Entity prefabEntity, T& outCopiedSparseIds) {
6115 outCopiedSparseIds.clear();
6116 if (m_sparseComponentsByComp.empty())
6119 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
6120 const auto comp = compKey.entity();
6121 if (!instantiate_copies_id(comp) || !copies_sparse_payload_inter(comp, prefabEntity, store))
6123 outCopiedSparseIds.push_back(comp);
6132 template <
typename T>
6133 void collect_prefab_added_ids(Archetype* pDstArchetype, EntitySpan copiedSparseIds, T& outAddedIds) {
6134 outAddedIds.clear();
6135 for (
const auto id: pDstArchetype->ids_view())
6136 outAddedIds.push_back(id);
6138 for (
const auto comp: copiedSparseIds) {
6139 if (sparse_copy_adds_id_inter(comp))
6140 outAddedIds.push_back(comp);
6152 GAIA_NODISCARD Entity instantiate_prefab_node_inter(
6153 Entity prefabEntity, Archetype* pDstArchetype, Entity parentInstance, EntitySpan copiedSparseIds,
6154 EntitySpan addedIds, [[maybe_unused]] EntitySpan addHookIds) {
6155 GAIA_ASSERT(valid(prefabEntity));
6156 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
6157 GAIA_ASSERT(pDstArchetype !=
nullptr);
6158#if GAIA_OBSERVERS_ENABLED
6159 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
6162 auto& ecSrc = cont(prefabEntity);
6163 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
6164 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
6166 EntityContainerCtx ctx{
true,
false};
6167 const auto instance = m_recs.entities.alloc(&ctx);
6168 auto& ecDst = m_recs.entities[instance.id()];
6169 auto* pDstChunk = pDstArchetype->foc_free_chunk();
6170 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
6171 pDstArchetype->try_update_free_chunk_idx();
6172 Chunk::copy_foreign_entity_data(ecSrc.pChunk, ecSrc.row, pDstChunk, ecDst.row);
6173 pDstChunk->update_versions();
6175 if (!prefabEntity.pair())
6176 ecDst.flags |= EntityContainerFlags::HasAliasOf;
6181 (void)copy_sparse_entity_data(prefabEntity, instance, copiedSparseIds);
6182#if GAIA_OBSERVERS_ENABLED
6183 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{&instance, 1});
6186 if (!prefabEntity.pair()) {
6187 invalidate_relation_caches(Is);
6189 const auto instanceKey = EntityLookupKey(instance);
6190 const auto prefabKey = EntityLookupKey(prefabEntity);
6191 m_entityToAsTargets[instanceKey].insert(prefabKey);
6192 m_entityToAsTargetsTravCache = {};
6193 m_entityToAsRelations[prefabKey].insert(instanceKey);
6194 m_entityToAsRelationsTravCache = {};
6195 invalidate_queries_for_entity({Is, prefabEntity});
6198#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
6199 if GAIA_UNLIKELY (tearing_down()) {
6200 (void)pDstArchetype;
6205 #if GAIA_ENABLE_ADD_DEL_HOOKS
6206 for (
const auto id: addHookIds) {
6207 const auto* pItem = component_item(instance,
id);
6208 GAIA_ASSERT(pItem !=
nullptr);
6209 const auto& hooks = pItem->hooks();
6210 GAIA_ASSERT(hooks.func_add !=
nullptr);
6211 hooks.func_add(*
this, *pItem, instance);
6215 #if GAIA_OBSERVERS_ENABLED
6216 m_observers.on_add(*
this, *pDstArchetype, addedIds, EntitySpan{&instance, 1});
6223#if GAIA_OBSERVERS_ENABLED
6224 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
6227 if (parentInstance != EntityBad)
6228 parent_direct(instance, parentInstance);
6237 GAIA_NODISCARD Entity instantiate_prefab_node_inter(Entity prefabEntity, Entity parentInstance) {
6238 auto* pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
6239 cnt::darray_ext<Entity, 16> copiedSparseIds;
6240 cnt::darray_ext<Entity, 16> addedIds;
6241 cnt::darray_ext<Entity, 16> addHookIds;
6242 collect_prefab_copied_sparse_ids(prefabEntity, copiedSparseIds);
6243 collect_prefab_added_ids(pDstArchetype, EntitySpan{copiedSparseIds}, addedIds);
6244 collect_add_hook_ids(EntitySpan{addedIds}, addHookIds);
6245 return instantiate_prefab_node_inter(
6246 prefabEntity, pDstArchetype, parentInstance, EntitySpan{copiedSparseIds}, EntitySpan{addedIds},
6247 EntitySpan{addHookIds});
6256 template <
typename Func>
6257 void instantiate_prefab_n_inter(
6258 const PrefabInstantiatePlanNode& node, Entity parentInstance, uint32_t count, Func& func) {
6259 GAIA_ASSERT(node.prefab != EntityBad);
6260 GAIA_ASSERT(node.pDstArchetype !=
nullptr);
6264#if GAIA_OBSERVERS_ENABLED
6265 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{node.addedIds});
6268 auto& ecSrc = cont(node.prefab);
6269 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
6271 if (parentInstance != EntityBad)
6272 prepare_parent_batch(parentInstance);
6274 const auto srcRow = ecSrc.row;
6275 auto* pSrcChunk = ecSrc.pChunk;
6276 auto* pDstArchetype = node.pDstArchetype;
6277 const bool bindIs = !node.prefab.pair();
6278 const auto prefabKey = EntityLookupKey(node.prefab);
6279 EntityContainerCtx ctx{
true,
false};
6281 auto& asRelations = m_entityToAsRelations[prefabKey];
6282 m_entityToAsTargets.reserve(m_entityToAsTargets.size() + count);
6283 asRelations.reserve(asRelations.size() + count);
6286 uint32_t left = count;
6288 auto* pDstChunk = pDstArchetype->foc_free_chunk();
6289 const uint32_t originalChunkSize = pDstChunk->size();
6290 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
6291 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
6293 GAIA_FOR_(toCreate, rowOffset) {
6294 const auto instance = m_recs.entities.alloc(&ctx);
6295 auto& ecDst = m_recs.entities[instance.id()];
6296 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
6298 ecDst.flags |= EntityContainerFlags::HasAliasOf;
6300 (void)copy_sparse_entity_data(node.prefab, instance, EntitySpan{node.copiedSparseIds});
6303 pDstArchetype->try_update_free_chunk_idx();
6304 Chunk::copy_foreign_entity_data_n(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
6305 pDstChunk->update_versions();
6307 auto entities = pDstChunk->entity_view();
6309 auto& asRelations = m_entityToAsRelations[prefabKey];
6310 invalidate_relation_caches(Is);
6312 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
6313 const auto instance = entities[rowIdx];
6314 m_entityToAsTargets[EntityLookupKey(instance)].insert(prefabKey);
6315 asRelations.insert(EntityLookupKey(instance));
6317 m_entityToAsTargetsTravCache = {};
6318 m_entityToAsRelationsTravCache = {};
6319 invalidate_queries_for_entity({Is, node.prefab});
6322#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
6323 if GAIA_UNLIKELY (tearing_down()) {
6325 (void)originalChunkSize;
6330 #if GAIA_ENABLE_ADD_DEL_HOOKS
6331 for (
const auto id: node.addHookIds) {
6332 const auto* pItem = component_item(entities[originalChunkSize],
id);
6333 GAIA_ASSERT(pItem !=
nullptr);
6334 const auto& hooks = pItem->hooks();
6335 GAIA_ASSERT(hooks.func_add !=
nullptr);
6337 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
6338 hooks.func_add(*
this, *pItem, entities[rowIdx]);
6343 #if GAIA_OBSERVERS_ENABLED
6344 m_observers.add_diff_targets(*
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
6346 *
this, *pDstArchetype, EntitySpan{node.addedIds},
6347 EntitySpan{entities.data() + originalChunkSize, toCreate});
6354 if (parentInstance != EntityBad)
6355 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
6357 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
6361#if GAIA_OBSERVERS_ENABLED
6362 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
6372 template <
typename T>
6373 void build_prefab_instantiate_plan(Entity prefabEntity, uint32_t parentIdx, Entity parentRelation, T& plan) {
6374 PrefabInstantiatePlanNode node{};
6375 node.prefab = prefabEntity;
6376 node.parentIdx = parentIdx;
6377 node.parentRelation = parentRelation;
6378 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
6379 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
6380 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
6381 collect_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
6383 const auto nodeIdx = (uint32_t)plan.size();
6384 plan.push_back(GAIA_MOV(node));
6386 cnt::darray_ext<PrefabChildEdge, 16> prefabChildren;
6387 gather_sorted_prefab_children(prefabEntity, prefabChildren);
6389 for (
const auto& child: prefabChildren)
6390 build_prefab_instantiate_plan(child.prefab, nodeIdx, child.relation, plan);
6398 GAIA_NODISCARD
bool instance_has_prefab_child(Entity parentInstance, Entity relation, Entity childPrefab)
const {
6399 if (childPrefab.pair())
6403 sources(relation, parentInstance, [&](Entity child) {
6406 if (has_direct(child, Pair(Is, childPrefab)))
6419 template <
typename T>
6420 uint32_t sync_prefab_instance(
6421 Entity prefabEntity, Entity instance,
const PrefabInstantiatePlanNode& node,
const T& prefabChildren) {
6422 uint32_t changes = 0;
6424 const auto isPair = Pair(Is, prefabEntity);
6425 for (
const auto id: node.pDstArchetype->ids_view()) {
6426 if (
id == isPair || has_direct(instance,
id))
6428 if (copy_owned_id_from_entity(prefabEntity, instance,
id))
6432 for (
const auto comp: node.copiedSparseIds) {
6433 if (has_direct(instance, comp))
6435 if (copy_owned_id_from_entity(prefabEntity, instance, comp))
6439 for (
const auto& child: prefabChildren) {
6440 if (instance_has_prefab_child(instance, child.relation, child.prefab))
6442 (void)instantiate_inter(child.prefab, instance, child.relation);
6453 uint32_t sync_prefab_inter(Entity prefabEntity, cnt::set<EntityLookupKey>& visited) {
6454 GAIA_ASSERT(valid(prefabEntity));
6456 if (!has_direct(prefabEntity, Prefab))
6459 const auto ins = visited.insert(EntityLookupKey(prefabEntity));
6463 PrefabInstantiatePlanNode node{};
6464 node.prefab = prefabEntity;
6465 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
6466 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
6467 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
6468 collect_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
6470 cnt::darray_ext<PrefabChildEdge, 16> prefabChildren;
6471 gather_sorted_prefab_children(prefabEntity, prefabChildren);
6473 uint32_t changes = 0;
6474 const auto& descendants = as_relations_trav_cache(prefabEntity);
6475 for (
const auto entity: descendants) {
6476 if (has_direct(entity, Prefab))
6478 changes += sync_prefab_instance(prefabEntity, entity, node, prefabChildren);
6481 for (
const auto& child: prefabChildren)
6482 changes += sync_prefab_inter(child.prefab, visited);
6492 GAIA_NODISCARD Entity
6493 instantiate_inter(Entity prefabEntity, Entity parentInstance, Entity parentRelation = Parent) {
6494 const auto directParentInstance = parentRelation == Parent ? parentInstance : EntityBad;
6495 const auto instance = instantiate_prefab_node_inter(prefabEntity, directParentInstance);
6496 if (parentInstance != EntityBad && parentRelation != Parent)
6497 attach_prefab_child_instance(instance, parentRelation, parentInstance);
6499 cnt::darray_ext<PrefabChildEdge, 16> prefabChildren;
6500 gather_sorted_prefab_children(prefabEntity, prefabChildren);
6502 for (
const auto& child: prefabChildren)
6503 (void)instantiate_inter(child.prefab, instance, child.relation);
6518 GAIA_ASSERT(valid(prefabEntity));
6520 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
6521 return copy(prefabEntity);
6523 return instantiate_inter(prefabEntity, EntityBad);
6537 GAIA_ASSERT(valid(prefabEntity));
6538 GAIA_ASSERT(valid(parentInstance));
6540 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
6541 const auto instance = copy(prefabEntity);
6542 parent_direct(instance, parentInstance);
6546 return instantiate_inter(prefabEntity, parentInstance);
6564 template <
typename Func = TFunc_Vo
id_With_Entity>
6566 instantiate_n(prefabEntity, EntityBad, count, func);
6576 instantiate_n(prefabEntity, parentInstance, count, func_void_with_entity);
6588 template <
typename Func>
6590 GAIA_ASSERT(valid(prefabEntity));
6591 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
6596 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
6597 if (parentInstance == EntityBad) {
6598 copy_n(prefabEntity, count, func);
6603 collect_nonfragmenting_relation_pairs(prefabEntity, nonfragmentingPairs);
6604 auto* pDstArchetype = cont(prefabEntity).pArchetype;
6606 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
6608 collect_copy_add_hook_ids(prefabEntity, *pDstArchetype, EntitySpan{nonfragmentingPairs}, addHookIds);
6610 prefabEntity, count, func, EntitySpan{}, EntitySpan{nonfragmentingPairs}, EntitySpan{addHookIds},
6616 build_prefab_instantiate_plan(prefabEntity, BadIndex, EntityBad, plan);
6617 if (plan.
size() == 1) {
6618 instantiate_prefab_n_inter(plan[0], parentInstance, count, func);
6622 const auto planSize = (uint32_t)plan.
size();
6623 GAIA_ASSERT(planSize <= uint32_t(-1) / count);
6625 spawned.
resize(planSize * count);
6627 uint32_t rootIdx = 0;
6628 auto collectRoot = [&](
Entity instance) {
6629 spawned[rootIdx++] = instance;
6631 instantiate_prefab_n_inter(plan[0], parentInstance, count, collectRoot);
6633 GAIA_FOR2_(1, planSize, planIdx) {
6634 uint32_t nodeIdx = 0;
6635 const auto nodeOffset = planIdx * count;
6636 auto collectNode = [&](
Entity instance) {
6637 spawned[nodeOffset + nodeIdx++] = instance;
6639 instantiate_prefab_n_inter(plan[planIdx], EntityBad, count, collectNode);
6641 const auto parentOffset = plan[planIdx].parentIdx * count;
6642 GAIA_FOR_(count, instanceIdx) {
6643 attach_prefab_child_instance(
6644 spawned[nodeOffset + instanceIdx], plan[planIdx].parentRelation, spawned[parentOffset + instanceIdx]);
6648 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
6650 GAIA_FOR_(count, idx) {
6651 push_copy_iter_group(func, group, spawned[idx]);
6653 flush_copy_iter_group(func, group);
6655 GAIA_FOR_(count, idx) {
6668 GAIA_ASSERT(valid(prefabEntity));
6671 return sync_prefab_inter(prefabEntity, visited);
6681 if (!valid(instanceRoot) || !valid(prefabEntity) || prefabEntity.
pair())
6684 const auto isPair =
Pair(Is, prefabEntity);
6685 if (has_direct(instanceRoot, isPair))
6686 return instanceRoot;
6688 Entity found = EntityBad;
6695 for (uint32_t i = 0; i < queue.
size() && found == EntityBad; ++i) {
6697 auto collectChild = [&](
Entity child) {
6699 const auto ins = visited.
insert(key);
6705 sources(Parent, queue[i], collectChild);
6706 sources(ChildOf, queue[i], collectChild);
6710 for (
const auto child: children) {
6711 if (has_direct(child, isPair)) {
6728 if (entity_deletion_active(entity))
6731 if (!entity.
pair()) {
6733 collect_entity_pairs(entity, pairEntities);
6735 auto& ec = fetch(entity);
6736 handle_del_entity(ec, entity, EntitySpan{pairEntities.
data(), pairEntities.
size()});
6740#if GAIA_USE_SAFE_ENTITY
6743 if (!m_safeEntityDels.empty() && m_entitiesDeleting.empty() && m_archetypeRowDelDepth == 0 &&
6744 m_compDtorDepth == 0 && !locked())
6745 del_released_safe_entities();
6754 if (!
object.
pair()) {
6755 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
6756 if (itSparseStore != m_sparseComponentsByComp.end()) {
6757 if (!component_is_non_fragmenting(
object)) {
6762 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
6765#if GAIA_OBSERVERS_ENABLED
6767 m_observers.prepare_diff(*
this, ObserverEvent::OnDel, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
6769 notify_inherited_del_dependents(entity,
object);
6770 notify_del_single(entity,
object);
6771 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
6772#if GAIA_OBSERVERS_ENABLED
6773 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
6795 template <
typename T>
6797 using CT = component_type_t<T>;
6798 using FT =
typename CT::TypeFull;
6800 if constexpr (uses_ct_sparse_storage<FT>()) {
6802 del(entity, register_component_id<FT>());
6804 const auto* pItem = comp_cache().template find<FT>();
6805 if (pItem !=
nullptr)
6806 del(entity, pItem->
entity);
6810 const auto* pItem = comp_cache().template find<FT>();
6811 if (pItem !=
nullptr && component_is_non_fragmenting(pItem->entity)) {
6812 del(entity, pItem->
entity);
6827 GAIA_ASSERT(!entity.
pair());
6828 GAIA_ASSERT(!entityBase.
pair());
6831 add(entity,
Pair(Is, entityBase));
6839 return is_inter<false>(entity, entityBase);
6849 return is_inter<true>(entity, entityBase);
6856 if GAIA_UNLIKELY (target.
pair())
6859 GAIA_ASSERT(valid_entity(target));
6862 return it != m_entityToAsRelations.end();
6871 add(entity,
Pair(ChildOf, parent));
6878 add(entity,
Pair(ChildOf, parent));
6886 return has(entity,
Pair(ChildOf, parent));
6894 parent_direct(entity, parentEntity);
6902 return has_direct(entity,
Pair(Parent, parentEntity));
6914 template <
typename T,
bool TriggerSetEffects>
6916 GAIA_ASSERT(valid(entity));
6918 if constexpr (uses_ct_sparse_storage<T>()) {
6919 const auto* pItem = comp_cache().template find<T>();
6920 if (pItem !=
nullptr) {
6921#if GAIA_ASSERT_ENABLED
6922 auto* pStore = sparse_component_store_erased(pItem->entity);
6923 GAIA_ASSERT(pStore !=
nullptr);
6924 GAIA_ASSERT(has_direct_sparse_component_inter(entity, pItem->
entity));
6927 if constexpr (TriggerSetEffects)
6928 finish_write(entity, pItem->
entity);
6930 ::gaia::ecs::update_version(m_worldVersion);
6931 mark_sparse_component_write(entity, pItem->
entity);
6937 auto& ec = cont(entity);
6938 modify_table_inter<T, TriggerSetEffects>(entity, ec);
6946 template <
typename T,
bool TriggerSetEffects>
6948 const auto source = (
Entity)entity;
6949 GAIA_ASSERT(valid(source));
6951 if constexpr (uses_ct_sparse_storage<T>()) {
6952 modify<T, TriggerSetEffects>(source);
6954 auto& ec = fetch(source);
6955 modify_table_inter<T, TriggerSetEffects>(source, ec);
6968 template <
typename T,
bool TriggerSetEffects>
6970 GAIA_ASSERT(valid(entity));
6971 GAIA_ASSERT(valid(
object));
6973 using FT =
typename component_type_t<T>::TypeFull;
6974 if constexpr (supports_sparse_component_storage<FT>()) {
6975 if (can_use_sparse_component_storage<FT>(
object)) {
6976#if GAIA_ASSERT_ENABLED
6977 auto* pStore = sparse_component_store_erased(
object);
6978 GAIA_ASSERT(pStore !=
nullptr);
6979 GAIA_ASSERT(has_direct_sparse_component_inter(entity,
object));
6982 if constexpr (TriggerSetEffects)
6983 finish_write(entity,
object);
6985 ::gaia::ecs::update_version(m_worldVersion);
6986 mark_sparse_component_write(entity,
object);
6992 auto& ec = cont(entity);
6993 modify_table_inter<TriggerSetEffects>(
object, ec);
7002 template <
typename T,
bool TriggerSetEffects>
7004 const auto source = (
Entity)entity;
7005 GAIA_ASSERT(valid(source));
7006 GAIA_ASSERT(valid(
object));
7008 using FT =
typename component_type_t<T>::TypeFull;
7009 if constexpr (supports_sparse_component_storage<FT>()) {
7010 if (can_use_sparse_component_storage<FT>(
object)) {
7011 modify<T, TriggerSetEffects>(source,
object);
7016 auto& ec = fetch(source);
7017 modify_table_inter<TriggerSetEffects>(
object, ec);
7028 GAIA_ASSERT(valid(entity));
7030 const auto& ec = cont(entity);
7040 const auto source = (
Entity)entity;
7041 GAIA_ASSERT(valid(source));
7043 const auto& ec = fetch(source);
7057 template <
typename T>
7060 using FT =
typename component_type_t<T>::TypeFull;
7061 using ValueType =
typename actual_type_t<T>::Type;
7062 const auto& item = add<FT>();
7063 return SetWriteProxyTyped<T, ValueType>{*
this, entity, item.
entity, get<T>(entity)};
7072 template <
typename T>
7075 using FT =
typename component_type_t<T>::TypeFull;
7076 using ValueType =
typename actual_type_t<T>::Type;
7077 const auto& item = add<FT>();
7078 const auto source = (
Entity)entity;
7079 return SetWriteProxyTyped<T, ValueType>{*
this, source, item.entity, get<T>(entity)};
7093 template <
typename T>
7096 return SetWriteProxyObject<typename actual_type_t<T>::Type>{*
this, entity, object, get<T>(entity,
object)};
7106 template <
typename T>
7109 const auto source = (
Entity)entity;
7110 return SetWriteProxyObject<typename actual_type_t<T>::Type>{*
this, source, object, get<T>(entity,
object)};
7121 template <
typename T>
7124 using FT =
typename component_type_t<T>::TypeFull;
7125 const auto& item = add<FT>();
7126 if constexpr (uses_ct_sparse_storage<FT>())
7127 return sparse_component_store_mut<FT>(item.entity).mut(entity);
7129 return acc_mut(entity).smut<T>();
7138 template <
typename T>
7141 using FT =
typename component_type_t<T>::TypeFull;
7142 const auto& item = add<FT>();
7143 if constexpr (uses_ct_sparse_storage<FT>())
7144 return sparse_component_store_mut<FT>(item.entity).mut((
Entity)entity);
7146 return acc_mut(entity).smut<T>();
7155 template <
typename T>
7158 using FT =
typename component_type_t<T>::TypeFull;
7159 if constexpr (supports_sparse_component_storage<FT>()) {
7160 if (can_use_sparse_component_storage<FT>(
object))
7161 return sparse_component_mut_value<FT>(
object, entity);
7163 return acc_mut(entity).smut<T>(object);
7171 template <
typename T>
7174 using FT =
typename component_type_t<T>::TypeFull;
7175 const auto source = (
Entity)entity;
7176 if constexpr (supports_sparse_component_storage<FT>()) {
7177 if (can_use_sparse_component_storage<FT>(
object))
7178 return sparse_component_mut_value<FT>(
object, source);
7180 return acc_mut(entity).smut<T>(object);
7193 template <
typename T>
7196 return sset<T>(entity);
7203 template <
typename T>
7206 return sset<T>(entity);
7215 template <
typename T>
7218 return sset<T>(entity,
object);
7226 template <
typename T>
7229 return sset<T>(entity,
object);
7238 if (component == EntityBad)
7241 const auto owner = id_owner_inter(entity, component);
7242 if (owner == EntityBad)
7245 const auto* pItem = component_item(owner, component);
7246 if (pItem ==
nullptr || !raw_component_supported(*pItem))
7248 if (component_uses_sparse_storage(component)) {
7249 const auto* pStore = sparse_component_store_erased(component);
7250 if (pStore ==
nullptr || !pStore->func_has(pStore->pStore, owner))
7252 return {pStore->func_get(pStore->pStore, owner), pItem->comp.size(), ComponentRawViewFlag_Valid};
7255 const auto& ec = fetch(owner);
7256 const auto compIdx = ec.pChunk->comp_idx(component);
7257 if (compIdx == ComponentIndexBad)
7260 const auto size = pItem->comp.size();
7262 return {
nullptr, 0, ComponentRawViewFlag_Valid};
7264 const auto row = uint32_t(ec.row);
7265 return {ec.pChunk->comp_ptr(compIdx, row), size, ComponentRawViewFlag_Valid};
7274 if (component == EntityBad)
7277 const auto* pEc = try_live_record(entity);
7281 const auto& ec = *pEc;
7283 const auto* pItem = component_item(entity, component);
7284 if (pItem ==
nullptr || !raw_component_supported(*pItem))
7286 if (component_uses_sparse_storage(component)) {
7287 const auto* pStore = sparse_component_store_erased(component);
7288 if (pStore ==
nullptr || !pStore->func_has(pStore->pStore, entity))
7290 return {pStore->func_mut(pStore->pStore, entity), pItem->comp.size(), ComponentRawViewFlag_Valid};
7293 const auto compIdx = core::get_index(ec.pChunk->ids_view(), component);
7294 if (compIdx == BadIndex)
7297 const auto size = pItem->comp.size();
7299 return {
nullptr, 0, ComponentRawViewFlag_Valid};
7301 const auto row = uint32_t(ec.row);
7302 return {ec.pChunk->comp_ptr_mut(compIdx, row), size, ComponentRawViewFlag_Valid};
7312 if (component == EntityBad)
7315 const auto owner = id_owner_inter(entity, component);
7316 if (owner == EntityBad)
7319 const auto* pItem = component_item(owner, component);
7320 if (pItem ==
nullptr || !soa_field_supported(*pItem) || fieldIdx >= pItem->comp.soa() ||
7321 pItem->soaSizes[fieldIdx] == 0)
7324 const auto& ec = fetch(owner);
7325 const auto compIdx = ec.pChunk->comp_idx(component);
7326 if (compIdx == ComponentIndexBad)
7329 const auto row = uint32_t(ec.row);
7330 const auto capacity = ec.pChunk->capacity();
7331 const std::span<const uint8_t> fieldSizes{pItem->soaSizes, pItem->comp.soa()};
7333 ec.pChunk->comp_ptr(compIdx), pItem->comp.alig(), fieldSizes, fieldIdx, row, capacity);
7334 return {pData, pItem->soaSizes[fieldIdx], ComponentRawViewFlag_Valid};
7344 if (component == EntityBad)
7347 const auto* pEc = try_live_record(entity);
7351 const auto& ec = *pEc;
7353 const auto* pItem = component_item(entity, component);
7354 if (pItem ==
nullptr || !soa_field_supported(*pItem) || fieldIdx >= pItem->comp.soa() ||
7355 pItem->soaSizes[fieldIdx] == 0)
7358 const auto compIdx = core::get_index(ec.pChunk->ids_view(), component);
7359 if (compIdx == BadIndex)
7362 const auto row = uint32_t(ec.row);
7363 const auto capacity = ec.pChunk->capacity();
7364 const std::span<const uint8_t> fieldSizes{pItem->soaSizes, pItem->comp.soa()};
7366 ec.pChunk->comp_ptr_mut(compIdx), pItem->comp.alig(), fieldSizes, fieldIdx, row, capacity);
7367 return {pData, pItem->soaSizes[fieldIdx], ComponentRawViewFlag_Valid};
7392 if (component == EntityBad || try_live_record(entity) ==
nullptr)
7395 const auto* pItem = component.
pair() ? comp_cache().find_pair_payload(component) : comp_cache().find(component);
7396 if (pItem ==
nullptr || !raw_component_payload_args_valid(*pItem, data, size))
7399 if (has_direct(entity, component))
7401 if (component_uses_sparse_storage(component)) {
7402 const auto mode = sparse_storage_mode(component);
7403 if (mode == SparseStorageMode::None)
7406 auto& store = sparse_component_store_erased_mut(component, *pItem);
7407 auto* pPayload = store.func_add(store.pStore, entity);
7409 memcpy(pPayload, data, size);
7410 finish_sparse_component_add_inter(entity, component, mode);
7415#if GAIA_OBSERVERS_ENABLED
7417 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&component, 1}, EntitySpan{&entity, 1});
7419 eb.add_inter_init(component);
7422 const auto payload = mut_raw(entity, component);
7423 GAIA_ASSERT(payload.valid());
7424 if (payload.valid() && size != 0)
7425 memcpy(payload.data, data, size);
7427 notify_add_single(entity, component);
7428#if GAIA_OBSERVERS_ENABLED
7429 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
7431 return payload.valid();
7441 if (component == EntityBad)
7444 const auto payload = mut_raw(entity, component);
7445 if (!payload.valid() || payload.size != size || (size != 0 && data ==
nullptr))
7448 memcpy(payload.data, data, size);
7450 finish_write(entity, component);
7458 if (!mut_raw(entity, component).valid()) {
7459 if (component == EntityBad)
7462 const auto* pEc = try_live_record(entity);
7466 const auto& ec = *pEc;
7467 const auto* pItem = component_item(entity, component);
7468 if (pItem ==
nullptr || !soa_field_supported(*pItem) ||
7469 core::get_index(ec.pChunk->ids_view(), component) == BadIndex)
7472 finish_write(entity, component);
7483 GAIA_ASSERT(valid(entity));
7485 const auto& ec = cont(entity);
7495 const auto source = (
Entity)entity;
7496 GAIA_ASSERT(valid(source));
7498 const auto& ec = fetch(source);
7509 template <
typename T>
7511 using FT =
typename component_type_t<T>::TypeFull;
7512 const auto compEntity = [&]() {
7514 const auto rel = comp_cache().template get<typename FT::rel>().entity;
7515 const auto tgt = comp_cache().template get<typename FT::tgt>().entity;
7518 return comp_cache().template get<FT>().entity;
7521 if constexpr (uses_ct_sparse_storage<FT>()) {
7522 const auto owner = id_owner_inter(entity, compEntity);
7523 GAIA_ASSERT(owner != EntityBad);
7524 const auto* pStore = sparse_component_store<FT>(compEntity);
7525 GAIA_ASSERT(pStore !=
nullptr);
7527 return pStore->get(owner);
7528 return pStore->get_entity(owner);
7530 const auto owner = id_owner_inter(entity, compEntity);
7531 GAIA_ASSERT(owner != EntityBad);
7532 const auto& ec = cont(owner);
7533 return ComponentGetter{*
this, ec.pChunk, owner, ec.row}.template get<T>();
7543 template <
typename T>
7544 GAIA_NODISCARD
decltype(
auto)
get(
Pair entity)
const {
7545 using FT =
typename component_type_t<T>::TypeFull;
7546 const auto compEntity = [&]() {
7548 const auto rel = comp_cache().template get<typename FT::rel>().entity;
7549 const auto tgt = comp_cache().template get<typename FT::tgt>().entity;
7552 return comp_cache().template get<FT>().entity;
7555 const auto source = (
Entity)entity;
7556 if constexpr (uses_ct_sparse_storage<FT>()) {
7557 const auto owner = id_owner_inter(source, compEntity);
7558 GAIA_ASSERT(owner != EntityBad);
7559 const auto* pStore = sparse_component_store<FT>(compEntity);
7560 GAIA_ASSERT(pStore !=
nullptr);
7561 return pStore->get(owner);
7563 const auto owner = id_owner_inter(source, compEntity);
7564 GAIA_ASSERT(owner != EntityBad);
7565 const auto& ec = fetch(owner);
7566 return ComponentGetter{*
this, ec.pChunk, owner, ec.row}.template get<T>();
7575 template <
typename T>
7577 using FT =
typename component_type_t<T>::TypeFull;
7578 if constexpr (supports_sparse_component_storage<FT>()) {
7579 if (can_use_sparse_component_storage<FT>(
object)) {
7580 const auto owner = id_owner_inter(entity,
object);
7581 GAIA_ASSERT(owner != EntityBad);
7583 return sparse_component_get_value<FT>(
object, owner);
7584 return sparse_component_get_entity_value<FT>(
object, owner);
7588 const auto owner = id_owner_inter(entity,
object);
7589 GAIA_ASSERT(owner != EntityBad);
7590 const auto& ec = cont(owner);
7591 return ComponentGetter{*
this, ec.pChunk, owner, ec.row}.template get<T>(
object);
7599 template <
typename T>
7601 using FT =
typename component_type_t<T>::TypeFull;
7602 const auto source = (
Entity)entity;
7603 if constexpr (supports_sparse_component_storage<FT>()) {
7604 if (can_use_sparse_component_storage<FT>(
object)) {
7605 const auto owner = id_owner_inter(source,
object);
7606 GAIA_ASSERT(owner != EntityBad);
7607 return sparse_component_get_value<FT>(
object, owner);
7611 const auto owner = id_owner_inter(source,
object);
7612 GAIA_ASSERT(owner != EntityBad);
7613 const auto& ec = fetch(owner);
7614 return ComponentGetter{*
this, ec.pChunk, owner, ec.row}.template get<T>(
object);
7624 if (entity.
pair()) {
7625 if (entity ==
Pair(All, All))
7628 if (is_wildcard(entity)) {
7633 GAIA_ASSERT(has(get(entity.
id())) && has(get(entity.
gen())));
7638 const auto* pPair = m_recs.pair_record_find(entity);
7639 if (pPair ==
nullptr)
7642 const auto& ec = *pPair;
7646#if GAIA_ASSERT_ENABLED
7648 GAIA_ASSERT(has(get(entity.
id())) && has(get(entity.
gen())));
7651 auto* pChunk = ec.pChunk;
7652 GAIA_ASSERT(pChunk !=
nullptr && ec.row < pChunk->size());
7661 if (entity.
id() >= m_recs.entities.size() || !m_recs.entities.has(entity.
id()))
7665 const auto& ec = m_recs.entities[entity.
id()];
7669 auto* pChunk = ec.pChunk;
7670 return pChunk !=
nullptr && ec.row < pChunk->size();
7688 return has_inter(entity,
object,
true);
7696 return has_inter(entity,
object,
false);
7704 return has_inter(entity, (
Entity)
pair,
false);
7713 GAIA_NODISCARD
bool has_inter(
Entity entity,
Entity object,
bool allowSemanticIs)
const {
7714 const auto* pEc = try_live_record(entity);
7718 const auto& ec = *pEc;
7720 if (
object.
pair() && has_nonfragmenting_relation_pair(entity,
object))
7722 if (!
object.
pair()) {
7723 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
7724 if (itSparseStore != m_sparseComponentsByComp.end())
7725 return has_direct_sparse_component_inter(entity,
object) ||
7726 (allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad);
7729 const auto* pArchetype = ec.pArchetype;
7731 if (
object.pair()) {
7732 if (allowSemanticIs &&
object.
id() == Is.
id() && !is_wildcard(
object.gen())) {
7733 const auto target = get(
object.gen());
7734 return valid(target) && is(entity, target);
7738 if (pArchetype->pairs() == 0)
7741 EntityId rel =
object.id();
7742 EntityId tgt =
object.gen();
7745 if (rel == All.
id() && tgt == All.
id())
7749 if (rel != All.
id() && tgt == All.
id()) {
7750 auto ids = pArchetype->ids_view();
7751 for (
auto id: ids) {
7762 if (rel == All.
id() && tgt != All.
id()) {
7763 auto ids = pArchetype->ids_view();
7764 for (
auto id: ids) {
7767 if (
id.gen() == tgt)
7775 if (pArchetype->has(
object))
7778 return allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad;
7791 template <
typename Func>
7792 bool each_id_inter(Entity entity,
const EntityIdOptions& options, Func&& func)
const {
7793 auto accepts_kind = [&](Entity id) {
7794 return id.pair() ? entity_id_kind_has(options.kind, EntityIdKind::Pair)
7795 : entity_id_kind_has(options.kind, EntityIdKind::Component);
7798 auto visit_direct = [&](Entity source,
auto&& onId) {
7799 const auto* pEc = try_live_record(source);
7800 if (pEc ==
nullptr || pEc->pArchetype ==
nullptr)
7803 if (entity_id_storage_has(options.storage, EntityIdStorage::Archetype)) {
7804 for (
const auto id: pEc->pArchetype->ids_view()) {
7805 if (!accepts_kind(
id))
7812 const bool wantDontFragment = entity_id_storage_has(options.storage, EntityIdStorage::DontFragment) &&
7813 entity_id_kind_has(options.kind, EntityIdKind::Component);
7814 const bool wantRelation = entity_id_storage_has(options.storage, EntityIdStorage::Relation) &&
7815 entity_id_kind_has(options.kind, EntityIdKind::Pair);
7816 if (!wantDontFragment && !wantRelation)
7819 cnt::darray_ext<Entity, 16> extra;
7820 if (wantDontFragment) {
7821 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
7822 const auto component = compKey.entity();
7823 if (component.pair() || !component_is_non_fragmenting(component))
7825 if (store.func_has(store.pStore, source))
7826 extra.push_back(component);
7830 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
7831 const auto target = store.target(source);
7832 if (target != EntityBad)
7833 extra.push_back(Pair(relKey.entity(), target));
7836 core::sort(extra.begin(), extra.end(), core::is_smaller<Entity>{});
7837 for (
const auto id: extra) {
7845 if (!visit_direct(entity, [&](Entity
id) {
7850 if (options.matchKind == QueryMatchKind::Direct)
7853 cnt::darray_ext<Entity, 16> inheritedIds;
7854 for (
const auto base: as_targets_trav_cache(entity)) {
7855 (void)visit_direct(base, [&](Entity
id) {
7856 if (!id_uses_inherit_policy(
id) || has_direct(entity,
id))
7858 if (core::get_index(inheritedIds,
id) != BadIndex)
7860 inheritedIds.push_back(
id);
7864 core::sort(inheritedIds.begin(), inheritedIds.end(), core::is_smaller<Entity>{});
7865 for (
const auto id: inheritedIds) {
7878 return {m_componentLookupPath.data(), m_componentLookupPath.size()};
7885 m_componentLookupPath.clear();
7886 m_componentLookupPath.reserve((uint32_t)scopes.size());
7887 for (
const auto scopeEntity: scopes) {
7888 GAIA_ASSERT(scopeEntity != EntityBad && valid(scopeEntity) && !scopeEntity.pair());
7889 if (scopeEntity == EntityBad || !valid(scopeEntity) || scopeEntity.pair())
7892 m_componentLookupPath.push_back(scopeEntity);
7899 return m_componentScope;
7907 GAIA_ASSERT(scope == EntityBad || (valid(scope) && !scope.
pair()));
7908 const auto prev = m_componentScope;
7909 if (scope == EntityBad || (valid(scope) && !scope.
pair())) {
7910 m_componentScope = scope;
7911 invalidate_scope_path_cache();
7921 template <
typename Func>
7923 struct ComponentScopeRestore final {
7926 ~ComponentScopeRestore() {
7927 world.scope(prevScope);
7931 ComponentScopeRestore restore{*
this, scope(scopeEntity)};
7942 if (path ==
nullptr || path[0] == 0)
7945 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(path, ComponentCacheItem::MaxNameLength) : len;
7946 if (l == 0 || l >= ComponentCacheItem::MaxNameLength)
7948 if (path[l - 1] ==
'.')
7951 Entity parent = EntityBad;
7952 uint32_t partBeg = 0;
7953 while (partBeg < l) {
7954 uint32_t partEnd = partBeg;
7955 while (partEnd < l && path[partEnd] !=
'.')
7957 if (partEnd == partBeg)
7960 const auto partLen = partEnd - partBeg;
7962 const auto it = m_nameToEntity.find(key);
7965 if (it != m_nameToEntity.end()) {
7967 if (parent != EntityBad && !is_child(curr, parent)) {
7968 GAIA_ASSERT2(
false,
"Module path collides with an existing entity name outside the requested scope");
7973 name(curr, path + partBeg, partLen);
7974 if (parent != EntityBad)
7975 child(curr, parent);
7979 partBeg = partEnd + 1;
8001 template <
typename T>
8003 GAIA_ASSERT(valid(entity));
8005 using FT =
typename component_type_t<T>::TypeFull;
8006 const auto compEntity = [&]() {
8008 const auto* pRel = comp_cache().template find<typename FT::rel>();
8009 const auto* pTgt = comp_cache().template find<typename FT::tgt>();
8010 if (pRel ==
nullptr || pTgt ==
nullptr)
8013 const auto rel = pRel->entity;
8014 const auto tgt = pTgt->entity;
8017 const auto* pItem = comp_cache().template find<FT>();
8018 return pItem !=
nullptr ? pItem->entity : EntityBad;
8021 if (compEntity == EntityBad)
8024 return id_owner_inter(entity, compEntity) != EntityBad;
8038 template <
typename Func>
8040 (void)each_id_inter(entity, options, [&](
Entity id) {
8052 template <
typename Func>
8054 (void)each_id_inter(entity, options, [&](
Entity id) {
8099 const auto& ec = m_recs.entities[entity.
id()];
8100 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
8101 if (compIdx == BadIndex)
8104 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
8105 GAIA_ASSERT(core::check_alignment(pDesc));
8106 return {pDesc->name, pDesc->name_len};
8114 auto entity = get(entityId);
8115 return name(entity);
8128 if (name ==
nullptr || name[0] == 0)
8131 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
8132 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
8134 if (memchr(name,
'.', l) !=
nullptr) {
8135 const auto entity = get_entity_inter(name, l);
8136 if (entity != EntityBad)
8140 return get_inter(name, l);
8150 if (name ==
nullptr || name[0] == 0)
8153 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
8154 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
8156 auto push_unique = [&](
Entity entity) {
8157 if (entity == EntityBad)
8159 for (
const auto existing: out) {
8160 if (existing == entity)
8166 push_unique(get_entity_inter(name, l));
8168 if (memchr(name,
'.', l) ==
nullptr && memchr(name,
':', l) ==
nullptr)
8169 add_comp_lookup_hits_inter(out, name, l);
8171 const bool isPath = memchr(name,
'.', l) !=
nullptr;
8172 const bool isSymbol = memchr(name,
':', l) !=
nullptr;
8173 add_comp_exact_hits_inter(out, name, l, isPath, isSymbol);
8175 push_unique(alias(name, l));
8183 GAIA_NODISCARD
Entity get(
const char* name, uint32_t len = 0)
const {
8184 return resolve(name, len);
8192 GAIA_NODISCARD
Entity find_named_entity_inter(
const char* name, uint32_t len = 0)
const {
8193 if (name ==
nullptr || name[0] == 0)
8196 const auto key = EntityNameLookupKey(name, len, 0);
8197 const auto it = m_nameToEntity.find(key);
8198 return it != m_nameToEntity.end() ? it->second : EntityBad;
8205 GAIA_NODISCARD
bool hierarchy_child_matches_parent(Entity child, Entity parent)
const {
8206 return is_child(child, parent) || is_parent(child, parent);
8213 GAIA_NODISCARD Entity get_entity_inter(
const char* name, uint32_t len = 0)
const {
8214 if (name ==
nullptr || name[0] == 0)
8218 while (name[len] !=
'\0')
8222 Entity parent = EntityBad;
8223 Entity child = EntityBad;
8224 uint32_t posDot = 0;
8225 std::span<const char> str(name, len);
8227 posDot = core::get_index(str,
'.');
8228 if (posDot == BadIndex)
8229 return find_named_entity_inter(str.data(), (uint32_t)str.size());
8234 parent = find_named_entity_inter(str.data(), posDot);
8235 if (parent == EntityBad)
8238 str = str.subspan(posDot + 1);
8239 while (!str.empty()) {
8240 posDot = core::get_index(str,
'.');
8242 if (posDot == BadIndex) {
8243 child = find_named_entity_inter(str.data(), (uint32_t)str.size());
8244 if (child == EntityBad || !hierarchy_child_matches_parent(child, parent))
8253 child = find_named_entity_inter(str.data(), posDot);
8254 if (child == EntityBad || !hierarchy_child_matches_parent(child, parent))
8258 str = str.subspan(posDot + 1);
8268 GAIA_NODISCARD Entity get_inter(
const char* name, uint32_t len = 0)
const {
8269 if (name ==
nullptr || name[0] == 0)
8272 auto key = EntityNameLookupKey(name, len, 0);
8273 const auto l = key.len();
8274 const bool isUnqualifiedCompName = is_unqualified_comp_name_inter(name, l);
8275 const auto namedEntity = find_named_entity_inter(name, l);
8277 if (has_comp_lookup_ctx_inter() && isUnqualifiedCompName) {
8278 if (
const auto* pScopedItem = resolve_component_name_inter(name, l); pScopedItem !=
nullptr)
8279 return pick_name_or_comp_inter(namedEntity, pScopedItem);
8282 if (namedEntity != EntityBad)
8285 if (
const auto* pItem = resolve_component_name_inter(name, l); pItem !=
nullptr)
8286 return pItem->entity;
8288 const auto aliasEntity = alias(name, l);
8289 if (aliasEntity != EntityBad)
8304 return m_pairLookup.relations(target);
8313 const auto* pEc = try_live_record(entity);
8314 if (pEc ==
nullptr || try_live_record(target) ==
nullptr)
8317 for (
const auto& it: m_nonFragmentingRelationsByRel) {
8318 if (it.second.target(entity) == target)
8319 return it.first.entity();
8322 const auto* pArchetype = pEc->pArchetype;
8325 if (pArchetype->pairs() == 0)
8328 const auto indices = pArchetype->pair_tgt_indices(target);
8329 if (indices.empty())
8332 const auto ids = pArchetype->ids_view();
8333 const auto e = ids[indices[0]];
8334 const auto& ecRel = m_recs.entities[e.id()];
8335 return *ecRel.pEntity;
8343 template <
typename Func>
8345 const auto* pEc = try_live_record(entity);
8346 if (pEc ==
nullptr || try_live_record(target) ==
nullptr)
8349 for (
const auto& it: m_nonFragmentingRelationsByRel) {
8350 if (it.second.target(entity) == target)
8351 func(it.first.entity());
8354 const auto* pArchetype = pEc->pArchetype;
8357 if (pArchetype->pairs() == 0)
8360 const auto ids = pArchetype->ids_view();
8361 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
8362 const auto e = ids[idsIdx];
8364 const auto& ecRel = m_recs.entities[e.id()];
8365 auto relation = *ecRel.pEntity;
8376 template <
typename Func>
8378 const auto* pEc = try_live_record(entity);
8379 if (pEc ==
nullptr || try_live_record(target) ==
nullptr)
8382 for (
const auto& it: m_nonFragmentingRelationsByRel) {
8383 if (it.second.target(entity) == target && !func(it.first.entity()))
8387 const auto* pArchetype = pEc->pArchetype;
8390 if (pArchetype->pairs() == 0)
8393 const auto ids = pArchetype->ids_view();
8394 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
8395 const auto e = ids[idsIdx];
8397 const auto& ecRel = m_recs.entities[e.id()];
8398 auto relation = *ecRel.pEntity;
8399 if (!func(relation))
8410 const auto itCache = m_entityToAsRelationsTravCache.find(key);
8411 if (itCache != m_entityToAsRelationsTravCache.end())
8412 return itCache->second;
8414 auto& cache = m_entityToAsRelationsTravCache[key];
8415 const auto it = m_entityToAsRelations.find(key);
8416 if (it == m_entityToAsRelations.end())
8421 stack.
reserve((uint32_t)it->second.size());
8422 for (
auto relation: it->second)
8425 while (!stack.
empty()) {
8426 const auto relationKey = stack.
back();
8429 const auto relation = relationKey.entity();
8432 const auto itChild = m_entityToAsRelations.find(relationKey);
8433 if (itChild == m_entityToAsRelations.end())
8436 for (
auto childRelation: itChild->second)
8449 const auto itCache = m_entityToAsTargetsTravCache.find(key);
8450 if (itCache != m_entityToAsTargetsTravCache.end())
8451 return itCache->second;
8453 auto& cache = m_entityToAsTargetsTravCache[key];
8454 const auto it = m_entityToAsTargets.find(key);
8455 if (it == m_entityToAsTargets.end())
8460 stack.
reserve((uint32_t)it->second.size());
8461 for (
auto target: it->second)
8464 while (!stack.
empty()) {
8465 const auto targetKey = stack.
back();
8468 const auto target = targetKey.
entity();
8469 cache.push_back(target);
8471 const auto itChild = m_entityToAsTargets.find(targetKey);
8472 if (itChild == m_entityToAsTargets.end())
8475 for (
auto childTarget: itChild->second)
8489 const auto itCache = m_targetsTravCache.find(key);
8490 if (itCache != m_targetsTravCache.end())
8491 return itCache->second;
8493 auto& cache = m_targetsTravCache[key];
8494 m_relationCachesPopulated =
true;
8495 if (try_live_record(relation) ==
nullptr || try_live_record(source) ==
nullptr)
8499 GAIA_FOR(MAX_TRAV_DEPTH) {
8500 const auto next = target(curr, relation);
8501 if (next == EntityBad || next == curr)
8504 cache.push_back(next);
8517 const auto itCache = m_targetsAllCache.find(key);
8518 if (itCache != m_targetsAllCache.end())
8519 return itCache->second;
8521 auto& cache = m_targetsAllCache[key];
8522 m_relationCachesPopulated =
true;
8523 const auto* pEc = try_live_record(source);
8527 const auto visitStamp = next_entity_visit_stamp();
8529 for (
const auto& it: m_nonFragmentingRelationsByRel) {
8530 const auto target = it.second.target(source);
8531 if (target != EntityBad && try_mark_entity_visited(target, visitStamp))
8532 cache.push_back(target);
8535 const auto* pArchetype = pEc->pArchetype;
8536 if (pArchetype->pairs() == 0)
8539 const auto ids = pArchetype->ids_view();
8540 for (
auto idsIdx: pArchetype->pair_indices()) {
8541 const auto id = ids[idsIdx];
8542 const auto target = pair_target_if_alive(
id);
8543 if (target == EntityBad)
8545 if (try_mark_entity_visited(target, visitStamp))
8546 cache.push_back(target);
8558 const auto itCache = m_sourcesAllCache.find(key);
8559 if (itCache != m_sourcesAllCache.end())
8560 return itCache->second;
8562 auto& cache = m_sourcesAllCache[key];
8563 m_relationCachesPopulated =
true;
8564 if (try_live_record(target) ==
nullptr)
8567 const auto visitStamp = next_entity_visit_stamp();
8569 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
8571 const auto* pSources = store.sources(target);
8572 if (pSources ==
nullptr)
8575 for (
auto source: *pSources) {
8576 if (valid(source) && try_mark_entity_visited(source, visitStamp))
8577 cache.push_back(source);
8581 const auto pair =
Pair(All, target);
8583 if (it == m_entityToArchetypeMap.end())
8586 for (
const auto& record: it->second) {
8587 const auto* pArchetype = record.pArchetype;
8588 if (pArchetype->is_req_del())
8591 for (
const auto* pChunk: pArchetype->chunks()) {
8592 const auto entities = pChunk->entity_view();
8593 GAIA_EACH(entities) {
8594 const auto source = entities[i];
8597 if (try_mark_entity_visited(source, visitStamp))
8598 cache.push_back(source);
8612 template <
typename Func>
8614 if (try_live_record(relation) ==
nullptr || try_live_record(source) ==
nullptr)
8618 GAIA_FOR(MAX_TRAV_DEPTH) {
8619 const auto next = target(curr, relation);
8620 if (next == EntityBad || next == curr)
8637 template <
typename Func>
8639 if (try_live_record(relation) ==
nullptr || try_live_record(source) ==
nullptr)
8643 GAIA_FOR(MAX_TRAV_DEPTH) {
8644 const auto next = target(curr, relation);
8645 if (next == EntityBad || next == curr)
8665 const auto itCache = m_srcBfsTravCache.find(key);
8666 if (itCache != m_srcBfsTravCache.end())
8667 return itCache->second;
8669 auto& cache = m_srcBfsTravCache[key];
8670 m_relationCachesPopulated =
true;
8671 if (!valid(relation) || !valid(rootTarget))
8681 for (uint32_t i = 0; i < queue.
size(); ++i) {
8682 const auto currTarget = queue[i];
8685 sources(relation, currTarget, [&](
Entity source) {
8687 const auto& ins = visited.
insert(keySource);
8696 for (
auto child: children) {
8697 cache.push_back(child);
8713 const auto itCache = m_depthOrderCache.find(key);
8714 if (itCache != m_depthOrderCache.end()) {
8715 GAIA_ASSERT(itCache->second != GroupIdMax &&
"depth_order requires an acyclic relation graph");
8716 return itCache->second;
8719 if (!valid(relation) || !valid(sourceTarget))
8723 m_relationCachesPopulated =
true;
8724 m_depthOrderCache[key] = GroupIdMax;
8727 targets(sourceTarget, relation, [&](
Entity next) {
8728 const auto nextDepth = depth_order_cache(relation, next);
8731 const auto candidate = nextDepth + 1;
8732 if (candidate > depth)
8736 m_depthOrderCache[key] = depth;
8744 template <
typename Func>
8749 const auto& relations = as_relations_trav_cache(target);
8750 for (
auto relation: relations)
8759 template <
typename Func>
8764 const auto& relations = as_relations_trav_cache(target);
8765 for (
auto relation: relations) {
8780 return m_pairLookup.targets(relation);
8789 const auto* pEc = try_live_record(entity);
8793 if (relation == All) {
8794 const auto& targets = targets_all_cache(entity);
8795 return targets.empty() ? EntityBad : targets[0];
8798 const auto* pRel = try_entity_record(relation);
8799 if (pRel ==
nullptr)
8802 if (record_uses_non_fragmenting_relation_storage(*pRel)) {
8803 const auto* pStore = nonfragmenting_relation_store(relation);
8804 if (pStore ==
nullptr)
8807 return pStore->target(entity);
8810 const auto* pArchetype = pEc->pArchetype;
8813 if (pArchetype->pairs() == 0)
8816 const auto ids = pArchetype->ids_view();
8817 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
8818 const auto e = ids[idsIdx];
8819 const auto target = pair_target_if_alive(e);
8820 if (target == EntityBad)
8833 template <
typename Func>
8835 const auto* pEc = try_live_record(entity);
8839 if (relation == All) {
8840 for (
auto target: targets_all_cache(entity))
8845 const auto* pRel = try_entity_record(relation);
8846 if (pRel ==
nullptr)
8849 if (record_uses_non_fragmenting_relation_storage(*pRel)) {
8850 const auto* pStore = nonfragmenting_relation_store(relation);
8851 if (pStore ==
nullptr)
8854 const auto target = pStore->target(entity);
8855 if (target != EntityBad)
8860 const auto* pArchetype = pEc->pArchetype;
8863 if (pArchetype->pairs() == 0)
8866 const auto ids = pArchetype->ids_view();
8867 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
8868 const auto e = ids[idsIdx];
8869 const auto target = pair_target_if_alive(e);
8870 if (target == EntityBad)
8882 template <
typename Func>
8884 const auto* pEc = try_live_record(entity);
8888 if (relation == All) {
8889 for (
auto target: targets_all_cache(entity)) {
8896 const auto* pRel = try_entity_record(relation);
8897 if (pRel ==
nullptr)
8900 if (record_uses_non_fragmenting_relation_storage(*pRel)) {
8901 const auto* pStore = nonfragmenting_relation_store(relation);
8902 if (pStore ==
nullptr)
8905 const auto target = pStore->target(entity);
8906 if (target != EntityBad)
8911 const auto* pArchetype = pEc->pArchetype;
8914 if (pArchetype->pairs() == 0)
8917 const auto ids = pArchetype->ids_view();
8918 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
8919 const auto e = ids[idsIdx];
8920 const auto target = pair_target_if_alive(e);
8921 if (target == EntityBad)
8932 GAIA_ASSERT(
pair.pair());
8933 if (!valid_entity_id((EntityId)
pair.gen()))
8936 const auto& ecTarget = m_recs.entities[
pair.gen()];
8937 if (ecTarget.pEntity ==
nullptr)
8940 const auto target = *ecTarget.pEntity;
8941 return valid(target) ? target : EntityBad;
8949 template <
typename Func>
8951 if (try_live_record(target) ==
nullptr)
8954 if (relation == All) {
8955 for (
auto source: sources_all_cache(target))
8960 const auto* pRel = try_entity_record(relation);
8961 if (pRel ==
nullptr)
8964 if (record_uses_non_fragmenting_relation_storage(*pRel)) {
8965 const auto* pStore = nonfragmenting_relation_store(relation);
8966 if (pStore ==
nullptr)
8969 const auto* pSources = pStore->sources(target);
8970 if (pSources ==
nullptr)
8973 for (
auto source: *pSources) {
8982 const auto pair =
Pair(relation, target);
8984 if (it == m_entityToArchetypeMap.end())
8987 for (
const auto& record: it->second) {
8988 const auto* pArchetype = record.pArchetype;
8989 if (pArchetype->is_req_del())
8992 for (
const auto* pChunk: pArchetype->chunks()) {
8993 auto entities = pChunk->entity_view();
8994 GAIA_EACH(entities) {
8995 const auto source = entities[i];
9010 template <
typename Func>
9012 if (try_live_record(target) ==
nullptr)
9015 if (relation == All) {
9016 for (
auto source: sources_all_cache(target)) {
9023 const auto* pRel = try_entity_record(relation);
9024 if (pRel ==
nullptr)
9027 if (record_uses_non_fragmenting_relation_storage(*pRel)) {
9028 const auto* pStore = nonfragmenting_relation_store(relation);
9029 if (pStore ==
nullptr)
9032 const auto* pSources = pStore->sources(target);
9033 if (pSources ==
nullptr)
9036 for (
auto source: *pSources) {
9046 const auto pair =
Pair(relation, target);
9048 if (it == m_entityToArchetypeMap.end())
9051 for (
const auto& record: it->second) {
9052 const auto* pArchetype = record.pArchetype;
9053 if (pArchetype->is_req_del())
9056 for (
const auto* pChunk: pArchetype->chunks()) {
9057 auto entities = pChunk->entity_view();
9058 GAIA_EACH(entities) {
9059 const auto source = entities[i];
9072 GAIA_NODISCARD uint64_t next_entity_visit_stamp()
const {
9073 ++m_entityVisitStamp;
9074 if (m_entityVisitStamp != 0)
9075 return m_entityVisitStamp;
9077 const auto cnt = (uint32_t)m_entityVisitStamps.size();
9079 m_entityVisitStamps[i] = 0;
9082 m_entityVisitStamp = 1;
9083 return m_entityVisitStamp;
9090 GAIA_NODISCARD
bool try_mark_entity_visited(Entity entity, uint64_t stamp)
const {
9091 GAIA_ASSERT(!entity.pair());
9092 if (entity.id() >= m_entityVisitStamps.size())
9093 m_entityVisitStamps.resize(m_recs.entities.size(), 0, cnt::ResizePolicy::Scale);
9095 auto& slot = m_entityVisitStamps[entity.id()];
9108 template <
typename Func>
9109 GAIA_NODISCARD
bool each_inherited_term_entity(Entity term, Func&& func)
const {
9110 cnt::set<EntityLookupKey> seen;
9111 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
9112 if (it == m_entityToArchetypeMap.end())
9115 for (
const auto& record: it->second) {
9116 const auto* pArchetype = record.pArchetype;
9117 if (pArchetype->is_req_del())
9120 for (
const auto* pChunk: pArchetype->chunks()) {
9121 const auto entities = pChunk->entity_view();
9122 GAIA_EACH(entities) {
9123 const auto entity = entities[i];
9124 GAIA_ASSERT(valid(entity));
9125 const auto entityKey = EntityLookupKey(entity);
9126 if (seen.contains(entityKey))
9128 seen.insert(entityKey);
9133 const auto& descendants = as_relations_trav_cache(entity);
9134 for (
const auto descendant: descendants) {
9135 GAIA_ASSERT(valid(descendant));
9136 const auto descendantKey = EntityLookupKey(descendant);
9137 if (seen.contains(descendantKey))
9139 seen.insert(descendantKey);
9141 if (!func(descendant))
9156 GAIA_NODISCARD uint32_t count_direct_term_entities_inter(Entity term,
bool allowSemanticIs)
const {
9157 if (term == EntityBad)
9160 if (allowSemanticIs && term.pair() && term.id() == Is.
id() && !is_wildcard(term.gen())) {
9161 const auto target = get(term.gen());
9165 return (uint32_t)as_relations_trav_cache(target).size() + 1;
9168 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
9170 (void)each_inherited_term_entity(term, [&](Entity) {
9177 if (term.pair() && relation_uses_non_fragmenting_storage(pair_rel(*
this, term))) {
9178 const auto relation = pair_rel(*
this, term);
9179 const auto* pStore = nonfragmenting_relation_store(relation);
9180 if (pStore ==
nullptr)
9183 if (is_wildcard(term.gen()))
9184 return pStore->source_count();
9186 const auto* pSources = pStore->sources(pair_tgt(*
this, term));
9187 return pSources !=
nullptr ? (uint32_t)pSources->size() : 0;
9190 if (!term.pair() && component_is_non_fragmenting(term)) {
9191 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
9192 return it != m_sparseComponentsByComp.end() ? it->second.func_count(it->second.pStore) : 0;
9195 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
9196 if (it == m_entityToArchetypeMap.end())
9200 for (
const auto& record: it->second) {
9201 const auto* pArchetype = record.pArchetype;
9202 if (pArchetype->is_req_del())
9204 for (
const auto* pChunk: pArchetype->chunks())
9205 cnt += pChunk->size();
9215 void collect_direct_term_entities_inter(Entity term, cnt::darray<Entity>& out,
bool allowSemanticIs)
const {
9216 if (term == EntityBad)
9219 if (allowSemanticIs && term.pair() && term.id() == Is.
id() && !is_wildcard(term.gen())) {
9220 const auto target = get(term.gen());
9224 out.push_back(target);
9225 const auto& relations = as_relations_trav_cache(target);
9226 out.reserve(out.size() + (uint32_t)relations.size());
9227 for (
auto relation: relations)
9228 out.push_back(relation);
9232 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
9233 (void)each_inherited_term_entity(term, [&](Entity entity) {
9234 out.push_back(entity);
9240 if (term.pair() && relation_uses_non_fragmenting_storage(pair_rel(*
this, term))) {
9241 const auto relation = pair_rel(*
this, term);
9242 const auto* pStore = nonfragmenting_relation_store(relation);
9243 if (pStore ==
nullptr)
9246 if (is_wildcard(term.gen())) {
9247 cnt::darray<EntityId> sourceIds;
9248 pStore->collect_source_ids(sourceIds);
9249 out.reserve(out.size() + pStore->source_count());
9250 for (
auto sourceId: sourceIds) {
9251 if (!m_recs.entities.has(sourceId))
9253 out.push_back(EntityContainer::handle(m_recs.entities[sourceId]));
9255 pStore->collect_pair_sources(out);
9259 const auto* pSources = pStore->sources(pair_tgt(*
this, term));
9260 if (pSources ==
nullptr)
9263 out.reserve(out.size() + (uint32_t)pSources->size());
9264 for (
auto source: *pSources)
9265 out.push_back(source);
9269 if (!term.pair() && component_is_non_fragmenting(term)) {
9270 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
9271 if (it != m_sparseComponentsByComp.end())
9272 it->second.func_collect_entities(it->second.pStore, out);
9276 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
9277 if (it == m_entityToArchetypeMap.end())
9280 for (
const auto& record: it->second) {
9281 const auto* pArchetype = record.pArchetype;
9282 if (pArchetype->is_req_del())
9285 for (
const auto* pChunk: pArchetype->chunks()) {
9286 const auto entities = pChunk->entity_view();
9288 out.push_back(entities[i]);
9300 each_direct_term_entity_inter(Entity term,
void* ctx,
bool (*func)(
void*, Entity),
bool allowSemanticIs)
const {
9301 if (term == EntityBad)
9304 if (allowSemanticIs && term.pair() && term.id() == Is.
id() && !is_wildcard(term.gen())) {
9305 const auto target = get(term.gen());
9309 if (!func(ctx, target))
9312 const auto& relations = as_relations_trav_cache(target);
9313 for (
auto relation: relations) {
9314 if (!func(ctx, relation))
9320 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
9321 return each_inherited_term_entity(term, [&](Entity entity) {
9322 return func(ctx, entity);
9326 if (term.pair() && relation_uses_non_fragmenting_storage(pair_rel(*
this, term))) {
9327 const auto relation = pair_rel(*
this, term);
9328 const auto* pStore = nonfragmenting_relation_store(relation);
9329 if (pStore ==
nullptr)
9332 if (is_wildcard(term.gen())) {
9333 cnt::darray<EntityId> sourceIds;
9334 cnt::darray<Entity> pairSources;
9335 pStore->collect_source_ids(sourceIds);
9336 pStore->collect_pair_sources(pairSources);
9337 for (
auto sourceId: sourceIds) {
9338 if (!m_recs.entities.has(sourceId))
9340 if (!func(ctx, EntityContainer::handle(m_recs.entities[sourceId])))
9343 for (
auto source: pairSources) {
9344 if (!func(ctx, source))
9350 const auto* pSources = pStore->sources(pair_tgt(*
this, term));
9351 if (pSources ==
nullptr)
9354 for (
auto source: *pSources) {
9355 if (!func(ctx, source))
9361 if (!term.pair() && component_is_non_fragmenting(term)) {
9362 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
9363 if (it == m_sparseComponentsByComp.end())
9365 return it->second.func_each_entity(it->second.pStore, ctx, func);
9368 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
9369 if (it == m_entityToArchetypeMap.end())
9372 for (
const auto& record: it->second) {
9373 const auto* pArchetype = record.pArchetype;
9374 if (pArchetype->is_req_del())
9377 for (
const auto* pChunk: pArchetype->chunks()) {
9378 const auto entities = pChunk->entity_view();
9379 GAIA_EACH(entities) {
9380 if (!func(ctx, entities[i]))
9394 return count_direct_term_entities_inter(term,
true);
9401 return count_direct_term_entities_inter(term,
false);
9408 collect_direct_term_entities_inter(term, out,
true);
9415 collect_direct_term_entities_inter(term, out,
false);
9424 return each_direct_term_entity_inter(term, ctx, func,
true);
9433 return each_direct_term_entity_inter(term, ctx, func,
false);
9441 template <
typename Func>
9443 if (!valid(relation) || !valid(rootTarget))
9446 if (m_enabledHierarchyVersion == 0) {
9447 const auto& cachedSources = sources_bfs_trav_cache(relation, rootTarget);
9448 for (
auto source: cachedSources)
9460 for (uint32_t i = 0; i < queue.
size(); ++i) {
9461 const auto currTarget = queue[i];
9464 sources(relation, currTarget, [&](
Entity source) {
9466 const auto ins = visited.
insert(key);
9475 for (
auto child: children) {
9476 if (!enabled(child))
9491 template <
typename Func>
9493 if (!valid(relation) || !valid(rootTarget))
9496 if (m_enabledHierarchyVersion == 0) {
9497 const auto& cachedSources = sources_bfs_trav_cache(relation, rootTarget);
9498 for (
auto source: cachedSources) {
9513 for (uint32_t i = 0; i < queue.
size(); ++i) {
9514 const auto currTarget = queue[i];
9517 sources(relation, currTarget, [&](
Entity source) {
9519 const auto ins = visited.
insert(key);
9528 for (
auto child: children) {
9529 if (!enabled(child))
9545 template <
typename Func>
9547 GAIA_ASSERT(valid(relation));
9548 if (!valid(relation))
9551 const auto& targets = as_targets_trav_cache(relation);
9552 for (
auto target: targets)
9561 template <
typename Func>
9563 GAIA_ASSERT(valid(relation));
9564 if (!valid(relation))
9567 const auto& targets = as_targets_trav_cache(relation);
9568 for (
auto target: targets)
9580 return *m_pCmdBufferST;
9586 return *m_pCmdBufferMT;
9591#if GAIA_SYSTEMS_ENABLED
9594 void systems_init();
9628 void systems_run(
Entity phase);
9632 SystemBuilder system();
9635 SystemRegistry& systems() {
9640 const SystemRegistry& systems()
const {
9646#if GAIA_OBSERVERS_ENABLED
9650 ObserverBuilder observer();
9653 ObserverRegistry& observers() {
9658 const ObserverRegistry& observers()
const {
9665 void defer_on_set_begin(uint32_t slotCount) {
9666 GAIA_ASSERT(m_deferOnSetDepth != (uint32_t)-1);
9667 if (m_deferOnSetDepth++ != 0)
9673 m_deferredOnSet.resize(slotCount);
9674 for (
auto& queue: m_deferredOnSet)
9681 void defer_on_set_end() {
9682 GAIA_ASSERT(m_deferOnSetDepth > 0);
9683 if (--m_deferOnSetDepth != 0)
9689 auto pending = GAIA_MOV(m_deferredOnSet);
9690 m_deferredOnSet = {};
9691 for (
const auto& queue: pending) {
9692 for (
const auto& item: queue)
9693 world_notify_on_set_entity(*this, item.term, item.entity);
9698 GAIA_NODISCARD
bool defer_on_set_active()
const {
9699 return m_deferOnSetDepth != 0;
9706 void defer_on_set_record(uint32_t slot, Entity term, Entity entity) {
9707 GAIA_ASSERT(slot < m_deferredOnSet.size());
9708 m_deferredOnSet[slot].push_back({term, entity});
9717 GAIA_ASSERT(m_deferSortInvDepth != (uint32_t)-1);
9718 if (m_deferSortInvDepth++ != 0)
9724 m_deferredSortInv.resize(slotCount);
9725 for (
auto& queue: m_deferredSortInv)
9733 GAIA_ASSERT(m_deferSortInvDepth > 0);
9734 if (--m_deferSortInvDepth != 0)
9739 auto pending = GAIA_MOV(m_deferredSortInv);
9740 m_deferredSortInv = {};
9741 for (
const auto& queue: pending) {
9742 for (
const auto& item: queue)
9743 invalidate_sorted_queries_for_entity(item.entity);
9750 return m_deferSortInvDepth != 0;
9757 GAIA_ASSERT(slot < m_deferredSortInv.size());
9758 m_deferredSortInv[slot].push_back({entity});
9768 GAIA_ASSERT(valid(entity));
9770 auto& ec = cont(entity);
9771 auto& archetype = *ec.pArchetype;
9772 auto* pChunk = ec.pChunk;
9773 const bool wasEnabled = !ec.data.dis;
9774#if GAIA_ASSERT_ENABLED
9775 verify_enable(*
this, archetype, entity);
9777 archetype.enable_entity(ec.pChunk, ec.row, enable, m_recs);
9779 if (wasEnabled != enable) {
9780 pChunk->update_world_version();
9781 pChunk->update_entity_order_version();
9782 update_version(m_enabledHierarchyVersion);
9783 update_version(m_worldVersion);
9792 const auto source = (
Entity)entity;
9793 GAIA_ASSERT(valid(source));
9795 auto& ec = fetch(source);
9796 auto& archetype = *ec.pArchetype;
9797 auto* pChunk = ec.pChunk;
9798 const bool wasEnabled = !ec.data.dis;
9799#if GAIA_ASSERT_ENABLED
9800 verify_enable(*
this, archetype, source);
9802 archetype.enable_entity(ec.pChunk, ec.row, enable, m_recs);
9804 if (wasEnabled != enable) {
9805 pChunk->update_world_version();
9806 pChunk->update_entity_order_version();
9807 update_version(m_enabledHierarchyVersion);
9808 update_version(m_worldVersion);
9815 GAIA_NODISCARD
bool enabled(
const EntityContainer& ec)
const {
9816 const bool entityStateInContainer = !ec.data.dis;
9817#if GAIA_ASSERT_ENABLED
9818 const bool entityStateInChunk = ec.pChunk->enabled(ec.row);
9819 GAIA_ASSERT(entityStateInChunk == entityStateInContainer);
9821 return entityStateInContainer;
9829 GAIA_ASSERT(valid(entity));
9831 return enabled(cont(entity));
9839 const auto source = (
Entity)entity;
9840 GAIA_ASSERT(valid(source));
9842 return enabled(fetch(source));
9851 const auto* pEc = try_live_record(entity);
9852 if (pEc ==
nullptr || try_live_record(relation) ==
nullptr || !enabled(*pEc))
9856 GAIA_FOR(MAX_TRAV_DEPTH) {
9857 const auto next = target(curr, relation);
9858 if (next == EntityBad || next == curr)
9874 const auto source = (
Entity)entity;
9875 const auto* pEc = try_live_record(source);
9876 if (pEc ==
nullptr || try_live_record(relation) ==
nullptr || !enabled(*pEc))
9880 GAIA_FOR(MAX_TRAV_DEPTH) {
9881 const auto next = target(curr, relation);
9882 if (next == EntityBad || next == curr)
9898 return cont(entity).pChunk;
9905 return fetch((
Entity)entity).pChunk;
9914 const auto& ec = cont(entity);
9924 const auto& ec = fetch((
Entity)entity);
9931 GAIA_NODISCARD uint32_t
size()
const {
9932 return m_recs.entities.item_count();
9941 uint32_t& outArchetypes, uint32_t& outChunks, uint32_t& outEntitiesTotal, uint32_t& outEntitiesActive)
const {
9942 outArchetypes = (uint32_t)m_archetypes.size();
9944 outEntitiesTotal = 0;
9945 outEntitiesActive = 0;
9947 for (
const auto* pArchetype: m_archetypes) {
9948 if (pArchetype ==
nullptr)
9950 const auto& chunks = pArchetype->chunks();
9951 outChunks += (uint32_t)chunks.size();
9952 for (
const auto* pChunk: chunks) {
9953 if (pChunk ==
nullptr)
9955 outEntitiesTotal += pChunk->size();
9956 outEntitiesActive += pChunk->size_enabled();
9964 return m_worldVersion;
9973 return it != m_relationVersions.end() ? it->second : 0;
9980 return m_enabledHierarchyVersion;
9986 return m_archetypeDeleteVersion;
9989 friend uint32_t world_rel_version(
const World& world,
Entity relation);
9990 friend uint32_t world_version(
const World& world);
9991 friend uint32_t world_archetype_delete_version(
const World& world);
9992 friend uint32_t world_entity_archetype_version(
const World& world,
Entity entity);
9998 const auto it = m_srcEntityVersions.find(key);
9999 if (it == m_srcEntityVersions.end())
10002 update_version(it->second);
10015 set_max_lifespan(entity, m_defaultMaxLifespan);
10023 auto* pEc = try_live_record(entity);
10024 if (pEc ==
nullptr)
10028 const auto prevLifespan = ec.pArchetype->max_lifespan();
10029 ec.pArchetype->set_max_lifespan(lifespan);
10031 if (prevLifespan == 0) {
10033 try_enqueue_archetype_for_deletion(*ec.pArchetype);
10041 GAIA_ASSERT(lifespan <= Archetype::MAX_ARCHETYPE_LIFESPAN);
10042 m_defaultMaxLifespan = lifespan;
10048 return m_defaultMaxLifespan;
10064#if GAIA_OBSERVERS_ENABLED && GAIA_ASSERT_ENABLED
10065 GAIA_ASSERT(!observer_callback_active());
10068#if GAIA_USE_SAFE_ENTITY
10069 del_released_safe_entities();
10105#if GAIA_OBSERVERS_ENABLED && GAIA_ASSERT_ENABLED
10106 GAIA_ASSERT(!observer_callback_active());
10109#if GAIA_SYSTEMS_ENABLED
10120 if GAIA_UNLIKELY (m_teardownActive)
10122 m_teardownActive =
true;
10124 GAIA_PROF_SCOPE(World::teardown);
10126#if GAIA_SYSTEMS_ENABLED
10128 m_systems.teardown();
10131#if GAIA_OBSERVERS_ENABLED
10132 m_observers.teardown();
10136 const auto prevReqArchetypes = m_reqArchetypesToDel.size();
10137 const auto prevReqEntities = m_reqEntitiesToDel.size();
10138 const auto prevChunks = m_chunksToDel.size();
10139 const auto prevArchetypes = m_archetypesToDel.size();
10144 if (m_reqArchetypesToDel.empty() && m_reqEntitiesToDel.empty() && m_chunksToDel.empty() &&
10145 m_archetypesToDel.empty())
10148 const bool madeProgress = m_reqArchetypesToDel.size() != prevReqArchetypes ||
10149 m_reqEntitiesToDel.size() != prevReqEntities ||
10150 m_chunksToDel.size() != prevChunks || m_archetypesToDel.size() != prevArchetypes;
10166 m_pRootArchetype =
nullptr;
10167 m_pEntityArchetype =
nullptr;
10168 m_pCompArchetype =
nullptr;
10169 m_nextArchetypeId = 0;
10170 m_defragLastArchetypeIdx = 0;
10171 m_worldVersion = 0;
10172 m_enabledHierarchyVersion = 0;
10173 m_archetypeDeleteVersion = 0;
10180 m_defragEntitiesPerTick = value;
10187 GAIA_LOG_N(
"Archetypes:%u", (uint32_t)m_archetypes.size());
10188 for (
auto* pArchetype: m_archetypes)
10189 Archetype::diag(*
this, *pArchetype);
10195 comp_cache().diag();
10201 validate_entities();
10203 GAIA_LOG_N(
"Deleted entities: %u", (uint32_t)m_recs.entities.get_free_items());
10204 if (m_recs.entities.get_free_items() != 0U) {
10205 GAIA_LOG_N(
" --> %u", (uint32_t)m_recs.entities.get_next_free_item());
10207 uint32_t iters = 0;
10208 auto fe = m_recs.entities.next_free(m_recs.entities.get_next_free_item());
10209 while (fe != IdentifierIdBad) {
10210 GAIA_LOG_N(
" --> %u", m_recs.entities.next_free(fe));
10211 fe = m_recs.entities.next_free(fe);
10213 if (iters > m_recs.entities.get_free_items())
10217 if ((iters == 0U) || iters > m_recs.entities.get_free_items())
10218 GAIA_LOG_E(
" Entities recycle list contains inconsistent data!");
10231 void cleanup_inter() {
10232 GAIA_PROF_SCOPE(World::cleanup_inter);
10238 m_queryCache.clear_archetype_tracking();
10239 m_reqArchetypesToDel = {};
10240 m_reqEntitiesToDel = {};
10241 m_archetypeRowsToDel = {};
10242 m_entitiesDeleting = {};
10243 m_entitiesDeletingSet = {};
10244#if GAIA_USE_SAFE_ENTITY
10245 m_safeEntityDels = {};
10247 m_chunksToDel = {};
10248 m_archetypesToDel = {};
10254#if GAIA_USE_WEAK_ENTITY
10255 for (
auto& ec: m_recs.entities)
10256 invalidate_weak_entities(ec);
10257 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it)
10258 invalidate_weak_entities(it->second);
10262 m_recs.entities = {};
10263 m_recs.pair_records_clear();
10268 for (
auto* pArchetype: m_archetypes)
10269 Archetype::destroy(pArchetype);
10271 m_entityToAsRelations = {};
10272 m_entityToAsRelationsTravCache = {};
10273 m_entityToAsTargets = {};
10274 m_entityToAsTargetsTravCache = {};
10275 m_targetsTravCache = {};
10276 m_srcBfsTravCache = {};
10277 m_depthOrderCache = {};
10278 m_sourcesAllCache = {};
10279 m_targetsAllCache = {};
10280 m_relationCachesPopulated =
false;
10281 m_hasOnDeleteTargetPolicy =
false;
10282 m_hasCantCombinePolicy =
false;
10283 m_pairLookup.clear();
10284 m_nonFragmentingRelationsByRel = {};
10285 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
10287 store.func_clear_store(store.pStore);
10288 store.func_del_store(store.pStore);
10290 m_sparseComponentsByComp = {};
10291 m_relationVersions = {};
10292 m_lastRelationVersionRelation = EntityBad;
10293 m_pLastRelationVersion =
nullptr;
10294 m_srcEntityVersions = {};
10297 m_archetypesById = {};
10298 m_archetypesByHash = {};
10303 m_entityToArchetypeMap = {};
10304 m_entityToArchetypeMapVersions = {};
10305 m_queryCache.clear();
10306 for (
auto* pScratch: m_queryMatchScratchStack)
10308 m_queryMatchScratchStack = {};
10309 m_queryMatchScratchDepth = 0;
10310 m_querySerMap = {};
10311 m_nextQuerySerId = 0;
10316 for (
auto& pair: m_aliasToEntity) {
10317 if (!pair.first.owned())
10320 mem::mem_free((
void*)pair.first.str());
10322 m_aliasToEntity = {};
10327 for (
auto& pair: m_nameToEntity) {
10328 if (!pair.first.owned())
10331 mem::mem_free((
void*)pair.first.str());
10333 m_nameToEntity = {};
10337 m_compCache.clear();
10344 GAIA_NODISCARD
bool valid(
const EntityContainer& ec, [[maybe_unused]] Entity entityExpected)
const {
10345 if ((ec.flags & EntityContainerFlags::Load) != 0) {
10346 return entityExpected.id() == ec.idx && entityExpected.gen() == ec.data.gen &&
10347 entityExpected.entity() == (bool)ec.data.ent && entityExpected.pair() == (bool)ec.data.pair;
10350 if (is_req_del(ec))
10354 const auto* pChunk = ec.pChunk;
10355 if (pChunk ==
nullptr || ec.row >= pChunk->size())
10358 const auto entityPresent = pChunk->entity_view()[ec.row];
10361 return entityExpected == entityPresent;
10367 GAIA_NODISCARD
bool valid_pair(Entity entity)
const {
10368 if (entity == EntityBad)
10371 GAIA_ASSERT(entity.pair());
10372 if (!entity.pair())
10376 if (is_wildcard(entity))
10379 const auto* pPair = m_recs.pair_record_find(entity);
10380 if (pPair ==
nullptr)
10383 const auto& ec = *pPair;
10384 return valid(ec, entity);
10390 GAIA_NODISCARD
bool valid_entity(Entity entity)
const {
10391 if (entity == EntityBad)
10394 GAIA_ASSERT(!entity.pair());
10399 if (entity.id() >= m_recs.entities.size())
10402 const auto* pEc = m_recs.entities.try_get(entity.id());
10403 if (pEc ==
nullptr)
10406 return valid(*pEc, entity);
10413 GAIA_NODISCARD
bool valid_entity_id(EntityId entityId)
const {
10414 if (entityId == EntityBad.id())
10418 if (entityId >= m_recs.entities.size())
10421 const auto* pEc = m_recs.entities.try_get(entityId);
10422 if (pEc ==
nullptr)
10425 const auto& ec = *pEc;
10426 if (ec.data.pair != 0)
10429 return valid(ec, Entity(entityId, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair));
10436 GAIA_ASSERT(m_structuralChangesLocked != (uint32_t)-1);
10437 ++m_structuralChangesLocked;
10444 GAIA_ASSERT(m_structuralChangesLocked > 0);
10445 --m_structuralChangesLocked;
10448#if GAIA_SYSTEMS_ENABLED
10450 void systems_done();
10457 return m_structuralChangesLocked != 0;
10464 return m_teardownActive;
10468 static constexpr uint32_t WorldSerializerVersion = 6;
10469#if GAIA_JSON_ENABLED
10470 static constexpr uint32_t WorldSerializerJSONVersion = 1;
10476 void save_component_layout(
ser::serializer& s, uint32_t lastCoreComponentId)
const {
10478 components.
reserve((uint32_t)m_compCache.m_compByEntityId.size());
10479 for (
const auto& [entityId, pItem]: m_compCache.m_compByEntityId) {
10480 if (entityId > lastCoreComponentId) {
10481 GAIA_ASSERT(pItem !=
nullptr);
10485 core::sort(components, [](
const ComponentCacheItem* pLeft,
const ComponentCacheItem* pRight) {
10486 return pLeft->entity.id() < pRight->entity.id();
10489 s.
save((uint32_t)components.
size());
10490 for (
const auto* pItem: components) {
10491 const auto symbol = pItem->symbol_name();
10492 s.
save(pItem->entity.val);
10493 s.
save(pItem->comp.val);
10494 s.
save(symbol.size());
10495 s.
save_raw(symbol.data(), symbol.size(), ser::serialization_type_id::c8);
10504 bool validate_component_layout(
10505 ser::serializer& s, uint32_t savedLastCoreComponentId, uint32_t currLastCoreComponentId)
const {
10506 uint32_t savedComponentCnt = 0;
10507 s.load(savedComponentCnt);
10509 uint32_t currentComponentCnt = 0;
10510 for (
const auto& [entityId, pItem]: m_compCache.m_compByEntityId) {
10512 if (entityId > currLastCoreComponentId)
10513 ++currentComponentCnt;
10515 if (savedComponentCnt != currentComponentCnt) {
10517 "World snapshot component count mismatch. Snapshot has %u, target world has %u.", savedComponentCnt,
10518 currentComponentCnt);
10522 GAIA_FOR(savedComponentCnt) {
10523 Identifier savedEntityValue = IdentifierBad;
10524 Identifier savedComponentValue = IdentifierBad;
10525 uint32_t symbolLen = 0;
10526 s.load(savedEntityValue);
10527 s.load(savedComponentValue);
10530 const auto* pSymbol = (
const char*)(s.data() + s.tell());
10531 s.seek(s.tell() + symbolLen);
10532 const util::str_view symbol(pSymbol, symbolLen);
10533 const auto* pItem = comp_cache().symbol(symbol);
10534 if (pItem ==
nullptr) {
10535 GAIA_LOG_E(
"World snapshot component '%.*s' is not registered.", (
int)symbolLen, pSymbol);
10539 const auto savedEntity = Entity(savedEntityValue);
10540 const auto expectedEntity =
10541 detail::remap_loaded_entity(savedEntity, savedLastCoreComponentId, currLastCoreComponentId);
10542 Component expectedComponent;
10543 expectedComponent.val = savedComponentValue;
10544 expectedComponent.data.id =
10545 detail::remap_loaded_entity_id(expectedComponent.id(), savedLastCoreComponentId, currLastCoreComponentId);
10546 if (pItem->entity != expectedEntity || pItem->comp != expectedComponent) {
10548 "World snapshot component layout mismatch for '%.*s'. Register components in the same order and "
10549 "with the same storage layout before loading.",
10550 (
int)symbolLen, pSymbol);
10560 void save_sparse_component_stores(ser::serializer& s)
const {
10561 if (m_sparseComponentsByComp.empty()) {
10562 s.save((uint32_t)0);
10566 cnt::darray<Entity> components;
10567 components.
reserve((uint32_t)m_sparseComponentsByComp.size());
10568 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
10569 if (store.func_count(store.pStore) != 0)
10570 components.
push_back(compKey.entity());
10572 core::sort(components, core::is_smaller<Entity>{});
10574 s.save((uint32_t)components.size());
10575 for (
auto component: components) {
10576 const auto storeIt = m_sparseComponentsByComp.find(EntityLookupKey(component));
10577 GAIA_ASSERT(storeIt != m_sparseComponentsByComp.end());
10578 const auto& store = storeIt->second;
10579 const auto* pItem =
10580 component.pair() ? comp_cache().find_pair_payload(component) : comp_cache().find(component);
10581 GAIA_ASSERT(pItem !=
nullptr);
10583 cnt::darray<Entity> owners;
10584 store.func_collect_entities(store.pStore, owners);
10585 core::sort(owners, core::is_smaller<Entity>{});
10588 s.save((uint32_t)owners.size());
10589 for (
auto owner: owners) {
10590 GAIA_ASSERT(valid(owner));
10592 if (pItem->comp.size() == 0)
10595 const auto* pData = store.func_get(store.pStore, owner);
10596 GAIA_ASSERT(pData !=
nullptr);
10597 pItem->save(s, pData, 0, 1, 1);
10605 bool load_sparse_component_stores(ser::serializer& s) {
10606 uint32_t componentCnt = 0;
10607 s.load(componentCnt);
10608 GAIA_FOR(componentCnt) {
10610 uint32_t ownerCnt = 0;
10614 const auto* pItem =
10615 component.pair() ? comp_cache().find_pair_payload(component) : comp_cache().find(component);
10618 if (pItem ==
nullptr || (pItem->comp.size() != 0 && pItem->comp.storage_type() != DataStorageType::Sparse)) {
10619 GAIA_LOG_E(
"Missing sparse component metadata for entity %u", component.id());
10622 auto& store = sparse_component_store_erased_mut(component, *pItem);
10623 GAIA_FOR_(ownerCnt, ownerIdx) {
10626 auto* pData = store.func_add(store.pStore, owner);
10627 if (pItem->comp.size() == 0)
10630 GAIA_ASSERT(pData !=
nullptr);
10631 pItem->load(s, pData, 0, 1, 1);
10641 void load_record_location(EntityContainer& ec, Entity entity) {
10642 const auto archetypeIdx = (uint32_t)((uintptr_t)ec.pArchetype);
10643 const auto chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
10644 if (archetypeIdx == (uint32_t)BadIndex) {
10645 ec.pArchetype =
nullptr;
10646 ec.pChunk =
nullptr;
10647 ec.pEntity =
nullptr;
10648 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
10652 ec.pArchetype = m_archetypes[archetypeIdx];
10653 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
10654 ec.pEntity = &ec.pChunk->entity_view()[ec.row];
10659 void save_to(ser::serializer s)
const {
10660 GAIA_ASSERT(s.valid());
10663 s.save((uint32_t)WorldSerializerVersion);
10667 const auto lastCoreComponentId = GAIA_ID(LastCoreComponent).id();
10668 s.save(lastCoreComponentId);
10669 save_component_layout(s, lastCoreComponentId);
10673 auto saveEntityContainer = [&](
const EntityContainer& ec) {
10675 s.save(ec.dataRaw);
10677 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) == 0);
10682 const bool deleted = (ec.flags & EntityContainerFlags::DeleteRequested) != 0;
10683 auto flags = ec.flags;
10684#if GAIA_USE_SAFE_ENTITY
10688 flags &= (EntityContainerFlagsType)(~(EntityContainerFlagsType)EntityContainerFlags::RefDecreased);
10690 s.save((uint32_t)(deleted ? 0 : 1));
10693 s.save((uint32_t)0);
10696 const uint32_t archetypeIdx = deleted ? (uint32_t)BadIndex : ec.pArchetype->list_idx();
10697 s.save(archetypeIdx);
10698 const uint32_t chunkIdx = deleted ? (uint32_t)BadIndex : ec.pChunk->idx();
10702 const auto recEntities = (uint32_t)m_recs.entities.size();
10703 const auto newEntities = recEntities - lastCoreComponentId;
10704 s.save(newEntities);
10705 GAIA_FOR2(lastCoreComponentId, recEntities) {
10706 const bool isAlive = m_recs.entities.has(i);
10709 saveEntityContainer(m_recs.entities[i]);
10711 s.save(m_recs.entities.handle(i).val);
10712 s.save(m_recs.entities.next_free(i));
10717 uint32_t pairsCnt = 0;
10718 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it) {
10719 const auto& pair = *it;
10721 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
10729 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it) {
10730 const auto& pair = *it;
10732 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
10735 saveEntityContainer(pair.second);
10739 s.save(m_recs.entities.m_nextFreeIdx);
10740 s.save(m_recs.entities.m_freeItems);
10745 s.save((uint32_t)m_archetypes.size());
10746 for (
auto* pArchetype: m_archetypes) {
10747 s.save((uint32_t)pArchetype->ids_view().size());
10748 for (
auto e: pArchetype->ids_view())
10751 pArchetype->save(s);
10754 s.save(m_worldVersion);
10758 save_sparse_component_stores(s);
10762 uint32_t edgeCnt = 0;
10763 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
10765 edgeCnt += store.source_count();
10769 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
10770 const auto relation = relKey.entity();
10771 cnt::darray<EntityId> sourceIds;
10772 cnt::darray<Entity> pairSources;
10773 store.collect_source_ids(sourceIds);
10774 store.collect_pair_sources(pairSources);
10775 for (
auto sourceId: sourceIds) {
10776 const auto source = get(sourceId);
10777 GAIA_ASSERT(valid(source));
10778 const auto target = store.target(source);
10779 GAIA_ASSERT(target != EntityBad);
10784 for (
auto source: pairSources) {
10785 GAIA_ASSERT(valid(source));
10786 const auto target = store.target(source);
10787 GAIA_ASSERT(target != EntityBad);
10797 s.save((uint32_t)m_nameToEntity.size());
10798 for (
const auto& pair: m_nameToEntity) {
10799 s.save(pair.second);
10800 const bool isOwnedStr = pair.first.owned();
10801 s.save(isOwnedStr);
10805 const auto* str = pair.first.str();
10806 const uint32_t len = pair.first.len();
10808 s.save_raw(str, len, ser::serialization_type_id::c8);
10813 else if (!pair.second.comp()) {
10814 const auto* str = pair.first.str();
10815 const uint32_t len = pair.first.len();
10817 const auto ptr_val = (uint64_t)str;
10818 s.save_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
10825 uint32_t aliasCnt = 0;
10826 GAIA_FOR((uint32_t)m_recs.entities.size()) {
10827 const auto entity = get((EntityId)i);
10828 if (!valid(entity) || entity.pair())
10831 const auto& ec = m_recs.entities[i];
10832 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
10833 if (compIdx == BadIndex)
10836 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
10837 if (pDesc->alias !=
nullptr)
10842 GAIA_FOR((uint32_t)m_recs.entities.size()) {
10843 const auto entity = get((EntityId)i);
10844 if (!valid(entity) || entity.pair())
10847 const auto& ec = m_recs.entities[i];
10848 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
10849 if (compIdx == BadIndex)
10852 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
10853 if (pDesc->alias ==
nullptr)
10857 s.save(pDesc->alias_len);
10858 s.save_raw(pDesc->alias, pDesc->alias_len, ser::serialization_type_id::c8);
10871 auto s = m_serializer;
10872 GAIA_ASSERT(s.valid());
10878#if GAIA_JSON_ENABLED
10887 ser::ser_json& writer, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default,
10888 const ser::RuntimeJsonPolicy& policy = {})
const;
10894 ser::json_str save_json(
bool& ok, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default)
const;
10898 uint64_t runtime_schema_hash()
const;
10903 bool save_runtime_schema_json(ser::ser_json& writer)
const;
10907 ser::json_str save_runtime_schema_json()
const;
10918 bool patch_comp_json(
10919 Entity entity, Entity component, ser::json_str_view pointer, ser::json_str_view value,
10920 ser::JsonDiagnostics& diagnostics,
const ser::RuntimeJsonPolicy& policy = {},
10921 uint64_t expectedRuntimeSchemaHash = 0);
10930 const char* json, uint32_t len, ser::JsonDiagnostics& diagnostics,
const ser::RuntimeJsonPolicy& policy = {});
10935 bool load_json(
const char* json, uint32_t len);
10943 load_json(ser::json_str_view json, ser::JsonDiagnostics& diagnostics,
const ser::RuntimeJsonPolicy& policy = {});
10948 bool load_json(ser::json_str_view json);
10963 auto s = inputSerializer.valid() ? inputSerializer : m_serializer;
10964 GAIA_ASSERT(s.valid());
10970 uint32_t version = 0;
10972 if (version < 2 || version > WorldSerializerVersion) {
10973 GAIA_LOG_E(
"Unsupported world version %u. Expected 2..%u.", version, WorldSerializerVersion);
10978 uint32_t lastCoreComponentId = 0;
10979 s.load(lastCoreComponentId);
10983 const auto currLastCoreComponentId = GAIA_ID(LastCoreComponent).id();
10984 if (lastCoreComponentId > currLastCoreComponentId) {
10986 "Unsupported world core boundary %u. Current runtime supports up to %u.", lastCoreComponentId,
10987 currLastCoreComponentId);
10990 if (version >= 6 && !validate_component_layout(s, lastCoreComponentId, currLastCoreComponentId))
10995 const detail::EntityLoadRemapGuard entityLoadRemapGuard(
10996 lastCoreComponentId, currLastCoreComponentId, version >= 5);
10997 auto remapLoadedEntityId = [&](uint32_t id) {
10998 return detail::remap_loaded_entity_id(
id, lastCoreComponentId, currLastCoreComponentId);
11003 auto loadEntityContainer = [&](EntityContainer& ec) {
11005 s.load(ec.dataRaw);
11008 if ((ec.flags & EntityContainerFlags::HasCantCombine) != 0)
11009 m_hasCantCombinePolicy =
true;
11010 if ((ec.flags & (EntityContainerFlags::OnDeleteTarget_Delete | EntityContainerFlags::OnDeleteTarget_Remove |
11011 EntityContainerFlags::OnDeleteTarget_Error)) != 0) {
11012 m_hasOnDeleteTargetPolicy =
true;
11014 ec.flags |= EntityContainerFlags::Load;
11016 ec.idx = remapLoadedEntityId(ec.idx);
11017 if (ec.data.pair != 0)
11018 ec.data.gen = remapLoadedEntityId(ec.data.gen);
11020#if GAIA_USE_SAFE_ENTITY
11027 GAIA_ASSERT(ec.unused == 0);
11030 uint32_t archetypeIdx = 0;
11031 s.load(archetypeIdx);
11032 ec.pArchetype = (Archetype*)((uintptr_t)archetypeIdx);
11034 uint32_t chunkIdx = 0;
11036 ec.pChunk = (Chunk*)((uintptr_t)chunkIdx);
11039 uint32_t newEntities = 0;
11040 s.load(newEntities);
11041 GAIA_FOR(newEntities) {
11042 bool isAlive =
false;
11045 EntityContainer ec{};
11046 loadEntityContainer(ec);
11047 m_recs.entities.add_live(GAIA_MOV(ec));
11049 Identifier
id = IdentifierBad;
11050 uint32_t nextFreeIdx = Entity::IdMask;
11052 s.load(nextFreeIdx);
11053 auto entity = Entity(
id);
11054 entity = detail::remap_loaded_entity(entity, lastCoreComponentId, currLastCoreComponentId);
11055 nextFreeIdx = remapLoadedEntityId(nextFreeIdx);
11056 GAIA_ASSERT(entity.id() == remapLoadedEntityId(lastCoreComponentId + i));
11057 m_recs.entities.add_free(entity, nextFreeIdx);
11061 uint32_t pairsCnt = 0;
11063 GAIA_FOR(pairsCnt) {
11064 EntityContainer ec{};
11065 loadEntityContainer(ec);
11068 const Entity pair = Pair(m_recs.entities.handle(ec.idx), m_recs.entities.handle(ec.data.gen));
11069 [[maybe_unused]]
const bool added = m_recs.pair_record_try_add(pair, GAIA_MOV(ec));
11070 GAIA_ASSERT(added);
11073 s.load(m_recs.entities.m_nextFreeIdx);
11074 m_recs.entities.m_nextFreeIdx = remapLoadedEntityId(m_recs.entities.m_nextFreeIdx);
11075 s.load(m_recs.entities.m_freeItems);
11080 uint32_t archetypesSize = 0;
11081 s.load(archetypesSize);
11082 m_archetypes.reserve(archetypesSize);
11083 GAIA_FOR(archetypesSize) {
11084 uint32_t idsSize = 0;
11086 Entity ids[ChunkHeader::MAX_COMPONENTS];
11087 GAIA_FOR_(idsSize, j) {
11092 const auto hashLookup = calc_lookup_hash({&ids[0], idsSize}).hash;
11094 auto* pArchetype = find_archetype({hashLookup}, {&ids[0], idsSize});
11095 if (pArchetype ==
nullptr) {
11097 pArchetype = create_archetype({&ids[0], idsSize});
11098 pArchetype->set_hashes({hashLookup});
11104 reg_archetype(pArchetype);
11108 pArchetype->load(s);
11111 s.load(m_worldVersion);
11114 if (version >= 5 && !load_sparse_component_stores(s))
11121 for (
auto& ec: m_recs.entities) {
11122 if ((ec.flags & EntityContainerFlags::Load) == 0)
11124 ec.flags &= (EntityContainerFlagsType)(~(
11125 EntityContainerFlagsType)EntityContainerFlags::Load);
11127 load_record_location(ec, EntityContainer::handle(ec));
11130 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it) {
11132 auto& ec = pair.second;
11135 if ((ec.flags & EntityContainerFlags::Load) == 0)
11138 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) != 0);
11139 ec.flags &= (EntityContainerFlagsType)(~(
11140 EntityContainerFlagsType)EntityContainerFlags::Load);
11142 load_record_location(ec, pair.first.entity());
11146 if (version < WorldSerializerVersion) {
11147 for (
const auto& [entityId, pItem]: m_compCache.m_compByEntityId) {
11149 GAIA_ASSERT(pItem !=
nullptr);
11150 auto comp = pItem->comp;
11151 comp.data.id = pItem->entity.id();
11152 sync_component_record(pItem->entity, comp);
11156 if (version >= 4) {
11157 uint32_t edgeCnt = 0;
11159 GAIA_FOR(edgeCnt) {
11167 if (!valid(source) || !valid(relation) || !valid(target) ||
11168 !relation_uses_non_fragmenting_storage(relation))
11171 assign_pair(Pair(relation, target), *m_pEntityArchetype);
11172 nonfragmenting_relation_set(source, relation, target);
11176#if GAIA_ASSERT_ENABLED
11177 for (
const auto& ec: m_recs.entities) {
11178 GAIA_ASSERT(ec.idx < m_recs.entities.size());
11179 GAIA_ASSERT(m_recs.entities.handle(ec.idx) == EntityContainer::handle(ec));
11181 if ((ec.flags & EntityContainerFlags::DeleteRequested) != 0)
11183 GAIA_ASSERT(ec.pArchetype !=
nullptr);
11184 GAIA_ASSERT(ec.pChunk !=
nullptr);
11185 GAIA_ASSERT(ec.pEntity !=
nullptr);
11190 m_nameToEntity = {};
11198 const auto& ec = fetch(entity);
11199 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
11200 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
11201 GAIA_ASSERT(core::check_alignment(pDesc));
11203 bool isOwned =
false;
11206 if (entity.comp()) {
11209 const auto& ci = comp_cache().get(entity);
11210 const auto symbol = ci.symbol_name();
11211 pDesc->name = symbol.data();
11213 GAIA_ASSERT(pDesc->name_len == symbol.size());
11214 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
11218 uint64_t ptr_val = 0;
11219 s.load_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
11222 pDesc->name = (
const char*)ptr_val;
11223 pDesc->name_len = len;
11224 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
11234 const char* entityStr = (
const char*)(s.data() + s.tell());
11235 s.seek(s.tell() + len);
11239 pDesc->name =
nullptr;
11240 pDesc->name_len = 0;
11244 name(entity, entityStr, len);
11250 m_aliasToEntity = {};
11251 for (
auto& ec: m_recs.entities) {
11252 const auto entity = EntityContainer::handle(ec);
11254 if (entity.pair() || ec.pChunk ==
nullptr)
11257 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
11258 if (compIdx == BadIndex)
11261 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
11262 GAIA_ASSERT(core::check_alignment(pDesc));
11263 pDesc->alias =
nullptr;
11264 pDesc->alias_len = 0;
11273 const auto& ec = fetch(entity);
11274 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
11275 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
11276 GAIA_ASSERT(core::check_alignment(pDesc));
11282 const char* aliasStr = (
const char*)(s.data() + s.tell());
11283 s.seek(s.tell() + len);
11285 pDesc->alias =
nullptr;
11286 pDesc->alias_len = 0;
11287 alias(entity, aliasStr, len);
11297 template <
typename TSerializer>
11298 bool load(TSerializer& inputSerializer) {
11299 return load(ser::make_serializer(inputSerializer));
11304 void sort_archetypes() {
11307 return a->
id() < b->id();
11311 core::sort(m_archetypes, sort_cond{}, [&](uint32_t left, uint32_t right) {
11312 Archetype* tmp = m_archetypes[left];
11314 m_archetypes[right]->list_idx(left);
11315 m_archetypes[left]->list_idx(right);
11317 m_archetypes.data()[left] = (Archetype*)m_archetypes[right];
11318 m_archetypes.data()[right] = tmp;
11322#if GAIA_USE_SAFE_ENTITY
11325 class CompDtorScope {
11329 explicit CompDtorScope(World& world): m_world(world) {
11330 ++m_world.m_compDtorDepth;
11333 --m_world.m_compDtorDepth;
11335 CompDtorScope(
const CompDtorScope&) =
delete;
11336 CompDtorScope& operator=(
const CompDtorScope&) =
delete;
11343 void remove_chunk(Archetype& archetype, Chunk& chunk) {
11344#if GAIA_USE_SAFE_ENTITY
11345 CompDtorScope dtorScope(*
this);
11347 archetype.del(&chunk);
11348 try_enqueue_archetype_for_deletion(archetype);
11353 void remove_chunk_from_delete_queue(uint32_t idx) {
11354 GAIA_ASSERT(idx < m_chunksToDel.size());
11356 auto* pChunk = m_chunksToDel[idx].pChunk;
11357 pChunk->clear_delete_queue_index();
11359 const auto lastIdx = (uint32_t)m_chunksToDel.size() - 1;
11360 if (idx != lastIdx) {
11361 auto* pMovedChunk = m_chunksToDel[lastIdx].pChunk;
11362 pMovedChunk->delete_queue_index(idx);
11365 core::swap_erase(m_chunksToDel, idx);
11372 void remove_entity(Archetype& archetype, Chunk& chunk, uint16_t row) {
11373#if GAIA_USE_SAFE_ENTITY
11374 CompDtorScope dtorScope(*
this);
11376 archetype.remove_entity(chunk, row, m_recs);
11377 try_enqueue_chunk_for_deletion(archetype, chunk);
11381 void del_empty_chunks() {
11382 GAIA_PROF_SCOPE(World::del_empty_chunks);
11384 for (uint32_t i = 0; i < m_chunksToDel.size();) {
11385 auto* pArchetype = m_chunksToDel[i].pArchetype;
11386 auto* pChunk = m_chunksToDel[i].pChunk;
11389 if (!pChunk->empty()) {
11391 revive_archetype(*pArchetype);
11392 remove_chunk_from_delete_queue(i);
11397 if (pChunk->progress_death()) {
11403 remove_chunk_from_delete_queue(i);
11404 remove_chunk(*pArchetype, *pChunk);
11410 void del_empty_archetype(Archetype* pArchetype) {
11411 GAIA_PROF_SCOPE(World::del_empty_archetype);
11413 GAIA_ASSERT(pArchetype !=
nullptr);
11414 GAIA_ASSERT(pArchetype->empty() || pArchetype->is_req_del());
11415 GAIA_ASSERT(!pArchetype->dying() || pArchetype->is_req_del());
11417 unreg_archetype(pArchetype);
11418 for (
auto& ec: m_recs.entities) {
11419 if (ec.pArchetype != pArchetype)
11422 ec.pArchetype =
nullptr;
11423 ec.pChunk =
nullptr;
11424 ec.pEntity =
nullptr;
11426 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it) {
11427 auto& ec = it->second;
11428 if (ec.pArchetype != pArchetype)
11431 ec.pArchetype =
nullptr;
11432 ec.pChunk =
nullptr;
11433 ec.pEntity =
nullptr;
11435 Archetype::destroy(pArchetype);
11439 void del_empty_archetypes() {
11440 GAIA_PROF_SCOPE(World::del_empty_archetypes);
11442 cnt::sarray_ext<Archetype*, 512> tmp;
11450 auto remove_from_queries = [&]() {
11454 for (
auto* pArchetype: tmp) {
11455 m_queryCache.remove_archetype_from_queries(pArchetype);
11456 del_empty_archetype(pArchetype);
11461 for (uint32_t i = 0; i < m_archetypesToDel.size();) {
11462 auto* pArchetype = m_archetypesToDel[i];
11465 if (!pArchetype->empty() || pArchetype->max_lifespan() == 0) {
11466 revive_archetype(*pArchetype);
11467 core::swap_erase(m_archetypesToDel, i);
11473 if (!pArchetype->is_req_del() && pArchetype->progress_death()) {
11478 tmp.push_back(pArchetype);
11481 core::swap_erase(m_archetypesToDel, i);
11484 if (tmp.size() == tmp.max_size())
11485 remove_from_queries();
11488 remove_from_queries();
11493 void revive_archetype(Archetype& archetype) {
11494 const bool wasReqDel = archetype.is_req_del();
11495 archetype.revive();
11497 update_version(m_archetypeDeleteVersion);
11498 m_reqArchetypesToDel.erase(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
11504 void try_enqueue_chunk_for_deletion(Archetype& archetype, Chunk& chunk) {
11505 if (chunk.dying() || !chunk.empty())
11520 chunk.start_dying();
11522 m_chunksToDel.push_back({&archetype, &chunk});
11523 chunk.delete_queue_index((uint32_t)m_chunksToDel.size() - 1);
11528 void try_enqueue_archetype_for_deletion(Archetype& archetype) {
11529 if (!archetype.ready_to_die())
11544 archetype.start_dying();
11546 m_archetypesToDel.push_back(&archetype);
11551 void defrag_chunks(uint32_t maxEntities) {
11552 GAIA_PROF_SCOPE(World::defrag_chunks);
11554 const auto maxIters = m_archetypes.size();
11556 GAIA_ASSERT(maxIters > 0);
11558 GAIA_FOR(maxIters) {
11559 const auto idx = (m_defragLastArchetypeIdx + 1) % maxIters;
11560 auto* pArchetype = m_archetypes[idx];
11561 defrag_archetype(*pArchetype, maxEntities);
11562 if (maxEntities == 0)
11565 m_defragLastArchetypeIdx = idx;
11572 void defrag_archetype(Archetype& archetype, uint32_t& maxEntities) {
11592 if (maxEntities == 0)
11595 const auto& chunks = archetype.chunks();
11596 if (chunks.size() < 2)
11599 uint32_t front = 0;
11600 uint32_t back = chunks.size() - 1;
11602 auto* pDstChunk = chunks[front];
11603 auto* pSrcChunk = chunks[back];
11606 while (front < back && (pDstChunk->full() || !pDstChunk->is_semi()))
11607 pDstChunk = chunks[++front];
11609 while (front < back && (pSrcChunk->empty() || !pSrcChunk->is_semi()))
11610 pSrcChunk = chunks[--back];
11613 while (front < back) {
11614 pDstChunk = chunks[front];
11615 pSrcChunk = chunks[back];
11617 const uint32_t entitiesInSrcChunk = pSrcChunk->size();
11618 const uint32_t spaceInDstChunk = pDstChunk->capacity() - pDstChunk->size();
11619 const uint32_t entitiesToMoveSrc = core::get_min(entitiesInSrcChunk, maxEntities);
11620 const uint32_t entitiesToMove = core::get_min(entitiesToMoveSrc, spaceInDstChunk);
11622 GAIA_FOR(entitiesToMove) {
11623 const auto lastSrcEntityIdx = entitiesInSrcChunk - i - 1;
11624 const auto entity = pSrcChunk->entity_view()[lastSrcEntityIdx];
11626 auto& ec = m_recs[entity];
11628 const auto srcRow = ec.row;
11629 const auto dstRow = pDstChunk->add_entity(entity);
11630 const bool wasEnabled = !ec.data.dis;
11633 archetype.enable_entity(pSrcChunk, srcRow,
true, m_recs);
11635 GAIA_ASSERT(srcRow == ec.row);
11638 pDstChunk->move_entity_data(entity, dstRow, m_recs);
11645 archetype.remove_entity_raw(*pSrcChunk, srcRow, m_recs);
11646 try_enqueue_chunk_for_deletion(archetype, *pSrcChunk);
11649 ec.pChunk = pDstChunk;
11650 ec.row = (uint16_t)dstRow;
11651 ec.pEntity = &pDstChunk->entity_view()[dstRow];
11654 archetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
11658 if (entitiesToMove > 0) {
11659 pSrcChunk->update_world_version();
11660 pDstChunk->update_world_version();
11661 pSrcChunk->update_entity_order_version();
11662 pDstChunk->update_entity_order_version();
11663 update_version(m_worldVersion);
11666 maxEntities -= entitiesToMove;
11667 if (maxEntities == 0)
11671 if (pSrcChunk->empty()) {
11672 while (front < back) {
11673 if (chunks[--back]->is_semi())
11680 while (front < back && pDstChunk->full())
11681 pDstChunk = chunks[++front];
11689 GAIA_NODISCARD Archetype* find_archetype(Archetype::LookupHash hashLookup, EntitySpan ids) {
11690 auto tmpArchetype = ArchetypeLookupChecker(ids);
11691 ArchetypeLookupKey key(hashLookup, &tmpArchetype);
11694 const auto it = m_archetypesByHash.find(key);
11695 if (it == m_archetypesByHash.end())
11698 auto* pArchetype = it->second;
11702 GAIA_NODISCARD
static auto
11707 find_component_index_record(ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
11708 return core::get_index_if(records, [&](
const auto& record) {
11709 return record.matches(pArchetype);
11713 GAIA_NODISCARD
static auto
11718 find_component_index_record(
const ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
11719 return core::get_index_if(records, [&](
const auto& record) {
11720 return record.matches(pArchetype);
11727 void update_entity_archetype_lookup_revision(EntityLookupKey entityKey) {
11728 auto [it, _] = m_entityToArchetypeMapVersions.try_emplace(entityKey, 0);
11731 if (it->second == 0)
11741 void add_entity_archetype_pair(
11742 Entity entity, Archetype* pArchetype, uint16_t compIdx = ComponentIndexBad, uint16_t matchCount = 1) {
11743 GAIA_ASSERT(pArchetype !=
nullptr);
11744 GAIA_ASSERT(matchCount > 0);
11746 EntityLookupKey entityKey(entity);
11747 auto prepared = m_entityToArchetypeMap.prepare_insert(entityKey);
11748 if (!prepared.found()) {
11749 ComponentIndexEntryArray records;
11750 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
11751 m_entityToArchetypeMap.emplace_prepared(prepared, entityKey, GAIA_MOV(records));
11755 auto it = m_entityToArchetypeMap.prepared_iterator(prepared);
11756 auto& records = it->second;
11757 const auto idx = find_component_index_record(records, pArchetype);
11758 if (idx == BadIndex) {
11759 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
11763 auto& record = records[idx];
11764 record.matchCount = (uint16_t)(record.matchCount + matchCount);
11765 if (compIdx != ComponentIndexBad)
11766 record.compIdx = compIdx;
11776 void add_new_entity_archetype_pair(
11777 Entity entity, Archetype* pArchetype, uint16_t compIdx = ComponentIndexBad, uint16_t matchCount = 1) {
11778 GAIA_ASSERT(pArchetype !=
nullptr);
11779 GAIA_ASSERT(matchCount > 0);
11781 EntityLookupKey entityKey(entity);
11782 auto prepared = m_entityToArchetypeMap.prepare_insert(entityKey);
11783 if (!prepared.found()) {
11784 ComponentIndexEntryArray records;
11785 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
11786 m_entityToArchetypeMap.emplace_prepared(prepared, entityKey, GAIA_MOV(records));
11790 auto it = m_entityToArchetypeMap.prepared_iterator(prepared);
11791 auto& records = it->second;
11792 if (!records.empty()) {
11793 auto& record = records.back();
11794 if (record.matches(pArchetype)) {
11795 record.matchCount = (uint16_t)(record.matchCount + matchCount);
11796 if (compIdx != ComponentIndexBad)
11797 record.compIdx = compIdx;
11802 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
11809 void add_pair_archetype_query_pairs(Entity pair, Archetype* pArchetype, uint16_t matchCount = 1) {
11810 GAIA_ASSERT(pair.pair());
11811 GAIA_ASSERT(pArchetype !=
nullptr);
11812 GAIA_ASSERT(matchCount > 0);
11814 const auto first = get(pair.id());
11815 const auto second = get(pair.gen());
11817 add_entity_archetype_pair(Pair(All, second), pArchetype, ComponentIndexBad, matchCount);
11818 add_entity_archetype_pair(Pair(first, All), pArchetype, ComponentIndexBad, matchCount);
11819 add_entity_archetype_pair(Pair(All, All), pArchetype, ComponentIndexBad, matchCount);
11826 void add_new_pair_archetype_query_pairs(Entity pair, Archetype* pArchetype, uint16_t matchCount = 1) {
11827 GAIA_ASSERT(pair.pair());
11828 GAIA_ASSERT(pArchetype !=
nullptr);
11829 GAIA_ASSERT(matchCount > 0);
11831 const auto first = get(pair.id());
11832 const auto second = get(pair.gen());
11834 add_new_entity_archetype_pair(Pair(All, second), pArchetype, ComponentIndexBad, matchCount);
11835 add_new_entity_archetype_pair(Pair(first, All), pArchetype, ComponentIndexBad, matchCount);
11836 add_new_entity_archetype_pair(Pair(All, All), pArchetype, ComponentIndexBad, matchCount);
11842 void del_entity_query_pair(Pair pair, Entity entityToRemove) {
11843 const auto entityKey = EntityLookupKey(pair);
11844 auto it = m_entityToArchetypeMap.find(entityKey);
11845 if (it == m_entityToArchetypeMap.end())
11847 auto& records = it->second;
11848 bool changed =
false;
11852 for (uint32_t i = records.size() - 1; i != (uint32_t)-1; --i) {
11853 auto& record = records[i];
11854 const auto* pArchetype = record.pArchetype;
11855 if (!pArchetype->has(entityToRemove))
11858 if ((!is_wildcard(pair.first()) && !is_wildcard(pair.second())) || record.matchCount <= 1)
11859 core::swap_erase_unsafe(records, i);
11861 --record.matchCount;
11866 update_entity_archetype_lookup_revision(entityKey);
11868 if (records.empty())
11869 m_entityToArchetypeMap.erase(it);
11876 void del_entity_query_pair(Pair pair, Archetype* pArchetypeToRemove) {
11877 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
11879 const auto entityKey = EntityLookupKey(pair);
11880 auto it = m_entityToArchetypeMap.find(entityKey);
11881 if (it == m_entityToArchetypeMap.end())
11884 auto& records = it->second;
11885 const auto idx = find_component_index_record(records, pArchetypeToRemove);
11886 if (idx != BadIndex) {
11887 core::swap_erase_unsafe(records, idx);
11888 update_entity_archetype_lookup_revision(entityKey);
11891 if (records.empty())
11892 m_entityToArchetypeMap.erase(it);
11898 void del_pair_archetype_query_pairs(Entity pair, Archetype* pArchetypeToRemove) {
11899 GAIA_ASSERT(pair.pair());
11900 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
11902 GAIA_ASSERT(pair.id() < m_recs.entities.size());
11903 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
11904 const auto first = m_recs.entities.handle(pair.id());
11905 const auto second = m_recs.entities.handle(pair.gen());
11907 del_entity_query_pair(Pair(All, second), pArchetypeToRemove);
11908 del_entity_query_pair(Pair(first, All), pArchetypeToRemove);
11909 del_entity_query_pair(Pair(All, All), pArchetypeToRemove);
11915 void del_pair_archetype_query_pairs(Entity pair, Entity entityToRemove) {
11916 GAIA_ASSERT(pair.pair());
11918 GAIA_ASSERT(pair.id() < m_recs.entities.size());
11919 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
11920 const auto first = m_recs.entities.handle(pair.id());
11921 const auto second = m_recs.entities.handle(pair.gen());
11923 del_entity_query_pair(Pair(All, second), entityToRemove);
11924 del_entity_query_pair(Pair(first, All), entityToRemove);
11925 del_entity_query_pair(Pair(All, All), entityToRemove);
11932 void del_entity_archetype_pair(Entity entity, Archetype* pArchetypeToRemove) {
11933 GAIA_ASSERT(entity != Pair(All, All));
11934 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
11936 const auto entityKey = EntityLookupKey(entity);
11937 auto it = m_entityToArchetypeMap.find(entityKey);
11938 if (it == m_entityToArchetypeMap.end())
11941 auto& records = it->second;
11942 const auto idx = find_component_index_record(records, pArchetypeToRemove);
11943 if (idx != BadIndex) {
11944 core::swap_erase_unsafe(records, idx);
11945 update_entity_archetype_lookup_revision(entityKey);
11948 if (records.empty())
11949 m_entityToArchetypeMap.erase(it);
11954 void del_archetype_entity_pairs(Archetype* pArchetype) {
11955 GAIA_ASSERT(pArchetype !=
nullptr);
11957 for (
const auto entity: pArchetype->ids_view()) {
11958 del_entity_archetype_pair(entity, pArchetype);
11960 if (!entity.pair())
11966 GAIA_ASSERT(entity.id() < m_recs.entities.size());
11967 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
11968 del_pair_archetype_query_pairs(entity, pArchetype);
11975 void del_entity_archetype_pairs(Entity entity, Archetype* pArchetype) {
11976 GAIA_ASSERT(entity != Pair(All, All));
11978 const auto entityKey = EntityLookupKey(entity);
11979 if (m_entityToArchetypeMap.erase(entityKey) != 0)
11980 update_entity_archetype_lookup_revision(entityKey);
11982 if (entity.pair()) {
11983 if (pArchetype !=
nullptr) {
11984 del_pair_archetype_query_pairs(entity, pArchetype);
11986 del_pair_archetype_query_pairs(entity, entity);
11994 GAIA_NODISCARD Archetype* create_archetype(EntitySpan entities) {
11995 GAIA_ASSERT(m_nextArchetypeId < (
decltype(m_nextArchetypeId))-1);
11996 auto* pArchetype = Archetype::create(*
this, m_nextArchetypeId++, m_worldVersion, entities);
11997 pArchetype->set_max_lifespan(m_defaultMaxLifespan);
11999 const auto entityCnt = (uint32_t)entities.size();
12000 GAIA_FOR(entityCnt) {
12001 auto entity = entities[i];
12002 add_new_entity_archetype_pair(entity, pArchetype, (uint16_t)i);
12004#if GAIA_OBSERVERS_ENABLED
12005 auto& ec = fetch(entity);
12006 if ((ec.flags & EntityContainerFlags::IsObserved) != 0 || m_observers.has_observers(entity)) {
12007 ec.flags |= EntityContainerFlags::IsObserved;
12008 pArchetype->observed_terms_inc();
12014 if (entity.pair()) {
12015 add_new_pair_archetype_query_pairs(entity, pArchetype);
12024 void reg_archetype(Archetype* pArchetype) {
12025 GAIA_ASSERT(pArchetype !=
nullptr);
12032 GAIA_ASSERT(pArchetype->list_idx() == BadIndex);
12035 [[maybe_unused]]
const auto it0 =
12036 m_archetypesById.emplace(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()), pArchetype);
12037 [[maybe_unused]]
const auto it1 =
12038 m_archetypesByHash.emplace(ArchetypeLookupKey(pArchetype->lookup_hash(), pArchetype), pArchetype);
12040 GAIA_ASSERT(it0.second);
12041 GAIA_ASSERT(it1.second);
12043 pArchetype->list_idx(m_archetypes.size());
12044 m_archetypes.emplace_back(pArchetype);
12046 m_queryCache.register_archetype_with_queries(pArchetype);
12051 void unreg_archetype(Archetype* pArchetype) {
12052 GAIA_ASSERT(pArchetype !=
nullptr);
12056 (m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));
12059 GAIA_ASSERT(pArchetype->list_idx() != BadIndex);
12064 del_archetype_entity_pairs(pArchetype);
12068 auto& edgeLefts = pArchetype->left_edges();
12069 for (
auto& itLeft: edgeLefts)
12070 remove_edge_from_archetype(pArchetype, itLeft.second, itLeft.first.entity());
12073 auto tmpArchetype = ArchetypeLookupChecker(pArchetype->ids_view());
12074 [[maybe_unused]]
const auto res0 =
12075 m_archetypesById.erase(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()));
12076 [[maybe_unused]]
const auto res1 =
12077 m_archetypesByHash.erase(ArchetypeLookupKey(pArchetype->lookup_hash(), &tmpArchetype));
12078 GAIA_ASSERT(res0 != 0);
12079 GAIA_ASSERT(res1 != 0);
12081 const auto idx = pArchetype->list_idx();
12082 GAIA_ASSERT(idx == core::get_index(m_archetypes, pArchetype));
12083 core::swap_erase(m_archetypes, idx);
12084 update_entity_archetype_lookup_revision(EntityBadLookupKey);
12085 if (!m_archetypes.empty() && idx != m_archetypes.size())
12086 m_archetypes[idx]->list_idx(idx);
12089#if GAIA_ASSERT_ENABLED
12095 static void print_archetype_entities(
const World& world,
const Archetype& archetype, Entity entity,
bool adding) {
12096 auto ids = archetype.ids_view();
12098 GAIA_LOG_W(
"Currently present:");
12100 const auto name = entity_name(world, ids[i]);
12101 GAIA_LOG_W(
"> [%u] %.*s", i, (
int)name.size(), name.empty() ?
"" : name.data());
12104 GAIA_LOG_W(
"Trying to %s:", adding ?
"add" :
"del");
12105 const auto name = entity_name(world, entity);
12106 GAIA_LOG_W(
"> %.*s", (
int)name.size(), name.empty() ?
"" : name.data());
12114 static void verify_add(
const World& world, Archetype& archetype, Entity entity, Entity addEntity) {
12116 if (world.locked()) {
12117 GAIA_ASSERT2(
false,
"Trying to add an entity while the world is locked");
12118 GAIA_LOG_W(
"Trying to add an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
12119 print_archetype_entities(world, archetype, entity,
false);
12124 if (is_wildcard(addEntity)) {
12125 GAIA_ASSERT2(
false,
"Adding wildcard pairs is not supported");
12126 print_archetype_entities(world, archetype, addEntity,
true);
12132 if (!world.component_is_non_fragmenting(addEntity)) {
12133 auto ids = archetype.ids_view();
12134 if GAIA_UNLIKELY (ids.size() + 1 > ChunkHeader::MAX_COMPONENTS) {
12135 GAIA_ASSERT2(
false,
"Trying to add too many entities to entity!");
12137 "Trying to add an entity to entity [%u:%u] but there's no space left!", entity.id(), entity.gen());
12138 print_archetype_entities(world, archetype, addEntity,
true);
12149 static void verify_del(
const World& world, Archetype& archetype, Entity entity, Entity func_del) {
12151 if (world.locked()) {
12152 GAIA_ASSERT2(
false,
"Trying to delete an entity while the world is locked");
12153 GAIA_LOG_W(
"Trying to delete an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
12154 print_archetype_entities(world, archetype, entity,
false);
12160 !archetype.has(func_del) &&
12161 (func_del.pair() || !world.has_direct_sparse_component_inter(entity, func_del))) {
12162 GAIA_ASSERT2(
false,
"Trying to remove an entity which wasn't added");
12163 GAIA_LOG_W(
"Trying to del an entity from entity [%u:%u] but it was never added", entity.id(), entity.gen());
12164 print_archetype_entities(world, archetype, func_del,
false);
12173 static void verify_enable(
const World& world, Archetype& archetype, Entity entity) {
12174 if (world.locked()) {
12175 GAIA_ASSERT2(
false,
"Trying to enable/disable an entity while the world is locked");
12176 GAIA_LOG_W(
"Trying to enable/disable an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
12177 print_archetype_entities(world, archetype, entity,
false);
12185 static void verify_move(
const World& world, Archetype& archetype, Entity entity) {
12186 if (world.locked()) {
12187 GAIA_ASSERT2(
false,
"Trying to move an entity while the world is locked");
12188 GAIA_LOG_W(
"Trying to move an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
12189 print_archetype_entities(world, archetype, entity,
false);
12199 GAIA_NODISCARD Archetype* foc_archetype_add(Archetype* pArchetypeLeft, Entity entity) {
12201 bool edgeNeedsRebuild =
false;
12203 const auto edge = pArchetypeLeft->find_edge_right(entity);
12204 if (edge != ArchetypeIdHashPairBad) {
12205 auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
12206 if (it != m_archetypesById.end() && it->second !=
nullptr)
12210 pArchetypeLeft->del_graph_edge_right_local(entity);
12211 edgeNeedsRebuild =
true;
12216 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
12218 auto entsOld = pArchetypeLeft->ids_view();
12219 const auto entsOldCnt = entsOld.size();
12220 entsNew.resize((uint32_t)entsOld.size() + 1);
12221 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
12222 entsNew[(uint32_t)entsOld.size()] = entity;
12227 sort(entsNew, SortComponentCond{});
12230 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
12231 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
12232 if (pArchetypeRight ==
nullptr) {
12233 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
12234 pArchetypeRight->set_hashes({hashLookup});
12235 reg_archetype(pArchetypeRight);
12236 edgeNeedsRebuild =
true;
12239 if (edgeNeedsRebuild)
12240 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
12242 return pArchetypeRight;
12250 GAIA_NODISCARD Archetype* foc_archetype_add_no_graph(Archetype* pArchetypeLeft, Entity entity) {
12251 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
12253 auto entsOld = pArchetypeLeft->ids_view();
12254 const auto entsOldCnt = entsOld.size();
12255 entsNew.resize((uint32_t)entsOld.size() + 1);
12256 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
12257 entsNew[(uint32_t)entsOld.size()] = entity;
12260 sort(entsNew, SortComponentCond{});
12262 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
12263 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
12264 if (pArchetypeRight !=
nullptr)
12265 return pArchetypeRight;
12267 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
12268 pArchetypeRight->set_hashes({hashLookup});
12269 reg_archetype(pArchetypeRight);
12270 return pArchetypeRight;
12278 GAIA_NODISCARD Archetype* foc_archetype_del(Archetype* pArchetypeRight, Entity entity) {
12280 bool edgeNeedsRebuild =
false;
12282 const auto edge = pArchetypeRight->find_edge_left(entity);
12283 if (edge != ArchetypeIdHashPairBad) {
12284 const auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
12285 if (it != m_archetypesById.end()) {
12286 auto* pArchetypeLeft = it->second;
12287 if (pArchetypeLeft !=
nullptr)
12288 return pArchetypeLeft;
12292 pArchetypeRight->del_graph_edge_left_local(entity);
12293 edgeNeedsRebuild =
true;
12297 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
12298 auto entsOld = pArchetypeRight->ids_view();
12301 for (
const auto e: entsOld) {
12305 entsNew.push_back(e);
12309 GAIA_ASSERT(entsNew.size() != entsOld.size());
12312 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
12313 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
12314 if (pArchetype ==
nullptr) {
12315 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
12316 pArchetype->set_hashes({hashLookup});
12317 reg_archetype(pArchetype);
12318 edgeNeedsRebuild =
true;
12321 if (edgeNeedsRebuild)
12322 pArchetype->build_graph_edges(pArchetypeRight, entity);
12332 GAIA_NODISCARD Archetype* foc_archetype_del_no_graph(Archetype* pArchetypeRight, Entity entity) {
12333 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
12334 auto entsOld = pArchetypeRight->ids_view();
12336 for (
const auto e: entsOld) {
12340 entsNew.push_back(e);
12343 GAIA_ASSERT(entsNew.size() != entsOld.size());
12345 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
12346 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
12347 if (pArchetype !=
nullptr)
12350 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
12351 pArchetype->set_hashes({hashLookup});
12352 reg_archetype(pArchetype);
12358 GAIA_NODISCARD
const auto& archetypes()
const {
12359 return m_archetypes;
12365 GAIA_NODISCARD Archetype& archetype(Entity entity) {
12366 const auto& ec = fetch(entity);
12367 return *ec.pArchetype;
12373 void del_name(EntityContainer& ec, Entity entity) {
12374 EntityBuilder(*
this, entity, ec).
del_name();
12379 void del_name(Entity entity) {
12380 EntityBuilder(*
this, entity).
del_name();
12386 static void del_name_lookup(cnt::map<EntityNameLookupKey, Entity>& lookup, EntityNameLookupKey key) {
12387 const auto it = lookup.find(key);
12391 GAIA_ASSERT(it != lookup.end());
12392 if (it != lookup.end()) {
12394 if (it->first.owned())
12395 mem::mem_free((
void*)key.str());
12405 void del_entity_names(EntityContainer& ec, Entity entity) {
12409 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
12410 if (compIdx == BadIndex)
12413 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut_gen<
false>(compIdx, ec.row));
12414 GAIA_ASSERT(core::check_alignment(pDesc));
12415 if (pDesc->name !=
nullptr) {
12416 del_name_lookup(m_nameToEntity, EntityNameLookupKey(pDesc->name, pDesc->name_len, 0));
12417 invalidate_scope_path_cache();
12418 pDesc->name =
nullptr;
12419 pDesc->name_len = 0;
12421 if (pDesc->alias !=
nullptr) {
12422 del_name_lookup(m_aliasToEntity, EntityNameLookupKey(pDesc->alias, pDesc->alias_len, 0));
12423 pDesc->alias =
nullptr;
12424 pDesc->alias_len = 0;
12432 void del_entity(Entity entity,
bool invalidate) {
12433 if (entity.pair() || entity == EntityBad)
12436 auto& ec = fetch(entity);
12437 del_entity_inter(ec, entity, invalidate);
12445 void del_entity(EntityContainer& ec, Entity entity,
bool invalidate) {
12446 if (entity == EntityBad)
12449 del_entity_inter(ec, entity, invalidate);
12456 void del_entity_inter(EntityContainer& ec, Entity entity,
bool invalidate) {
12457 GAIA_ASSERT(entity.pair() || entity.id() > GAIA_ID(LastCoreComponent).
id());
12461 if (m_recs.entities.item_count() == 0)
12464#if GAIA_ASSERT_ENABLED
12465 auto* pChunk = ec.pChunk;
12466 GAIA_ASSERT(pChunk !=
nullptr);
12473 del_entity_names(ec, entity);
12474 remove_entity(*ec.pArchetype, *ec.pChunk, ec.row);
12475 remove_src_entity_version(entity);
12481 invalidate_entity(entity);
12487 void del_entities(Archetype& archetype) {
12490 cnt::darray<Entity> rows;
12491 collect_unrequested_rows(archetype, rows);
12495 ArchetypeRowDelScope scope(*
this);
12496 prepare_del_archetype_rows(archetype, rows);
12497 for (
const auto entity: rows)
12498 m_archetypeRowsToDel.push_back(entity);
12501 for (
auto* pChunk: archetype.chunks()) {
12502 validate_chunk(pChunk);
12505 for (
auto e: pChunk->entity_view()) {
12506 auto* pEc = try_fetch_record(e);
12507 if (pEc ==
nullptr)
12510 GAIA_ASSERT((pEc->flags & EntityContainerFlags::DeleteRequested) != 0);
12511 pEc->pArchetype =
nullptr;
12512 pEc->pChunk =
nullptr;
12513 pEc->pEntity =
nullptr;
12518 if (pChunk->queued_for_deletion())
12519 remove_chunk_from_delete_queue(pChunk->delete_queue_index());
12521 remove_chunk(archetype, *pChunk);
12524 validate_entities();
12529 void del_inter(Entity entity) {
12530 auto on_delete = [
this](Entity entityToDel) {
12531 auto& ec = fetch(entityToDel);
12532 handle_del_entity(ec, entityToDel, EntitySpan{});
12535 if (is_wildcard(entity)) {
12536 const auto rel = get(entity.id());
12537 const auto tgt = get(entity.gen());
12540 if (rel == All && tgt == All) {
12541 GAIA_ASSERT2(
false,
"Not supported yet");
12544 else if (rel == All) {
12545 if (
const auto* pTargets = relations(tgt)) {
12548 cnt::darray_ext<Entity, 64> tmp;
12549 for (
auto key: *pTargets)
12550 tmp.push_back(key.entity());
12552 on_delete(Pair(e, tgt));
12556 else if (tgt == All) {
12557 if (
const auto* pRelations = targets(rel)) {
12560 cnt::darray_ext<Entity, 64> tmp;
12561 for (
auto key: *pRelations)
12562 tmp.push_back(key.entity());
12564 on_delete(Pair(rel, e));
12573 void del_finalize_archetypes() {
12574 GAIA_PROF_SCOPE(World::del_finalize_archetypes);
12577 while (!m_reqArchetypesToDel.empty()) {
12578 const auto it = m_reqArchetypesToDel.begin();
12579 auto* pArchetype = it->archetype();
12580 m_reqArchetypesToDel.erase(it);
12581 if (pArchetype ==
nullptr)
12584 del_entities(*pArchetype);
12594 void del_finalize_entities() {
12595 GAIA_PROF_SCOPE(World::del_finalize_entities);
12597 for (
auto it = m_reqEntitiesToDel.begin(); it != m_reqEntitiesToDel.end();) {
12598 const auto e = it->entity();
12601 if (m_entityToArchetypeMap.contains(*it)) {
12606#if GAIA_USE_SAFE_ENTITY
12609 if (
const auto* pEc = try_fetch_record(e); pEc !=
nullptr && pEc->refCnt != 0) {
12616 invalidate_entity(e);
12618 it = m_reqEntitiesToDel.erase(it);
12623 void del_finalize() {
12624 GAIA_PROF_SCOPE(World::del_finalize);
12626 del_finalize_archetypes();
12627 del_finalize_entities();
12635 GAIA_NODISCARD
bool archetype_cond_match(Archetype& archetype, Pair cond, Entity target)
const {
12640 auto ids = archetype.ids_view();
12642 if (target.
pair()) {
12643 for (
auto e: ids) {
12647 if (e.gen() != target.
gen())
12650 const auto& ec = m_recs.entities[e.id()];
12651 const auto entity = ec.pChunk->entity_view()[ec.row];
12652 if (!has(entity, cond))
12658 for (
auto e: ids) {
12674 void move_to_archetype(Archetype& srcArchetype, Archetype& dstArchetype) {
12675 GAIA_ASSERT(&srcArchetype != &dstArchetype);
12677 bool updated =
false;
12679 for (
auto* pSrcChunk: srcArchetype.chunks()) {
12680 auto srcEnts = pSrcChunk->entity_view();
12681 if (srcEnts.empty())
12694 uint32_t i = (uint32_t)srcEnts.size();
12696 auto* pDstChunk = dstArchetype.foc_free_chunk();
12697 const uint32_t dstSpaceLeft = pDstChunk->capacity() - pDstChunk->size();
12698 const uint32_t cnt = core::get_min(dstSpaceLeft, i);
12699 for (uint32_t j = 0; j < cnt; ++j) {
12700 auto e = srcEnts[i - j - 1];
12701 move_entity(e, fetch(e), dstArchetype, *pDstChunk);
12704 pDstChunk->update_world_version();
12705 pDstChunk->update_entity_order_version();
12707 GAIA_ASSERT(cnt <= i);
12711 pSrcChunk->update_world_version();
12712 pSrcChunk->update_entity_order_version();
12717 update_version(m_worldVersion);
12724 GAIA_NODISCARD Archetype* calc_dst_archetype_ent(Archetype* pArchetype, Entity entity) {
12725 GAIA_ASSERT(!is_wildcard(entity));
12727 auto ids = pArchetype->ids_view();
12728 for (
auto id: ids) {
12732 return foc_archetype_del(pArchetype,
id);
12742 GAIA_NODISCARD Archetype* calc_dst_archetype_all_ent(Archetype* pArchetype, Entity entity) {
12743 GAIA_ASSERT(is_wildcard(entity));
12745 Archetype* pDstArchetype = pArchetype;
12747 auto ids = pArchetype->ids_view();
12748 for (
auto id: ids) {
12749 if (!
id.pair() ||
id.gen() != entity.gen())
12752 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
12755 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
12762 GAIA_NODISCARD Archetype* calc_dst_archetype_ent_all(Archetype* pArchetype, Entity entity) {
12763 GAIA_ASSERT(is_wildcard(entity));
12765 Archetype* pDstArchetype = pArchetype;
12767 auto ids = pArchetype->ids_view();
12768 for (
auto id: ids) {
12769 if (!
id.pair() ||
id.
id() != entity.id())
12772 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
12775 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
12782 GAIA_NODISCARD Archetype* calc_dst_archetype_all_all(Archetype* pArchetype, [[maybe_unused]] Entity entity) {
12783 GAIA_ASSERT(is_wildcard(entity));
12785 Archetype* pDstArchetype = pArchetype;
12786 bool found =
false;
12788 auto ids = pArchetype->ids_view();
12789 for (
auto id: ids) {
12793 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
12797 return found ? pDstArchetype :
nullptr;
12805 GAIA_NODISCARD Archetype* calc_dst_archetype(Archetype* pArchetype, Entity entity) {
12806 if (entity.pair()) {
12807 auto rel = entity.id();
12808 auto tgt = entity.gen();
12811 if (rel == All.
id() || tgt == All.
id()) {
12813 if (rel != All.
id() && tgt == All.
id())
12814 return calc_dst_archetype_ent_all(pArchetype, entity);
12817 if (rel == All.
id() && tgt != All.
id())
12818 return calc_dst_archetype_all_ent(pArchetype, entity);
12821 return calc_dst_archetype_all_all(pArchetype, EntityBad);
12826 return calc_dst_archetype_ent(pArchetype, entity);
12831 void req_del(Archetype& archetype) {
12832 if (archetype.is_req_del())
12835 cnt::darray<Entity> rows;
12836 collect_unrequested_rows(archetype, rows);
12837 prepare_del_archetype_rows(archetype, rows);
12839 archetype.req_del();
12840 update_version(m_archetypeDeleteVersion);
12841 m_reqArchetypesToDel.insert(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
12846 if (archetype.max_lifespan() == 0) {
12847 archetype.set_max_lifespan(1);
12848 try_enqueue_archetype_for_deletion(archetype);
12853 GAIA_ASSERT(m_archetypeRowDelDepth != 0);
12854 for (
const auto entity: rows)
12855 m_archetypeRowsToDel.push_back(entity);
12859 class ArchetypeRowDelScope {
12863 explicit ArchetypeRowDelScope(World& world): m_world(world) {
12864 ++m_world.m_archetypeRowDelDepth;
12866 ~ArchetypeRowDelScope() {
12867 if (--m_world.m_archetypeRowDelDepth == 0)
12868 m_world.req_del_archetype_rows();
12870 ArchetypeRowDelScope(
const ArchetypeRowDelScope&) =
delete;
12871 ArchetypeRowDelScope& operator=(
const ArchetypeRowDelScope&) =
delete;
12877 void collect_unrequested_rows(
const Archetype& archetype, cnt::darray<Entity>& rows) {
12878 for (
const auto* pChunk: archetype.chunks()) {
12879 for (
const auto entity: pChunk->entity_view()) {
12880 const auto* pEc = try_fetch_record(entity);
12881 if (pEc !=
nullptr && (pEc->flags & EntityContainerFlags::DeleteRequested) == 0)
12882 rows.push_back(entity);
12891 void prepare_del_archetype_rows(
const Archetype& archetype,
const cnt::darray<Entity>& rows) {
12892 if (archetype.pairs_is() != 0) {
12893 for (
const auto entity: rows)
12894 unlink_live_is_relations(entity);
12899 for (
const auto entity: rows) {
12900 if (!entity_deletion_active(entity))
12901 del_entity_registrations(entity);
12904 if (!may_notify_del_entity_components(archetype) || tearing_down())
12907 cnt::darray_ext<Entity, 32> entitiesToNotify;
12908 for (
const auto entity: rows) {
12909 if (!entity_deletion_active(entity)) {
12910 entity_deletion_enter(entity);
12911 entitiesToNotify.push_back(entity);
12917 for (
const auto entity: entitiesToNotify)
12918 notify_del_entity_components(fetch(entity), entity);
12919 for (uint32_t i = entitiesToNotify.size(); i > 0; --i)
12920 entity_deletion_leave(entitiesToNotify[i - 1]);
12927 void req_del_archetype_rows() {
12929 ++m_archetypeRowDelDepth;
12930 while (!m_archetypeRowsToDel.empty()) {
12931 auto rows = GAIA_MOV(m_archetypeRowsToDel);
12932 m_archetypeRowsToDel = {};
12934 m_reqEntitiesToDel.reserve(m_reqEntitiesToDel.size() + rows.size());
12935 for (
const auto entity: rows)
12936 req_del_archetype_row(entity);
12938 --m_archetypeRowDelDepth;
12943 void req_del_archetype_row(Entity entity) {
12944 auto* pEc = try_fetch_record(entity);
12947 if (pEc ==
nullptr || (pEc->flags & EntityContainerFlags::DeleteRequested) != 0 || pEc->pArchetype ==
nullptr ||
12948 !pEc->pArchetype->is_req_del())
12952 if ((ec.flags & (EntityContainerFlags::OnDelete_Error | EntityContainerFlags::OnDeleteTarget_Error)) != 0) {
12953 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
12955 "Trying to delete an entity [%u.%u] that is forbidden from being deleted", entity.id(), entity.gen());
12958 invalidate_weak_entities(ec);
12962#if GAIA_USE_SAFE_ENTITY
12963 release_world_ref(ec);
12971 GAIA_ASSERT(m_pArchetypeRowInRules ==
nullptr);
12972 m_pArchetypeRowInRules = &ec;
12976 del_entity_registrations(entity);
12978 if (!entity_deletion_active(entity)) {
12979 cnt::darray_ext<Entity, 64> pairEntities;
12980 if (!entity.pair())
12981 collect_entity_pairs(entity, pairEntities);
12983 entity_deletion_enter(entity);
12985 if (!entity.pair())
12986 del_entity_target_rules(ec, entity);
12987 del_entity_id_rules(ec, entity);
12988 del_pair_entities(EntitySpan{pairEntities.data(), pairEntities.size()});
12989 entity_deletion_leave(entity);
12992 m_pArchetypeRowInRules =
nullptr;
12997 if (ec.pArchetype ==
nullptr || !ec.pArchetype->is_req_del())
13000#if GAIA_OBSERVERS_ENABLED
13001 del_nonfragmenting_relation_source_observed(entity);
13003 del_nonfragmenting_relation_source(entity);
13004 del_entity_names(ec, entity);
13005 remove_src_entity_version(entity);
13006 del_sparse_components(entity);
13009 invalidate_weak_entities(ec);
13010 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
13013#if GAIA_USE_SAFE_ENTITY
13017 static void release_world_ref(EntityContainer& ec) {
13018 if ((ec.flags & EntityContainerFlags::RefDecreased) != 0)
13022 ec.flags |= EntityContainerFlags::RefDecreased;
13029 void safe_ref_add(Entity entity) {
13030 ++fetch(entity).refCnt;
13031 if (!entity.pair())
13034 ++m_recs.entities[entity.id()].refCnt;
13035 ++m_recs.entities[entity.gen()].refCnt;
13042 void safe_ref_del(Entity entity) {
13043 auto* pEc = try_fetch_record(entity);
13044 if GAIA_UNLIKELY (pEc ==
nullptr)
13047 safe_ref_del(*pEc, entity);
13048 if (!entity.pair())
13052 const EntityId endpoints[] = {(EntityId)entity.id(), (EntityId)entity.gen()};
13053 for (
const auto id: endpoints) {
13054 auto* pEndpoint = m_recs.entities.try_get(
id);
13055 GAIA_ASSERT(pEndpoint !=
nullptr);
13056 if (pEndpoint !=
nullptr)
13057 safe_ref_del(*pEndpoint, EntityContainer::handle(*pEndpoint));
13064 void safe_ref_del(EntityContainer& ec, Entity entity) {
13065 GAIA_ASSERT(ec.refCnt > 0);
13066 if (--ec.refCnt != 0)
13071 if (m_compDtorDepth != 0 || locked())
13072 m_safeEntityDels.push_back(entity);
13079 void del_released_safe_entities() {
13081 while (!m_safeEntityDels.empty()) {
13082 auto dels = GAIA_MOV(m_safeEntityDels);
13083 m_safeEntityDels = {};
13084 for (
const auto entity: dels) {
13085 const auto* pEc = try_fetch_record(entity);
13086 if (pEc !=
nullptr && pEc->refCnt == 0)
13097 template <
typename Container>
13098 void collect_entity_pairs(Entity entity, Container& pairEntities)
const {
13099 if (
const auto* pTargets = targets(entity)) {
13100 for (
auto targetKey: *pTargets)
13101 pairEntities.push_back(Pair(entity, targetKey.entity()));
13103 if (
const auto* pRelations = relations(entity)) {
13104 for (
auto relationKey: *pRelations) {
13105 const auto relation = relationKey.entity();
13106 if (relation != entity)
13107 pairEntities.push_back(Pair(relation, entity));
13115 void del_entity_target_rules(
const EntityContainer& ec, Entity entity) {
13116 const bool deleteTargets = (ec.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0 ||
13117 has_nonfragmenting_relation_target_cond(entity, Pair(OnDeleteTarget, Delete));
13118#if GAIA_OBSERVERS_ENABLED
13119 cnt::darray<Entity> cascadeTargets;
13121 collect_delete_cascade_sources(entity, Pair(OnDeleteTarget, Delete), cascadeTargets);
13122 auto cascadeDelDiffCtx = ObserverRegistry::DiffDispatchCtx{};
13123 if (!cascadeTargets.empty()) {
13124 cascadeDelDiffCtx = m_observers.prepare_diff(
13125 *
this, ObserverEvent::OnDel, EntitySpan{cascadeTargets.data(), cascadeTargets.size()},
13126 EntitySpan{cascadeTargets.data(), cascadeTargets.size()});
13130 if (deleteTargets) {
13132 req_del_entities_with(Pair(All, entity), Pair(OnDeleteTarget, Delete));
13135 rem_from_entities(Pair(All, entity));
13138#if GAIA_OBSERVERS_ENABLED
13139 m_observers.finish_diff(*
this, GAIA_MOV(cascadeDelDiffCtx));
13145 void del_entity_registrations([[maybe_unused]] Entity entity) {
13146#if GAIA_OBSERVERS_ENABLED
13147 observers().del(*
this, entity);
13149#if GAIA_SYSTEMS_ENABLED
13150 systems().del(entity);
13157 void del_entity_id_rules(
const EntityContainer& ec, Entity entity) {
13158 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
13160 req_del_entities_with(entity);
13163 rem_from_entities(entity);
13171 void req_del_inter(EntityContainer& ec, Entity entity) {
13173 if (is_req_del_or_queued(ec))
13176#if GAIA_USE_SAFE_ENTITY
13179 release_world_ref(ec);
13181 unlink_live_is_relations(entity);
13182#if GAIA_OBSERVERS_ENABLED
13183 del_nonfragmenting_relation_source_observed(entity);
13184 del_nonfragmenting_relation_source(entity);
13186 m_observers.prepare_diff(*
this, ObserverEvent::OnDel, EntitySpan{&entity, 1}, EntitySpan{&entity, 1});
13188 del_nonfragmenting_relation_source(entity);
13190 notify_del_entity_components(ec, entity);
13191 del_entity(ec, entity,
false);
13192#if GAIA_OBSERVERS_ENABLED
13193 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
13195 del_sparse_components(entity);
13200 invalidate_weak_entities(ec);
13201 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
13207 void req_del(EntityContainer& ec, Entity entity) {
13208 if (entity_deletion_active(entity))
13211 entity_deletion_enter(entity);
13212 req_del_inter(ec, entity);
13213 entity_deletion_leave(entity);
13218 void invalidate_pair_removal_caches(Entity entity) {
13219 if (!entity.pair())
13222 auto invalidate_relation = [
this](Entity relation) {
13223 if (relation == EntityBad)
13225 touch_rel_version(relation);
13226 invalidate_queries_for_rel(relation);
13229 if (entity.id() != All.
id()) {
13230 invalidate_relation(try_get(entity.id()));
13231 }
else if (entity.gen() != All.
id()) {
13232 const auto target = try_get(entity.gen());
13233 if (target != EntityBad) {
13234 if (
const auto* pRelations = relations(target)) {
13235 for (
auto relationKey: *pRelations)
13236 invalidate_relation(relationKey.entity());
13240 for (
auto it = m_pairLookup.relation_begin(); it != m_pairLookup.relation_end(); ++it)
13241 invalidate_relation(it->first.entity());
13244 clear_relation_caches();
13250 void unlink_live_is_relation(Entity source, Entity target) {
13251 const auto sourceKey = EntityLookupKey(source);
13252 const auto targetKey = EntityLookupKey(target);
13254 invalidate_queries_for_entity({Is, target});
13256 if (
const auto itTargets = m_entityToAsTargets.find(sourceKey); itTargets != m_entityToAsTargets.end()) {
13257 itTargets->second.erase(targetKey);
13258 if (itTargets->second.empty())
13259 m_entityToAsTargets.erase(itTargets);
13261 m_entityToAsTargetsTravCache = {};
13263 if (
const auto itRelations = m_entityToAsRelations.find(targetKey);
13264 itRelations != m_entityToAsRelations.end()) {
13265 itRelations->second.erase(sourceKey);
13266 if (itRelations->second.empty())
13267 m_entityToAsRelations.erase(itRelations);
13269 m_entityToAsRelationsTravCache = {};
13274 void unlink_live_is_relations(Entity source) {
13275 const auto itTargets = m_entityToAsTargets.find(EntityLookupKey(source));
13276 if (itTargets == m_entityToAsTargets.end())
13279 cnt::darray_ext<Entity, 4> targets;
13280 for (
auto targetKey: itTargets->second)
13281 targets.push_back(targetKey.entity());
13283 for (
auto target: targets)
13284 unlink_live_is_relation(source, target);
13290 void unlink_stale_is_relations_by_target_id(Entity source, EntityId targetId) {
13291 const auto sourceKey = EntityLookupKey(source);
13292 const auto itTargets = m_entityToAsTargets.find(sourceKey);
13293 if (itTargets == m_entityToAsTargets.end())
13296 cnt::darray_ext<EntityLookupKey, 4> removedTargets;
13297 for (
auto targetKey: itTargets->second) {
13298 if (targetKey.entity().id() == targetId)
13299 removedTargets.push_back(targetKey);
13302 for (
auto targetKey: removedTargets) {
13303 invalidate_queries_for_structural_entity(EntityLookupKey(Pair{Is, targetKey.
entity()}));
13304 itTargets->second.erase(targetKey);
13306 const auto itRelations = m_entityToAsRelations.find(targetKey);
13307 if (itRelations != m_entityToAsRelations.end()) {
13308 itRelations->second.erase(sourceKey);
13309 if (itRelations->second.empty())
13310 m_entityToAsRelations.erase(itRelations);
13314 if (itTargets->second.empty())
13315 m_entityToAsTargets.erase(itTargets);
13317 m_entityToAsTargetsTravCache = {};
13318 m_entityToAsRelationsTravCache = {};
13326 template <
typename Func>
13327 void each_delete_cascade_direct_source(Entity target, Pair cond, Func&& func) {
13328 GAIA_ASSERT(!target.
pair());
13330 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13331 const auto relation = relKey.entity();
13332 if (!has(relation, cond))
13335 const auto* pSources = store.sources(target);
13336 if (pSources ==
nullptr)
13339 for (
auto source: *pSources)
13343 const auto pairEntity = Pair(All, target);
13344 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(pairEntity));
13345 if (it != m_entityToArchetypeMap.end()) {
13346 for (
const auto& record: it->second) {
13347 auto* pArchetype = record.pArchetype;
13348 if (pArchetype ==
nullptr || pArchetype->is_req_del())
13350 if (!archetype_cond_match(*pArchetype, cond, pairEntity))
13353 for (
const auto* pChunk: pArchetype->chunks()) {
13354 const auto entities = pChunk->entity_view();
13355 GAIA_EACH(entities)
13366 GAIA_NODISCARD
bool has_delete_cascade_direct_sources(Entity target, Pair cond)
const {
13367 GAIA_ASSERT(!target.
pair());
13369 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13370 if (store.sources(target) !=
nullptr && has(relKey.entity(), cond))
13374 const auto pairEntity = Pair(All, target);
13375 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(pairEntity));
13376 if (it == m_entityToArchetypeMap.end())
13379 for (
const auto& record: it->second) {
13380 auto* pArchetype = record.pArchetype;
13381 if (pArchetype ==
nullptr || pArchetype->is_req_del())
13383 if (!archetype_cond_match(*pArchetype, cond, pairEntity))
13386 for (
const auto* pChunk: pArchetype->chunks()) {
13387 if (!pChunk->empty())
13399 void collect_delete_cascade_direct_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
13400 GAIA_ASSERT(!target.
pair());
13401 const auto visitStamp = next_entity_visit_stamp();
13402 each_delete_cascade_direct_source(target, cond, [&](Entity source) {
13403 if (!valid(source))
13405 if (!try_mark_entity_visited(source, visitStamp))
13407 out.push_back(source);
13415 void collect_delete_cascade_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
13416 GAIA_ASSERT(!target.
pair());
13417 const auto visitStamp = next_entity_visit_stamp();
13418 cnt::darray_ext<Entity, 32> targetsToVisit;
13419 (void)try_mark_entity_visited(target, visitStamp);
13420 targetsToVisit.push_back(target);
13422 for (uint32_t i = 0; i < targetsToVisit.size(); ++i) {
13423 const auto currTarget = targetsToVisit[i];
13424 each_delete_cascade_direct_source(currTarget, cond, [&](Entity source) {
13425 if (!valid(source))
13427 if (!try_mark_entity_visited(source, visitStamp))
13430 out.push_back(source);
13431 targetsToVisit.push_back(source);
13438 void req_del_entities_with(Entity entity) {
13439 GAIA_PROF_SCOPE(World::req_del_entities_with);
13440 ArchetypeRowDelScope scope(*
this);
13442 GAIA_ASSERT(entity != Pair(All, All));
13444 auto req_del_nonfragmenting_pair = [&](Entity relation, Entity target) {
13445 cnt::darray<Entity> sourcesToDel;
13446 sources(relation, target, [&](Entity source) {
13447 sourcesToDel.push_back(source);
13450 for (
auto source: sourcesToDel)
13451 del_cascade_entity(source);
13454 if (entity.pair()) {
13455 if (entity.id() != All.
id()) {
13456 const auto relation = try_get(entity.id());
13457 const auto target = try_get(entity.gen());
13458 if (relation != EntityBad && target != EntityBad && relation_uses_non_fragmenting_storage(relation))
13459 req_del_nonfragmenting_pair(relation, target);
13461 const auto target = try_get(entity.gen());
13462 if (target == EntityBad)
13463 goto skip_req_del_all_target;
13464 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13465 if (store.sources(target) ==
nullptr)
13468 req_del_nonfragmenting_pair(relKey.entity(), target);
13470 skip_req_del_all_target:;
13472 }
else if (relation_uses_non_fragmenting_storage(entity)) {
13473 if (
const auto* pTargets = targets(entity)) {
13474 for (
auto targetKey: *pTargets)
13475 req_del_nonfragmenting_pair(entity, targetKey.entity());
13479 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
13480 if (it == m_entityToArchetypeMap.end())
13483 for (
const auto& record: it->second)
13484 req_del(*record.pArchetype);
13491 void req_del_entities_with(Entity entity, Pair cond) {
13492 cnt::set<EntityLookupKey> visited;
13493 req_del_entities_with(entity, cond, visited);
13500 void req_del_entities_with(Entity entity, Pair cond, cnt::set<EntityLookupKey>& visited) {
13501 GAIA_PROF_SCOPE(World::req_del_entities_with);
13502 ArchetypeRowDelScope scope(*
this);
13504 GAIA_ASSERT(entity != Pair(All, All));
13505 if (!visited.insert(EntityLookupKey(entity)).second)
13508 cnt::darray<Entity> cascadeTargets;
13509 if (entity.pair() && entity.id() == All.
id()) {
13510 const auto target = try_get(entity.gen());
13511 if (target != EntityBad)
13512 collect_delete_cascade_direct_sources(target, cond, cascadeTargets);
13515 auto req_del_nonfragmenting_pair = [&](Entity relation, Entity target) {
13516 if (!has(relation, cond))
13519 cnt::darray<Entity> sourcesToDel;
13520 sources(relation, target, [&](Entity source) {
13521 sourcesToDel.push_back(source);
13524 for (
auto source: sourcesToDel)
13525 del_cascade_entity(source);
13528 for (
auto source: cascadeTargets) {
13529 if (has_delete_cascade_direct_sources(source, cond))
13530 req_del_entities_with(Pair(All, source), cond, visited);
13533 if (entity.pair()) {
13534 if (entity.id() != All.
id()) {
13535 const auto relation = try_get(entity.id());
13536 const auto target = try_get(entity.gen());
13537 if (relation != EntityBad && target != EntityBad && relation_uses_non_fragmenting_storage(relation))
13538 req_del_nonfragmenting_pair(relation, target);
13540 const auto target = try_get(entity.gen());
13541 if (target == EntityBad)
13542 goto skip_req_del_all_target_cond;
13543 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13544 if (store.sources(target) ==
nullptr)
13547 req_del_nonfragmenting_pair(relKey.entity(), target);
13549 skip_req_del_all_target_cond:;
13551 }
else if (relation_uses_non_fragmenting_storage(entity)) {
13552 if (
const auto* pTargets = targets(entity)) {
13553 for (
auto targetKey: *pTargets)
13554 req_del_nonfragmenting_pair(entity, targetKey.entity());
13558 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
13559 if (it == m_entityToArchetypeMap.end())
13562 for (
const auto& record: it->second) {
13563 auto* pArchetype = record.pArchetype;
13565 if (!archetype_cond_match(*pArchetype, cond, entity))
13568 req_del(*pArchetype);
13574 GAIA_NODISCARD
static bool pair_matches_removal_selector(Entity pair, Entity selector) {
13575 GAIA_ASSERT(pair.pair());
13576 GAIA_ASSERT(selector.pair());
13578 if (selector.id() != All.
id() && pair.id() != selector.id())
13580 return selector.gen() == All.
id() || pair.gen() == selector.gen();
13584 void del_sparse_pair_payloads(Archetype& archetype, Entity selector) {
13585 GAIA_ASSERT(selector.pair());
13587 for (
const auto pair: archetype.ids_view()) {
13588 if (!pair.pair() || !pair_matches_removal_selector(pair, selector))
13591 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(pair));
13592 if (it == m_sparseComponentsByComp.end())
13595 for (
const auto* pChunk: archetype.chunks()) {
13596 const auto entities = pChunk->entity_view();
13597 GAIA_EACH(entities)
13598 it->second.func_del(it->second.pStore, entities[i]);
13609 GAIA_NODISCARD
static bool archetype_has_rows(
const Archetype& archetype) {
13610 for (
const auto* pChunk: archetype.chunks()) {
13611 if (pChunk->size() != 0)
13619 void rem_from_entities(Entity entity) {
13620 GAIA_PROF_SCOPE(World::rem_from_entities);
13622 invalidate_pair_removal_caches(entity);
13624 auto rem_nonfragmenting_pair = [&](Entity relation, Entity target) {
13625 cnt::darray<Entity> sourcesToRem;
13626 sources(relation, target, [&](Entity source) {
13627 sourcesToRem.push_back(source);
13630 for (
auto source: sourcesToRem)
13631 del(source, Pair(relation, target));
13634 if (entity.pair()) {
13635 if (entity.id() != All.
id()) {
13636 const auto relation = try_get(entity.id());
13637 const auto target = try_get(entity.gen());
13638 if (relation != EntityBad && target != EntityBad && relation_uses_non_fragmenting_storage(relation))
13639 rem_nonfragmenting_pair(relation, target);
13641 const auto target = try_get(entity.gen());
13642 if (target == EntityBad)
13643 goto skip_rem_all_target;
13644 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13645 if (store.sources(target) ==
nullptr)
13648 rem_nonfragmenting_pair(relKey.entity(), target);
13650 skip_rem_all_target:;
13652 }
else if (relation_uses_non_fragmenting_storage(entity)) {
13653 if (
const auto* pTargets = targets(entity)) {
13654 for (
auto targetKey: *pTargets)
13655 rem_nonfragmenting_pair(entity, targetKey.entity());
13659 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
13660 if (it == m_entityToArchetypeMap.end())
13665 const auto srcRecords = it->second;
13668 if (!entity.pair()) {
13669 auto& ec = fetch(entity);
13670 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
13671 auto ids = ec.pArchetype->ids_view();
13672 const auto idx = core::get_index(ids, entity);
13673 if (idx != BadIndex)
13674 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
13678#if GAIA_OBSERVERS_ENABLED
13679 cnt::set<EntityLookupKey> diffTermSet;
13680 cnt::darray<Entity> diffTerms;
13681 cnt::darray<Entity> diffTargets;
13682 for (
const auto& record: srcRecords) {
13683 auto* pArchetype = record.pArchetype;
13684 if (pArchetype->is_req_del() || !archetype_has_rows(*pArchetype))
13687 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
13688 if (pDstArchetype ==
nullptr)
13691 for (
auto id: pArchetype->ids_view()) {
13692 bool matches =
false;
13693 if (entity.pair()) {
13694 if (entity.id() == All.
id() && entity.gen() == All.
id())
13695 matches =
id.pair();
13696 else if (entity.id() == All.
id())
13697 matches =
id.pair() &&
id.gen() == entity.gen();
13698 else if (entity.gen() == All.
id())
13699 matches =
id.pair() &&
id.id() == entity.id();
13701 matches =
id == entity;
13703 matches =
id == entity;
13708 if (diffTermSet.insert(EntityLookupKey(
id)).second)
13709 diffTerms.push_back(
id);
13712 for (
const auto* pChunk: pArchetype->chunks()) {
13713 const auto entities = pChunk->entity_view();
13714 GAIA_EACH(entities)
13715 diffTargets.push_back(entities[i]);
13718 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
13719 ? ObserverRegistry::DiffDispatchCtx{}
13720 : m_observers.prepare_diff(
13721 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
13722 EntitySpan{diffTargets.data(), diffTargets.size()});
13726 for (
const auto& record: srcRecords) {
13727 auto* pArchetype = record.pArchetype;
13728 if (pArchetype->is_req_del() || !archetype_has_rows(*pArchetype))
13731 if (entity.pair()) {
13732 cnt::darray_ext<Entity, 16> removedIsTargets;
13733 for (
auto id: pArchetype->ids_view()) {
13734 bool matches =
false;
13735 if (entity.id() == All.
id() && entity.gen() == All.
id())
13736 matches =
id.pair();
13737 else if (entity.id() == All.
id())
13738 matches =
id.pair() &&
id.gen() == entity.gen();
13739 else if (entity.gen() == All.
id())
13740 matches =
id.pair() &&
id.id() == entity.id();
13742 matches =
id == entity;
13744 if (!matches || !
id.pair() ||
id.
id() != Is.
id())
13747 const auto target = try_get(
id.gen());
13748 if (target != EntityBad)
13749 removedIsTargets.push_back(target);
13752 if (!removedIsTargets.empty()) {
13753 for (
const auto* pChunk: pArchetype->chunks()) {
13754 auto entities = pChunk->entity_view();
13755 GAIA_EACH(entities) {
13756 for (
const auto target: removedIsTargets)
13757 unlink_live_is_relation(entities[i], target);
13763 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
13764 if (pDstArchetype !=
nullptr) {
13766 del_sparse_pair_payloads(*pArchetype, entity);
13767 move_to_archetype(*pArchetype, *pDstArchetype);
13771#if GAIA_OBSERVERS_ENABLED
13772 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
13774 expire_emptied_archetypes(srcRecords);
13781 void expire_emptied_archetypes(
const ComponentIndexEntryArray& records) {
13782 for (
const auto& record: records) {
13783 auto* pArchetype = record.pArchetype;
13784 if (pArchetype->is_req_del() || archetype_has_rows(*pArchetype))
13787 pArchetype->set_max_lifespan(1);
13788 try_enqueue_archetype_for_deletion(*pArchetype);
13796 void rem_from_entities(Entity entity, Pair cond) {
13797 GAIA_PROF_SCOPE(World::rem_from_entities);
13799 invalidate_pair_removal_caches(entity);
13801 auto rem_nonfragmenting_pair = [&](Entity relation, Entity target) {
13802 if (!has(relation, cond))
13805 cnt::darray<Entity> sourcesToRem;
13806 sources(relation, target, [&](Entity source) {
13807 sourcesToRem.push_back(source);
13810 for (
auto source: sourcesToRem)
13811 del(source, Pair(relation, target));
13814 if (entity.pair()) {
13815 if (entity.id() != All.
id()) {
13816 const auto relation = try_get(entity.id());
13817 const auto target = try_get(entity.gen());
13818 if (relation != EntityBad && target != EntityBad && relation_uses_non_fragmenting_storage(relation))
13819 rem_nonfragmenting_pair(relation, target);
13821 const auto target = try_get(entity.gen());
13822 if (target == EntityBad)
13823 goto skip_rem_all_target_cond;
13824 for (
const auto& [relKey, store]: m_nonFragmentingRelationsByRel) {
13825 if (store.sources(target) ==
nullptr)
13828 rem_nonfragmenting_pair(relKey.entity(), target);
13830 skip_rem_all_target_cond:;
13832 }
else if (relation_uses_non_fragmenting_storage(entity)) {
13833 if (
const auto* pTargets = targets(entity)) {
13834 for (
auto targetKey: *pTargets)
13835 rem_nonfragmenting_pair(entity, targetKey.entity());
13839 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
13840 if (it == m_entityToArchetypeMap.end())
13845 const auto srcRecords = it->second;
13848 if (!entity.pair()) {
13849 auto& ec = fetch(entity);
13850 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
13851 auto ids = ec.pArchetype->ids_view();
13852 const auto idx = core::get_index(ids, entity);
13853 if (idx != BadIndex)
13854 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
13858#if GAIA_OBSERVERS_ENABLED
13859 cnt::set<EntityLookupKey> diffTermSet;
13860 cnt::darray<Entity> diffTerms;
13861 cnt::darray<Entity> diffTargets;
13862 for (
const auto& record: srcRecords) {
13863 auto* pArchetype = record.pArchetype;
13864 if (pArchetype->is_req_del() || !archetype_has_rows(*pArchetype))
13867 if (!archetype_cond_match(*pArchetype, cond, entity))
13870 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
13871 if (pDstArchetype ==
nullptr)
13874 for (
auto id: pArchetype->ids_view()) {
13875 bool matches =
false;
13876 if (entity.pair()) {
13877 if (entity.id() == All.
id() && entity.gen() == All.
id())
13878 matches =
id.pair();
13879 else if (entity.id() == All.
id())
13880 matches =
id.pair() &&
id.gen() == entity.gen();
13881 else if (entity.gen() == All.
id())
13882 matches =
id.pair() &&
id.id() == entity.id();
13884 matches =
id == entity;
13886 matches =
id == entity;
13891 if (diffTermSet.insert(EntityLookupKey(
id)).second)
13892 diffTerms.push_back(
id);
13895 for (
const auto* pChunk: pArchetype->chunks()) {
13896 const auto entities = pChunk->entity_view();
13897 GAIA_EACH(entities)
13898 diffTargets.push_back(entities[i]);
13901 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
13902 ? ObserverRegistry::DiffDispatchCtx{}
13903 : m_observers.prepare_diff(
13904 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
13905 EntitySpan{diffTargets.data(), diffTargets.size()});
13908 for (
const auto& record: srcRecords) {
13909 auto* pArchetype = record.pArchetype;
13910 if (pArchetype->is_req_del() || !archetype_has_rows(*pArchetype))
13914 if (!archetype_cond_match(*pArchetype, cond, entity))
13917 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
13918 if (pDstArchetype !=
nullptr) {
13920 del_sparse_pair_payloads(*pArchetype, entity);
13921 move_to_archetype(*pArchetype, *pDstArchetype);
13925#if GAIA_OBSERVERS_ENABLED
13926 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
13928 expire_emptied_archetypes(srcRecords);
13934 void del_cascade_entity(Entity entity) {
13935 if (!valid(entity))
13938 cnt::darray_ext<Entity, 64> pairEntities;
13939 collect_entity_pairs(entity, pairEntities);
13940 handle_del_entity(fetch(entity), entity, EntitySpan{pairEntities.data(), pairEntities.size()},
true);
13947 void del_pair_entities(EntitySpan pairEntities) {
13948 for (
auto pair: pairEntities) {
13950 handle_del_entity(fetch(pair), pair, EntitySpan{},
true);
13970 void handle_del_entity(
13971 EntityContainer& ec, Entity entity, EntitySpan pairEntities, [[maybe_unused]]
bool cascade =
false) {
13972 GAIA_PROF_SCOPE(World::handle_del_entity);
13973 ArchetypeRowDelScope scope(*
this);
13975 GAIA_ASSERT(!is_wildcard(entity));
13976 if (entity_deletion_active(entity))
13979 if (entity.pair()) {
13980 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
13981 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
13983 "Trying to delete a pair [%u.%u] %s that is forbidden from being deleted", entity.id(), entity.gen(),
13988 const auto tgt = try_get(entity.gen());
13989 const bool hasLiveTarget = tgt != EntityBad;
13990 if (hasLiveTarget) {
13991 const auto& ecTgt = fetch(tgt);
13992 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
13993 has_nonfragmenting_relation_target_cond(tgt, Pair(OnDeleteTarget, Error))) {
13995 false,
"Trying to delete an entity that is forbidden from being deleted (target restriction)");
13997 "Trying to delete a pair [%u.%u] %s that is forbidden from being deleted (target restriction)",
13998 entity.id(), entity.gen(), name(entity));
14003#if GAIA_USE_SAFE_ENTITY
14005 release_world_ref(ec);
14009 if (ec.refCnt != 0 && !cascade)
14013 entity_deletion_enter(entity);
14019 if (is_req_del_or_queued(ec)) {
14020 entity_deletion_leave(entity);
14024 del_entity_registrations(entity);
14025 del_entity_id_rules(ec, entity);
14027 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
14028 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
14030 "Trying to delete an entity [%u.%u] %s that is forbidden from being deleted", entity.id(), entity.gen(),
14035 if ((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
14036 has_nonfragmenting_relation_target_cond(entity, Pair(OnDeleteTarget, Error))) {
14037 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted (a pair's target)");
14039 "Trying to delete an entity [%u.%u] %s that is forbidden from being deleted (a pair's target)",
14040 entity.id(), entity.gen(), name(entity));
14044#if GAIA_USE_SAFE_ENTITY
14046 release_world_ref(ec);
14049 if (ec.refCnt != 0 && !cascade)
14053 entity_deletion_enter(entity);
14055 del_entity_target_rules(ec, entity);
14058 if (is_req_del_or_queued(ec)) {
14059 del_pair_entities(pairEntities);
14060 entity_deletion_leave(entity);
14064 del_entity_registrations(entity);
14065 del_entity_id_rules(ec, entity);
14068 del_pair_entities(pairEntities);
14071 req_del_inter(ec, entity);
14073 entity_deletion_leave(entity);
14080 static void invalidate_weak_entities([[maybe_unused]] EntityContainer& ec) {
14081#if GAIA_USE_WEAK_ENTITY
14082 while (ec.pWeakTracker !=
nullptr) {
14083 auto* pTracker = ec.pWeakTracker;
14084 ec.pWeakTracker = pTracker->next;
14085 if (ec.pWeakTracker !=
nullptr)
14086 ec.pWeakTracker->prev =
nullptr;
14088 auto* pWeakEntity = pTracker->pWeakEntity;
14089 GAIA_ASSERT(pWeakEntity !=
nullptr);
14090 GAIA_ASSERT(pWeakEntity->m_pTracker == pTracker);
14091 pWeakEntity->m_pTracker =
nullptr;
14092 pWeakEntity->m_entity = EntityBad;
14102 void remove_edge_from_archetype(Archetype* pArchetype, ArchetypeGraphEdge edgeLeft, Entity edgeEntity) {
14103 GAIA_ASSERT(pArchetype !=
nullptr);
14105 const auto edgeLeftIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
14106 if (edgeLeftIt == m_archetypesById.end())
14109 auto* pArchetypeLeft = edgeLeftIt->second;
14110 GAIA_ASSERT(pArchetypeLeft !=
nullptr);
14113 pArchetypeLeft->del_graph_edges(pArchetype, edgeEntity);
14116 auto& archetypesRight = pArchetype->right_edges();
14117 for (
auto& it: archetypesRight) {
14118 const auto& edgeRight = it.second;
14119 const auto edgeRightIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
14120 if (edgeRightIt == m_archetypesById.end())
14123 auto* pArchetypeRight = edgeRightIt->second;
14126 pArchetype->del_graph_edges(pArchetypeRight, it.first.entity());
14132 void remove_edges(Entity entityToRemove) {
14133 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entityToRemove));
14134 if (it == m_entityToArchetypeMap.end())
14137 for (
const auto& record: it->second) {
14138 auto* pArchetype = record.pArchetype;
14139 remove_edge_from_archetype(pArchetype, pArchetype->find_edge_left(entityToRemove), entityToRemove);
14145 void remove_edges_from_pairs(Entity entity) {
14151 const auto* tgts = targets(entity);
14152 if (tgts !=
nullptr) {
14153 for (
auto target: *tgts)
14154 remove_edges(Pair(entity, target.entity()));
14157 const auto* rels = relations(entity);
14158 if (rels !=
nullptr) {
14159 for (
auto relation: *rels)
14160 remove_edges(Pair(relation.entity(), entity));
14166 void del_graph_edges(Entity entity) {
14167 remove_edges(entity);
14168 remove_edges_from_pairs(entity);
14173 void touch_rel_version(Entity relation) {
14174 if (m_pLastRelationVersion !=
nullptr && m_lastRelationVersionRelation == relation) {
14175 ++*m_pLastRelationVersion;
14176 if (*m_pLastRelationVersion == 0)
14177 *m_pLastRelationVersion = 1;
14181 const EntityLookupKey key(relation);
14182 const auto ret = m_relationVersions.try_emplace(key, 1);
14183 auto it = ret.first;
14186 if (it->second == 0)
14190 m_lastRelationVersionRelation = relation;
14191 m_pLastRelationVersion = &it->second;
14195 void clear_relation_caches() {
14196 if (!m_relationCachesPopulated)
14198 m_relationCachesPopulated =
false;
14200 m_targetsTravCache = {};
14201 m_srcBfsTravCache = {};
14202 m_depthOrderCache = {};
14203 m_sourcesAllCache = {};
14204 m_targetsAllCache = {};
14209 void invalidate_relation_caches(Entity relation) {
14210 touch_rel_version(relation);
14211 invalidate_queries_for_rel(relation);
14212 clear_relation_caches();
14221 bool del_pair_record(Entity entity, Archetype*& pArchetype, Entity& rel, Entity& tgt) {
14222 GAIA_ASSERT(entity.pair());
14224 EntityContainer ec{};
14225 if (!m_recs.pair_record_remove(entity, ec))
14228 pArchetype = ec.pArchetype;
14232 GAIA_ASSERT(entity.id() < m_recs.entities.size());
14233 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
14234 rel = m_recs.entities.handle(entity.id());
14235 tgt = m_recs.entities.handle(entity.gen());
14242 void del_pair_lookup(Entity rel, Entity tgt) {
14243 m_pairLookup.del_pair(rel, tgt);
14248 void del_entity_pair_lookup(Entity entity) {
14249 m_pairLookup.del_entity_pairs(entity);
14254 void del_pair_data_for_entity(Entity entity) {
14255 Archetype* pArchetype =
nullptr;
14257 del_sparse_components(entity);
14258 del_nonfragmenting_relation_source(entity);
14260 if (entity.pair()) {
14262 del_sparse_component_store(entity);
14264 if (
auto* pEc = m_recs.pair_record_find(entity))
14265 invalidate_weak_entities(*pEc);
14268 if (del_pair_record(entity, pArchetype, rel, tgt))
14269 del_pair_lookup(rel, tgt);
14271 auto& rec = m_recs.entities[entity.id()];
14273 invalidate_weak_entities(rec);
14277 m_recs.entities.free(entity);
14280 del_nonfragmenting_relation(entity);
14282 del_sparse_component_store(entity);
14286 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0 && ec.pArchetype !=
nullptr) {
14287 ArchetypeRowDelScope scope(*
this);
14288 req_del(*ec.pArchetype);
14291 ec.pArchetype =
nullptr;
14292 ec.pChunk =
nullptr;
14293 ec.pEntity =
nullptr;
14294 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::DeleteRequested,
false);
14297 del_entity_pair_lookup(entity);
14300 del_entity_archetype_pairs(entity, pArchetype);
14305 void invalidate_entity(Entity entity) {
14306 del_graph_edges(entity);
14307 del_pair_data_for_entity(entity);
14315 void store_entity(EntityContainer& ec, Entity entity, Archetype* pArchetype, Chunk* pChunk) {
14316 GAIA_ASSERT(pArchetype !=
nullptr);
14317 GAIA_ASSERT(pChunk !=
nullptr);
14319 !locked() &&
"Entities can't be stored while the world is locked "
14320 "(structural changes are forbidden during this time!)");
14322 ec.pArchetype = pArchetype;
14323 ec.pChunk = pChunk;
14324 ec.row = pChunk->add_entity(entity);
14325 ec.pEntity = &pChunk->entity_view()[ec.row];
14326 GAIA_ASSERT(entity.pair() || ec.data.gen == entity.gen());
14335 void move_entity(Entity entity, EntityContainer& ec, Archetype& dstArchetype, Chunk& dstChunk) {
14336 GAIA_PROF_SCOPE(World::move_entity);
14338 auto* pDstChunk = &dstChunk;
14339 auto* pSrcChunk = ec.pChunk;
14341 GAIA_ASSERT(pDstChunk != pSrcChunk);
14343 const auto srcRow0 = ec.row;
14344 const auto dstRow = pDstChunk->add_entity(entity);
14345 const bool wasEnabled = !ec.data.dis;
14347 auto& srcArchetype = *ec.pArchetype;
14348 const bool archetypeChanged = srcArchetype.id() != dstArchetype.id();
14349#if GAIA_ASSERT_ENABLED
14350 verify_move(*
this, srcArchetype, entity);
14354 srcArchetype.enable_entity(pSrcChunk, srcRow0,
true, m_recs);
14356 const auto srcRow = ec.row;
14359 if (dstArchetype.id() == srcArchetype.id()) {
14360 pDstChunk->move_entity_data(entity, dstRow, m_recs);
14362 pDstChunk->move_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, dstRow);
14366 remove_entity(srcArchetype, *pSrcChunk, srcRow);
14369 dstArchetype.try_update_free_chunk_idx();
14372 ec.pArchetype = &dstArchetype;
14373 ec.pChunk = pDstChunk;
14374 ec.row = (uint16_t)dstRow;
14375 ec.pEntity = &pDstChunk->entity_view()[dstRow];
14376 if (archetypeChanged)
14377 update_src_entity_version(entity);
14380 dstArchetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
14383 GAIA_ASSERT(valid(entity));
14384 validate_chunk(pSrcChunk);
14385 validate_chunk(pDstChunk);
14386 validate_entities();
14393 void move_entity_raw(Entity entity, EntityContainer& ec, Archetype& dstArchetype) {
14395 ec.pChunk->update_world_version();
14396 ec.pChunk->update_entity_order_version();
14398 auto* pDstChunk = dstArchetype.foc_free_chunk();
14399 move_entity(entity, ec, dstArchetype, *pDstChunk);
14402 pDstChunk->update_world_version();
14403 pDstChunk->update_entity_order_version();
14404 update_version(m_worldVersion);
14411 Chunk* move_entity(Entity entity, Archetype& dstArchetype) {
14413 auto& ec = fetch(entity);
14414 if (ec.pArchetype == &dstArchetype)
14418 ec.pChunk->update_world_version();
14419 ec.pChunk->update_entity_order_version();
14421 auto* pDstChunk = dstArchetype.foc_free_chunk();
14422 move_entity(entity, ec, dstArchetype, *pDstChunk);
14425 pDstChunk->update_world_version();
14426 pDstChunk->update_entity_order_version();
14427 update_version(m_worldVersion);
14434 void validate_archetype_edges([[maybe_unused]]
const Archetype* pArchetype)
const {
14435#if GAIA_ECS_VALIDATE_ARCHETYPE_GRAPH && GAIA_ASSERT_ENABLED
14436 GAIA_ASSERT(pArchetype !=
nullptr);
14439 const auto& archetypesLeft = pArchetype->left_edges();
14440 for (
const auto& it: archetypesLeft) {
14441 const auto& edge = it.second;
14442 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
14443 if (edgeIt == m_archetypesById.end())
14446 const auto entity = it.first.entity();
14447 const auto* pArchetypeRight = edgeIt->second;
14450 const auto edgeRight = pArchetypeRight->find_edge_right(entity);
14451 GAIA_ASSERT(edgeRight != ArchetypeIdHashPairBad);
14454 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
14455 GAIA_ASSERT(it2 != m_archetypesById.end());
14456 const auto* pArchetype2 = it2->second;
14457 GAIA_ASSERT(pArchetype2 == pArchetype);
14461 const auto& archetypesRight = pArchetype->right_edges();
14462 for (
const auto& it: archetypesRight) {
14463 const auto& edge = it.second;
14464 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
14465 if (edgeIt == m_archetypesById.end())
14468 const auto entity = it.first.entity();
14469 const auto* pArchetypeRight = edgeIt->second;
14472 const auto edgeLeft = pArchetypeRight->find_edge_left(entity);
14473 GAIA_ASSERT(edgeLeft != ArchetypeIdHashPairBad);
14476 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
14477 GAIA_ASSERT(it2 != m_archetypesById.end());
14478 const auto* pArchetype2 = it2->second;
14479 GAIA_ASSERT(pArchetype2 == pArchetype);
14485 void validate_entities()
const {
14486#if GAIA_ECS_VALIDATE_ENTITY_LIST
14487 m_recs.entities.validate();
14493 void validate_chunk([[maybe_unused]] Chunk* pChunk)
const {
14494#if GAIA_ECS_VALIDATE_CHUNKS && GAIA_ASSERT_ENABLED
14495 GAIA_ASSERT(pChunk !=
nullptr);
14497 const auto entities = pChunk->entity_view();
14498 for (uint16_t row = 0; row < entities.size(); ++row) {
14499 const auto entity = entities[row];
14500 const EntityContainer* pEc =
nullptr;
14501 if (entity.pair()) {
14502 pEc = m_recs.pair_record_find(entity);
14503 }
else if (entity.id() < m_recs.entities.size()) {
14504 pEc = &m_recs.entities[entity.id()];
14507 if (pEc ==
nullptr) {
14508 GAIA_ASSERT(!valid(entity));
14511 if (!valid(entity)) {
14512 GAIA_ASSERT(is_req_del_or_queued(*pEc));
14515 GAIA_ASSERT(pEc->pChunk == pChunk);
14516 GAIA_ASSERT(pEc->row == row);
14517 GAIA_ASSERT(pEc->pEntity == &entities[row]);
14520 for (
const auto& ec: m_recs.entities) {
14521 if (ec.pChunk != pChunk)
14523 const Entity entity(ec.idx, ec.data.gen, ec.data.ent != 0,
false);
14524 if (!valid(entity)) {
14525 GAIA_ASSERT(is_req_del_or_queued(ec));
14528 GAIA_ASSERT(ec.row < entities.size());
14529 GAIA_ASSERT(entities[ec.row] == entity);
14530 GAIA_ASSERT(ec.pEntity == &entities[ec.row]);
14533 for (
auto it = m_recs.pair_record_begin(); it != m_recs.pair_record_end(); ++it) {
14534 const auto& pair = *it;
14535 const auto& ec = pair.second;
14536 if (ec.pChunk != pChunk)
14538 const auto entity = pair.first.entity();
14539 if (!valid(entity)) {
14540 GAIA_ASSERT(is_req_del_or_queued(ec));
14543 GAIA_ASSERT(ec.row < entities.size());
14544 GAIA_ASSERT(entities[ec.row] == entity);
14545 GAIA_ASSERT(ec.pEntity == &entities[ec.row]);
14556 template <
bool CheckIn>
14557 GAIA_NODISCARD
bool is_inter(Entity entity, Entity entityBase)
const {
14558 if GAIA_UNLIKELY (entity.pair() || entityBase.pair())
14561 GAIA_ASSERT(valid_entity(entity));
14562 GAIA_ASSERT(valid_entity(entityBase));
14564 if constexpr (!CheckIn) {
14565 if (entity == entityBase)
14569 const auto& targets = as_targets_trav_cache(entity);
14570 for (
auto target: targets) {
14571 if (target == entityBase)
14583 template <
bool CheckIn,
typename Func>
14584 void as_up_trav(Entity entity, Func func) {
14585 if GAIA_UNLIKELY (entity.pair())
14590 GAIA_ASSERT(try_entity_record(entity) !=
nullptr);
14592 if constexpr (!CheckIn) {
14596 const auto& ec = m_recs.entities[entity.id()];
14597 const auto* pArchetype = ec.pArchetype;
14600 if (pArchetype->pairs_is() == 0)
14603 for (uint32_t i = 0; i < pArchetype->pairs_is(); ++i) {
14604 auto e = pArchetype->entity_from_pairs_as_idx(i);
14605 const auto& ecTarget = m_recs.entities[e.gen()];
14606 auto target = *ecTarget.pEntity;
14609 as_up_trav<CheckIn>(target, func);
14618 template <
typename T>
14619 const ComponentCacheItem& reg_core_entity(Entity
id, Archetype* pArchetype) {
14620 auto comp = add(*pArchetype,
id.entity(),
id.pair());
14621 const auto& ci = comp_cache_mut().add<T>(id);
14622 GAIA_ASSERT(ci.entity ==
id);
14623 GAIA_ASSERT(comp ==
id);
14632 template <
typename T>
14633 const ComponentCacheItem& reg_core_entity(Entity
id) {
14634 return reg_core_entity<T>(
id, m_pRootArchetype);
14642 static ComponentDesc primitive_type_desc(
const char* name, uint32_t nameLen, uint32_t size) {
14643 ComponentDesc desc{};
14644 desc.name = util::str_view(name, nameLen);
14647 desc.storageType = DataStorageType::Table;
14648 desc.runtimeType.typeKind = RuntimeTypeKind::Primitive;
14658 const ComponentCacheItem& reg_core_primitive_type(Entity
id,
const char* name, uint32_t nameLen, uint32_t size) {
14659 auto comp = add(*m_pCompArchetype,
id.entity(),
id.pair());
14660 const auto desc = primitive_type_desc(name, nameLen, size);
14661 const auto& ci = comp_cache_mut().add(
id, desc);
14662 GAIA_ASSERT(ci.entity ==
id);
14663 GAIA_ASSERT(comp ==
id);
14665 finalize_component_registration(ci,
false);
14676#if GAIA_ECS_CHUNK_ALLOCATOR
14677 ChunkAllocator::get().flush();
14684 void assign_entity(Entity entity, Archetype& archetype) {
14685 GAIA_ASSERT(!entity.pair());
14687 auto* pChunk = archetype.foc_free_chunk();
14688 store_entity(m_recs.entities[entity.id()], entity, &archetype, pChunk);
14689 pChunk->update_versions();
14690 archetype.try_update_free_chunk_idx();
14693 pChunk->call_ctors(pChunk->size() - 1, 1);
14695#if GAIA_ASSERT_ENABLED
14696 const auto& ec = m_recs.entities[entity.id()];
14697 GAIA_ASSERT(ec.pChunk == pChunk);
14698 auto entityExpected = pChunk->entity_view()[ec.row];
14699 GAIA_ASSERT(entityExpected == entity);
14707 bool assign_pair_record(Entity entity, Archetype& archetype) {
14708 GAIA_ASSERT(entity.pair());
14711 GAIA_ASSERT(&archetype == m_pEntityArchetype);
14713 auto init_record = [&](EntityContainer& ec) {
14715 ec.idx = entity.id();
14716 ec.data.gen = entity.gen();
14720 auto* pChunk = archetype.foc_free_chunk();
14721 store_entity(ec, entity, &archetype, pChunk);
14722 pChunk->update_versions();
14723 archetype.try_update_free_chunk_idx();
14726 auto* pRecord = m_recs.pair_record_find(entity);
14727 if (pRecord !=
nullptr) {
14728 if ((pRecord->flags & EntityContainerFlags::DeleteRequested) == 0)
14731 m_reqEntitiesToDel.erase(EntityLookupKey(entity));
14734 invalidate_weak_entities(*pRecord);
14735 init_record(*pRecord);
14739 EntityContainer ec{};
14741 m_recs.pair_record_add(entity, GAIA_MOV(ec));
14747 void add_pair_lookup(Entity entity) {
14748 GAIA_ASSERT(entity.pair());
14750 const auto rel = get(entity.id());
14751 const auto tgt = get(entity.gen());
14752 m_pairLookup.add_pair(rel, tgt);
14758 void assign_pair(Entity entity, Archetype& archetype) {
14759 if (!assign_pair_record(entity, archetype))
14762 add_pair_lookup(entity);
14764#if GAIA_OBSERVERS_ENABLED
14765 m_observers.try_mark_term_observed(*
this, entity);
14774 GAIA_NODISCARD Entity add(Archetype& archetype,
bool isEntity,
bool isPair) {
14775 EntityContainerCtx ctx{isEntity, isPair};
14776 const auto entity = m_recs.entities.alloc(&ctx);
14777 assign_entity(entity, archetype);
14786 template <
typename Func>
14787 void add_entity_n(Archetype& archetype, uint32_t count, Func func) {
14788 EntityContainerCtx ctx{
true,
false};
14789#if GAIA_OBSERVERS_ENABLED
14790 const auto addedIds = EntitySpan{archetype.ids_view()};
14791 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
14792 if (!addedIds.empty())
14793 addDiffCtx = m_observers.prepare_diff_add_new(*
this, addedIds);
14796 uint32_t left = count;
14798 auto* pChunk = archetype.foc_free_chunk();
14799 const uint32_t originalChunkSize = pChunk->size();
14800 const uint32_t freeSlotsInChunk = pChunk->capacity() - originalChunkSize;
14801 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
14803 GAIA_FOR(toCreate) {
14804 const auto entityNew = m_recs.entities.alloc(&ctx);
14805 auto& ecNew = m_recs.entities[entityNew.id()];
14806 store_entity(ecNew, entityNew, &archetype, pChunk);
14808#if GAIA_ASSERT_ENABLED
14809 GAIA_ASSERT(ecNew.pChunk == pChunk);
14810 auto entityExpected = pChunk->entity_view()[ecNew.row];
14811 GAIA_ASSERT(entityExpected == entityNew);
14816 archetype.try_update_free_chunk_idx();
14819 pChunk->call_ctors(originalChunkSize, toCreate);
14823 auto entities = pChunk->entity_view();
14824 GAIA_FOR2(originalChunkSize, pChunk->size()) func(entities[i]);
14827 pChunk->update_versions();
14829#if GAIA_OBSERVERS_ENABLED
14830 if (!addedIds.empty()) {
14831 auto entities = pChunk->entity_view();
14832 const auto targets = EntitySpan{entities.data() + originalChunkSize, toCreate};
14833 m_observers.add_diff_targets(*
this, addDiffCtx, targets);
14834 m_observers.on_add(*
this, archetype, addedIds, targets);
14839 }
while (left > 0);
14841#if GAIA_OBSERVERS_ENABLED
14842 if (!addedIds.empty())
14843 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
14849 GAIA_PROF_SCOPE(World::gc);
14851 del_empty_chunks();
14852 defrag_chunks(m_defragEntitiesPerTick);
14853 del_empty_archetypes();
14863 if GAIA_UNLIKELY (serId == QueryIdBad) {
14864#if GAIA_ASSERT_ENABLED
14865 uint32_t safetyCounter = 0;
14869#if GAIA_ASSERT_ENABLED
14872 GAIA_ASSERT(safetyCounter < 100000);
14875 serId = ++m_nextQuerySerId;
14877 GAIA_ASSERT(serId != 0);
14882 auto ret = m_querySerMap.try_emplace(serId);
14886 return ret.first->second;
14890 return m_querySerMap[serId];
14896 auto it = m_querySerMap.find(serId);
14897 if (it == m_querySerMap.end())
14900 m_querySerMap.erase(it);
14901 serId = QueryIdBad;
14907 m_queryCache.invalidate_queries_for_entity(entityKey, QueryCache::ChangeKind::Structural);
14913 if (!m_queryCache.has_relation_query_dependencies())
14916 m_queryCache.invalidate_queries_for_rel(relation, QueryCache::ChangeKind::DynamicResult);
14922 return m_queryCache.has_sorted_queries();
14929 return m_queryCache.has_sorted_queries_for_entity(entity);
14935 m_queryCache.invalidate_sorted_queries_for_entity(entity);
14940 m_queryCache.invalidate_sorted_queries();
14946 GAIA_ASSERT(
is_pair.first() == Is);
14960 as_up_trav<false>(e, [&](
Entity target) {
14971 auto expr = util::trim(exprRaw);
14975 if (expr[0] ==
'(') {
14976 if (expr.size() < 5 || expr.back() !=
')')
14979 const auto idStr = expr.subspan(1, expr.size() - 2);
14980 const auto commaIdx = core::get_index(idStr,
',');
14981 if (commaIdx == BadIndex)
14984 const auto first = name_to_entity(idStr.subspan(0, commaIdx));
14985 if (first == EntityBad || first.pair())
14987 const auto second = name_to_entity(idStr.subspan(commaIdx + 1));
14988 if (second == EntityBad || second.pair())
14995 if (expr.size() == 1 && expr[0] ==
'*')
14998 return get_inter(expr.data(), (uint32_t)expr.size());
15007 auto expr = util::trim(exprRaw);
15009 if (expr[0] ==
'%') {
15010 if (expr[1] !=
'e') {
15011 GAIA_ASSERT2(
false,
"Expression '%' not terminated");
15015 auto id = (Identifier)va_arg(args,
unsigned long long);
15019 if (expr[0] ==
'(') {
15020 if (expr.back() !=
')') {
15021 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
15025 const auto idStr = expr.subspan(1, expr.size() - 2);
15026 const auto commaIdx = core::get_index(idStr,
',');
15028 const auto first = expr_to_entity(args, idStr.subspan(0, commaIdx));
15029 if (first == EntityBad)
15031 const auto second = expr_to_entity(args, idStr.subspan(commaIdx + 1));
15032 if (second == EntityBad)
15039 auto idStr = util::trim(expr);
15042 if (idStr.size() == 1 && idStr[0] ==
'*')
15046 const auto* pItem = resolve_component_name_inter(idStr.data(), (uint32_t)idStr.size());
15047 if (pItem ==
nullptr) {
15048 GAIA_ASSERT2(
false,
"Component not found");
15049 GAIA_LOG_W(
"Component '%.*s' not found", (uint32_t)idStr.size(), idStr.data());
15053 return pItem->entity;