91 inline static bool s_enableUniqueNameDuplicateAssert =
true;
98 friend void lock(
World&);
99 friend void unlock(
World&);
101 friend void query_match_scratch_release(
World&,
bool);
102 friend uint32_t world_component_index_bucket_size(
const World&,
Entity);
105 template <
typename T>
106 friend decltype(
auto) world_direct_entity_arg(
World& world,
Entity entity);
107 template <
typename T>
108 friend decltype(
auto) world_direct_entity_arg_raw(
World& world,
Entity entity);
109 template <
typename T>
110 friend decltype(
auto) world_query_entity_arg_by_id(
World& world,
Entity entity,
Entity id);
111 template <
typename T>
112 friend decltype(
auto) world_query_entity_arg_by_id_raw(
World& world,
Entity entity,
Entity id);
118 using TFunc_Void_With_Entity = void(
Entity);
119 static void func_void_with_entity([[maybe_unused]]
Entity entity) {}
125 struct ExclusiveAdjunctStore {
131 uint32_t srcToTgtCnt = 0;
136 struct SparseComponentStoreErased {
137 void* pStore =
nullptr;
138 void (*func_del)(
void*, Entity) =
nullptr;
139 bool (*func_has)(
const void*, Entity) =
nullptr;
140 bool (*func_copy_entity)(
void*, Entity, Entity) =
nullptr;
141 uint32_t (*func_count)(
const void*) =
nullptr;
142 void (*func_collect_entities)(
const void*, cnt::darray<Entity>&) =
nullptr;
143 bool (*func_for_each_entity)(
const void*,
void*, bool (*)(
void*, Entity)) =
nullptr;
144 void (*func_clear_store)(
void*) =
nullptr;
145 void (*func_del_store)(
void*) =
nullptr;
148 template <
typename T>
149 struct SparseComponentStore final {
150 cnt::sparse_storage<SparseComponentRecord<T>> data;
152 static cnt::sparse_id sid(Entity entity) {
153 return (cnt::sparse_id)entity.id();
156 T& add(Entity entity) {
157 const auto sparseId = sid(entity);
158 if (data.has(sparseId))
159 return data[sparseId].value;
161 auto& item = data.add(SparseComponentRecord<T>{entity});
165 T& mut(Entity entity) {
166 GAIA_ASSERT(data.has(sid(entity)));
167 return data[sid(entity)].value;
170 const T& get(Entity entity)
const {
171 GAIA_ASSERT(data.has(sid(entity)));
172 return data[sid(entity)].value;
175 void del_entity(Entity entity) {
176 const auto sparseId = sid(entity);
177 if (!data.empty() && data.has(sparseId))
181 bool has(Entity entity)
const {
184 return data.has(sid(entity));
187 uint32_t count()
const {
188 return (uint32_t)data.size();
191 void collect_entities(cnt::darray<Entity>& out)
const {
192 out.reserve(out.size() + (uint32_t)data.size());
193 for (
const auto& item: data)
194 out.push_back(item.entity);
202 template <
typename T>
203 static SparseComponentStoreErased make_sparse_component_store_erased(SparseComponentStore<T>* pStore) {
204 SparseComponentStoreErased store{};
205 store.pStore = pStore;
206 store.func_del = [](
void* pStoreRaw, Entity entity) {
207 static_cast<SparseComponentStore<T>*
>(pStoreRaw)->del_entity(entity);
209 store.func_has = [](
const void* pStoreRaw, Entity entity) {
210 return static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->has(entity);
212 store.func_copy_entity = [](
void* pStoreRaw, Entity dstEntity, Entity srcEntity) {
213 auto* pStore =
static_cast<SparseComponentStore<T>*
>(pStoreRaw);
214 if (!pStore->has(srcEntity))
217 auto& dst = pStore->add(dstEntity);
218 dst = pStore->get(srcEntity);
221 store.func_count = [](
const void* pStoreRaw) {
222 return static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->count();
224 store.func_collect_entities = [](
const void* pStoreRaw, cnt::darray<Entity>& out) {
225 static_cast<const SparseComponentStore<T>*
>(pStoreRaw)->collect_entities(out);
227 store.func_for_each_entity = [](
const void* pStoreRaw,
void* pCtx, bool (*func)(
void*, Entity)) {
228 for (
const auto& item: static_cast<const SparseComponentStore<T>*>(pStoreRaw)->data) {
229 if (!func(pCtx, item.entity))
234 store.func_clear_store = [](
void* pStoreRaw) {
235 static_cast<SparseComponentStore<T>*
>(pStoreRaw)->clear_store();
237 store.func_del_store = [](
void* pStoreRaw) {
238 delete static_cast<SparseComponentStore<T>*
>(pStoreRaw);
244 typename TApi,
typename TValue,
bool DeriveFromValue = std::is_class_v<TValue> && !std::is_final_v<TValue>>
245 class SetWriteProxyTyped;
247 template <
typename TApi,
typename TValue>
248 class SetWriteProxyTyped<TApi, TValue, true>:
public TValue {
249 World* m_pWorld =
nullptr;
250 Entity m_entity = EntityBad;
251 Entity m_term = EntityBad;
254 if (m_pWorld ==
nullptr)
257 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
262 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
263 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
265 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
266 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
268 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
269 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
271 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
272 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
273 m_term(other.m_term) {
274 other.m_pWorld =
nullptr;
277 ~SetWriteProxyTyped() {
281 SetWriteProxyTyped& operator=(
const TValue& value) {
282 static_cast<TValue&
>(*this) = value;
286 SetWriteProxyTyped& operator=(TValue&& value) {
287 static_cast<TValue&
>(*this) = GAIA_MOV(value);
291 GAIA_NODISCARD
operator TValue&() {
295 GAIA_NODISCARD
operator const TValue&()
const {
300 template <
typename TApi,
typename TValue>
301 class SetWriteProxyTyped<TApi, TValue, false> {
302 World* m_pWorld =
nullptr;
303 Entity m_entity = EntityBad;
304 Entity m_term = EntityBad;
308 if (m_pWorld ==
nullptr)
311 m_pWorld->template write_back_set_typed<TApi, TValue>(m_entity, m_term, m_value);
316 SetWriteProxyTyped(World& world, Entity entity, Entity term,
const TValue& value):
317 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
319 SetWriteProxyTyped(World& world, Entity entity, Entity term, TValue&& value):
320 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
322 SetWriteProxyTyped(
const SetWriteProxyTyped&) =
delete;
323 SetWriteProxyTyped& operator=(
const SetWriteProxyTyped&) =
delete;
325 SetWriteProxyTyped(SetWriteProxyTyped&& other)
noexcept:
326 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
327 other.m_pWorld =
nullptr;
330 ~SetWriteProxyTyped() {
334 SetWriteProxyTyped& operator=(
const TValue& value) {
339 SetWriteProxyTyped& operator=(TValue&& value) {
340 m_value = GAIA_MOV(value);
344 GAIA_NODISCARD
operator TValue&() {
348 GAIA_NODISCARD
operator const TValue&()
const {
352 GAIA_NODISCARD TValue* operator->() {
356 GAIA_NODISCARD
const TValue* operator->()
const {
361 template <
typename TValue,
bool DeriveFromValue = std::is_
class_v<TValue> && !std::is_final_v<TValue>>
362 class SetWriteProxyObject;
364 template <
typename TValue>
365 class SetWriteProxyObject<TValue, true>:
public TValue {
366 World* m_pWorld =
nullptr;
367 Entity m_entity = EntityBad;
368 Entity m_term = EntityBad;
371 if (m_pWorld ==
nullptr)
374 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term,
static_cast<const TValue&
>(*
this));
379 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
380 TValue(value), m_pWorld(&world), m_entity(entity), m_term(term) {}
382 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
383 TValue(GAIA_MOV(value)), m_pWorld(&world), m_entity(entity), m_term(term) {}
385 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
386 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
388 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
389 TValue(
static_cast<TValue&&
>(other)), m_pWorld(other.m_pWorld), m_entity(other.m_entity),
390 m_term(other.m_term) {
391 other.m_pWorld =
nullptr;
394 ~SetWriteProxyObject() {
398 SetWriteProxyObject& operator=(
const TValue& value) {
399 static_cast<TValue&
>(*this) = value;
403 SetWriteProxyObject& operator=(TValue&& value) {
404 static_cast<TValue&
>(*this) = GAIA_MOV(value);
408 GAIA_NODISCARD
operator TValue&() {
412 GAIA_NODISCARD
operator const TValue&()
const {
417 template <
typename TValue>
418 class SetWriteProxyObject<TValue, false> {
419 World* m_pWorld =
nullptr;
420 Entity m_entity = EntityBad;
421 Entity m_term = EntityBad;
425 if (m_pWorld ==
nullptr)
428 m_pWorld->template write_back_set_object<TValue>(m_entity, m_term, m_value);
433 SetWriteProxyObject(World& world, Entity entity, Entity term,
const TValue& value):
434 m_pWorld(&world), m_entity(entity), m_term(term), m_value(value) {}
436 SetWriteProxyObject(World& world, Entity entity, Entity term, TValue&& value):
437 m_pWorld(&world), m_entity(entity), m_term(term), m_value(GAIA_MOV(value)) {}
439 SetWriteProxyObject(
const SetWriteProxyObject&) =
delete;
440 SetWriteProxyObject& operator=(
const SetWriteProxyObject&) =
delete;
442 SetWriteProxyObject(SetWriteProxyObject&& other)
noexcept:
443 m_pWorld(other.m_pWorld), m_entity(other.m_entity), m_term(other.m_term), m_value(GAIA_MOV(other.m_value)) {
444 other.m_pWorld =
nullptr;
447 ~SetWriteProxyObject() {
451 SetWriteProxyObject& operator=(
const TValue& value) {
456 SetWriteProxyObject& operator=(TValue&& value) {
457 m_value = GAIA_MOV(value);
461 GAIA_NODISCARD
operator TValue&() {
465 GAIA_NODISCARD
operator const TValue&()
const {
469 GAIA_NODISCARD TValue* operator->() {
473 GAIA_NODISCARD
const TValue* operator->()
const {
478 template <
typename T>
479 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity) {
480 static_assert(!is_pair<T>::value);
481 using FT =
typename component_type_t<T>::TypeFull;
482 const auto& item = add<FT>();
483 if constexpr (supports_out_of_line_component<FT>()) {
484 if (is_out_of_line_component(item.entity))
485 return sparse_component_store_mut<FT>(item.entity).mut(entity);
488 const auto& ec = m_recs.entities[entity.id()];
489 if constexpr (entity_kind_v<T> == EntityKind::EK_Gen)
490 return ec.pChunk->template set<T>(ec.row);
492 return ec.pChunk->template set<T>();
495 template <
typename T>
496 GAIA_NODISCARD
decltype(
auto) mut_im(Entity entity, Entity
object) {
497 static_assert(!is_pair<T>::value);
498 using FT =
typename component_type_t<T>::TypeFull;
499 if constexpr (supports_out_of_line_component<FT>()) {
500 if (can_use_out_of_line_component<FT>(
object))
501 return sparse_component_store_mut<FT>(
object).mut(entity);
504 const auto& ec = m_recs.entities[entity.id()];
505 return ec.pChunk->template set<T>(ec.row,
object);
513 void finish_write(Entity entity, Entity term) {
514 if (tearing_down() || !valid(entity))
517 if (is_out_of_line_component(term)) {
518 world_notify_on_set_entity(*
this, term, entity);
522 auto compIdx = uint32_t(BadIndex);
524 const auto& ec = fetch(entity);
525 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
528 if (compIdx == BadIndex) {
529 (void)
override(entity, term);
530 const auto& ec = fetch(entity);
531 compIdx = world_component_index_comp_idx(*
this, *ec.pArchetype, term);
532 if (compIdx == BadIndex)
536 const auto& ec = fetch(entity);
537 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
538 (void)ec.pChunk->comp_ptr_mut_gen<
true>(compIdx, row);
539 world_notify_on_set_entity(*
this, term, entity);
542 template <
typename TApi,
typename TValue>
543 void write_back_set_typed(Entity entity, Entity term,
const TValue& value) {
544 using FT =
typename component_type_t<TApi>::TypeFull;
545 ::gaia::ecs::update_version(m_worldVersion);
547 if constexpr (supports_out_of_line_component<FT>()) {
548 if (is_out_of_line_component(term)) {
549 sparse_component_store_mut<FT>(term).add(entity) = value;
550 finish_write(entity, term);
555 const auto& ec = fetch(entity);
556 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
557 ComponentSetter{*
this, ec.pChunk, entity, row}.sset<TApi>(value);
558 finish_write(entity, term);
561 template <
typename TValue>
562 void write_back_set_object(Entity entity, Entity term,
const TValue& value) {
563 using FT =
typename component_type_t<TValue>::TypeFull;
564 ::gaia::ecs::update_version(m_worldVersion);
565 if constexpr (supports_out_of_line_component<FT>()) {
566 if (can_use_out_of_line_component<FT>(term)) {
567 sparse_component_store_mut<FT>(term).add(entity) = value;
568 finish_write(entity, term);
573 const auto& ec = fetch(entity);
574 const auto row = uint16_t(ec.row * (1U - (uint32_t)term.kind()));
575 ComponentSetter{*
this, ec.pChunk, entity, row}.template smut<TValue>(term) = value;
576 finish_write(entity, term);
581#if GAIA_OBSERVERS_ENABLED
582 class ObserverRegistry {
583 struct DiffObserverIndex {
585 cnt::map<EntityLookupKey, cnt::darray<Entity>> direct;
587 cnt::map<EntityLookupKey, cnt::darray<Entity>> sourceTerm;
589 cnt::map<EntityLookupKey, cnt::darray<Entity>> traversalRelation;
591 cnt::map<EntityLookupKey, cnt::darray<Entity>> pairRelation;
593 cnt::map<EntityLookupKey, cnt::darray<Entity>> pairTarget;
595 cnt::darray<Entity> all;
597 cnt::darray<Entity> global;
600 struct PropagatedTargetCacheKey {
601 Entity bindingRelation = EntityBad;
602 Entity traversalRelation = EntityBad;
603 Entity rootTarget = EntityBad;
604 QueryTravKind travKind = QueryTravKind::None;
605 uint8_t travDepth = QueryTermOptions::TravDepthUnlimited;
607 size_t hash()
const {
608 size_t seed = EntityLookupKey(bindingRelation).hash();
609 seed ^= EntityLookupKey(traversalRelation).hash() + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
610 seed ^= EntityLookupKey(rootTarget).hash() + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
611 seed ^= size_t(travKind) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
612 seed ^= size_t(travDepth) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
616 bool operator==(
const PropagatedTargetCacheKey& other)
const {
617 return bindingRelation == other.bindingRelation && traversalRelation == other.traversalRelation &&
618 rootTarget == other.rootTarget && travKind == other.travKind && travDepth == other.travDepth;
622 struct PropagatedTargetCacheEntry {
623 uint32_t bindingRelationVersion = 0;
624 uint32_t traversalRelationVersion = 0;
625 cnt::darray<Entity> targets;
628 struct DiffDispatcher {
630 ObserverRuntimeData* pObs =
nullptr;
631 uint32_t matchesBeforeIdx = UINT32_MAX;
634 struct MatchCacheEntry {
635 ObserverRuntimeData* pObsRepresentative =
nullptr;
636 QueryInfo* pQueryInfoRepresentative =
nullptr;
637 uint64_t queryHash = 0;
638 cnt::darray<Entity> matches;
641 struct TargetNarrowCacheEntry {
642 ObserverPlan::DiffPlan::DispatchKind kind = ObserverPlan::DiffPlan::DispatchKind::LocalTargets;
643 Entity bindingRelation = EntityBad;
644 Entity traversalRelation = EntityBad;
645 QueryTravKind travKind = QueryTravKind::None;
646 uint8_t travDepth = QueryTermOptions::TravDepthUnlimited;
647 QueryEntityArray triggerTerms{};
648 uint8_t triggerTermCount = 0;
649 cnt::darray<Entity> targets;
653 ObserverEvent
event = ObserverEvent::OnAdd;
654 cnt::darray<Snapshot> observers;
655 cnt::darray<MatchCacheEntry> matchesBeforeCache;
656 cnt::darray<Entity> targets;
658 bool targeted =
false;
659 bool targetsAddedAfterPrepare =
false;
660 bool resetTraversalCaches =
false;
663 static void collect_query_matches(World& world, ObserverRuntimeData& obs, cnt::darray<Entity>& out) {
665 if (!world.valid(obs.entity))
668 const auto& ec = world.fetch(obs.entity);
669 if (!world.enabled(ec))
673 obs.query.each([&](Entity entity) {
674 out.push_back(entity);
677 core::sort(out, [](Entity left, Entity right) {
678 return left.value() < right.value();
682 static void collect_query_target_matches(
683 World& world, ObserverRuntimeData& obs, EntitySpan targets, cnt::darray<Entity>& out) {
685 if (!world.valid(obs.entity))
688 const auto& ec = world.fetch(obs.entity);
689 if (!world.enabled(ec))
692 auto& queryInfo = obs.query.fetch();
693 for (
auto entity: targets) {
694 if (!world.valid(entity))
697 const auto& ecTarget = world.fetch(entity);
698 if (ecTarget.pArchetype ==
nullptr)
701 if (obs.query.matches_any(queryInfo, *ecTarget.pArchetype, EntitySpan{&entity, 1}))
702 out.push_back(entity);
706 static void append_valid_targets(World& world, cnt::darray<Entity>& out, EntitySpan targets) {
707 for (
auto entity: targets) {
708 if (world.valid(entity))
709 out.push_back(entity);
713 static void copy_target_narrow_plan(
const ObserverRuntimeData& obs, TargetNarrowCacheEntry& entry) {
714 entry.kind = obs.plan.diff.dispatchKind;
715 entry.bindingRelation = obs.plan.diff.bindingRelation;
716 entry.traversalRelation = obs.plan.diff.traversalRelation;
717 entry.travKind = obs.plan.diff.travKind;
718 entry.travDepth = obs.plan.diff.travDepth;
719 entry.triggerTermCount = obs.plan.diff.traversalTriggerTermCount;
720 GAIA_FOR(obs.plan.diff.traversalTriggerTermCount) {
721 entry.triggerTerms[i] = obs.plan.diff.traversalTriggerTerms[i];
725 static bool same_target_narrow_plan(
const ObserverRuntimeData& obs,
const TargetNarrowCacheEntry& entry) {
726 if (obs.plan.diff.dispatchKind != entry.kind || obs.plan.diff.bindingRelation != entry.bindingRelation ||
727 obs.plan.diff.traversalRelation != entry.traversalRelation ||
728 obs.plan.diff.travKind != entry.travKind || obs.plan.diff.travDepth != entry.travDepth ||
729 obs.plan.diff.traversalTriggerTermCount != entry.triggerTermCount)
732 GAIA_FOR(obs.plan.diff.traversalTriggerTermCount) {
733 if (obs.plan.diff.traversalTriggerTerms[i] != entry.triggerTerms[i])
740 static void normalize_targets(cnt::darray<Entity>& targets) {
744 core::sort(targets, [](Entity left, Entity right) {
745 return left.value() < right.value();
749 for (uint32_t i = 0; i < targets.size(); ++i) {
750 if (outIdx != 0 && targets[i] == targets[outIdx - 1])
752 targets[outIdx++] = targets[i];
754 targets.resize(outIdx);
757 static uint64_t query_hash(ObserverRuntimeData& obs) {
758 auto& queryInfo = obs.query.fetch();
759 return queryInfo.ctx().hashLookup.hash;
762 static bool same_query_ctx(
const QueryCtx& left,
const QueryCtx& right) {
763 if (left.hashLookup != right.hashLookup)
766 const auto& leftData = left.data;
767 const auto& rightData = right.data;
768 if (leftData.idsCnt != rightData.idsCnt || leftData.changedCnt != rightData.changedCnt ||
769 leftData.readWriteMask != rightData.readWriteMask || leftData.cacheSrcTrav != rightData.cacheSrcTrav ||
770 leftData.sortBy != rightData.sortBy || leftData.sortByFunc != rightData.sortByFunc ||
771 leftData.groupBy != rightData.groupBy || leftData.groupByFunc != rightData.groupByFunc)
774 GAIA_FOR(leftData.idsCnt) {
775 if (leftData.terms[i] != rightData.terms[i])
779 GAIA_FOR(leftData.changedCnt) {
780 if (leftData.changed[i] != rightData.changed[i])
787 static int32_t find_match_cache_entry(cnt::darray<MatchCacheEntry>& cache, ObserverRuntimeData& obs) {
788 auto& queryInfo = obs.query.fetch();
789 auto& queryCtx = queryInfo.ctx();
790 const auto queryHash = queryCtx.hashLookup.hash;
792 GAIA_FOR((uint32_t)cache.size()) {
793 auto& entry = cache[i];
794 if (entry.pQueryInfoRepresentative == &queryInfo)
796 if (entry.queryHash != queryHash || entry.pObsRepresentative ==
nullptr)
799 auto& repQueryInfo = entry.pObsRepresentative->query.fetch();
800 if (same_query_ctx(queryCtx, repQueryInfo.ctx()))
807 static Context prepare(
808 ObserverRegistry& registry, World& world, ObserverEvent event, EntitySpan terms,
809 EntitySpan targetEntities = {}) {
812 const auto& index = registry.diff_index(event);
814 if (terms.empty() && index.all.empty() && index.global.empty())
817 bool hasEntityLifecycleTerm =
false;
818 if (!targetEntities.empty() && !terms.empty()) {
819 for (
auto term: terms) {
823 for (
auto target: targetEntities) {
824 if (term == target) {
825 hasEntityLifecycleTerm =
true;
830 if (hasEntityLifecycleTerm)
835 if (!hasEntityLifecycleTerm && !targetEntities.empty() && !terms.empty() &&
836 !SharedDispatch::has_terms(index.sourceTerm, terms) &&
837 !SharedDispatch::has_pair_relations(world, index.traversalRelation, terms)) {
839 ctx.targets.reserve((uint32_t)targetEntities.size());
840 append_valid_targets(world, ctx.targets, targetEntities);
841 normalize_targets(ctx.targets);
844 registry.m_relevant_observers_tmp.clear();
845 const auto matchStamp = ++registry.m_current_match_stamp;
847 SharedDispatch::collect_diff_from_list(registry, world, index.all, matchStamp);
849 for (
auto term: terms) {
850 SharedDispatch::collect_from_map<true>(registry, world, index.direct, term, matchStamp);
855 if (!is_wildcard(term.id()) && world.valid_entity_id((EntityId)term.id())) {
856 const auto relation = entity_from_id(world, term.id());
857 if (world.valid(relation)) {
858 SharedDispatch::collect_from_map<true>(
859 registry, world, index.traversalRelation, relation, matchStamp);
860 SharedDispatch::collect_from_map<true>(registry, world, index.pairRelation, relation, matchStamp);
864 if (!is_wildcard(term.gen()) && world.valid_entity_id((EntityId)term.gen())) {
865 const auto target = world.get(term.gen());
866 if (world.valid(target))
867 SharedDispatch::collect_from_map<true>(registry, world, index.pairTarget, target, matchStamp);
872 if (hasEntityLifecycleTerm)
873 SharedDispatch::collect_diff_from_list(registry, world, index.all, matchStamp);
874 if (!terms.empty() && !hasEntityLifecycleTerm)
875 SharedDispatch::collect_diff_from_list(registry, world, index.global, matchStamp);
877 if (!ctx.targeted && !targetEntities.empty() && !registry.m_relevant_observers_tmp.empty()) {
878 cnt::darray<Entity> narrowedTargets;
879 cnt::darray<TargetNarrowCacheEntry> narrowCache;
880 bool canNarrow =
true;
882 for (
auto* pObs: registry.m_relevant_observers_tmp) {
886 const TargetNarrowCacheEntry* pEntry =
nullptr;
887 for (
const auto& entry: narrowCache) {
888 if (same_target_narrow_plan(*pObs, entry)) {
894 if (pEntry ==
nullptr) {
895 narrowCache.push_back({});
896 auto& entry = narrowCache.back();
897 copy_target_narrow_plan(*pObs, entry);
899 if (!collect_diff_targets_for_observer(
900 registry, world, *pObs, terms, targetEntities, entry.targets)) {
908 for (
auto entity: pEntry->targets)
909 narrowedTargets.push_back(entity);
913 normalize_targets(narrowedTargets);
915 ctx.targets = GAIA_MOV(narrowedTargets);
919 if (registry.m_relevant_observers_tmp.empty())
923 for (
auto* pObs: registry.m_relevant_observers_tmp) {
924 ctx.observers.push_back({});
925 auto& snapshot = ctx.observers.back();
926 snapshot.pObs = pObs;
927 if (!ctx.resetTraversalCaches && observer_uses_changed_traversal_relation(world, *pObs, terms))
928 ctx.resetTraversalCaches =
true;
930 auto cacheIdx = find_match_cache_entry(ctx.matchesBeforeCache, *pObs);
931 if (cacheIdx == -1) {
932 ctx.matchesBeforeCache.push_back({});
933 auto& entry = ctx.matchesBeforeCache.back();
934 entry.pObsRepresentative = pObs;
935 entry.pQueryInfoRepresentative = &pObs->query.fetch();
936 entry.queryHash = query_hash(*pObs);
938 collect_query_target_matches(
939 world, *pObs, EntitySpan{ctx.targets.data(), ctx.targets.size()}, entry.matches);
941 collect_query_matches(world, *pObs, entry.matches);
942 cacheIdx = (int32_t)ctx.matchesBeforeCache.size() - 1;
945 snapshot.matchesBeforeIdx = (uint32_t)cacheIdx;
951 static Context prepare_add_new(ObserverRegistry& registry, World& world, EntitySpan terms) {
953 ctx.event = ObserverEvent::OnAdd;
954 const auto& index = registry.diff_index(ObserverEvent::OnAdd);
956 if (terms.empty() && index.all.empty() && index.global.empty())
959 if (terms.empty() || SharedDispatch::has_terms(index.sourceTerm, terms) ||
960 SharedDispatch::has_pair_relations(world, index.traversalRelation, terms)) {
961 return prepare(registry, world, ObserverEvent::OnAdd, terms);
964 registry.m_relevant_observers_tmp.clear();
965 const auto matchStamp = ++registry.m_current_match_stamp;
966 for (
auto term: terms) {
967 SharedDispatch::collect_from_map<true>(registry, world, index.direct, term, matchStamp);
972 if (!is_wildcard(term.id()) && world.valid_entity_id((EntityId)term.id())) {
973 const auto relation = entity_from_id(world, term.id());
974 if (world.valid(relation))
975 SharedDispatch::collect_from_map<true>(registry, world, index.pairRelation, relation, matchStamp);
978 if (!is_wildcard(term.gen()) && world.valid_entity_id((EntityId)term.gen())) {
979 const auto target = world.get(term.gen());
980 if (world.valid(target))
981 SharedDispatch::collect_from_map<true>(registry, world, index.pairTarget, target, matchStamp);
984 SharedDispatch::collect_diff_from_list(registry, world, index.global, matchStamp);
986 if (registry.m_relevant_observers_tmp.empty())
991 ctx.targetsAddedAfterPrepare =
true;
992 for (
auto* pObs: registry.m_relevant_observers_tmp) {
993 if (!ctx.resetTraversalCaches && observer_uses_changed_traversal_relation(world, *pObs, terms))
994 ctx.resetTraversalCaches =
true;
995 ctx.observers.push_back({});
996 ctx.observers.back().pObs = pObs;
1002 static void append_targets(World& world, Context& ctx, EntitySpan targets) {
1003 if (!ctx.active || !ctx.targeted || targets.empty())
1006 append_valid_targets(world, ctx.targets, targets);
1009 static void finish(World& world, Context&& ctx) {
1013 if (ctx.resetTraversalCaches) {
1014 world.m_targetsTravCache = {};
1015 world.m_srcBfsTravCache = {};
1016 world.m_depthOrderCache = {};
1017 world.m_sourcesAllCache = {};
1018 world.m_targetsAllCache = {};
1019 world.m_entityToAsTargetsTravCache = {};
1020 world.m_entityToAsRelationsTravCache = {};
1024 normalize_targets(ctx.targets);
1026 cnt::darray<MatchCacheEntry> matchesAfterCache;
1027 cnt::darray<Entity> delta;
1029 for (
auto& snapshot: ctx.observers) {
1030 auto* pObs = snapshot.pObs;
1031 if (pObs ==
nullptr || !world.valid(pObs->entity))
1034 auto afterCacheIdx = find_match_cache_entry(matchesAfterCache, *pObs);
1035 if (afterCacheIdx == -1) {
1036 matchesAfterCache.push_back({});
1037 auto& entry = matchesAfterCache.back();
1038 entry.pObsRepresentative = pObs;
1039 entry.pQueryInfoRepresentative = &pObs->query.fetch();
1040 entry.queryHash = query_hash(*pObs);
1042 collect_query_target_matches(
1043 world, *pObs, EntitySpan{ctx.targets.data(), ctx.targets.size()}, entry.matches);
1045 collect_query_matches(world, *pObs, entry.matches);
1046 afterCacheIdx = (int32_t)matchesAfterCache.size() - 1;
1049 const auto& matchesAfter = matchesAfterCache[(uint32_t)afterCacheIdx].matches;
1051 if (ctx.targetsAddedAfterPrepare && ctx.event == ObserverEvent::OnAdd) {
1052 SharedDispatch::execute_targets(world, *pObs, EntitySpan{matchesAfter});
1056 GAIA_ASSERT(snapshot.matchesBeforeIdx < ctx.matchesBeforeCache.size());
1057 const auto& before = ctx.matchesBeforeCache[snapshot.matchesBeforeIdx].matches;
1059 uint32_t beforeIdx = 0;
1060 uint32_t afterMatchIdx = 0;
1061 while (beforeIdx < before.size() || afterMatchIdx < matchesAfter.size()) {
1062 if (beforeIdx == before.size()) {
1063 if (ctx.event == ObserverEvent::OnAdd)
1064 delta.push_back(matchesAfter[afterMatchIdx]);
1069 if (afterMatchIdx == matchesAfter.size()) {
1070 if (ctx.event == ObserverEvent::OnDel)
1071 delta.push_back(before[beforeIdx]);
1076 const auto beforeEntity = before[beforeIdx];
1077 const auto afterEntity = matchesAfter[afterMatchIdx];
1078 if (beforeEntity == afterEntity) {
1084 if (beforeEntity < afterEntity) {
1085 if (ctx.event == ObserverEvent::OnDel)
1086 delta.push_back(beforeEntity);
1089 if (ctx.event == ObserverEvent::OnAdd)
1090 delta.push_back(afterEntity);
1095 SharedDispatch::execute_targets(world, *pObs, EntitySpan{delta.data(), delta.size()});
1100 struct DirectDispatcher {
1102 ObserverRegistry& registry, World& world,
const Archetype& archetype, EntitySpan entsAdded,
1103 EntitySpan targets) {
1104 if GAIA_UNLIKELY (world.tearing_down())
1107 if (!archetype.has_observed_terms() && !SharedDispatch::has_terms(registry.m_observer_map_add, entsAdded) &&
1108 !SharedDispatch::has_semantic_is_terms(world, registry.m_observer_map_add_is, entsAdded) &&
1109 !SharedDispatch::has_inherited_terms(world, registry.m_observer_map_add, entsAdded))
1112 const
bool archetypeIsPrefab = archetype.has(Prefab);
1113 const auto matchStamp = ++registry.m_current_match_stamp;
1114 for (auto comp: entsAdded) {
1115 SharedDispatch::collect_for_event_term(registry, world, registry.m_observer_map_add, comp, matchStamp);
1116 if (!is_semantic_is_term(comp))
1119 const auto target = world.get(comp.gen());
1120 if (!world.valid(target))
1123 SharedDispatch::collect_for_is_target(
1124 registry, world, registry.m_observer_map_add_is, target, matchStamp);
1125 for (
auto inheritedTarget: world.as_targets_trav_cache(target))
1126 SharedDispatch::collect_for_is_target(
1127 registry, world, registry.m_observer_map_add_is, inheritedTarget, matchStamp);
1128 SharedDispatch::collect_for_inherited_terms(
1129 registry, world, registry.m_observer_map_add, target, matchStamp);
1132 for (
auto* pObs: registry.m_relevant_observers_tmp) {
1134 if (!obs.plan.uses_direct_dispatch())
1136 QueryInfo* pQueryInfo =
nullptr;
1137 if (archetypeIsPrefab) {
1138 pQueryInfo = &obs.query.fetch();
1139 if (!pQueryInfo->matches_prefab_entities())
1143 if (SharedDispatch::matches_direct_targets(obs, archetype, targets, pQueryInfo)) {
1144 SharedDispatch::execute_targets(world, obs, targets);
1148 registry.m_relevant_observers_tmp.clear();
1152 ObserverRegistry& registry, World& world,
const Archetype& archetype, EntitySpan entsRemoved,
1153 EntitySpan targets) {
1154 if GAIA_UNLIKELY (world.tearing_down())
1157 const
bool archetypeIsPrefab = archetype.has(Prefab);
1158 if (!archetype.has_observed_terms() &&
1159 !SharedDispatch::has_terms(registry.m_observer_map_del, entsRemoved) &&
1160 !SharedDispatch::has_semantic_is_terms(world, registry.m_observer_map_del_is, entsRemoved) &&
1161 !SharedDispatch::has_inherited_terms(world, registry.m_observer_map_del, entsRemoved))
1164 const auto matchStamp = ++registry.m_current_match_stamp;
1165 for (auto comp: entsRemoved) {
1166 SharedDispatch::collect_for_event_term(registry, world, registry.m_observer_map_del, comp, matchStamp);
1167 if (!is_semantic_is_term(comp))
1170 const auto target = world.get(comp.gen());
1171 if (!world.valid(target))
1174 SharedDispatch::collect_for_is_target(
1175 registry, world, registry.m_observer_map_del_is, target, matchStamp);
1176 for (
auto inheritedTarget: world.as_targets_trav_cache(target))
1177 SharedDispatch::collect_for_is_target(
1178 registry, world, registry.m_observer_map_del_is, inheritedTarget, matchStamp);
1179 SharedDispatch::collect_for_inherited_terms(
1180 registry, world, registry.m_observer_map_del, target, matchStamp);
1183 for (
auto* pObs: registry.m_relevant_observers_tmp) {
1185 if (!obs.plan.uses_direct_dispatch())
1187 QueryInfo* pQueryInfo =
nullptr;
1188 if (archetypeIsPrefab) {
1189 pQueryInfo = &obs.query.fetch();
1190 if (!pQueryInfo->matches_prefab_entities())
1194 bool matches = SharedDispatch::matches_direct_targets(obs, archetype, targets, pQueryInfo);
1195 if (obs.plan.exec_kind() == ObserverPlan::ExecKind::DirectFast && obs.plan.is_fast_negative())
1199 SharedDispatch::execute_targets(world, obs, targets);
1203 registry.m_relevant_observers_tmp.clear();
1206 static void on_set(ObserverRegistry& registry, World& world, Entity term, EntitySpan targets) {
1207 if GAIA_UNLIKELY (world.tearing_down())
1209 if (targets.empty())
1212 const auto itObservers = registry.m_observer_map_set.find(EntityLookupKey(term));
1213 if (itObservers == registry.m_observer_map_set.end() || itObservers->second.empty())
1216 registry.m_relevant_observers_tmp.clear();
1217 const auto matchStamp = ++registry.m_current_match_stamp;
1218 SharedDispatch::collect_from_map<false>(registry, world, registry.m_observer_map_set, term, matchStamp);
1220 for (auto* pObs: registry.m_relevant_observers_tmp) {
1222 for (
auto entity: targets) {
1223 if (!world.valid(entity))
1226 auto& queryInfo = obs.query.fetch();
1227 const auto& ec = world.fetch(entity);
1228 if (ec.pArchetype ==
nullptr)
1230 if (ec.pArchetype->has(Prefab) && !queryInfo.matches_prefab_entities())
1232 if (!obs.query.matches_any(queryInfo, *ec.pArchetype, EntitySpan{&entity, 1}))
1235 SharedDispatch::execute_targets(world, obs, EntitySpan{&entity, 1});
1239 registry.m_relevant_observers_tmp.clear();
1243 struct SharedDispatch {
1244 template <
bool DiffOnly,
typename TObserverMap>
1245 static void collect_from_map(
1246 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity term, uint64_t matchStamp) {
1247 const auto it = map.find(EntityLookupKey(term));
1248 if (it == map.end())
1251 for (
auto observer: it->second) {
1252 auto* pObs = registry.data_try(observer);
1253 GAIA_ASSERT(pObs !=
nullptr);
1254 if (pObs ==
nullptr)
1256 if (pObs->lastMatchStamp == matchStamp)
1259 const auto& ec = world.fetch(observer);
1260 if (!world.enabled(ec))
1263 if constexpr (DiffOnly) {
1264 if (!pObs->plan.uses_diff_dispatch())
1268 pObs->lastMatchStamp = matchStamp;
1269 registry.m_relevant_observers_tmp.push_back(pObs);
1273 static void collect_diff_from_list(
1274 ObserverRegistry& registry, World& world,
const cnt::darray<Entity>& observers, uint64_t matchStamp) {
1275 for (
auto observer: observers) {
1276 auto* pObs = registry.data_try(observer);
1277 GAIA_ASSERT(pObs !=
nullptr);
1278 if (pObs ==
nullptr || !pObs->plan.uses_diff_dispatch())
1280 if (pObs->lastMatchStamp == matchStamp)
1283 const auto& ec = world.fetch(observer);
1284 if (!world.enabled(ec))
1287 pObs->lastMatchStamp = matchStamp;
1288 registry.m_relevant_observers_tmp.push_back(pObs);
1292 template <
typename TObserverMap>
1293 GAIA_NODISCARD
static bool has_terms(
const TObserverMap& map, EntitySpan terms) {
1294 for (
auto term: terms) {
1295 const auto it = map.find(EntityLookupKey(term));
1296 if (it != map.end() && !it->second.empty())
1303 template <
typename TObserverMap>
1304 GAIA_NODISCARD
static bool has_pair_relations(World& world,
const TObserverMap& map, EntitySpan terms) {
1305 for (
auto term: terms) {
1309 const auto relation = entity_from_id(world, term.id());
1310 if (!world.valid(relation))
1313 const auto it = map.find(EntityLookupKey(relation));
1314 if (it != map.end() && !it->second.empty())
1321 template <
typename TObserverMap>
1322 static void collect_for_event_term(
1323 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity term, uint64_t matchStamp) {
1324 if (!world.valid(term))
1327 if (!is_semantic_is_term(term)) {
1328 if ((world.fetch(term).flags & EntityContainerFlags::IsObserved) == 0)
1332 collect_from_map<false>(registry, world, map, term, matchStamp);
1335 template <
typename TObserverMap>
1336 static void collect_for_is_target(
1337 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity target, uint64_t matchStamp) {
1338 collect_from_map<false>(registry, world, map, target, matchStamp);
1341 template <
typename Func>
1342 static void for_each_inherited_term(World& world, Entity baseEntity, Func&& func) {
1343 auto collectTerms = [&](Entity entity) {
1344 if (!world.valid(entity))
1347 const auto& ec = world.fetch(entity);
1348 if (ec.pArchetype ==
nullptr)
1351 for (
const auto id: ec.pArchetype->ids_view()) {
1352 if (
id.pair() || is_wildcard(
id) || !world.valid(
id))
1354 if (world.target(
id, OnInstantiate) != Inherit)
1361 collectTerms(baseEntity);
1362 for (
const auto inheritedBase: world.as_targets_trav_cache(baseEntity))
1363 collectTerms(inheritedBase);
1366 template <
typename TObserverMap>
1367 GAIA_NODISCARD
static bool has_semantic_is_terms(World& world,
const TObserverMap& map, EntitySpan terms) {
1368 for (
auto term: terms) {
1369 if (!is_semantic_is_term(term))
1372 const auto target = world.get(term.gen());
1373 if (!world.valid(target))
1376 if (map.find(EntityLookupKey(target)) != map.end())
1379 for (
auto inheritedTarget: world.as_targets_trav_cache(target)) {
1380 if (map.find(EntityLookupKey(inheritedTarget)) != map.end())
1388 template <
typename TObserverMap>
1389 GAIA_NODISCARD
static bool has_inherited_terms(World& world,
const TObserverMap& map, EntitySpan terms) {
1390 for (
auto term: terms) {
1391 if (!is_semantic_is_term(term))
1394 const auto target = world.get(term.gen());
1395 if (!world.valid(target))
1399 for_each_inherited_term(world, target, [&](Entity inheritedId) {
1402 const auto it = map.find(EntityLookupKey(inheritedId));
1403 found = it != map.end() && !it->second.empty();
1413 template <
typename TObserverMap>
1414 static void collect_for_inherited_terms(
1415 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity baseEntity,
1416 uint64_t matchStamp) {
1417 for_each_inherited_term(world, baseEntity, [&](Entity inheritedId) {
1418 collect_for_event_term(registry, world, map, inheritedId, matchStamp);
1422 static void execute_targets(World& world, ObserverRuntimeData& obs, EntitySpan targets) {
1423 if (targets.empty())
1427 it.set_world(&world);
1429 it.set_comp_indices(0);
1430 obs.exec(it, targets);
1433 static bool matches_direct_targets(
1434 ObserverRuntimeData& obs,
const Archetype& archetype, EntitySpan targets,
1435 QueryInfo* pQueryInfo =
nullptr) {
1436 switch (obs.plan.exec_kind()) {
1437 case ObserverPlan::ExecKind::DirectFast:
1438 if (obs.plan.is_fast_positive())
1440 if (obs.plan.is_fast_negative())
1443 case ObserverPlan::ExecKind::DirectQuery:
1445 case ObserverPlan::ExecKind::DiffLocal:
1446 case ObserverPlan::ExecKind::DiffPropagated:
1447 case ObserverPlan::ExecKind::DiffFallback:
1451 auto& queryInfo = pQueryInfo !=
nullptr ? *pQueryInfo : obs.query.fetch();
1452 return obs.query.matches_any(queryInfo, archetype, targets);
1457 using DiffDispatchCtx = DiffDispatcher::Context;
1461 cnt::darray<ObserverRuntimeData*> m_relevant_observers_tmp;
1463 cnt::map<EntityLookupKey, ObserverRuntimeData> m_observer_data;
1465 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_add;
1467 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_del;
1469 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_set;
1471 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_add_is;
1473 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_del_is;
1475 DiffObserverIndex m_diff_index_add;
1477 DiffObserverIndex m_diff_index_del;
1479 cnt::map<PropagatedTargetCacheKey, PropagatedTargetCacheEntry> m_propagated_target_cache;
1481 uint64_t m_current_match_stamp = 0;
1483 GAIA_NODISCARD DiffObserverIndex& diff_index(ObserverEvent event) {
1484 GAIA_ASSERT(event == ObserverEvent::OnAdd || event == ObserverEvent::OnDel);
1485 return event == ObserverEvent::OnAdd ? m_diff_index_add : m_diff_index_del;
1488 GAIA_NODISCARD
const DiffObserverIndex& diff_index(ObserverEvent event)
const {
1489 GAIA_ASSERT(event == ObserverEvent::OnAdd || event == ObserverEvent::OnDel);
1490 return event == ObserverEvent::OnAdd ? m_diff_index_add : m_diff_index_del;
1493 GAIA_NODISCARD
bool has_observers_for_term(Entity term)
const {
1494 const auto termKey = EntityLookupKey(term);
1495 return m_observer_map_add.find(termKey) != m_observer_map_add.end() ||
1496 m_observer_map_del.find(termKey) != m_observer_map_del.end() ||
1497 m_observer_map_set.find(termKey) != m_observer_map_set.end();
1500 GAIA_NODISCARD
static bool can_mark_term_observed(World& world, Entity term) {
1502 return world.valid(term);
1504 if (is_wildcard(term))
1507 const auto it = world.m_recs.pairs.find(EntityLookupKey(term));
1508 return it != world.m_recs.pairs.end() && world.valid(it->second, term);
1511 GAIA_NODISCARD
static bool
1512 is_semantic_is_term(Entity term, QueryMatchKind matchKind = QueryMatchKind::Semantic) {
1513 return matchKind != QueryMatchKind::Direct && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen());
1516 void mark_term_observed(World& world, Entity term,
bool observed) {
1517 auto& ec = world.fetch(term);
1518 const bool wasObserved = (ec.flags & EntityContainerFlags::IsObserved) != 0;
1519 if (wasObserved == observed)
1523 ec.flags |= EntityContainerFlags::IsObserved;
1525 ec.flags &= ~EntityContainerFlags::IsObserved;
1527 const auto it = world.m_entityToArchetypeMap.find(EntityLookupKey(term));
1528 if (it == world.m_entityToArchetypeMap.end())
1531 for (
const auto& record: it->second) {
1532 auto* pArchetype = record.pArchetype;
1534 pArchetype->observed_terms_inc();
1536 pArchetype->observed_terms_dec();
1540 template <
typename TObserverMap>
1541 static void add_observer_to_map(TObserverMap& map, Entity term, Entity observer) {
1542 const auto entityKey = EntityLookupKey(term);
1543 const auto it = map.find(entityKey);
1544 if (it == map.end())
1545 map.emplace(entityKey, cnt::darray<Entity>{observer});
1547 it->second.push_back(observer);
1550 template <
typename TObserverMap>
1551 static void add_observer_to_map_unique(TObserverMap& map, Entity term, Entity observer) {
1552 const auto entityKey = EntityLookupKey(term);
1553 const auto it = map.find(entityKey);
1554 if (it == map.end())
1555 map.emplace(entityKey, cnt::darray<Entity>{observer});
1557 add_observer_to_list(it->second, observer);
1560 static void add_observer_to_list(cnt::darray<Entity>& list, Entity observer) {
1561 if (core::has(list, observer))
1563 list.push_back(observer);
1566 static void remove_observer_from_list(cnt::darray<Entity>& list, Entity observer) {
1567 for (uint32_t i = 0; i < list.size();) {
1568 if (list[i] == observer)
1569 core::swap_erase_unsafe(list, i);
1575 static void collect_traversal_descendants(
1576 World& world, Entity relation, Entity root, QueryTravKind travKind, uint8_t travDepth, uint64_t visitStamp,
1577 cnt::darray<Entity>& outTargets) {
1578 cnt::set<EntityLookupKey> visitedPairs;
1579 auto try_mark_visited = [&](Entity entity) {
1581 return visitedPairs.insert(EntityLookupKey(entity)).second;
1582 return world.try_mark_entity_visited(entity, visitStamp);
1585 if (query_trav_has(travKind, QueryTravKind::Self)) {
1586 if (try_mark_visited(root))
1587 outTargets.push_back(root);
1590 if (!query_trav_has(travKind, QueryTravKind::Up))
1593 if (travDepth == QueryTermOptions::TravDepthUnlimited && !query_trav_has(travKind, QueryTravKind::Down)) {
1594 world.sources_bfs(relation, root, [&](Entity source) {
1595 if (try_mark_visited(source))
1596 outTargets.push_back(source);
1601 if (travDepth == 1) {
1602 world.sources(relation, root, [&](Entity source) {
1603 if (try_mark_visited(source))
1604 outTargets.push_back(source);
1609 cnt::darray_ext<Entity, 32> queue;
1610 cnt::darray_ext<uint8_t, 32> depths;
1611 queue.push_back(root);
1612 depths.push_back(0);
1614 for (uint32_t i = 0; i < queue.size(); ++i) {
1615 const auto curr = queue[i];
1616 const auto currDepth = depths[i];
1617 if (travDepth != QueryTermOptions::TravDepthUnlimited && currDepth >= travDepth)
1620 world.sources(relation, curr, [&](Entity source) {
1621 if (!try_mark_visited(source))
1624 outTargets.push_back(source);
1625 queue.push_back(source);
1626 depths.push_back((uint8_t)(currDepth + 1));
1631 static PropagatedTargetCacheEntry& ensure_propagated_targets_cached(
1632 ObserverRegistry& registry, World& world,
const ObserverRuntimeData& obs, Entity changedSource) {
1633 const PropagatedTargetCacheKey key{
1634 obs.plan.diff.bindingRelation, obs.plan.diff.traversalRelation, changedSource, obs.plan.diff.travKind,
1635 obs.plan.diff.travDepth};
1637 auto& entry = registry.m_propagated_target_cache[key];
1638 const auto bindingRelationVersion = world.rel_version(obs.plan.diff.bindingRelation);
1639 const auto traversalRelationVersion = world.rel_version(obs.plan.diff.traversalRelation);
1640 const bool cacheValid = entry.bindingRelationVersion == bindingRelationVersion &&
1641 entry.traversalRelationVersion == traversalRelationVersion;
1644 entry.bindingRelationVersion = bindingRelationVersion;
1645 entry.traversalRelationVersion = traversalRelationVersion;
1646 entry.targets.clear();
1648 const auto visitStamp = world.next_entity_visit_stamp();
1649 cnt::darray<Entity> bindingTargets;
1650 collect_traversal_descendants(
1651 world, obs.plan.diff.traversalRelation, changedSource, obs.plan.diff.travKind, obs.plan.diff.travDepth,
1652 visitStamp, bindingTargets);
1653 for (
auto bindingTarget: bindingTargets) {
1654 world.sources(obs.plan.diff.bindingRelation, bindingTarget, [&](Entity source) {
1655 entry.targets.push_back(source);
1659 DiffDispatcher::normalize_targets(entry.targets);
1665 static void collect_propagated_targets_cached(
1666 ObserverRegistry& registry, World& world,
const ObserverRuntimeData& obs, Entity changedSource,
1667 uint64_t visitStamp, cnt::set<EntityLookupKey>& visitedPairs, cnt::darray<Entity>& outTargets) {
1668 auto& entry = ensure_propagated_targets_cached(registry, world, obs, changedSource);
1670 for (
auto source: entry.targets) {
1671 const bool isNew = source.pair() ? visitedPairs.insert(EntityLookupKey(source)).second
1672 : world.try_mark_entity_visited(source, visitStamp);
1674 outTargets.push_back(source);
1678 static void append_propagated_targets_cached(
1679 ObserverRegistry& registry, World& world,
const ObserverRuntimeData& obs, Entity changedSource,
1680 cnt::darray<Entity>& outTargets) {
1681 auto& entry = ensure_propagated_targets_cached(registry, world, obs, changedSource);
1683 for (
auto source: entry.targets)
1684 outTargets.push_back(source);
1687 static bool collect_source_traversal_diff_targets(
1688 ObserverRegistry& registry, World& world, ObserverRuntimeData& obs, EntitySpan changedTerms,
1689 EntitySpan changedSources, cnt::darray<Entity>& outTargets) {
1690 if (changedSources.empty())
1692 if (!obs.plan.uses_propagated_diff_targets())
1694 if (obs.plan.diff.bindingRelation == EntityBad || obs.plan.diff.traversalRelation == EntityBad ||
1695 obs.plan.diff.traversalTriggerTermCount == 0)
1698 bool termTriggered =
false;
1699 for (
auto changedTerm: changedTerms) {
1700 for (
auto changedSource: changedSources) {
1701 if (!changedTerm.pair() && changedTerm == changedSource) {
1702 termTriggered =
true;
1709 if (changedTerm.pair() && entity_from_id(world, changedTerm.id()) == obs.plan.diff.traversalRelation) {
1710 termTriggered =
true;
1714 GAIA_FOR(obs.plan.diff.traversalTriggerTermCount) {
1715 if (obs.plan.diff.traversalTriggerTerms[i] == changedTerm) {
1716 termTriggered =
true;
1727 if (changedSources.size() == 1) {
1728 append_propagated_targets_cached(registry, world, obs, changedSources[0], outTargets);
1732 const auto visitStamp = world.next_entity_visit_stamp();
1733 cnt::set<EntityLookupKey> visitedPairs;
1734 for (
auto changedSource: changedSources)
1735 collect_propagated_targets_cached(
1736 registry, world, obs, changedSource, visitStamp, visitedPairs, outTargets);
1741 static bool collect_diff_targets_for_observer(
1742 ObserverRegistry& registry, World& world, ObserverRuntimeData& obs, EntitySpan changedTerms,
1743 EntitySpan changedTargets, cnt::darray<Entity>& outTargets) {
1744 switch (obs.plan.exec_kind()) {
1745 case ObserverPlan::ExecKind::DiffLocal:
1746 DiffDispatcher::append_valid_targets(world, outTargets, changedTargets);
1748 case ObserverPlan::ExecKind::DiffPropagated:
1749 return collect_source_traversal_diff_targets(
1750 registry, world, obs, changedTerms, changedTargets, outTargets);
1751 case ObserverPlan::ExecKind::DiffFallback:
1753 case ObserverPlan::ExecKind::DirectQuery:
1754 case ObserverPlan::ExecKind::DirectFast:
1761 static bool observer_uses_changed_traversal_relation(
1762 World& world,
const ObserverRuntimeData& obs, EntitySpan changedTerms) {
1763 if (obs.plan.diff.traversalRelationCount == 0 || changedTerms.empty())
1766 for (
auto changedTerm: changedTerms) {
1767 if (!changedTerm.pair())
1770 const auto relation = entity_from_id(world, changedTerm.id());
1771 if (!world.valid(relation))
1774 GAIA_FOR(obs.plan.diff.traversalRelationCount) {
1775 if (obs.plan.diff.traversalRelations[i] == relation)
1783 static bool is_dynamic_pair_endpoint(EntityId endpoint) {
1784 return is_wildcard(endpoint) || is_variable(endpoint);
1787 static bool is_observer_term_globally_dynamic(Entity term) {
1788 if (term == EntityBad || term == All)
1792 return is_variable((EntityId)term.id());
1794 const bool relDynamic = is_dynamic_pair_endpoint(term.id());
1795 const bool tgtDynamic = is_dynamic_pair_endpoint(term.gen());
1796 return relDynamic && tgtDynamic;
1800 GAIA_NODISCARD
bool has_observers(Entity term)
const {
1801 return has_observers_for_term(term);
1804 GAIA_NODISCARD
bool has_on_set_observers(Entity term)
const {
1805 return m_observer_map_set.find(EntityLookupKey(term)) != m_observer_map_set.end();
1808 void add_diff_observer_term(World& world, Entity observer, Entity term,
const QueryTermOptions& options) {
1809 GAIA_ASSERT(world.valid(observer));
1811 const auto& ec = world.fetch(observer);
1812 const auto compIdx = ec.pChunk->comp_idx(Observer);
1813 const auto& obs = *
reinterpret_cast<const Observer_*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
1814 auto& index = diff_index(obs.event);
1816 switch (obs.event) {
1817 case ObserverEvent::OnAdd:
1818 case ObserverEvent::OnDel:
1820 case ObserverEvent::OnSet:
1824 add_observer_to_list(index.all, observer);
1826 bool registered =
false;
1828 if (term != EntityBad && term != All) {
1829 add_observer_to_map_unique(index.direct, term, observer);
1833 if (term != EntityBad && term != All && options.entSrc != EntityBad) {
1834 add_observer_to_map_unique(index.sourceTerm, term, observer);
1838 if (options.entTrav != EntityBad) {
1839 if (term != EntityBad && term != All)
1840 add_observer_to_map_unique(index.sourceTerm, term, observer);
1841 add_observer_to_map_unique(index.traversalRelation, options.entTrav, observer);
1846 const bool relDynamic = is_dynamic_pair_endpoint(term.id());
1847 const bool tgtDynamic = is_dynamic_pair_endpoint(term.gen());
1849 if (relDynamic && !tgtDynamic) {
1850 add_observer_to_map_unique(index.pairTarget, world.get(term.gen()), observer);
1854 if (tgtDynamic && !relDynamic) {
1855 add_observer_to_map_unique(index.pairRelation, entity_from_id(world, term.id()), observer);
1860 if (!registered || is_observer_term_globally_dynamic(term))
1861 add_observer_to_list(index.global, observer);
1864 GAIA_NODISCARD DiffDispatchCtx
1865 prepare_diff(World& world, ObserverEvent event, EntitySpan terms, EntitySpan targetEntities = {}) {
1866 if GAIA_UNLIKELY (world.tearing_down())
1868 return DiffDispatcher::prepare(*
this, world, event, terms, targetEntities);
1871 GAIA_NODISCARD DiffDispatchCtx prepare_diff_add_new(World& world, EntitySpan terms) {
1872 if GAIA_UNLIKELY (world.tearing_down())
1874 return DiffDispatcher::prepare_add_new(*
this, world, terms);
1877 void append_diff_targets(World& world, DiffDispatchCtx& ctx, EntitySpan targets) {
1878 DiffDispatcher::append_targets(world, ctx, targets);
1881 void finish_diff(World& world, DiffDispatchCtx&& ctx) {
1882 if GAIA_UNLIKELY (world.tearing_down())
1884 DiffDispatcher::finish(world, GAIA_MOV(ctx));
1888 for (
auto& it: m_observer_data) {
1889 auto& obs = it.second;
1890 obs.on_each_func = {};
1893 obs.lastMatchStamp = 0;
1896 m_relevant_observers_tmp = {};
1897 m_observer_data = {};
1898 m_observer_map_add = {};
1899 m_observer_map_del = {};
1900 m_observer_map_set = {};
1901 m_observer_map_add_is = {};
1902 m_observer_map_del_is = {};
1903 m_diff_index_add = {};
1904 m_diff_index_del = {};
1905 m_propagated_target_cache = {};
1908 ObserverRuntimeData& data_add(Entity observer) {
1909 return m_observer_data[EntityLookupKey(observer)];
1912 GAIA_NODISCARD ObserverRuntimeData* data_try(Entity observer) {
1913 const auto it = m_observer_data.find(EntityLookupKey(observer));
1914 if (it == m_observer_data.end())
1919 GAIA_NODISCARD
const ObserverRuntimeData* data_try(Entity observer)
const {
1920 const auto it = m_observer_data.find(EntityLookupKey(observer));
1921 if (it == m_observer_data.end())
1926 GAIA_NODISCARD ObserverRuntimeData& data(Entity observer) {
1927 auto* pData = data_try(observer);
1928 GAIA_ASSERT(pData !=
nullptr);
1932 GAIA_NODISCARD
const ObserverRuntimeData& data(Entity observer)
const {
1933 const auto* pData = data_try(observer);
1934 GAIA_ASSERT(pData !=
nullptr);
1938 void try_mark_term_observed(World& world, Entity term) {
1939 if (!can_mark_term_observed(world, term))
1941 if (!has_observers_for_term(term))
1943 if ((world.fetch(term).flags & EntityContainerFlags::IsObserved) != 0)
1946 mark_term_observed(world, term,
true);
1953 void add(World& world, Entity term, Entity observer, QueryMatchKind matchKind = QueryMatchKind::Semantic) {
1954 GAIA_ASSERT(!observer.pair());
1955 GAIA_ASSERT(world.valid(observer));
1959 GAIA_ASSERT(term.pair() || world.valid(term));
1961 const auto wasObserved = has_observers_for_term(term);
1962 const auto canMarkObserved = can_mark_term_observed(world, term);
1963 const auto& ec = world.fetch(observer);
1964 const auto compIdx = ec.pChunk->comp_idx(Observer);
1965 const auto& obs = *
reinterpret_cast<const Observer_*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
1966 switch (obs.event) {
1967 case ObserverEvent::OnAdd:
1968 add_observer_to_map(m_observer_map_add, term, observer);
1969 if (is_semantic_is_term(term, matchKind))
1970 add_observer_to_map(m_observer_map_add_is, world.get(term.gen()), observer);
1972 case ObserverEvent::OnDel:
1973 add_observer_to_map(m_observer_map_del, term, observer);
1974 if (is_semantic_is_term(term, matchKind))
1975 add_observer_to_map(m_observer_map_del_is, world.get(term.gen()), observer);
1977 case ObserverEvent::OnSet:
1978 add_observer_to_map(m_observer_map_set, term, observer);
1981 if (!wasObserved && canMarkObserved)
1982 mark_term_observed(world, term,
true);
1988 void del(World& w, Entity term) {
1989 GAIA_ASSERT(w.valid(term));
1991 const auto termKey = EntityLookupKey(term);
1992 const auto erasedData = m_observer_data.erase(termKey);
1993 const auto erasedOnAdd = m_observer_map_add.erase(termKey);
1994 const auto erasedOnDel = m_observer_map_del.erase(termKey);
1995 const auto erasedOnSet = m_observer_map_set.erase(termKey);
1996 if (is_semantic_is_term(term)) {
1997 const auto isKey = EntityLookupKey(w.get(term.gen()));
1998 m_observer_map_add_is.erase(isKey);
1999 m_observer_map_del_is.erase(isKey);
2001 if ((erasedOnAdd != 0 || erasedOnDel != 0 || erasedOnSet != 0) && can_mark_term_observed(w, term))
2002 mark_term_observed(w, term,
false);
2006 if (erasedData == 0)
2009 auto remove_observer_from_map = [&](
auto& map) {
2010 for (
auto it = map.begin(); it != map.end();) {
2011 auto& observers = it->second;
2012 for (uint32_t i = 0; i < observers.size();) {
2013 if (observers[i] == term)
2014 core::swap_erase_unsafe(observers, i);
2019 if (observers.empty()) {
2020 const auto mappedTerm = it->first.entity();
2021 auto itToErase = it++;
2022 map.erase(itToErase);
2024 if (can_mark_term_observed(w, mappedTerm) && !has_observers_for_term(mappedTerm))
2025 mark_term_observed(w, mappedTerm,
false);
2030 remove_observer_from_map(m_observer_map_add);
2031 remove_observer_from_map(m_observer_map_del);
2032 remove_observer_from_map(m_observer_map_set);
2033 remove_observer_from_map(m_observer_map_add_is);
2034 remove_observer_from_map(m_observer_map_del_is);
2035 auto remove_observer_from_diff_index = [&](
auto& index) {
2036 remove_observer_from_map(index.direct);
2037 remove_observer_from_map(index.sourceTerm);
2038 remove_observer_from_map(index.traversalRelation);
2039 remove_observer_from_map(index.pairRelation);
2040 remove_observer_from_map(index.pairTarget);
2041 remove_observer_from_list(index.all, term);
2042 remove_observer_from_list(index.global, term);
2044 remove_observer_from_diff_index(m_diff_index_add);
2045 remove_observer_from_diff_index(m_diff_index_del);
2053 void on_add(World& world,
const Archetype& archetype, EntitySpan ents_added, EntitySpan targets) {
2054 DirectDispatcher::on_add(*
this, world, archetype, ents_added, targets);
2062 void on_del(World& world,
const Archetype& archetype, EntitySpan ents_removed, EntitySpan targets) {
2063 DirectDispatcher::on_del(*
this, world, archetype, ents_removed, targets);
2066 void on_set(World& world, Entity term, EntitySpan targets) {
2067 DirectDispatcher::on_set(*
this, world, term, targets);
2075 ComponentCache m_compCache;
2077 QueryCache m_queryCache;
2080 cnt::darray<QueryMatchScratch*> m_queryMatchScratchStack;
2082 uint32_t m_queryMatchScratchDepth = 0;
2088 QuerySerMap m_querySerMap;
2089 uint32_t m_nextQuerySerId = 0;
2092 uint32_t m_emptySpace0 = 0;
2095 EntityToArchetypeMap m_entityToArchetypeMap;
2104 PairMap m_entityToAsTargets;
2107 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsTargetsTravCache;
2115 PairMap m_entityToAsRelations;
2118 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_entityToAsRelationsTravCache;
2121 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsTravCache;
2124 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_srcBfsTravCache;
2127 mutable cnt::map<EntityLookupKey, uint32_t> m_depthOrderCache;
2130 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_sourcesAllCache;
2133 mutable cnt::map<EntityLookupKey, cnt::darray<Entity>> m_targetsAllCache;
2139 cnt::map<EntityLookupKey, ExclusiveAdjunctStore> m_exclusiveAdjunctByRel;
2141 EntityArrayMap m_srcToExclusiveAdjunctRel;
2143 cnt::map<EntityLookupKey, SparseComponentStoreErased> m_sparseComponentsByComp;
2145 cnt::map<EntityLookupKey, uint32_t> m_relationVersions;
2148 mutable cnt::map<EntityLookupKey, uint32_t> m_srcEntityVersions;
2151 ArchetypeDArray m_archetypes;
2153 ArchetypeMapByHash m_archetypesByHash;
2155 ArchetypeMapById m_archetypesById;
2158 Archetype* m_pRootArchetype =
nullptr;
2160 Archetype* m_pEntityArchetype =
nullptr;
2162 Archetype* m_pCompArchetype =
nullptr;
2164 ArchetypeId m_nextArchetypeId = 0;
2167 uint32_t m_emptySpace1 = 0;
2170 EntityContainers m_recs;
2173 cnt::map<EntityNameLookupKey, Entity> m_nameToEntity;
2175 cnt::map<EntityNameLookupKey, Entity> m_aliasToEntity;
2177 Entity m_componentScope = EntityBad;
2179 cnt::darray<Entity> m_componentLookupPath;
2181 mutable util::str m_componentScopePathCache;
2183 mutable Entity m_componentScopePathCacheEntity = EntityBad;
2185 mutable bool m_componentScopePathCacheValid =
false;
2188 cnt::set<ArchetypeLookupKey> m_reqArchetypesToDel;
2190 cnt::set<EntityLookupKey> m_reqEntitiesToDel;
2192#if GAIA_OBSERVERS_ENABLED
2194 ObserverRegistry m_observers;
2198 CommandBufferST* m_pCmdBufferST;
2200 CommandBufferMT* m_pCmdBufferMT;
2202 bool m_teardownActive =
false;
2204 ecs::Query m_systemsQuery;
2206 mutable cnt::darray<uint64_t> m_entityVisitStamps;
2208 mutable uint64_t m_entityVisitStamp = 0;
2211 cnt::set<EntityLookupKey> m_entitiesToDel;
2213 cnt::darray<ArchetypeChunkPair> m_chunksToDel;
2215 ArchetypeDArray m_archetypesToDel;
2217 uint32_t m_defragLastArchetypeIdx = 0;
2219 uint32_t m_defragEntitiesPerTick = 100;
2222 uint32_t m_worldVersion = 0;
2224 uint32_t m_enabledHierarchyVersion = 0;
2226 uint32_t m_structuralChangesLocked = 0;
2231 m_pCmdBufferST(cmd_buffer_st_create(*this)),
2233 m_pCmdBufferMT(cmd_buffer_mt_create(*this)) {
2240 cmd_buffer_destroy(*m_pCmdBufferST);
2241 cmd_buffer_destroy(*m_pCmdBufferMT);
2244 World(World&&) =
delete;
2245 World(
const World&) =
delete;
2246 World& operator=(World&&) =
delete;
2247 World& operator=(
const World&) =
delete;
2256 *
const_cast<World*
>(
this), m_queryCache,
2258 m_nextArchetypeId, m_worldVersion, m_entityToArchetypeMap, m_archetypes);
2266 q.kind(QueryCacheKind::None);
2276#if GAIA_ASSERT_ENABLED
2278 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
2279 if (!valid(entity)) {
2283 const bool allowStaleExactPair = entity.pair() && m_recs.pairs.contains(
EntityLookupKey(entity));
2284 GAIA_ASSERT(allowStaleExactPair);
2287 return m_recs[entity];
2294#if GAIA_ASSERT_ENABLED
2296 GAIA_ASSERT(!entity.pair() || !is_wildcard(entity));
2297 if (!valid(entity)) {
2301 const bool allowStaleExactPair = entity.pair() && m_recs.pairs.contains(
EntityLookupKey(entity));
2302 GAIA_ASSERT(allowStaleExactPair);
2305 return m_recs[entity];
2315 if ((ec.
flags & EntityContainerFlags::DeleteRequested) != 0)
2317 GAIA_ASSERT((ec.
flags & EntityContainerFlags::Load) == 0);
2325 return (fetch(entity).flags & EntityContainerFlags::IsDontFragment) != 0;
2332 return valid(relation) && !relation.pair() && is_dont_fragment(relation);
2340 if (!valid(relation) || relation.pair())
2343 const auto& ec = fetch(relation);
2344 return (ec.flags & EntityContainerFlags::IsExclusive) != 0 &&
2345 (ec.flags & EntityContainerFlags::IsDontFragment) != 0;
2351 if (!valid(relation) || relation.pair())
2354 return has(relation, Exclusive) && has(relation, Traversable);
2360 return valid(relation) && !relation.pair() && !is_dont_fragment(relation);
2366 return is_hierarchy_relation(relation) && is_fragmenting_relation(relation);
2374 return is_fragmenting_relation(relation);
2381 return is_fragmenting_hierarchy_relation(relation);
2389 if (!valid(component) || component.pair() || component.entity())
2392 const auto* pItem = comp_cache().
find(component);
2393 if (pItem ==
nullptr || component.kind() != EntityKind::EK_Gen || pItem->comp.soa() != 0)
2396 return component_has_out_of_line_data(pItem->comp);
2404 if (!is_out_of_line_component(component))
2407 return (fetch(component).flags & EntityContainerFlags::IsDontFragment) != 0;
2416 auto& item = comp_cache_mut().
get(component);
2419 auto& ec = m_recs.entities[component.id()];
2420 if (ec.pArchetype ==
nullptr || ec.pChunk ==
nullptr)
2423 const auto compIdx = core::get_index(ec.pArchetype->ids_view(), GAIA_ID(
Component));
2424 if (compIdx == BadIndex)
2427 auto* pComp =
reinterpret_cast<Component*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
2436 if (component.comp())
2437 set_component_sparse_storage(component);
2439 if ((ec.
flags & EntityContainerFlags::IsDontFragment) != 0)
2442 ec.
flags |= EntityContainerFlags::IsDontFragment;
2450 GAIA_ASSERT(valid(component));
2451 GAIA_ASSERT(component.comp());
2452 GAIA_ASSERT(!component.pair());
2453 GAIA_ASSERT(!component.entity());
2454 GAIA_ASSERT(component.kind() == EntityKind::EK_Gen);
2456 const auto& item = comp_cache().
get(component);
2457 GAIA_ASSERT(item.entity == component);
2459 if (item.comp.storage_type() == DataStorageType::Sparse)
2462 GAIA_ASSERT(item.comp.soa() == 0);
2463 if (item.comp.soa() != 0)
2466 const auto directTermEntityCnt = count_direct_term_entities_direct(component);
2467 GAIA_ASSERT(directTermEntityCnt == 0);
2468 if (directTermEntityCnt != 0)
2471 auto comp = item.comp;
2472 comp.data.storage = (IdentifierData)DataStorageType::Sparse;
2473 sync_component_record(component, comp);
2480 template <
typename T>
2482 using U =
typename actual_type_t<T>::Type;
2483 return !
is_pair<T>::value && entity_kind_v<T> == EntityKind::EK_Gen && !mem::is_soa_layout_v<U>;
2490 template <
typename T>
2492 if constexpr (!supports_out_of_line_component<T>())
2498 const auto* pItem = comp_cache().
find(
object);
2499 if (pItem ==
nullptr || pItem->entity !=
object || !is_out_of_line_component(
object))
2502 using U =
typename actual_type_t<T>::Type;
2503 return pItem->
comp.size() == (uint32_t)
sizeof(U) && pItem->comp.alig() == (uint32_t)
alignof(U) &&
2504 pItem->comp.soa() == 0 &&
object.kind() == entity_kind_v<T>;
2512 template <
typename T>
2514 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
2515 if (it == m_sparseComponentsByComp.end())
2518 return static_cast<SparseComponentStore<T>*
>(it->second.pStore);
2525 template <
typename T>
2527 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
2528 if (it == m_sparseComponentsByComp.end())
2531 return static_cast<const SparseComponentStore<T>*
>(it->second.pStore);
2538 template <
typename T>
2541 const auto it = m_sparseComponentsByComp.find(key);
2542 if (it != m_sparseComponentsByComp.end())
2543 return *
static_cast<SparseComponentStore<T>*
>(it->second.pStore);
2545 auto* pStore =
new SparseComponentStore<T>();
2546 m_sparseComponentsByComp.emplace(key, make_sparse_component_store_erased(pStore));
2553 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
2555 store.func_del(store.pStore, entity);
2562 const auto it = m_sparseComponentsByComp.find(
EntityLookupKey(component));
2563 if (it == m_sparseComponentsByComp.end())
2566 it->second.func_clear_store(it->second.pStore);
2567 it->second.func_del_store(it->second.pStore);
2568 m_sparseComponentsByComp.erase(it);
2575 const auto it = m_exclusiveAdjunctByRel.find(
EntityLookupKey(relation));
2576 if (it == m_exclusiveAdjunctByRel.end())
2589 static void ensure_exclusive_adjunct_src_capacity(ExclusiveAdjunctStore& store,
Entity source) {
2590 const auto required = (uint32_t)source.id() + 1;
2591 if (store.srcToTgt.size() >= required)
2594 const auto oldSize = (uint32_t)store.srcToTgt.size();
2595 auto newSize = oldSize == 0 ? 16U : oldSize;
2596 while (newSize < required)
2599 store.srcToTgt.resize(newSize, EntityBad);
2600 store.srcToTgtIdx.resize(newSize, BadIndex);
2603 static void ensure_exclusive_adjunct_tgt_capacity(ExclusiveAdjunctStore& store, Entity target) {
2604 const auto required = target.id() + 1;
2605 if (store.tgtToSrc.size() >= required)
2608 const auto oldSize = (uint32_t)store.tgtToSrc.size();
2609 auto newSize = oldSize == 0 ? 16U : oldSize;
2610 while (newSize < required)
2613 store.tgtToSrc.resize(newSize);
2616 GAIA_NODISCARD
static Entity exclusive_adjunct_target(
const ExclusiveAdjunctStore& store, Entity source) {
2617 if (source.id() >= store.srcToTgt.size())
2620 return store.srcToTgt[source.id()];
2623 GAIA_NODISCARD
static const cnt::darray<Entity>*
2624 exclusive_adjunct_sources(
const ExclusiveAdjunctStore& store, Entity target) {
2625 if (target.id() >= store.tgtToSrc.size())
2628 const auto& sources = store.tgtToSrc[target.id()];
2629 return sources.empty() ? nullptr : &sources;
2632 static void del_exclusive_adjunct_target_source(ExclusiveAdjunctStore& store, Entity target, Entity source) {
2633 GAIA_ASSERT(target.id() < store.tgtToSrc.size());
2634 if (target.id() >= store.tgtToSrc.size())
2637 auto& sources = store.tgtToSrc[target.id()];
2638 const auto idx = source.id() < store.srcToTgtIdx.size() ? store.srcToTgtIdx[source.id()] : BadIndex;
2639 GAIA_ASSERT(idx != BadIndex && idx < sources.size());
2640 if (idx == BadIndex || idx >= sources.size())
2643 const auto lastIdx = (uint32_t)sources.size() - 1;
2644 if (idx != lastIdx) {
2645 const auto movedSource = sources[lastIdx];
2646 sources[idx] = movedSource;
2647 GAIA_ASSERT(movedSource.id() < store.srcToTgtIdx.size());
2648 store.srcToTgtIdx[movedSource.id()] = idx;
2654 void exclusive_adjunct_track_src_relation(Entity source, Entity relation) {
2655 auto& rels = m_srcToExclusiveAdjunctRel[EntityLookupKey(source)];
2656 if (!core::has(rels, relation))
2657 rels.push_back(relation);
2660 void exclusive_adjunct_untrack_src_relation(Entity source, Entity relation) {
2661 const auto it = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
2662 if (it == m_srcToExclusiveAdjunctRel.end())
2665 auto& rels = it->second;
2666 const auto idx = core::get_index(rels, relation);
2667 if (idx != BadIndex)
2668 core::swap_erase_unsafe(rels, idx);
2670 m_srcToExclusiveAdjunctRel.erase(it);
2673 void exclusive_adjunct_set(Entity source, Entity relation, Entity target) {
2674 GAIA_ASSERT(is_exclusive_dont_fragment_relation(relation));
2676 auto& store = exclusive_adjunct_store_mut(relation);
2677 ensure_exclusive_adjunct_src_capacity(store, source);
2678 const auto oldTarget = store.srcToTgt[source.id()];
2679 if (oldTarget != EntityBad) {
2680 if (oldTarget == target)
2683 del_exclusive_adjunct_target_source(store, oldTarget, source);
2685 ++store.srcToTgtCnt;
2686 exclusive_adjunct_track_src_relation(source, relation);
2689 ensure_exclusive_adjunct_tgt_capacity(store, target);
2690 auto& sources = store.tgtToSrc[target.id()];
2691 store.srcToTgt[source.id()] = target;
2692 store.srcToTgtIdx[source.id()] = (uint32_t)sources.size();
2693 sources.push_back(source);
2695 touch_rel_version(relation);
2696 invalidate_queries_for_rel(relation);
2697 m_targetsTravCache = {};
2698 m_srcBfsTravCache = {};
2699 m_depthOrderCache = {};
2700 m_sourcesAllCache = {};
2701 m_targetsAllCache = {};
2704 bool exclusive_adjunct_del(Entity source, Entity relation, Entity target) {
2705 const auto itStore = m_exclusiveAdjunctByRel.find(EntityLookupKey(relation));
2706 if (itStore == m_exclusiveAdjunctByRel.end())
2709 auto& store = itStore->second;
2710 const auto oldTarget = exclusive_adjunct_target(store, source);
2711 if (oldTarget == EntityBad)
2713 if (target != EntityBad && oldTarget != target)
2716 del_exclusive_adjunct_target_source(store, oldTarget, source);
2717 store.srcToTgt[source.id()] = EntityBad;
2718 store.srcToTgtIdx[source.id()] = BadIndex;
2719 GAIA_ASSERT(store.srcToTgtCnt > 0);
2720 --store.srcToTgtCnt;
2722 exclusive_adjunct_untrack_src_relation(source, relation);
2723 if (store.srcToTgtCnt == 0)
2724 m_exclusiveAdjunctByRel.erase(itStore);
2726 touch_rel_version(relation);
2727 invalidate_queries_for_rel(relation);
2728 m_targetsTravCache = {};
2729 m_srcBfsTravCache = {};
2730 m_depthOrderCache = {};
2731 m_sourcesAllCache = {};
2732 m_targetsAllCache = {};
2737 GAIA_NODISCARD
bool has_exclusive_adjunct_pair(Entity source, Entity
object)
const {
2741 const auto srcRelsIt = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
2742 if (srcRelsIt == m_srcToExclusiveAdjunctRel.end())
2745 const auto relId =
object.id();
2746 const auto tgtId =
object.gen();
2748 if (relId != All.id()) {
2749 const auto relation = get(relId);
2750 if (!is_exclusive_dont_fragment_relation(relation))
2753 const auto* pStore = exclusive_adjunct_store(relation);
2754 if (pStore ==
nullptr)
2757 const auto target = exclusive_adjunct_target(*pStore, source);
2758 if (target == EntityBad)
2761 return tgtId == All.id() || target.id() == tgtId;
2764 if (tgtId == All.id())
2765 return !srcRelsIt->second.empty();
2767 const auto target = get(tgtId);
2768 for (
auto relKey: srcRelsIt->second) {
2769 const auto relation = relKey;
2770 const auto* pStore = exclusive_adjunct_store(relation);
2771 if (pStore ==
nullptr)
2774 if (exclusive_adjunct_target(*pStore, source) == target)
2781 void del_exclusive_adjunct_source(Entity source) {
2782 const auto it = m_srcToExclusiveAdjunctRel.find(EntityLookupKey(source));
2783 if (it == m_srcToExclusiveAdjunctRel.end())
2786 cnt::darray<Entity> relations;
2787 for (
auto relKey: it->second)
2788 relations.push_back(relKey);
2790 for (
auto relation: relations) {
2791 touch_rel_version(relation);
2792 invalidate_queries_for_rel(relation);
2793 (void)exclusive_adjunct_del(source, relation, EntityBad);
2795 m_targetsTravCache = {};
2796 m_srcBfsTravCache = {};
2797 m_depthOrderCache = {};
2798 m_sourcesAllCache = {};
2799 m_targetsAllCache = {};
2802 void del_exclusive_adjunct_relation(Entity relation) {
2803 const auto itStore = m_exclusiveAdjunctByRel.find(EntityLookupKey(relation));
2804 if (itStore == m_exclusiveAdjunctByRel.end())
2807 cnt::darray<Entity> sources;
2808 const auto& srcToTgt = itStore->second.srcToTgt;
2809 GAIA_FOR((uint32_t)srcToTgt.size()) {
2810 if (srcToTgt[i] == EntityBad)
2813 sources.push_back(get((EntityId)i));
2816 touch_rel_version(relation);
2817 invalidate_queries_for_rel(relation);
2818 for (
auto source: sources)
2819 (void)exclusive_adjunct_del(source, relation, EntityBad);
2820 m_targetsTravCache = {};
2821 m_srcBfsTravCache = {};
2822 m_depthOrderCache = {};
2823 m_sourcesAllCache = {};
2824 m_targetsAllCache = {};
2829 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
2830 if (exclusive_adjunct_sources(store, target) ==
nullptr)
2833 if (has(relKey.entity(), cond))
2845 m_serializer = ser::make_serializer(m_stream);
2851 GAIA_ASSERT(serializer.valid());
2852 m_serializer = serializer;
2857 template <
typename TSerializer>
2859 set_serializer(ser::make_serializer(serializer));
2865 return m_serializer;
2879 uint32_t m_rowSrc = 0;
2891 uint8_t m_graphEdgeOpCount = 0;
2893 bool m_graphEdgeIsAdd =
false;
2895#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
2896 static constexpr uint32_t MAX_TERMS = 32;
2897 static_assert(MAX_TERMS <= ChunkHeader::MAX_COMPONENTS);
2904 m_world(world), m_pArchetypeSrc(ec.pArchetype), m_pChunkSrc(ec.pChunk), m_rowSrc(ec.row),
2905 m_pArchetype(ec.pArchetype), m_entity(entity) {
2907 GAIA_ASSERT(ec.
pChunk->entity_view()[ec.
row] == entity);
2911 const auto& ec = world.
fetch(entity);
2912 m_pArchetypeSrc = ec.pArchetype;
2913 m_pChunkSrc = ec.pChunk;
2916 m_pArchetype = ec.pArchetype;
2932 if (m_pArchetype ==
nullptr) {
2933 reset_graph_edge_tracking();
2938 if (m_pArchetypeSrc != m_pArchetype) {
2939 auto& ec = m_world.fetch(m_entity);
2940 GAIA_ASSERT(ec.pArchetype == m_pArchetypeSrc);
2941#if GAIA_OBSERVERS_ENABLED
2942 auto delDiffCtx = tl_del_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
2943 : m_world.m_observers.prepare_diff(
2944 m_world, ObserverEvent::OnDel,
EntitySpan{tl_del_comps},
2946 auto addDiffCtx = tl_new_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
2947 : m_world.m_observers.prepare_diff(
2948 m_world, ObserverEvent::OnAdd,
EntitySpan{tl_new_comps},
2953 trigger_del_hooks(*m_pArchetype);
2956 m_world.move_entity_raw(m_entity, ec, *m_pArchetype);
2960 if (m_graphEdgeOpCount == 1 && m_graphEdgeEntity != EntityBad) {
2961 if (m_graphEdgeIsAdd)
2962 rebuild_graph_edge(m_pArchetypeSrc, m_pArchetype, m_graphEdgeEntity);
2964 rebuild_graph_edge(m_pArchetype, m_pArchetypeSrc, m_graphEdgeEntity);
2967 if (m_targetNameKey.str() !=
nullptr || m_targetAliasKey.str() !=
nullptr) {
2968 const auto compIdx = ec.pChunk->comp_idx(GAIA_ID(
EntityDesc));
2970 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut_gen<
false>(compIdx, ec.row));
2971 GAIA_ASSERT(core::check_alignment(pDesc));
2974 if (m_targetNameKey.str() !=
nullptr) {
2975 pDesc->name = m_targetNameKey.str();
2976 pDesc->name_len = m_targetNameKey.len();
2980 if (m_targetAliasKey.str() !=
nullptr) {
2981 pDesc->alias = m_targetAliasKey.str();
2982 pDesc->alias_len = m_targetAliasKey.len();
2987 trigger_add_hooks(*m_pArchetype);
2988#if GAIA_OBSERVERS_ENABLED
2989 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
2990 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
2992 cleanup_deleted_out_of_line_components();
2994 m_pArchetypeSrc = ec.pArchetype;
2995 m_pChunkSrc = ec.pChunk;
3000#if GAIA_ASSERT_ENABLED
3001 auto& ec = m_world.fetch(m_entity);
3002 GAIA_ASSERT(ec.pChunk == m_pChunkSrc);
3005#if GAIA_OBSERVERS_ENABLED
3006 auto delDiffCtx = tl_del_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
3007 : m_world.m_observers.prepare_diff(
3008 m_world, ObserverEvent::OnDel,
EntitySpan{tl_del_comps},
3010 auto addDiffCtx = tl_new_comps.empty() ? ObserverRegistry::DiffDispatchCtx{}
3011 : m_world.m_observers.prepare_diff(
3012 m_world, ObserverEvent::OnAdd,
EntitySpan{tl_new_comps},
3016 if (m_targetNameKey.str() !=
nullptr || m_targetAliasKey.str() !=
nullptr) {
3019 GAIA_ASSERT(core::check_alignment(pDesc));
3022 if (m_targetNameKey.str() !=
nullptr) {
3023 pDesc->name = m_targetNameKey.str();
3024 pDesc->name_len = m_targetNameKey.len();
3028 if (m_targetAliasKey.str() !=
nullptr) {
3029 pDesc->alias = m_targetAliasKey.str();
3030 pDesc->alias_len = m_targetAliasKey.len();
3034#if GAIA_OBSERVERS_ENABLED
3035 m_world.m_observers.finish_diff(m_world, GAIA_MOV(delDiffCtx));
3036 m_world.m_observers.finish_diff(m_world, GAIA_MOV(addDiffCtx));
3038 cleanup_deleted_out_of_line_components();
3042 m_pArchetype =
nullptr;
3043 m_targetNameKey = {};
3044 m_targetAliasKey = {};
3045 reset_graph_edge_tracking();
3055 void name(
const char* name, uint32_t len = 0) {
3056 name_inter<true>(name, len);
3071 name_inter<false>(name, len);
3078 void alias(
const char* alias, uint32_t len = 0) {
3079 alias_inter<true>(alias, len);
3087 alias_inter<false>(alias, len);
3092 if (m_entity.pair())
3097 const auto compIdx = core::get_index(m_pChunkSrc->ids_view(), GAIA_ID(
EntityDesc));
3098 if (compIdx == BadIndex)
3102 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->comp_ptr(compIdx, m_rowSrc));
3103 GAIA_ASSERT(core::check_alignment(pDesc));
3104 if (pDesc->name ==
nullptr)
3113 m_world.invalidate_scope_path_cache();
3115 pDesc->name =
nullptr;
3116 pDesc->name_len = 0;
3117 if (pDesc->alias ==
nullptr)
3120 m_targetNameKey = {};
3125 if (m_entity.pair())
3130 const auto compIdx = core::get_index(m_pChunkSrc->ids_view(), GAIA_ID(
EntityDesc));
3131 if (compIdx == BadIndex)
3135 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(m_pChunkSrc->comp_ptr(compIdx, m_rowSrc));
3136 GAIA_ASSERT(core::check_alignment(pDesc));
3137 if (pDesc->alias ==
nullptr)
3147 pDesc->alias =
nullptr;
3148 pDesc->alias_len = 0;
3149 if (pDesc->name ==
nullptr)
3152 m_targetAliasKey = {};
3158 GAIA_PROF_SCOPE(EntityBuilder::add);
3159 GAIA_ASSERT(m_world.valid(m_entity));
3160 GAIA_ASSERT(m_world.valid(entity));
3169 GAIA_PROF_SCOPE(EntityBuilder::add);
3170 GAIA_ASSERT(m_world.valid(m_entity));
3171 GAIA_ASSERT(m_world.valid(
pair.first()));
3172 GAIA_ASSERT(m_world.valid(
pair.second()));
3182 return add(
Pair(Is, entityBase));
3195 return static_cast<const World&
>(m_world).is(entity, entityBase);
3200 return add(
Pair(ChildOf, parent));
3204 template <
typename T>
3207 const auto rel = m_world.template reg_comp<typename T::rel>().entity;
3208 const auto tgt = m_world.template reg_comp<typename T::tgt>().entity;
3213 return m_world.template reg_comp<T>().entity;
3217 template <
typename T>
3220 add(register_component<T>());
3227 GAIA_PROF_SCOPE(EntityBuilder::del);
3228 GAIA_ASSERT(m_world.valid(m_entity));
3229 GAIA_ASSERT(m_world.valid(entity));
3237 GAIA_PROF_SCOPE(EntityBuilder::add);
3238 GAIA_ASSERT(m_world.valid(m_entity));
3239 GAIA_ASSERT(m_world.valid(
pair.first()));
3240 GAIA_ASSERT(m_world.valid(
pair.second()));
3245 template <
typename T>
3248 del(register_component<T>());
3255 void trigger_add_hooks(
const Archetype& newArchetype) {
3256#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3257 if GAIA_UNLIKELY (m_world.tearing_down()) {
3258 tl_new_comps.clear();
3265 #if GAIA_ENABLE_ADD_DEL_HOOKS
3269 for (
auto entity: tl_new_comps) {
3273 const auto& item = m_world.comp_cache().get(entity);
3274 const auto& hooks = ComponentCache::hooks(item);
3275 if (hooks.func_add !=
nullptr)
3276 hooks.func_add(m_world, item, m_entity);
3281 #if GAIA_OBSERVERS_ENABLED
3283 m_world.m_observers.on_add(m_world, newArchetype,
std::span<Entity>{tl_new_comps}, {&m_entity, 1});
3288 tl_new_comps.clear();
3296 void trigger_del_hooks(
const Archetype& newArchetype) {
3297#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3298 if GAIA_UNLIKELY (m_world.tearing_down()) {
3299 tl_del_comps.clear();
3304 m_world.notify_inherited_del_dependents(m_entity,
std::span<Entity>{tl_del_comps});
3307 #if GAIA_OBSERVERS_ENABLED
3309 m_world.m_observers.on_del(m_world, newArchetype,
std::span<Entity>{tl_del_comps}, {&m_entity, 1});
3314 #if GAIA_ENABLE_ADD_DEL_HOOKS
3318 for (
auto entity: tl_del_comps) {
3322 const auto& item = m_world.comp_cache().get(entity);
3323 const auto& hooks = ComponentCache::hooks(item);
3324 if (hooks.func_del !=
nullptr)
3325 hooks.func_del(m_world, item, m_entity);
3334 void cleanup_deleted_out_of_line_components() {
3335 for (
auto entity: tl_del_comps) {
3336 if (entity.pair() || !m_world.is_out_of_line_component(entity) ||
3337 m_world.is_non_fragmenting_out_of_line_component(entity))
3340 const auto it = m_world.m_sparseComponentsByComp.find(EntityLookupKey(entity));
3341 if (it != m_world.m_sparseComponentsByComp.end())
3342 it->second.func_del(it->second.pStore, m_entity);
3345 tl_del_comps.clear();
3348 bool handle_add_entity(Entity entity) {
3349 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> targets;
3351 const auto& ecMain = m_world.fetch(entity);
3353 m_world.invalidate_scope_path_cache();
3356 if ((ecMain.flags & EntityContainerFlags::HasCantCombine) != 0) {
3357 m_world.targets(entity, CantCombine, [&targets](Entity target) {
3358 targets.push_back(target);
3360 for (
auto e: targets) {
3361 if (m_pArchetype->
has(e)) {
3362#if GAIA_ASSERT_ENABLED
3363 GAIA_ASSERT2(
false,
"Trying to add an entity which can't be combined with the source");
3364 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
3372 if (entity.pair()) {
3374 const auto& ecRel = m_world.m_recs.entities[entity.id()];
3375 if ((ecRel.flags & EntityContainerFlags::IsExclusive) != 0) {
3377 entity.id(), ecRel.data.gen, (
bool)ecRel.data.ent, (
bool)ecRel.data.pair,
3378 (EntityKind)ecRel.data.kind);
3379 auto tgt = m_world.try_get(entity.gen());
3380 if (tgt == EntityBad)
3386 m_world.targets_if(m_entity, rel, [&targets](Entity target) {
3387 targets.push_back(target);
3390 return targets.size() < 2;
3393 const auto targetsCnt = targets.size();
3394 if (targetsCnt > 1) {
3395#if GAIA_ASSERT_ENABLED
3397 false,
"Trying to add a pair with exclusive relationship but there are multiple targets present. "
3398 "Make sure to add the Exclusive property before any relationships with it are created.");
3399 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
3406 const auto tgtNew = *targets.begin();
3407 if (targetsCnt == 1 && tgt != tgtNew) {
3412 if (!can_del(entity)) {
3413#if GAIA_ASSERT_ENABLED
3415 false,
"Trying to replace an exclusive relationship but the entity which is getting removed has "
3417 print_archetype_entities(m_world, *m_pArchetype, entity,
true);
3422 handle_del(ecs::Pair(rel, tgtNew));
3430 m_world.targets(entity, Requires, [&targets](Entity target) {
3431 targets.push_back(target);
3434 for (
auto e: targets) {
3435 auto* pArchetype = m_pArchetype;
3436 handle_add<false>(e);
3437 if (m_pArchetype != pArchetype)
3438 handle_add_entity(e);
3445 GAIA_NODISCARD
bool has_Requires_tgt(Entity entity)
const {
3447 auto ids = m_pArchetype->ids_view();
3449 if (m_world.has(e, Pair(Requires, entity)))
3456 static void set_flag(EntityContainerFlagsType& flags, EntityContainerFlags flag,
bool enable) {
3463 void set_flag(Entity entity, EntityContainerFlags flag,
bool enable) {
3464 auto& ec = m_world.fetch(entity);
3465 set_flag(ec.flags, flag, enable);
3468 void try_set_flags(Entity entity,
bool enable) {
3469 auto& ecMain = m_world.fetch(m_entity);
3470 try_set_CantCombine(ecMain, entity, enable);
3472 auto& ec = m_world.fetch(entity);
3473 try_set_Is(ec, entity, enable);
3474 try_set_IsExclusive(ecMain, entity, enable);
3476 try_set_sticky_component_traits(ecMain, entity);
3477 try_set_IsSingleton(ecMain, entity, enable);
3478 try_set_OnDelete(ecMain, entity, enable);
3479 try_set_OnDeleteTarget(entity, enable);
3482 void try_set_Is(EntityContainer& ec, Entity entity,
bool enable) {
3483 if (!entity.pair() || entity.id() != Is.id())
3486 set_flag(ec.flags, EntityContainerFlags::HasAliasOf, enable);
3489 void try_set_CantCombine(EntityContainer& ec, Entity entity,
bool enable) {
3490 if (!entity.pair() || entity.id() != CantCombine.id())
3493 GAIA_ASSERT(entity != m_entity);
3502 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
true);
3503 else if ((ec.flags & EntityContainerFlags::HasCantCombine) != 0) {
3504 uint32_t targets = 0;
3505 m_world.targets(m_entity, CantCombine, [&targets]([[maybe_unused]] Entity entity) {
3509 set_flag(ec.flags, EntityContainerFlags::HasCantCombine,
false);
3513 void try_set_IsExclusive(EntityContainer& ec, Entity entity,
bool enable) {
3514 if (entity.pair() || entity.id() != Exclusive.id())
3517 set_flag(ec.flags, EntityContainerFlags::IsExclusive, enable);
3520 void try_set_sticky_component_traits(EntityContainer& ecMain, Entity entity) {
3524 if (entity.id() == DontFragment.id()) {
3525 m_world.set_component_dont_fragment(m_entity, ecMain);
3529 if (entity.id() == Sparse.id())
3530 m_world.set_component_sparse_storage(m_entity);
3533 void try_set_OnDeleteTarget(Entity entity,
bool enable) {
3537 const auto rel = m_world.try_get(entity.id());
3538 const auto tgt = m_world.try_get(entity.gen());
3539 if (rel == EntityBad || tgt == EntityBad)
3544 if (m_world.has(rel, Pair(OnDeleteTarget, Delete)))
3545 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Delete, enable);
3546 else if (m_world.has(rel, Pair(OnDeleteTarget, Remove)))
3547 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Remove, enable);
3548 else if (m_world.has(rel, Pair(OnDeleteTarget, Error)))
3549 set_flag(tgt, EntityContainerFlags::OnDeleteTarget_Error, enable);
3552 void try_set_OnDelete(EntityContainer& ec, Entity entity,
bool enable) {
3553 if (entity == Pair(OnDelete, Delete))
3554 set_flag(ec.flags, EntityContainerFlags::OnDelete_Delete, enable);
3555 else if (entity == Pair(OnDelete, Remove))
3556 set_flag(ec.flags, EntityContainerFlags::OnDelete_Remove, enable);
3557 else if (entity == Pair(OnDelete, Error))
3558 set_flag(ec.flags, EntityContainerFlags::OnDelete_Error, enable);
3561 void try_set_IsSingleton(EntityContainer& ec, Entity entity,
bool enable) {
3562 const bool isSingleton = enable && m_entity == entity;
3563 set_flag(ec.flags, EntityContainerFlags::IsSingleton, isSingleton);
3566 void handle_DependsOn(Entity entity,
bool enable) {
3608 template <
bool IsBootstrap>
3609 bool handle_add(Entity entity) {
3610 const bool isDontFragmentPair =
3611 entity.pair() && m_world.is_exclusive_dont_fragment_relation(m_world.get(entity.id()));
3612#if GAIA_ASSERT_ENABLED
3613 if (!isDontFragmentPair)
3614 World::verify_add(m_world, *m_pArchetype, m_entity, entity);
3618 if (isDontFragmentPair ? m_world.has(m_entity, entity) : m_pArchetype->has(entity))
3621 if (entity.pair()) {
3622 auto relation = m_world.get(entity.id());
3623 m_world.touch_rel_version(relation);
3624 m_world.invalidate_queries_for_rel(relation);
3625 m_world.m_targetsTravCache = {};
3626 m_world.m_srcBfsTravCache = {};
3627 m_world.m_depthOrderCache = {};
3628 m_world.m_sourcesAllCache = {};
3629 m_world.m_targetsAllCache = {};
3632 try_set_flags(entity,
true);
3635 if (entity.pair() && entity.id() == Is.id()) {
3636 auto e = m_world.try_get(entity.gen());
3640 EntityLookupKey entityKey(m_entity);
3641 EntityLookupKey eKey(e);
3644 auto& entity_to_e = m_world.m_entityToAsTargets[entityKey];
3645 entity_to_e.insert(eKey);
3646 m_world.m_entityToAsTargetsTravCache = {};
3648 auto& e_to_entity = m_world.m_entityToAsRelations[eKey];
3649 e_to_entity.insert(entityKey);
3650 m_world.m_entityToAsRelationsTravCache = {};
3657 m_world.invalidate_queries_for_entity({Is, e});
3660 if (isDontFragmentPair) {
3661 const auto relation = m_world.try_get(entity.id());
3662 const auto target = m_world.try_get(entity.gen());
3663 if (relation == EntityBad || target == EntityBad)
3666 m_world.exclusive_adjunct_set(m_entity, relation, target);
3668 m_pArchetype = m_world.foc_archetype_add_no_graph(m_pArchetype, entity);
3669 note_graph_edge(entity,
true);
3672 if constexpr (!IsBootstrap) {
3673 handle_DependsOn(entity,
true);
3675#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3676 tl_new_comps.push_back(entity);
3683 void handle_del(Entity entity) {
3684 if (entity.pair() && !m_world.valid(entity)) {
3685 if (m_pArchetype->
has(entity)) {
3686 const auto relation = m_world.try_get(entity.id());
3687 if (relation != EntityBad) {
3688 m_world.touch_rel_version(relation);
3689 m_world.invalidate_queries_for_rel(relation);
3690 m_world.m_targetsTravCache = {};
3691 m_world.m_srcBfsTravCache = {};
3692 m_world.m_depthOrderCache = {};
3693 m_world.m_sourcesAllCache = {};
3694 m_world.m_targetsAllCache = {};
3697 if (entity.id() == Is.id())
3698 m_world.unlink_stale_is_relations_by_target_id(m_entity, entity.gen());
3700 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
3701 note_graph_edge(entity,
false);
3706 const bool isDontFragmentPair =
3707 entity.pair() && m_world.is_exclusive_dont_fragment_relation(m_world.get(entity.id()));
3709 m_world.invalidate_scope_path_cache();
3711#if GAIA_ASSERT_ENABLED
3712 if (!isDontFragmentPair)
3713 World::verify_del(m_world, *m_pArchetype, m_entity, entity);
3717 if (!(isDontFragmentPair ? m_world.has(m_entity, entity) : m_pArchetype->has(entity)))
3720 if (entity.pair()) {
3721 auto relation = m_world.get(entity.id());
3722 m_world.touch_rel_version(relation);
3723 m_world.invalidate_queries_for_rel(relation);
3724 m_world.m_targetsTravCache = {};
3725 m_world.m_srcBfsTravCache = {};
3726 m_world.m_depthOrderCache = {};
3727 m_world.m_sourcesAllCache = {};
3728 m_world.m_targetsAllCache = {};
3731 try_set_flags(entity,
false);
3732 handle_DependsOn(entity,
false);
3735 if (entity.pair() && entity.id() == Is.id()) {
3736 auto e = m_world.try_get(entity.gen());
3737 if (e != EntityBad) {
3738 m_world.unlink_live_is_relation(m_entity, e);
3742 if (isDontFragmentPair) {
3743 const auto relation = m_world.try_get(entity.id());
3744 const auto target = m_world.try_get(entity.gen());
3745 if (relation != EntityBad && target != EntityBad)
3746 (void)m_world.exclusive_adjunct_del(m_entity, relation, target);
3748 m_pArchetype = m_world.foc_archetype_del_no_graph(m_pArchetype, entity);
3749 note_graph_edge(entity,
false);
3752#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
3753 tl_del_comps.push_back(entity);
3757 void add_inter(Entity entity) {
3758 GAIA_ASSERT(!is_wildcard(entity));
3760 if (entity.pair()) {
3762 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
3765 if (!handle_add_entity(entity))
3768 handle_add<false>(entity);
3771 void note_graph_edge(Entity entity,
bool isAdd) {
3772 ++m_graphEdgeOpCount;
3773 m_graphEdgeEntity = entity;
3774 m_graphEdgeIsAdd = isAdd;
3777 void reset_graph_edge_tracking() {
3778 m_graphEdgeEntity = EntityBad;
3779 m_graphEdgeOpCount = 0;
3780 m_graphEdgeIsAdd =
false;
3785 static void rebuild_graph_edge(Archetype* pArchetypeLeft, Archetype* pArchetypeRight, Entity entity) {
3786 pArchetypeLeft->del_graph_edge_right_local(entity);
3787 pArchetypeRight->del_graph_edge_left_local(entity);
3788 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
3791 void add_inter_init(Entity entity) {
3792 GAIA_ASSERT(!is_wildcard(entity));
3794 if (entity.pair()) {
3796 m_world.assign_pair(entity, *m_world.m_pEntityArchetype);
3799 if (!handle_add_entity(entity))
3802 handle_add<true>(entity);
3805 GAIA_NODISCARD
bool can_del(Entity entity)
const noexcept {
3806 if (has_Requires_tgt(entity))
3812 GAIA_NODISCARD
bool is_sticky_component_trait(Entity entity)
const noexcept {
3813 if (entity.pair() || !m_entity.comp())
3816 return entity.id() == DontFragment.id() || entity.id() == Sparse.id();
3819 bool del_inter(Entity entity) {
3820 if (is_sticky_component_trait(entity))
3823 if (!can_del(entity))
3830 void del_name_inter(EntityNameLookupKey key) {
3831 const auto it = m_world.m_nameToEntity.find(key);
3835 GAIA_ASSERT(it != m_world.m_nameToEntity.end());
3836 if (it != m_world.m_nameToEntity.end()) {
3838 if (it->first.owned())
3839 mem::mem_free((
void*)key.str());
3841 m_world.m_nameToEntity.erase(it);
3845 void del_alias_inter(EntityNameLookupKey key) {
3846 const auto it = m_world.m_aliasToEntity.find(key);
3850 GAIA_ASSERT(it != m_world.m_aliasToEntity.end());
3851 if (it != m_world.m_aliasToEntity.end()) {
3853 if (it->first.owned())
3854 mem::mem_free((
void*)key.str());
3856 m_world.m_aliasToEntity.erase(it);
3860 template <
bool IsOwned>
3861 void name_inter(
const char* name, uint32_t len) {
3863 GAIA_ASSERT(!m_entity.pair());
3864 if (m_entity.pair())
3868 if (name ==
nullptr) {
3869 GAIA_ASSERT(len == 0);
3874 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
3879 const bool hasInvalidCharacter = name[i] ==
'.';
3880 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity names");
3881 if (hasInvalidCharacter)
3885 EntityNameLookupKey key(
3886 name, len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len, IsOwned);
3890 auto it = m_world.m_nameToEntity.find(key);
3891 if (it == m_world.m_nameToEntity.end()) {
3893 if (m_targetNameKey.str() !=
nullptr) {
3894 del_name_inter(m_targetNameKey);
3896 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(EntityDesc));
3897 if (compIdx != BadIndex) {
3898 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->comp_ptr_mut(compIdx, m_rowSrc));
3899 GAIA_ASSERT(core::check_alignment(pDesc));
3900 if (pDesc->name !=
nullptr) {
3901 del_name_inter(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0));
3902 pDesc->name =
nullptr;
3906 add_inter(GAIA_ID(EntityDesc));
3911 it = m_world.m_nameToEntity.emplace(key, m_entity).first;
3913#if GAIA_ASSERT_ENABLED
3914 if (it->second != m_entity && World::s_enableUniqueNameDuplicateAssert)
3915 GAIA_ASSERT(
false &&
"Trying to set non-unique name for an entity");
3922 if constexpr (IsOwned) {
3924 char* entityStr = (
char*)mem::mem_alloc(key.len() + 1);
3925 memcpy((
void*)entityStr, (
const void*)name, key.len());
3926 entityStr[key.len()] = 0;
3928 m_targetNameKey = EntityNameLookupKey(entityStr, key.len(), 1, {key.hash()});
3935 m_targetNameKey = key;
3942 m_world.invalidate_scope_path_cache();
3945 template <
bool IsOwned>
3946 void alias_inter(
const char* alias, uint32_t len) {
3948 GAIA_ASSERT(!m_entity.pair());
3949 if (m_entity.pair())
3953 if (alias ==
nullptr) {
3954 GAIA_ASSERT(len == 0);
3959 GAIA_ASSERT(len < ComponentCacheItem::MaxNameLength);
3964 const bool hasInvalidCharacter = alias[i] ==
'.';
3965 GAIA_ASSERT(!hasInvalidCharacter &&
"Character '.' can't be used in entity aliases");
3966 if (hasInvalidCharacter)
3970 EntityNameLookupKey key(
3971 alias, len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len, IsOwned);
3973 auto it = m_world.m_aliasToEntity.find(key);
3974 if (it == m_world.m_aliasToEntity.end()) {
3976 if (m_targetAliasKey.str() !=
nullptr) {
3977 del_alias_inter(m_targetAliasKey);
3979 const auto compIdx = core::get_index(m_pArchetypeSrc->ids_view(), GAIA_ID(EntityDesc));
3980 if (compIdx != BadIndex) {
3981 auto* pDesc =
reinterpret_cast<EntityDesc*
>(m_pChunkSrc->comp_ptr_mut(compIdx, m_rowSrc));
3982 GAIA_ASSERT(core::check_alignment(pDesc));
3983 if (pDesc->alias !=
nullptr) {
3984 del_alias_inter(EntityNameLookupKey(pDesc->alias, pDesc->alias_len, 0));
3985 pDesc->alias =
nullptr;
3989 add_inter(GAIA_ID(EntityDesc));
3993 it = m_world.m_aliasToEntity.emplace(key, m_entity).first;
3995#if GAIA_ASSERT_ENABLED
3996 if (it->second != m_entity && World::s_enableUniqueNameDuplicateAssert)
3997 GAIA_ASSERT(
false &&
"Trying to set non-unique alias for an entity");
4004 if constexpr (IsOwned) {
4006 char* aliasStr = (
char*)mem::mem_alloc(key.len() + 1);
4007 memcpy((
void*)aliasStr, (
const void*)alias, key.len());
4008 aliasStr[key.len()] = 0;
4010 m_targetAliasKey = EntityNameLookupKey(aliasStr, key.len(), 1, {key.hash()});
4017 m_targetAliasKey = key;
4047 if (symbol ==
nullptr || symbol[0] == 0)
4050 const auto* pItem = comp_cache().symbol(symbol, len);
4051 return pItem !=
nullptr ? pItem->
entity : EntityBad;
4058 const auto* pItem = comp_cache().
find(component);
4059 return pItem !=
nullptr ? comp_cache().symbol_name(*pItem) :
util::str_view{};
4066 GAIA_NODISCARD
Entity path(
const char* path, uint32_t len = 0)
const {
4067 if (path ==
nullptr || path[0] == 0)
4070 const auto* pItem = comp_cache().path(path, len);
4071 return pItem !=
nullptr ? pItem->entity : EntityBad;
4078 const auto* pItem = comp_cache().
find(component);
4079 return pItem !=
nullptr ? comp_cache().path_name(*pItem) :
util::str_view{};
4087 bool path(
Entity component,
const char* path, uint32_t len = 0) {
4088 auto* pItem = comp_cache_mut().
find(component);
4089 return pItem !=
nullptr ? comp_cache_mut().path(*pItem, path, len) :
false;
4096 GAIA_NODISCARD
Entity alias(
const char* alias, uint32_t len = 0)
const {
4097 if (alias ==
nullptr || alias[0] == 0)
4100 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
4101 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
4103 return it != m_aliasToEntity.end() ? it->second : EntityBad;
4113 const auto& ec = m_recs.entities[entity.id()];
4114 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
4115 if (compIdx == BadIndex)
4118 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
4119 GAIA_ASSERT(core::check_alignment(pDesc));
4120 return {pDesc->alias, pDesc->alias_len};
4129 if (!valid(entity) || entity.pair())
4132 const auto before = this->alias(entity);
4134 const auto after = this->alias(entity);
4135 if (alias ==
nullptr)
4136 return !before.empty() && after.empty();
4138 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
4148 if (!valid(entity) || entity.pair())
4151 const auto before = this->alias(entity);
4153 const auto after = this->alias(entity);
4154 if (alias ==
nullptr)
4155 return !before.empty() && after.empty();
4157 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(alias, ComponentCacheItem::MaxNameLength) : len;
4166 const auto* pItem = comp_cache().
find(entity);
4167 if (pItem ==
nullptr)
4170 const auto aliasValue = alias(entity);
4171 if (!aliasValue.empty())
4174 const auto pathValue = path(entity);
4175 if (!pathValue.empty()) {
4176 const auto symbolEntity = symbol(pathValue.data(), pathValue.size());
4177 if (symbolEntity == EntityBad || symbolEntity == entity)
4181 return symbol(entity);
4187 void invalidate_scope_path_cache()
const {
4188 m_componentScopePathCache.clear();
4189 m_componentScopePathCacheEntity = EntityBad;
4190 m_componentScopePathCacheValid =
false;
4197 GAIA_NODISCARD
bool build_scope_path(Entity scope,
util::str& out)
const {
4199 if (!valid(scope) || scope.pair())
4204 while (curr != EntityBad) {
4205 const auto currName = name(curr);
4206 if (currName.empty()) {
4211 segments.push_back(currName);
4212 curr = target(curr, ChildOf);
4215 if (segments.empty())
4218 uint32_t totalLen = 0;
4219 for (
auto segment: segments)
4220 totalLen += segment.size();
4221 totalLen += (uint32_t)segments.size() - 1;
4224 for (uint32_t i = (uint32_t)segments.size(); i > 0; --i) {
4227 out.
append(segments[i - 1]);
4236 GAIA_NODISCARD
bool current_scope_path(util::str& out)
const {
4237 if (m_componentScope == EntityBad) {
4238 invalidate_scope_path_cache();
4243 if (m_componentScopePathCacheValid && m_componentScopePathCacheEntity == m_componentScope) {
4244 out.assign(m_componentScopePathCache.view());
4248 if (!build_scope_path(m_componentScope, out)) {
4249 invalidate_scope_path_cache();
4253 m_componentScopePathCache.assign(out.view());
4254 m_componentScopePathCacheEntity = m_componentScope;
4255 m_componentScopePathCacheValid =
true;
4259 template <
typename Func>
4260 bool find_component_in_scope_chain_inter(Entity scopeEntity,
const char* name, uint32_t len, Func&& func)
const {
4261 if (scopeEntity == EntityBad)
4264 util::str scopePath;
4265 if (!build_scope_path(scopeEntity, scopePath))
4268 util::str scopedName;
4269 scopedName.reserve(scopePath.size() + 1 + len);
4271 while (!scopePath.empty()) {
4273 scopedName.append(scopePath.view());
4274 scopedName.append(
'.');
4275 scopedName.append(name, len);
4277 if (
const auto* pItem = m_compCache.path(scopedName.data(), (uint32_t)scopedName.size()); pItem !=
nullptr) {
4282 const auto parentSepIdx = scopePath.view().find_last_of(
'.');
4283 if (parentSepIdx == BadIndex)
4286 scopePath.assign(util::str_view(scopePath.data(), parentSepIdx));
4299 GAIA_NODISCARD
const ComponentCacheItem* resolve_component_name_inter(
const char* name, uint32_t len = 0)
const {
4300 GAIA_ASSERT(name !=
nullptr);
4302 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
4303 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
4304 const bool isPath = memchr(name,
'.', l) !=
nullptr;
4305 const bool isSymbol = memchr(name,
':', l) !=
nullptr;
4308 if (
const auto* pItem = m_compCache.path(name, l); pItem !=
nullptr)
4312 if (!isPath && !isSymbol) {
4313 const auto* pFound = (
const ComponentCacheItem*)
nullptr;
4314 const auto findAndStore = [&](
const ComponentCacheItem& item) {
4318 if (find_component_in_scope_chain_inter(m_componentScope, name, l, findAndStore))
4321 for (
const auto scopeEntity: m_componentLookupPath) {
4322 if (scopeEntity == m_componentScope)
4324 if (find_component_in_scope_chain_inter(scopeEntity, name, l, findAndStore))
4329 if (
const auto* pItem = m_compCache.symbol(name, l); pItem !=
nullptr)
4333 if (
const auto* pItem = m_compCache.path(name, l); pItem !=
nullptr)
4336 if (
const auto* pItem = m_compCache.short_symbol(name, l); pItem !=
nullptr)
4341 const auto aliasEntity = alias(name, l);
4342 return aliasEntity != EntityBad ? m_compCache.find(aliasEntity) :
nullptr;
4352 return entity.pair()
4353 ? valid_pair(entity)
4354 : valid_entity(entity);
4365 if (!valid_entity_id(
id))
4368 const auto& ec = m_recs.entities[id];
4369 return Entity(
id, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind);
4374 return valid_entity_id(
id) ? get(
id) : EntityBad;
4380 template <
typename T>
4382 return comp_cache().
get<T>().entity;
4388 template <
typename T>
4390#if GAIA_ECS_AUTO_COMPONENT_REGISTRATION
4393 return comp_cache().template get<T>();
4410 GAIA_NODISCARD
Entity add(EntityKind kind = EntityKind::EK_Gen) {
4411 return add(*m_pEntityArchetype,
true,
false, kind);
4418 const auto entity = add(kind);
4419 add(entity, Prefab);
4426 template <
typename Func = TFunc_Vo
id_With_Entity>
4427 void add_n(uint32_t count, Func func = func_void_with_entity) {
4428 add_entity_n(*m_pEntityArchetype, count, func);
4436 template <
typename Func = TFunc_Vo
id_With_Entity>
4437 void add_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
4438 auto& ec = m_recs.entities[entity.id()];
4440 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4441 GAIA_ASSERT(ec.pChunk !=
nullptr);
4443 add_entity_n(*ec.pArchetype, count, func);
4449 template <
typename T>
4453 using CT = component_type_t<T>;
4454 using FT =
typename CT::TypeFull;
4455 constexpr auto kind = CT::Kind;
4457 const auto* pItem = comp_cache().
find<FT>();
4458 if (pItem !=
nullptr)
4461 const auto entity = add(*m_pCompArchetype,
false,
false, kind);
4463 (void)current_scope_path(scopePath);
4465 const auto& item = comp_cache_mut().
add<FT>(entity, scopePath.
view());
4466 sset<Component>(item.entity) = item.
comp;
4468 name_raw(item.entity, item.name.str(), item.name.len());
4469#if GAIA_ECS_AUTO_COMPONENT_SCHEMA
4470 auto_populate_component_schema<FT>(comp_cache_mut().get(item.entity));
4487 add(
const char* name, uint32_t size, DataStorageType storageType, uint32_t alig = 1, uint32_t soa = 0,
4489 EntityKind kind = EntityKind::EK_Gen) {
4490 GAIA_ASSERT(name !=
nullptr);
4492 const auto len = (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength);
4493 GAIA_ASSERT(len > 0 && len < ComponentCacheItem::MaxNameLength);
4495 if (
const auto* pItem = comp_cache().symbol(name, len); pItem !=
nullptr)
4498 const auto entity = add(*m_pCompArchetype,
false,
false, kind);
4500 (void)current_scope_path(scopePath);
4501 const auto& item = comp_cache_mut().
add(
4502 entity, name, len, size, storageType, alig, soa, pSoaSizes, hashLookup, scopePath.
view());
4504 auto& ec = m_recs.entities[item.entity.id()];
4505 const auto compIdx = core::get_index(ec.pArchetype->ids_view(), GAIA_ID(
Component));
4506 GAIA_ASSERT(compIdx != BadIndex);
4507 auto* pComp =
reinterpret_cast<Component*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
4511 name_raw(item.entity, item.name.str(), item.name.len());
4512 if (item.comp.storage_type() == DataStorageType::Sparse)
4513 add(item.entity, Sparse);
4522#if GAIA_ASSERT_ENABLED
4523 if (!
object.
pair()) {
4524 const auto* pItem = comp_cache().
find(
object);
4525 if (pItem !=
nullptr && pItem->entity ==
object && is_out_of_line_component(
object))
4527 false,
"Out-of-line runtime components require an explicit typed value when added by entity id");
4547 template <
typename T>
4549 using FT =
typename component_type_t<T>::TypeFull;
4550 const auto& item = add<FT>();
4551 if constexpr (supports_out_of_line_component<FT>()) {
4552 if (is_out_of_line_component(item.entity)) {
4553 if (!is_non_fragmenting_out_of_line_component(item.entity)) {
4554 (void)sparse_component_store_mut<FT>(item.entity).add(entity);
4558#if GAIA_OBSERVERS_ENABLED
4559 auto addDiffCtx = m_observers.prepare_diff(
4562 (void)sparse_component_store_mut<FT>(item.entity).add(entity);
4563 notify_add_single(entity, item.entity);
4564#if GAIA_OBSERVERS_ENABLED
4565 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4581 template <
typename T>
4583 static_assert(core::is_raw_v<T>);
4585 if constexpr (supports_out_of_line_component<typename component_type_t<T>::TypeFull>()) {
4586 using FT =
typename component_type_t<T>::TypeFull;
4587 if (can_use_out_of_line_component<FT>(
object)) {
4588 auto& data = sparse_component_store_mut<FT>(
object).add(entity);
4589 data = GAIA_FWD(value);
4591 if (!is_non_fragmenting_out_of_line_component(
object)) {
4592#if GAIA_OBSERVERS_ENABLED
4594 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
4597 eb.add_inter_init(
object);
4599 notify_add_single(entity,
object);
4600#if GAIA_OBSERVERS_ENABLED
4601 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4605#if GAIA_OBSERVERS_ENABLED
4607 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
4609 notify_add_single(entity,
object);
4610#if GAIA_OBSERVERS_ENABLED
4611 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4618 eb.add_inter_init(
object);
4621 const auto& ec = fetch(entity);
4623 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
4624 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset(
object, GAIA_FWD(value));
4625 notify_add_single(entity,
object);
4634 template <typename T, typename U = typename actual_type_t<T>::Type>
4636 using FT =
typename component_type_t<T>::TypeFull;
4638 const auto& item = add<FT>();
4639 if (is_out_of_line_component(item.entity)) {
4640 auto& data = sparse_component_store_mut<FT>(item.entity).add(entity);
4641 data = GAIA_FWD(value);
4643 if (!is_non_fragmenting_out_of_line_component(item.entity)) {
4645 builder.
add(item.entity);
4649#if GAIA_OBSERVERS_ENABLED
4650 auto addDiffCtx = m_observers.prepare_diff(
4653 notify_add_single(entity, item.entity);
4654#if GAIA_OBSERVERS_ENABLED
4655 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4663 builder.
add(
object);
4666 const auto& ec = m_recs.entities[entity.id()];
4668 const auto idx = uint16_t(ec.row * (1U - (uint32_t)
object.kind()));
4669 ComponentSetter{*
this, ec.pChunk, entity, idx}.sset<T>(GAIA_FWD(value));
4675 return override_inter(entity,
object);
4686 template <
typename T>
4687 GAIA_NODISCARD
bool override(
Entity entity) {
4689 using FT =
typename component_type_t<T>::TypeFull;
4690 const auto& item = add<FT>();
4692 if constexpr (supports_out_of_line_component<FT>()) {
4693 if (is_out_of_line_component(item.entity)) {
4694 if (has_direct(entity, item.entity))
4697 const auto inheritedOwner = inherited_id_owner(entity, item.entity);
4698 if (inheritedOwner == EntityBad)
4701 auto* pStore = sparse_component_store<FT>(item.entity);
4702 GAIA_ASSERT(pStore !=
nullptr);
4703 auto& data = sparse_component_store_mut<FT>(item.entity).add(entity);
4704 data = pStore->get(inheritedOwner);
4706 if (!is_non_fragmenting_out_of_line_component(item.entity)) {
4708 eb.add_inter_init(item.entity);
4715 return override_inter(entity, item.entity);
4720 template <
typename T>
4723 using FT =
typename component_type_t<T>::TypeFull;
4725 if constexpr (supports_out_of_line_component<FT>()) {
4726 if (can_use_out_of_line_component<FT>(
object)) {
4727 if (has_direct(entity,
object))
4730 const auto inheritedOwner = inherited_id_owner(entity,
object);
4731 if (inheritedOwner == EntityBad)
4734 auto* pStore = sparse_component_store<FT>(
object);
4735 GAIA_ASSERT(pStore !=
nullptr);
4736 auto& data = sparse_component_store_mut<FT>(
object).add(entity);
4737 data = pStore->get(inheritedOwner);
4739 if (!is_non_fragmenting_out_of_line_component(
object)) {
4741 eb.add_inter_init(
object);
4748 return override_inter(entity,
object);
4758 GAIA_ASSERT(!entity.pair());
4759 GAIA_ASSERT(valid(entity));
4765 for (uint32_t i = (uint32_t)ids.size() - 1; i != (uint32_t)-1; --i)
4781 GAIA_ASSERT(!srcEntity.pair());
4782 GAIA_ASSERT(valid(srcEntity));
4784 auto& ec = m_recs.entities[srcEntity.id()];
4785 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4786 GAIA_ASSERT(ec.pChunk !=
nullptr);
4788 auto* pDstArchetype = ec.pArchetype;
4794 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4796 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
4797 auto& ecDst = m_recs.entities[dstEntity.id()];
4798 Chunk::copy_foreign_entity_data(ec.pChunk, ec.row, ecDst.pChunk, ecDst.row);
4801 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
4802 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
4805 copy_sparse_entity_data(srcEntity, dstEntity, [](
Entity) {
4821 template <
typename Func = TFunc_Vo
id_With_Entity>
4822 void copy_n(
Entity entity, uint32_t count, Func func = func_void_with_entity) {
4823 copy_n_inter(entity, count, func,
EntitySpan{});
4826#if GAIA_OBSERVERS_ENABLED
4835 GAIA_ASSERT(!srcEntity.pair());
4836 GAIA_ASSERT(valid(srcEntity));
4838 auto& ec = m_recs.entities[srcEntity.id()];
4839 GAIA_ASSERT(ec.pArchetype !=
nullptr);
4840 GAIA_ASSERT(ec.pChunk !=
nullptr);
4842 auto* pDstArchetype = ec.pArchetype;
4845 const bool hasEntityDesc = pDstArchetype->has<
EntityDesc>();
4847 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(
EntityDesc));
4849 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
4850 const auto sparseIdCount = copied_sparse_id_count(srcEntity, [](
Entity) {
4853 const auto addedIdCount = archetypeIdCount + sparseIdCount;
4854 auto* pAddedIds = addedIdCount != 0U ? (Entity*)alloca(
sizeof(Entity) * addedIdCount) : nullptr;
4855 write_archetype_ids(*pDstArchetype, pAddedIds);
4856 write_copied_sparse_ids(
4861 pAddedIds + archetypeIdCount);
4862 #if GAIA_OBSERVERS_ENABLED
4863 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{pAddedIds, addedIdCount});
4867 if (hasEntityDesc) {
4868 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
4869 auto& ecDst = m_recs.entities[dstEntity.id()];
4870 Chunk::copy_foreign_entity_data(ec.pChunk, ec.row, ecDst.pChunk, ecDst.row);
4873 dstEntity = add(*pDstArchetype, srcEntity.entity(), srcEntity.pair(), srcEntity.kind());
4874 Chunk::copy_entity_data(srcEntity, dstEntity, m_recs);
4877 (void)copy_sparse_entity_data(srcEntity, dstEntity, [](Entity) {
4880 m_observers.append_diff_targets(*
this, addDiffCtx, EntitySpan{&dstEntity, 1});
4882 m_observers.on_add(*
this, *pDstArchetype, EntitySpan{pAddedIds, addedIdCount}, EntitySpan{&dstEntity, 1});
4883 #if GAIA_OBSERVERS_ENABLED
4884 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4899 template <
typename Func = TFunc_Vo
id_With_Entity>
4900 void copy_ext_n(Entity entity, uint32_t count, Func func = func_void_with_entity) {
4901 auto& ec = m_recs.entities[entity.id()];
4902 auto* pDstArchetype = ec.pArchetype;
4903 if (pDstArchetype->has<EntityDesc>())
4904 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
4906 const auto archetypeIdCount = (uint32_t)pDstArchetype->ids_view().size();
4907 const auto sparseIdCount = copied_sparse_id_count(entity, [](Entity) {
4910 const auto addedIdCount = archetypeIdCount + sparseIdCount;
4911 auto* pAddedIds = addedIdCount != 0U ? (Entity*)alloca(
sizeof(Entity) * addedIdCount) : nullptr;
4912 write_archetype_ids(*pDstArchetype, pAddedIds);
4913 write_copied_sparse_ids(
4918 pAddedIds + archetypeIdCount);
4919 #if GAIA_OBSERVERS_ENABLED
4920 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{pAddedIds, addedIdCount});
4923 entity, count, func, EntitySpan{pAddedIds, addedIdCount}, EntityBad
4924 #if GAIA_OBSERVERS_ENABLED
4929 #if GAIA_OBSERVERS_ENABLED
4930 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
4936 struct CopyIterGroupState {
4937 Archetype* pArchetype =
nullptr;
4938 Chunk* pChunk =
nullptr;
4939 uint16_t startRow = 0;
4943 struct PrefabInstantiatePlanNode {
4944 Entity prefab = EntityBad;
4945 uint32_t parentIdx = BadIndex;
4946 Archetype* pDstArchetype =
nullptr;
4947 cnt::darray_ext<Entity, 16> copiedSparseIds;
4948 cnt::darray_ext<Entity, 16> addedIds;
4949 cnt::darray_ext<Entity, 16> addHookIds;
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]);
4968 template <typename Func>
4969 void flush_copy_iter_group(Func& func, CopyIterGroupState& group) {
4970 if (group.count == 0)
4975 it.set_archetype(group.pArchetype);
4976 it.set_chunk(group.pChunk);
4977 it.set_range(group.startRow, group.count);
4982 template <
typename Func>
4983 void push_copy_iter_group(Func& func, CopyIterGroupState& group, Entity instance) {
4984 const auto& ec = fetch(instance);
4986 if (group.count != 0 && ec.pArchetype == group.pArchetype && ec.pChunk == group.pChunk &&
4987 ec.row == uint16_t(group.startRow + group.count)) {
4992 flush_copy_iter_group(func, group);
4993 group.pArchetype = ec.pArchetype;
4994 group.pChunk = ec.pChunk;
4995 group.startRow = ec.row;
4999 void prepare_parent_batch(Entity parentEntity) {
5000 GAIA_ASSERT(valid(parentEntity));
5002 const auto parentPair = Pair(Parent, parentEntity);
5003 assign_pair(parentPair, *m_pEntityArchetype);
5005 touch_rel_version(Parent);
5006 invalidate_queries_for_rel(Parent);
5007 m_targetsTravCache = {};
5008 m_srcBfsTravCache = {};
5009 m_depthOrderCache = {};
5010 m_sourcesAllCache = {};
5011 m_targetsAllCache = {};
5013 auto& ecParent = fetch(parentEntity);
5014 EntityBuilder::set_flag(ecParent.flags, EntityContainerFlags::OnDeleteTarget_Delete,
true);
5018 Entity parentEntity, Archetype& archetype, Chunk& chunk, uint32_t originalChunkSize, uint32_t toCreate) {
5019 GAIA_ASSERT(valid(parentEntity));
5024 auto entities = chunk.entity_view();
5025#if GAIA_OBSERVERS_ENABLED
5026 const Entity parentPair = Pair(Parent, parentEntity);
5027 auto addDiffCtx = m_observers.prepare_diff(
5028 *
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1},
5029 EntitySpan{entities.data() + originalChunkSize, toCreate});
5031 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
5032 exclusive_adjunct_set(entities[rowIdx], Parent, parentEntity);
5035#if GAIA_OBSERVERS_ENABLED
5037 *
this, archetype, EntitySpan{&parentPair, 1}, EntitySpan{entities.data() + originalChunkSize, toCreate});
5038 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5042 void parent_direct(Entity entity, Entity parentEntity) {
5043 GAIA_ASSERT(valid(entity));
5044 GAIA_ASSERT(valid(parentEntity));
5046 prepare_parent_batch(parentEntity);
5047#if GAIA_OBSERVERS_ENABLED
5048 const Entity parentPair = Pair(Parent, parentEntity);
5050 m_observers.prepare_diff(*
this, ObserverEvent::OnAdd, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
5052 exclusive_adjunct_set(entity, Parent, parentEntity);
5054#if GAIA_OBSERVERS_ENABLED
5055 const auto& ec = fetch(entity);
5056 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&parentPair, 1}, EntitySpan{&entity, 1});
5057 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5061 void notify_add_single(Entity entity, Entity
object) {
5062#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5063 if GAIA_UNLIKELY (tearing_down())
5066 const auto& ec = fetch(entity);
5070 #if GAIA_ENABLE_ADD_DEL_HOOKS
5071 if (
object.comp()) {
5072 const auto& item = comp_cache().
get(
object);
5073 const auto& hooks = ComponentCache::hooks(item);
5074 if (hooks.func_add !=
nullptr)
5075 hooks.func_add(*
this, item, entity);
5079 #if GAIA_OBSERVERS_ENABLED
5080 m_observers.on_add(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
5090 void notify_del_single(Entity entity, Entity
object) {
5091#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5092 if GAIA_UNLIKELY (tearing_down())
5095 const auto& ec = fetch(entity);
5099 #if GAIA_OBSERVERS_ENABLED
5100 m_observers.on_del(*
this, *ec.pArchetype, EntitySpan{&object, 1}, EntitySpan{&entity, 1});
5103 #if GAIA_ENABLE_ADD_DEL_HOOKS
5104 if (
object.comp()) {
5105 const auto& item = comp_cache().
get(
object);
5106 const auto& hooks = ComponentCache::hooks(item);
5107 if (hooks.func_del !=
nullptr)
5108 hooks.func_del(*
this, item, entity);
5119 GAIA_NODISCARD
bool has_semantic_match_without_source(
5120 Entity entity, Entity
object, Entity excludedSource, cnt::set<EntityLookupKey>& visited)
const {
5121 const auto inserted = visited.insert(EntityLookupKey(entity));
5122 if (!inserted.second)
5125 if (entity != excludedSource && has_direct(entity,
object))
5128 const auto it = m_entityToAsTargets.find(EntityLookupKey(entity));
5129 if (it == m_entityToAsTargets.end())
5132 for (
const auto baseKey: it->second) {
5133 if (has_semantic_match_without_source(baseKey.entity(),
object, excludedSource, visited))
5140 void notify_inherited_del_dependents(Entity source, Entity
object) {
5141#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5142 const auto& descendants = as_relations_trav_cache(source);
5143 for (
const auto descendant: descendants) {
5144 if (descendant == source)
5146 if (has_direct(descendant,
object) || !has(descendant,
object))
5149 cnt::set<EntityLookupKey> visited;
5150 if (has_semantic_match_without_source(descendant,
object, source, visited))
5153 notify_del_single(descendant,
object);
5161 void notify_inherited_del_dependents(Entity source, EntitySpan removedObjects) {
5162 for (
const auto object: removedObjects)
5163 notify_inherited_del_dependents(source, object);
5166 template <
typename Func>
5168 Entity entity, uint32_t count, Func& func, EntitySpan addedIds, Entity parentInstance = EntityBad
5169#
if GAIA_OBSERVERS_ENABLED
5171 ObserverRegistry::DiffDispatchCtx* pAddDiffCtx =
nullptr
5174 GAIA_ASSERT(!entity.pair());
5175 GAIA_ASSERT(valid(entity));
5176 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
5181#if GAIA_OBSERVERS_ENABLED
5182 const bool useLocalAddDiff = !addedIds.empty() && pAddDiffCtx ==
nullptr;
5183 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
5184 if (useLocalAddDiff)
5185 addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
5188 auto& ec = m_recs.entities[entity.id()];
5190 GAIA_ASSERT(ec.pChunk !=
nullptr);
5191 GAIA_ASSERT(ec.pArchetype !=
nullptr);
5193 auto* pSrcChunk = ec.pChunk;
5194 auto* pDstArchetype = ec.pArchetype;
5195 const auto hasEntityDesc = pDstArchetype->has<EntityDesc>();
5197 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
5199 if (parentInstance != EntityBad)
5200 prepare_parent_batch(parentInstance);
5205 const auto srcRow = ec.row;
5207 EntityContainerCtx ctx{
true,
false, EntityKind::EK_Gen};
5209 uint32_t left = count;
5211 auto* pDstChunk = pDstArchetype->foc_free_chunk();
5212 const uint32_t originalChunkSize = pDstChunk->size();
5213 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
5214 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
5216 GAIA_FOR(toCreate) {
5217 const auto entityNew = m_recs.entities.alloc(&ctx);
5218 auto& ecNew = m_recs.entities[entityNew.id()];
5219 store_entity(ecNew, entityNew, pDstArchetype, pDstChunk);
5221#if GAIA_ASSERT_ENABLED
5222 GAIA_ASSERT(ecNew.pChunk == pDstChunk);
5223 auto entityExpected = pDstChunk->entity_view()[ecNew.row];
5224 GAIA_ASSERT(entityExpected == entityNew);
5227 if (hasEntityDesc) {
5228 Chunk::copy_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, ecNew.row);
5231 copy_sparse_entity_data(entity, entityNew, [](Entity) {
5236 pDstArchetype->try_update_free_chunk_idx();
5238 if (!hasEntityDesc) {
5239 pDstChunk->call_gen_ctors(originalChunkSize, toCreate);
5242 GAIA_PROF_SCOPE(World::copy_n_entity_data);
5243 Chunk::copy_entity_data_n_same_chunk(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
5247 pDstChunk->update_versions();
5249#if GAIA_OBSERVERS_ENABLED
5250 if (!addedIds.empty()) {
5251 auto entities = pDstChunk->entity_view();
5252 if (pAddDiffCtx !=
nullptr)
5253 m_observers.append_diff_targets(
5254 *
this, *pAddDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
5255 else if (useLocalAddDiff)
5256 m_observers.append_diff_targets(
5257 *
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
5259 *
this, *pDstArchetype, addedIds, EntitySpan{entities.data() + originalChunkSize, toCreate});
5263 if (parentInstance != EntityBad)
5264 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
5266 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
5270#if GAIA_OBSERVERS_ENABLED
5271 if (useLocalAddDiff)
5272 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5276 GAIA_NODISCARD
bool id_uses_inherit_policy(Entity
id)
const {
5277 return !is_wildcard(
id) && valid(
id) && target(
id, OnInstantiate) == Inherit;
5280 GAIA_NODISCARD Entity inherited_id_owner(Entity entity, Entity
id)
const {
5281 if (!id_uses_inherit_policy(
id))
5284 const auto& targets = as_targets_trav_cache(entity);
5285 for (
const auto target: targets) {
5286 if (has_inter(target,
id,
false))
5293 GAIA_NODISCARD
bool instantiate_copies_id(Entity
id)
const {
5294 const auto policy = target(
id, OnInstantiate);
5295 if (policy == EntityBad || policy == Override)
5297 if (policy == DontInherit || policy == Inherit)
5302 template <
typename T>
5303 void gather_sorted_prefab_children(Entity prefabEntity, T& outChildren) {
5304 sources(Parent, prefabEntity, [&](Entity childPrefab) {
5305 if (!has_direct(childPrefab, Prefab))
5307 outChildren.push_back(childPrefab);
5310 core::sort(outChildren, [](Entity left, Entity right) {
5311 return left.id() < right.id();
5315 template <
typename Func>
5317 copy_sparse_entity_data(Entity srcEntity, Entity dstEntity, Func&& shouldCopy, Entity* pCopiedIds =
nullptr) {
5318 uint32_t copiedCnt = 0;
5319 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
5320 const auto comp = compKey.entity();
5321 if (!store.func_has(store.pStore, srcEntity) || !shouldCopy(comp))
5324 GAIA_ASSERT(store.func_copy_entity !=
nullptr);
5325 if (!store.func_copy_entity(store.pStore, dstEntity, srcEntity))
5328 if (pCopiedIds !=
nullptr)
5329 pCopiedIds[copiedCnt] = comp;
5336 uint32_t copy_sparse_entity_data(
5337 Entity srcEntity, Entity dstEntity, EntitySpan copiedSparseIds, Entity* pCopiedIds =
nullptr) {
5338 uint32_t copiedCnt = 0;
5339 for (
const auto comp: copiedSparseIds) {
5340 auto it = m_sparseComponentsByComp.find(EntityLookupKey(comp));
5341 GAIA_ASSERT(it != m_sparseComponentsByComp.end());
5343 auto& store = it->second;
5344 if (!store.func_has(store.pStore, srcEntity))
5347 GAIA_ASSERT(store.func_copy_entity !=
nullptr);
5348 if (!store.func_copy_entity(store.pStore, dstEntity, srcEntity))
5351 if (pCopiedIds !=
nullptr)
5352 pCopiedIds[copiedCnt] = comp;
5359 void write_archetype_ids(
const Archetype& dstArchetype, Entity* pDst)
const {
5360 for (
const auto id: dstArchetype.ids_view())
5364 template <
typename Func>
5365 GAIA_NODISCARD uint32_t copied_sparse_id_count(Entity srcEntity, Func&& shouldCopySparse)
const {
5367 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5368 const auto comp = compKey.entity();
5369 if (!store.func_has(store.pStore, srcEntity) || !shouldCopySparse(comp) ||
5370 !is_non_fragmenting_out_of_line_component(comp))
5378 template <
typename Func>
5379 void write_copied_sparse_ids(Entity srcEntity, Func&& shouldCopySparse, Entity* pDst)
const {
5380 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5381 const auto comp = compKey.entity();
5382 if (!store.func_has(store.pStore, srcEntity) || !shouldCopySparse(comp) ||
5383 !is_non_fragmenting_out_of_line_component(comp))
5389 GAIA_NODISCARD
bool override_inter(Entity entity, Entity
object) {
5390 GAIA_ASSERT(valid(entity));
5391 GAIA_ASSERT(
object.pair() || valid(
object));
5393 if (has_direct(entity,
object))
5396 const auto inheritedOwner = inherited_id_owner(entity,
object);
5397 if (inheritedOwner == EntityBad)
5400 if (!
object.pair()) {
5401 const auto* pItem = comp_cache().
find(
object);
5402 if (pItem !=
nullptr && pItem->entity ==
object) {
5403 if (is_out_of_line_component(
object)) {
5404 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
5405 if (itSparseStore == m_sparseComponentsByComp.end())
5408 GAIA_ASSERT(itSparseStore->second.func_copy_entity !=
nullptr);
5409 if (!itSparseStore->second.func_copy_entity(itSparseStore->second.pStore, entity, inheritedOwner))
5412 if (!is_non_fragmenting_out_of_line_component(
object)) {
5413 EntityBuilder eb(*
this, entity);
5414 eb.add_inter_init(
object);
5420 if (pItem->comp.size() != 0U) {
5421 add(entity,
object);
5423 const auto& ecDst = fetch(entity);
5424 const auto& ecSrc = fetch(inheritedOwner);
5425 const auto compIdxDst = ecDst.pChunk->comp_idx(
object);
5426 const auto compIdxSrc = ecSrc.pChunk->comp_idx(
object);
5427 GAIA_ASSERT(compIdxDst != BadIndex && compIdxSrc != BadIndex);
5429 const auto idxDst = uint16_t(ecDst.row * (1U - (uint32_t)
object.kind()));
5430 const auto idxSrc = uint16_t(ecSrc.row * (1U - (uint32_t)
object.kind()));
5431 void* pDst = ecDst.pChunk->comp_ptr_mut(compIdxDst);
5432 const void* pSrc = ecSrc.pChunk->comp_ptr(compIdxSrc);
5433 pItem->copy(pDst, pSrc, idxDst, idxSrc, ecDst.pChunk->capacity(), ecSrc.pChunk->capacity());
5439 add(entity,
object);
5443 GAIA_NODISCARD
bool copy_owned_id_from_entity(Entity srcEntity, Entity dstEntity, Entity
object) {
5444 GAIA_ASSERT(valid(srcEntity));
5445 GAIA_ASSERT(valid(dstEntity));
5446 GAIA_ASSERT(
object.pair() || valid(
object));
5448 if (has_direct(dstEntity,
object))
5451 if (!
object.pair()) {
5452 const auto* pItem = comp_cache().
find(
object);
5453 if (pItem !=
nullptr && pItem->entity ==
object) {
5454 if (is_out_of_line_component(
object)) {
5455 const auto itSparseStore = m_sparseComponentsByComp.find(EntityLookupKey(
object));
5456 if (itSparseStore == m_sparseComponentsByComp.end())
5459 GAIA_ASSERT(itSparseStore->second.func_copy_entity !=
nullptr);
5460 if (!is_non_fragmenting_out_of_line_component(
object)) {
5461 if (!itSparseStore->second.func_copy_entity(itSparseStore->second.pStore, dstEntity, srcEntity))
5464 EntityBuilder eb(*
this, dstEntity);
5465 eb.add_inter_init(
object);
5467 notify_add_single(dstEntity,
object);
5470#if GAIA_OBSERVERS_ENABLED
5471 auto addDiffCtx = m_observers.prepare_diff(
5472 *
this, ObserverEvent::OnAdd, EntitySpan{&object, 1}, EntitySpan{&dstEntity, 1});
5474 if (!itSparseStore->second.func_copy_entity(itSparseStore->second.pStore, dstEntity, srcEntity))
5476 notify_add_single(dstEntity,
object);
5477#if GAIA_OBSERVERS_ENABLED
5478 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5483 if (pItem->comp.size() != 0U) {
5484 EntityBuilder eb(*
this, dstEntity);
5485 eb.add_inter_init(
object);
5488 const auto& ecDst = fetch(dstEntity);
5489 const auto& ecSrc = fetch(srcEntity);
5490 const auto compIdxDst = ecDst.pChunk->comp_idx(
object);
5491 const auto compIdxSrc = ecSrc.pChunk->comp_idx(
object);
5492 GAIA_ASSERT(compIdxDst != BadIndex && compIdxSrc != BadIndex);
5494 const auto idxDst = uint16_t(ecDst.row * (1U - (uint32_t)
object.kind()));
5495 const auto idxSrc = uint16_t(ecSrc.row * (1U - (uint32_t)
object.kind()));
5496 void* pDst = ecDst.pChunk->comp_ptr_mut(compIdxDst);
5497 const void* pSrc = ecSrc.pChunk->comp_ptr(compIdxSrc);
5498 pItem->copy(pDst, pSrc, idxDst, idxSrc, ecDst.pChunk->capacity(), ecSrc.pChunk->capacity());
5499 notify_add_single(dstEntity,
object);
5505 add(dstEntity,
object);
5509 GAIA_NODISCARD Archetype* instantiate_prefab_dst_archetype(Entity prefabEntity) {
5510 GAIA_ASSERT(!prefabEntity.pair());
5511 GAIA_ASSERT(valid(prefabEntity));
5512 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
5514 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
5517 auto& ecSrc = m_recs.entities[prefabEntity.id()];
5518 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
5520 auto* pDstArchetype = ecSrc.pArchetype;
5521 if (pDstArchetype->has<EntityDesc>())
5522 pDstArchetype = foc_archetype_del(pDstArchetype, GAIA_ID(EntityDesc));
5523 if (pDstArchetype->has(Prefab))
5524 pDstArchetype = foc_archetype_del(pDstArchetype, Prefab);
5526 for (
const auto id: ecSrc.pArchetype->ids_view()) {
5527 if (
id.pair() &&
id.
id() == Is.id()) {
5528 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
5532 if (!instantiate_copies_id(
id))
5533 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
5536 const auto isPair = Pair(Is, prefabEntity);
5537 assign_pair(isPair, *m_pEntityArchetype);
5538 pDstArchetype = foc_archetype_add(pDstArchetype, isPair);
5540 return pDstArchetype;
5543 template <
typename T>
5544 void collect_prefab_copied_sparse_ids(Entity prefabEntity, T& outCopiedSparseIds) {
5545 outCopiedSparseIds.clear();
5546 if (m_sparseComponentsByComp.empty())
5549 for (
const auto& [compKey, store]: m_sparseComponentsByComp) {
5550 const auto comp = compKey.entity();
5551 if (!store.func_has(store.pStore, prefabEntity) || !instantiate_copies_id(comp) ||
5552 !is_out_of_line_component(comp))
5554 outCopiedSparseIds.push_back(comp);
5558 template <
typename T>
5559 void collect_prefab_added_ids(Archetype* pDstArchetype, EntitySpan copiedSparseIds, T& outAddedIds) {
5560 outAddedIds.clear();
5561 for (
const auto id: pDstArchetype->ids_view())
5562 outAddedIds.push_back(id);
5564 for (
const auto comp: copiedSparseIds) {
5565 if (is_non_fragmenting_out_of_line_component(comp))
5566 outAddedIds.push_back(comp);
5570 template <
typename T>
5571 void collect_prefab_add_hook_ids(EntitySpan addedIds, T& outHookIds) {
5573 for (
const auto id: addedIds) {
5577 const auto& item = comp_cache().
get(
id);
5578 if (ComponentCache::hooks(item).func_add !=
nullptr)
5579 outHookIds.push_back(
id);
5583 GAIA_NODISCARD Entity instantiate_prefab_node_inter(
5584 Entity prefabEntity, Archetype* pDstArchetype, Entity parentInstance, EntitySpan copiedSparseIds,
5585 EntitySpan addedIds, EntitySpan addHookIds) {
5586 GAIA_ASSERT(!prefabEntity.pair());
5587 GAIA_ASSERT(valid(prefabEntity));
5588 GAIA_ASSERT(has_direct(prefabEntity, Prefab));
5589 GAIA_ASSERT(pDstArchetype !=
nullptr);
5590#if GAIA_OBSERVERS_ENABLED
5591 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{addedIds});
5594 auto& ecSrc = m_recs.entities[prefabEntity.id()];
5595 GAIA_ASSERT(ecSrc.pArchetype !=
nullptr);
5596 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
5598 EntityContainerCtx ctx{
true,
false, prefabEntity.kind()};
5599 const auto instance = m_recs.entities.alloc(&ctx);
5600 auto& ecDst = m_recs.entities[instance.id()];
5601 auto* pDstChunk = pDstArchetype->foc_free_chunk();
5602 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
5603 pDstArchetype->try_update_free_chunk_idx();
5604 Chunk::copy_foreign_entity_data(ecSrc.pChunk, ecSrc.row, pDstChunk, ecDst.row);
5605 pDstChunk->update_versions();
5607 ecDst.flags |= EntityContainerFlags::HasAliasOf;
5612 (void)copy_sparse_entity_data(prefabEntity, instance, copiedSparseIds);
5613#if GAIA_OBSERVERS_ENABLED
5614 m_observers.append_diff_targets(*
this, addDiffCtx, EntitySpan{&instance, 1});
5617 touch_rel_version(Is);
5618 invalidate_queries_for_rel(Is);
5619 m_targetsTravCache = {};
5620 m_srcBfsTravCache = {};
5621 m_depthOrderCache = {};
5622 m_sourcesAllCache = {};
5623 m_targetsAllCache = {};
5625 const auto instanceKey = EntityLookupKey(instance);
5626 const auto prefabKey = EntityLookupKey(prefabEntity);
5627 m_entityToAsTargets[instanceKey].insert(prefabKey);
5628 m_entityToAsTargetsTravCache = {};
5629 m_entityToAsRelations[prefabKey].insert(instanceKey);
5630 m_entityToAsRelationsTravCache = {};
5631 invalidate_queries_for_entity({Is, prefabEntity});
5633#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5634 if GAIA_UNLIKELY (tearing_down()) {
5635 (void)pDstArchetype;
5640 #if GAIA_ENABLE_ADD_DEL_HOOKS
5641 for (
const auto id: addHookIds) {
5642 const auto& item = comp_cache().
get(
id);
5643 const auto& hooks = ComponentCache::hooks(item);
5644 GAIA_ASSERT(hooks.func_add !=
nullptr);
5645 hooks.func_add(*
this, item, instance);
5649 #if GAIA_OBSERVERS_ENABLED
5650 m_observers.on_add(*
this, *pDstArchetype, addedIds, EntitySpan{&instance, 1});
5657#if GAIA_OBSERVERS_ENABLED
5658 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5661 if (parentInstance != EntityBad)
5662 parent_direct(instance, parentInstance);
5667 GAIA_NODISCARD Entity instantiate_prefab_node_inter(Entity prefabEntity, Entity parentInstance) {
5668 auto* pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
5669 cnt::darray_ext<Entity, 16> copiedSparseIds;
5670 cnt::darray_ext<Entity, 16> addedIds;
5671 cnt::darray_ext<Entity, 16> addHookIds;
5672 collect_prefab_copied_sparse_ids(prefabEntity, copiedSparseIds);
5673 collect_prefab_added_ids(pDstArchetype, EntitySpan{copiedSparseIds}, addedIds);
5674 collect_prefab_add_hook_ids(EntitySpan{addedIds}, addHookIds);
5675 return instantiate_prefab_node_inter(
5676 prefabEntity, pDstArchetype, parentInstance, EntitySpan{copiedSparseIds}, EntitySpan{addedIds},
5677 EntitySpan{addHookIds});
5680 template <
typename Func>
5681 void instantiate_prefab_n_inter(
5682 const PrefabInstantiatePlanNode& node, Entity parentInstance, uint32_t count, Func& func) {
5683 GAIA_ASSERT(node.prefab != EntityBad);
5684 GAIA_ASSERT(node.pDstArchetype !=
nullptr);
5688#if GAIA_OBSERVERS_ENABLED
5689 auto addDiffCtx = m_observers.prepare_diff_add_new(*
this, EntitySpan{node.addedIds});
5692 auto& ecSrc = m_recs.entities[node.prefab.id()];
5693 GAIA_ASSERT(ecSrc.pChunk !=
nullptr);
5695 if (parentInstance != EntityBad)
5696 prepare_parent_batch(parentInstance);
5698 const auto srcRow = ecSrc.row;
5699 auto* pSrcChunk = ecSrc.pChunk;
5700 auto* pDstArchetype = node.pDstArchetype;
5701 const auto prefabKey = EntityLookupKey(node.prefab);
5702 EntityContainerCtx ctx{
true,
false, node.prefab.kind()};
5704 uint32_t left = count;
5706 auto* pDstChunk = pDstArchetype->foc_free_chunk();
5707 const uint32_t originalChunkSize = pDstChunk->size();
5708 const uint32_t freeSlotsInChunk = pDstChunk->capacity() - originalChunkSize;
5709 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
5711 GAIA_FOR_(toCreate, rowOffset) {
5712 const auto instance = m_recs.entities.alloc(&ctx);
5713 auto& ecDst = m_recs.entities[instance.id()];
5714 store_entity(ecDst, instance, pDstArchetype, pDstChunk);
5715 ecDst.flags |= EntityContainerFlags::HasAliasOf;
5717 (void)copy_sparse_entity_data(node.prefab, instance, EntitySpan{node.copiedSparseIds});
5720 pDstArchetype->try_update_free_chunk_idx();
5721 Chunk::copy_foreign_entity_data_n(pSrcChunk, srcRow, pDstChunk, originalChunkSize, toCreate);
5722 pDstChunk->update_versions();
5724 touch_rel_version(Is);
5725 invalidate_queries_for_rel(Is);
5726 m_targetsTravCache = {};
5727 m_srcBfsTravCache = {};
5728 m_depthOrderCache = {};
5729 m_sourcesAllCache = {};
5730 m_targetsAllCache = {};
5732 auto entities = pDstChunk->entity_view();
5733 auto& asRelations = m_entityToAsRelations[prefabKey];
5734 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
5735 const auto instance = entities[rowIdx];
5736 m_entityToAsTargets[EntityLookupKey(instance)].insert(prefabKey);
5737 asRelations.insert(EntityLookupKey(instance));
5739 m_entityToAsTargetsTravCache = {};
5740 m_entityToAsRelationsTravCache = {};
5741 invalidate_queries_for_entity({Is, node.prefab});
5743#if GAIA_ENABLE_ADD_DEL_HOOKS || GAIA_OBSERVERS_ENABLED
5744 if GAIA_UNLIKELY (tearing_down()) {
5746 (void)originalChunkSize;
5751 #if GAIA_ENABLE_ADD_DEL_HOOKS
5752 for (
const auto id: node.addHookIds) {
5753 const auto& item = comp_cache().
get(
id);
5754 const auto& hooks = ComponentCache::hooks(item);
5755 GAIA_ASSERT(hooks.func_add !=
nullptr);
5757 GAIA_FOR2_(originalChunkSize, originalChunkSize + toCreate, rowIdx) {
5758 hooks.func_add(*
this, item, entities[rowIdx]);
5763 #if GAIA_OBSERVERS_ENABLED
5764 m_observers.append_diff_targets(
5765 *
this, addDiffCtx, EntitySpan{entities.data() + originalChunkSize, toCreate});
5767 *
this, *pDstArchetype, EntitySpan{node.addedIds},
5768 EntitySpan{entities.data() + originalChunkSize, toCreate});
5775 if (parentInstance != EntityBad)
5776 parent_batch(parentInstance, *pDstArchetype, *pDstChunk, originalChunkSize, toCreate);
5778 invoke_copy_batch_callback(func, pDstArchetype, pDstChunk, originalChunkSize, toCreate);
5782#if GAIA_OBSERVERS_ENABLED
5783 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
5787 template <
typename T>
5788 void build_prefab_instantiate_plan(Entity prefabEntity, uint32_t parentIdx, T& plan) {
5789 PrefabInstantiatePlanNode node{};
5790 node.prefab = prefabEntity;
5791 node.parentIdx = parentIdx;
5792 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
5793 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
5794 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
5795 collect_prefab_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
5797 const auto nodeIdx = (uint32_t)plan.size();
5798 plan.push_back(GAIA_MOV(node));
5800 cnt::darray_ext<Entity, 16> prefabChildren;
5801 gather_sorted_prefab_children(prefabEntity, prefabChildren);
5803 for (
const auto childPrefab: prefabChildren)
5804 build_prefab_instantiate_plan(childPrefab, nodeIdx, plan);
5807 GAIA_NODISCARD
bool instance_has_prefab_child(Entity parentInstance, Entity childPrefab)
const {
5809 sources(Parent, parentInstance, [&](Entity child) {
5812 if (has_direct(child, Pair(Is, childPrefab)))
5818 uint32_t sync_prefab_instance(
5819 Entity prefabEntity, Entity instance,
const PrefabInstantiatePlanNode& node, EntitySpan prefabChildren) {
5820 uint32_t changes = 0;
5822 const auto isPair = Pair(Is, prefabEntity);
5823 for (
const auto id: node.pDstArchetype->ids_view()) {
5824 if (
id == isPair || has_direct(instance,
id))
5826 if (copy_owned_id_from_entity(prefabEntity, instance,
id))
5830 for (
const auto comp: node.copiedSparseIds) {
5831 if (has_direct(instance, comp))
5833 if (copy_owned_id_from_entity(prefabEntity, instance, comp))
5837 for (
const auto childPrefab: prefabChildren) {
5838 if (instance_has_prefab_child(instance, childPrefab))
5840 (void)instantiate(childPrefab, instance);
5847 uint32_t sync_prefab_inter(Entity prefabEntity, cnt::set<EntityLookupKey>& visited) {
5848 GAIA_ASSERT(!prefabEntity.pair());
5849 GAIA_ASSERT(valid(prefabEntity));
5851 if (!has_direct(prefabEntity, Prefab))
5854 const auto ins = visited.insert(EntityLookupKey(prefabEntity));
5858 PrefabInstantiatePlanNode node{};
5859 node.prefab = prefabEntity;
5860 node.pDstArchetype = instantiate_prefab_dst_archetype(prefabEntity);
5861 collect_prefab_copied_sparse_ids(prefabEntity, node.copiedSparseIds);
5862 collect_prefab_added_ids(node.pDstArchetype, EntitySpan{node.copiedSparseIds}, node.addedIds);
5863 collect_prefab_add_hook_ids(EntitySpan{node.addedIds}, node.addHookIds);
5865 cnt::darray_ext<Entity, 16> prefabChildren;
5866 gather_sorted_prefab_children(prefabEntity, prefabChildren);
5868 uint32_t changes = 0;
5869 const auto& descendants = as_relations_trav_cache(prefabEntity);
5870 for (
const auto entity: descendants) {
5871 if (has_direct(entity, Prefab))
5873 changes += sync_prefab_instance(prefabEntity, entity, node, EntitySpan{prefabChildren});
5876 for (
const auto childPrefab: prefabChildren)
5877 changes += sync_prefab_inter(childPrefab, visited);
5883 GAIA_NODISCARD Entity instantiate_inter(Entity prefabEntity, Entity parentInstance) {
5884 const auto instance = instantiate_prefab_node_inter(prefabEntity, parentInstance);
5886 cnt::darray_ext<Entity, 16> prefabChildren;
5887 gather_sorted_prefab_children(prefabEntity, prefabChildren);
5889 for (
const auto childPrefab: prefabChildren)
5890 (void)instantiate_inter(childPrefab, instance);
5900 GAIA_ASSERT(!prefabEntity.pair());
5901 GAIA_ASSERT(valid(prefabEntity));
5903 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab))
5904 return copy(prefabEntity);
5906 return instantiate_inter(prefabEntity, EntityBad);
5914 GAIA_ASSERT(!prefabEntity.pair());
5915 GAIA_ASSERT(valid(prefabEntity));
5916 GAIA_ASSERT(valid(parentInstance));
5918 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
5919 const auto instance = copy(prefabEntity);
5920 parent_direct(instance, parentInstance);
5924 return instantiate_inter(prefabEntity, parentInstance);
5939 template <
typename Func = TFunc_Vo
id_With_Entity>
5941 instantiate_n(prefabEntity, EntityBad, count, func);
5946 instantiate_n(prefabEntity, parentInstance, count, func_void_with_entity);
5951 template <
typename Func>
5953 GAIA_ASSERT(!prefabEntity.pair());
5954 GAIA_ASSERT(valid(prefabEntity));
5955 GAIA_ASSERT(parentInstance == EntityBad || valid(parentInstance));
5960 if GAIA_UNLIKELY (!has_direct(prefabEntity, Prefab)) {
5961 if (parentInstance == EntityBad) {
5962 copy_n(prefabEntity, count, func);
5966 copy_n_inter(prefabEntity, count, func,
EntitySpan{}, parentInstance);
5973 if constexpr (std::is_invocable_v<Func, CopyIter&>) {
5974 build_prefab_instantiate_plan(prefabEntity, BadIndex, plan);
5975 if (plan.size() == 1) {
5976 instantiate_prefab_n_inter(plan[0], parentInstance, count, func);
5980 CopyIterGroupState group;
5982 roots.reserve(count);
5983 auto collectRoot = [&](
Entity instance) {
5984 roots.push_back(instance);
5986 instantiate_prefab_n_inter(plan[0], parentInstance, count, collectRoot);
5988 spawned.resize((uint32_t)plan.size());
5990 GAIA_FOR_(count, rootIdx) {
5991 spawned[0] = roots[rootIdx];
5992 GAIA_FOR2_(1, (uint32_t)plan.size(), planIdx) {
5993 const auto parent = spawned[plan[planIdx].parentIdx];
5994 spawned[planIdx] = instantiate_prefab_node_inter(
5995 plan[planIdx].prefab, plan[planIdx].pDstArchetype, parent,
EntitySpan{plan[planIdx].copiedSparseIds},
5999 push_copy_iter_group(func, group, roots[rootIdx]);
6002 flush_copy_iter_group(func, group);
6004 build_prefab_instantiate_plan(prefabEntity, BadIndex, plan);
6005 if (plan.size() == 1) {
6006 instantiate_prefab_n_inter(plan[0], parentInstance, count, func);
6011 roots.reserve(count);
6012 auto collectRoot = [&](
Entity instance) {
6013 roots.push_back(instance);
6015 instantiate_prefab_n_inter(plan[0], parentInstance, count, collectRoot);
6017 spawned.resize((uint32_t)plan.size());
6019 GAIA_FOR_(count, rootIdx) {
6020 spawned[0] = roots[rootIdx];
6021 GAIA_FOR2_(1, (uint32_t)plan.size(), planIdx) {
6022 const auto parent = spawned[plan[planIdx].parentIdx];
6023 spawned[planIdx] = instantiate_prefab_node_inter(
6024 plan[planIdx].prefab, plan[planIdx].pDstArchetype, parent,
EntitySpan{plan[planIdx].copiedSparseIds},
6028 func(roots[rootIdx]);
6037 GAIA_ASSERT(!prefabEntity.pair());
6038 GAIA_ASSERT(valid(prefabEntity));
6041 return sync_prefab_inter(prefabEntity, visited);
6049 if (!entity.pair()) {
6051 del_inter(
Pair(entity, All));
6052 del_inter(
Pair(All, entity));
6063 if (!
object.
pair()) {
6064 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
6065 if (itSparseStore != m_sparseComponentsByComp.end()) {
6066 if (!is_non_fragmenting_out_of_line_component(
object)) {
6071 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
6074#if GAIA_OBSERVERS_ENABLED
6076 m_observers.prepare_diff(*
this, ObserverEvent::OnDel,
EntitySpan{&object, 1},
EntitySpan{&entity, 1});
6078 notify_inherited_del_dependents(entity,
object);
6079 notify_del_single(entity,
object);
6080 itSparseStore->second.func_del(itSparseStore->second.pStore, entity);
6081#if GAIA_OBSERVERS_ENABLED
6082 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
6104 template <
typename T>
6106 using CT = component_type_t<T>;
6107 using FT =
typename CT::TypeFull;
6109 if constexpr (supports_out_of_line_component<FT>()) {
6110 if (
const auto* pItem = comp_cache().template find<FT>();
6111 pItem !=
nullptr && is_out_of_line_component(pItem->entity)) {
6112 if (sparse_component_store<FT>(pItem->entity) !=
nullptr)
6113 del(entity, pItem->entity);
6126 add(entity,
Pair(Is, entityBase));
6134 return is_inter<false>(entity, entityBase);
6144 return is_inter<true>(entity, entityBase);
6147 GAIA_NODISCARD
bool is_base(
Entity target)
const {
6148 GAIA_ASSERT(valid_entity(target));
6155 return it != m_entityToAsRelations.end();
6162 add(entity,
Pair(ChildOf, parent));
6168 return has(entity,
Pair(ChildOf, parent));
6173 add(entity,
Pair(Parent, parentEntity));
6178 return has(entity,
Pair(Parent, parentEntity));
6191#if GAIA_ENABLE_HOOKS
6193 bool TriggerSetEffects
6197 GAIA_ASSERT(valid(entity));
6199 if constexpr (supports_out_of_line_component<T>()) {
6200 const auto* pItem = comp_cache().template find<T>();
6201 if (pItem !=
nullptr && is_out_of_line_component(pItem->entity)) {
6202 auto* pStore = sparse_component_store<typename component_type_t<T>::TypeFull>(pItem->entity);
6203 GAIA_ASSERT(pStore !=
nullptr);
6204 GAIA_ASSERT(pStore->has(entity));
6206 ::gaia::ecs::update_version(m_worldVersion);
6208#if GAIA_OBSERVERS_ENABLED
6209 if constexpr (TriggerSetEffects)
6210 world_notify_on_set_entity(*
this, pItem->entity, entity);
6216 auto& ec = m_recs.entities[entity.id()];
6217 ec.pChunk->template modify<
6219#if GAIA_ENABLE_HOOKS
6225#if GAIA_OBSERVERS_ENABLED
6226 if constexpr (TriggerSetEffects) {
6229 const auto rel = comp_cache().template get<typename T::rel>().entity;
6230 const auto tgt = comp_cache().template get<typename T::tgt>().entity;
6233 term = comp_cache().template get<T>().entity;
6235 world_notify_on_set(*
this, term, *ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
6249#if GAIA_ENABLE_HOOKS
6251 bool TriggerSetEffects
6255 GAIA_ASSERT(valid(entity));
6256 GAIA_ASSERT(valid(
object));
6258 using FT =
typename component_type_t<T>::TypeFull;
6259 if constexpr (supports_out_of_line_component<FT>()) {
6260 if (can_use_out_of_line_component<FT>(
object)) {
6261 auto* pStore = sparse_component_store<FT>(
object);
6262 GAIA_ASSERT(pStore !=
nullptr);
6263 GAIA_ASSERT(pStore->has(entity));
6265 ::gaia::ecs::update_version(m_worldVersion);
6267#if GAIA_OBSERVERS_ENABLED
6268 if constexpr (TriggerSetEffects)
6269 world_notify_on_set_entity(*
this,
object, entity);
6275 auto& ec = m_recs.entities[entity.id()];
6276 const auto compIdx = ec.pChunk->comp_idx(
object);
6277 GAIA_ASSERT(compIdx != ComponentIndexBad);
6279 if constexpr (TriggerSetEffects)
6280 ec.pChunk->finish_write(compIdx, ec.row, (uint16_t)(ec.row + 1));
6282 ec.pChunk->update_world_version(compIdx);
6293 GAIA_ASSERT(valid(entity));
6295 const auto& ec = m_recs.entities[entity.id()];
6309 template <
typename T>
6312 using FT =
typename component_type_t<T>::TypeFull;
6313 using ValueType =
typename actual_type_t<T>::Type;
6314 const auto& item = add<FT>();
6315 return SetWriteProxyTyped<T, ValueType>{*
this, entity, item.entity, get<T>(entity)};
6329 template <
typename T>
6332 return SetWriteProxyObject<typename actual_type_t<T>::Type>{*
this, entity, object, get<T>(entity,
object)};
6342 template <
typename T>
6345 using FT =
typename component_type_t<T>::TypeFull;
6346 const auto& item = add<FT>();
6347 if constexpr (supports_out_of_line_component<FT>()) {
6348 if (is_out_of_line_component(item.entity))
6349 return sparse_component_store_mut<FT>(item.entity).mut(entity);
6351 return acc_mut(entity).smut<T>();
6356 template <
typename T>
6359 using FT =
typename component_type_t<T>::TypeFull;
6360 if constexpr (supports_out_of_line_component<FT>()) {
6361 if (can_use_out_of_line_component<FT>(
object))
6362 return sparse_component_store_mut<FT>(
object).mut(entity);
6364 return acc_mut(entity).smut<T>(object);
6376 template <
typename T>
6379 return sset<T>(entity);
6384 template <
typename T>
6387 return sset<T>(entity,
object);
6398 GAIA_ASSERT(valid(entity));
6400 const auto& ec = m_recs.entities[entity.id()];
6411 template <
typename T>
6413 using FT =
typename component_type_t<T>::TypeFull;
6414 const auto compEntity = [&]() {
6416 const auto rel = comp_cache().template get<typename FT::rel>().entity;
6417 const auto tgt = comp_cache().template get<typename FT::tgt>().entity;
6420 return comp_cache().template get<FT>().entity;
6423 if constexpr (supports_out_of_line_component<FT>()) {
6424 const auto* pItem = comp_cache().template find<FT>();
6425 if (pItem !=
nullptr && is_out_of_line_component(pItem->entity)) {
6426 const auto* pStore = sparse_component_store<FT>(pItem->entity);
6427 GAIA_ASSERT(pStore !=
nullptr);
6428 if (pStore->has(entity))
6429 return pStore->get(entity);
6431 const auto inheritedOwner = inherited_id_owner(entity, compEntity);
6432 GAIA_ASSERT(inheritedOwner != EntityBad);
6433 return pStore->get(inheritedOwner);
6437 const auto& ec = m_recs.entities[entity.id()];
6438 if (ec.pArchetype->has(compEntity))
6439 return acc(entity).template get<T>();
6441 const auto inheritedOwner = inherited_id_owner(entity, compEntity);
6442 GAIA_ASSERT(inheritedOwner != EntityBad);
6443 return acc(inheritedOwner).template get<T>();
6447 template <
typename T>
6449 using FT =
typename component_type_t<T>::TypeFull;
6450 if constexpr (supports_out_of_line_component<FT>()) {
6451 if (can_use_out_of_line_component<FT>(
object)) {
6452 const auto* pStore = sparse_component_store<FT>(
object);
6453 GAIA_ASSERT(pStore !=
nullptr);
6454 if (pStore->has(entity))
6455 return pStore->get(entity);
6457 const auto inheritedOwner = inherited_id_owner(entity,
object);
6458 GAIA_ASSERT(inheritedOwner != EntityBad);
6459 return pStore->get(inheritedOwner);
6463 const auto& ec = m_recs.entities[entity.id()];
6464 if (ec.pArchetype->has(
object))
6465 return acc(entity).template get<T>(
object);
6467 const auto inheritedOwner = inherited_id_owner(entity,
object);
6468 GAIA_ASSERT(inheritedOwner != EntityBad);
6469 return acc(inheritedOwner).template get<T>(
object);
6479 if (entity.pair()) {
6480 if (entity ==
Pair(All, All))
6483 if (is_wildcard(entity)) {
6488 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
6494 if (it == m_recs.pairs.end())
6497 const auto& ec = it->second;
6501#if GAIA_ASSERT_ENABLED
6503 GAIA_ASSERT(has(get(entity.id())) && has(get(entity.gen())));
6506 auto* pChunk = ec.pChunk;
6507 GAIA_ASSERT(pChunk !=
nullptr && ec.row < pChunk->size());
6516 if (entity.id() >= m_recs.entities.size() || !m_recs.entities.has(entity.id()))
6520 const auto& ec = m_recs.entities[entity.id()];
6524 auto* pChunk = ec.pChunk;
6525 return pChunk !=
nullptr && ec.row < pChunk->size();
6543 return has_inter(entity,
object,
true);
6551 return has_inter(entity,
object,
false);
6556 return has_inter(entity, (
Entity)
pair,
false);
6560 GAIA_NODISCARD
bool has_inter(
Entity entity,
Entity object,
bool allowSemanticIs)
const {
6561 const auto& ec = fetch(entity);
6565 if (
object.
pair() && has_exclusive_adjunct_pair(entity,
object))
6567 if (!
object.
pair()) {
6568 const auto itSparseStore = m_sparseComponentsByComp.find(
EntityLookupKey(
object));
6569 if (itSparseStore != m_sparseComponentsByComp.end())
6570 return itSparseStore->second.func_has(itSparseStore->second.pStore, entity) ||
6571 (allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad);
6574 const auto* pArchetype = ec.pArchetype;
6576 if (
object.pair()) {
6577 if (allowSemanticIs &&
object.
id() == Is.id() && !is_wildcard(
object.gen())) {
6578 const auto target = get(
object.gen());
6579 return valid(target) && is(entity, target);
6583 if (pArchetype->pairs() == 0)
6586 EntityId rel =
object.id();
6587 EntityId tgt =
object.gen();
6590 if (rel == All.id() && tgt == All.id())
6594 if (rel != All.id() && tgt == All.id()) {
6595 auto ids = pArchetype->ids_view();
6596 for (
auto id: ids) {
6607 if (rel == All.id() && tgt != All.id()) {
6608 auto ids = pArchetype->ids_view();
6609 for (
auto id: ids) {
6612 if (
id.gen() == tgt)
6620 if (pArchetype->has(
object))
6623 return allowSemanticIs && inherited_id_owner(entity,
object) != EntityBad;
6631 return {m_componentLookupPath.data(), m_componentLookupPath.size()};
6638 m_componentLookupPath.clear();
6639 m_componentLookupPath.reserve((uint32_t)scopes.size());
6640 for (
const auto scopeEntity: scopes) {
6641 GAIA_ASSERT(scopeEntity != EntityBad && valid(scopeEntity) && !scopeEntity.pair());
6642 if (scopeEntity == EntityBad || !valid(scopeEntity) || scopeEntity.pair())
6645 m_componentLookupPath.push_back(scopeEntity);
6652 return m_componentScope;
6660 GAIA_ASSERT(scope == EntityBad || (valid(scope) && !scope.pair()));
6661 const auto prev = m_componentScope;
6662 if (scope == EntityBad || (valid(scope) && !scope.pair())) {
6663 m_componentScope = scope;
6664 invalidate_scope_path_cache();
6674 template <
typename Func>
6676 struct ComponentScopeRestore final {
6679 ~ComponentScopeRestore() {
6680 world.scope(prevScope);
6684 ComponentScopeRestore restore{*
this, scope(scopeEntity)};
6695 if (path ==
nullptr || path[0] == 0)
6698 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(path, ComponentCacheItem::MaxNameLength) : len;
6699 if (l == 0 || l >= ComponentCacheItem::MaxNameLength)
6701 if (path[l - 1] ==
'.')
6704 Entity parent = EntityBad;
6705 uint32_t partBeg = 0;
6706 while (partBeg < l) {
6707 uint32_t partEnd = partBeg;
6708 while (partEnd < l && path[partEnd] !=
'.')
6710 if (partEnd == partBeg)
6713 const auto partLen = partEnd - partBeg;
6715 const auto it = m_nameToEntity.find(key);
6718 if (it != m_nameToEntity.end()) {
6720 if (parent != EntityBad && !
static_cast<const World&
>(*this).
child(curr, parent)) {
6721 GAIA_ASSERT2(
false,
"Module path collides with an existing entity name outside the requested scope");
6726 name(curr, path + partBeg, partLen);
6727 if (parent != EntityBad)
6728 child(curr, parent);
6732 partBeg = partEnd + 1;
6754 template <
typename T>
6756 GAIA_ASSERT(valid(entity));
6758 using FT =
typename component_type_t<T>::TypeFull;
6759 const auto compEntity = [&]() {
6761 const auto* pRel = comp_cache().template find<typename FT::rel>();
6762 const auto* pTgt = comp_cache().template find<typename FT::tgt>();
6763 if (pRel ==
nullptr || pTgt ==
nullptr)
6766 const auto rel = pRel->entity;
6767 const auto tgt = pTgt->entity;
6770 const auto* pItem = comp_cache().template find<FT>();
6771 return pItem !=
nullptr ? pItem->entity : EntityBad;
6774 if (compEntity == EntityBad)
6777 if constexpr (supports_out_of_line_component<FT>()) {
6778 const auto* pItem = comp_cache().template find<FT>();
6779 if (pItem !=
nullptr && is_out_of_line_component(pItem->entity)) {
6780 const auto* pStore = sparse_component_store<FT>(pItem->entity);
6781 return pStore !=
nullptr && (pStore->has(entity) || inherited_id_owner(entity, compEntity) != EntityBad);
6785 const auto& ec = m_recs.entities[entity.id()];
6789 return ec.pArchetype->has(compEntity) || inherited_id_owner(entity, compEntity) != EntityBad;
6832 const auto& ec = m_recs.entities[entity.id()];
6833 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(
EntityDesc));
6834 if (compIdx == BadIndex)
6837 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
6838 GAIA_ASSERT(core::check_alignment(pDesc));
6839 return {pDesc->name, pDesc->name_len};
6847 auto entity = get(entityId);
6848 return name(entity);
6861 if (name ==
nullptr || name[0] == 0)
6864 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
6865 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
6867 if (memchr(name,
'.', l) !=
nullptr) {
6868 const auto entity = get_entity_inter(name, l);
6869 if (entity != EntityBad)
6873 return get_inter(name, l);
6883 if (name ==
nullptr || name[0] == 0)
6886 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
6887 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
6889 auto push_unique = [&](
Entity entity) {
6890 if (entity == EntityBad)
6892 for (
const auto existing: out) {
6893 if (existing == entity)
6896 out.push_back(entity);
6899 push_unique(get_entity_inter(name, l));
6901 if (memchr(name,
'.', l) ==
nullptr && memchr(name,
':', l) ==
nullptr) {
6903 push_unique(item.entity);
6907 (void)find_component_in_scope_chain_inter(m_componentScope, name, l, pushLookupHit);
6908 for (
const auto scopeEntity: m_componentLookupPath) {
6909 if (scopeEntity == m_componentScope)
6912 (void)find_component_in_scope_chain_inter(scopeEntity, name, l, pushLookupHit);
6916 if (
const auto* pItem = m_compCache.symbol(name, l); pItem !=
nullptr)
6917 push_unique(pItem->entity);
6919 if (
const auto* pItem = m_compCache.path(name, l); pItem !=
nullptr) {
6920 push_unique(pItem->entity);
6925 push_unique(item.
entity);
6929 if (out.empty() && memchr(name,
'.', l) ==
nullptr && memchr(name,
':', l) ==
nullptr) {
6930 if (
const auto* pItem = m_compCache.short_symbol(name, l); pItem !=
nullptr)
6931 push_unique(pItem->entity);
6934 push_unique(alias(name, l));
6942 GAIA_NODISCARD
Entity get(
const char* name, uint32_t len = 0)
const {
6943 return resolve(name, len);
6947 GAIA_NODISCARD
Entity find_named_entity_inter(
const char* name, uint32_t len = 0)
const {
6948 if (name ==
nullptr || name[0] == 0)
6951 const auto key = EntityNameLookupKey(name, len, 0);
6952 const auto it = m_nameToEntity.find(key);
6953 return it != m_nameToEntity.end() ? it->second : EntityBad;
6956 GAIA_NODISCARD Entity get_entity_inter(
const char* name, uint32_t len = 0)
const {
6957 if (name ==
nullptr || name[0] == 0)
6961 while (name[len] !=
'\0')
6965 Entity parent = EntityBad;
6966 Entity child = EntityBad;
6967 uint32_t posDot = 0;
6970 posDot = core::get_index(str,
'.');
6971 if (posDot == BadIndex)
6972 return find_named_entity_inter(str.data(), (uint32_t)str.size());
6977 parent = find_named_entity_inter(str.data(), posDot);
6978 if (parent == EntityBad)
6981 str = str.subspan(posDot + 1);
6982 while (!str.empty()) {
6983 posDot = core::get_index(str,
'.');
6985 if (posDot == BadIndex) {
6986 child = find_named_entity_inter(str.data(), (uint32_t)str.size());
6987 if (child == EntityBad || !this->child(child, parent))
6996 child = find_named_entity_inter(str.data(), posDot);
6997 if (child == EntityBad || !this->child(child, parent))
7001 str = str.subspan(posDot + 1);
7007 GAIA_NODISCARD Entity get_inter(
const char* name, uint32_t len = 0)
const {
7008 if (name ==
nullptr || name[0] == 0)
7011 auto key = EntityNameLookupKey(name, len, 0);
7012 const auto l = key.len();
7014 if ((m_componentScope != EntityBad || !m_componentLookupPath.empty()) && memchr(name,
'.', l) ==
nullptr &&
7015 memchr(name,
':', l) ==
nullptr) {
7016 const auto* pScopedItem = resolve_component_name_inter(name, l);
7017 if (pScopedItem !=
nullptr) {
7018 const auto it = m_nameToEntity.find(key);
7019 if (it == m_nameToEntity.end())
7020 return pScopedItem->entity;
7022 if (m_compCache.find(it->second) !=
nullptr)
7023 return pScopedItem->entity;
7027 const auto it = m_nameToEntity.find(key);
7028 if (it != m_nameToEntity.end())
7032 const auto* pItem = resolve_component_name_inter(name, l);
7033 if (pItem !=
nullptr)
7034 return pItem->entity;
7036 const auto aliasEntity = alias(name, l);
7037 if (aliasEntity != EntityBad)
7052 if (it == m_tgtToRel.end())
7064 GAIA_ASSERT(valid(entity));
7068 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
7069 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
7070 for (
auto relKey: itAdjunctRels->second) {
7071 const auto relation = relKey;
7072 const auto* pStore = exclusive_adjunct_store(relation);
7073 if (pStore ==
nullptr)
7076 if (exclusive_adjunct_target(*pStore, entity) == target)
7081 const auto& ec = fetch(entity);
7082 const auto* pArchetype = ec.pArchetype;
7085 if (pArchetype->pairs() == 0)
7088 const auto indices = pArchetype->pair_tgt_indices(target);
7089 if (indices.empty())
7092 const auto ids = pArchetype->ids_view();
7093 const auto e = ids[indices[0]];
7094 const auto& ecRel = m_recs.entities[e.id()];
7095 return *ecRel.pEntity;
7103 template <
typename Func>
7105 GAIA_ASSERT(valid(entity));
7109 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
7110 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
7111 for (
auto relKey: itAdjunctRels->second) {
7112 const auto relation = relKey;
7113 const auto* pStore = exclusive_adjunct_store(relation);
7114 if (pStore ==
nullptr)
7117 if (exclusive_adjunct_target(*pStore, entity) == target)
7122 const auto& ec = fetch(entity);
7123 const auto* pArchetype = ec.pArchetype;
7126 if (pArchetype->pairs() == 0)
7129 const auto ids = pArchetype->ids_view();
7130 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
7131 const auto e = ids[idsIdx];
7133 const auto& ecRel = m_recs.entities[e.id()];
7134 auto relation = *ecRel.pEntity;
7145 template <
typename Func>
7147 GAIA_ASSERT(valid(entity));
7151 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(
EntityLookupKey(entity));
7152 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
7153 for (
auto relKey: itAdjunctRels->second) {
7154 const auto relation = relKey;
7155 const auto* pStore = exclusive_adjunct_store(relation);
7156 if (pStore ==
nullptr)
7159 if (exclusive_adjunct_target(*pStore, entity) == target) {
7160 if (!func(relation))
7166 const auto& ec = fetch(entity);
7167 const auto* pArchetype = ec.pArchetype;
7170 if (pArchetype->pairs() == 0)
7173 const auto ids = pArchetype->ids_view();
7174 for (
auto idsIdx: pArchetype->pair_tgt_indices(target)) {
7175 const auto e = ids[idsIdx];
7177 const auto& ecRel = m_recs.entities[e.id()];
7178 auto relation = *ecRel.pEntity;
7179 if (!func(relation))
7188 const auto itCache = m_entityToAsRelationsTravCache.find(key);
7189 if (itCache != m_entityToAsRelationsTravCache.end())
7190 return itCache->second;
7192 auto& cache = m_entityToAsRelationsTravCache[key];
7193 const auto it = m_entityToAsRelations.find(key);
7194 if (it == m_entityToAsRelations.end())
7199 stack.reserve((uint32_t)it->second.size());
7200 for (
auto relation: it->second)
7201 stack.push_back(relation);
7203 while (!stack.empty()) {
7204 const auto relationKey = stack.back();
7207 const auto relation = relationKey.entity();
7208 cache.push_back(relation);
7210 const auto itChild = m_entityToAsRelations.find(relationKey);
7211 if (itChild == m_entityToAsRelations.end())
7214 for (
auto childRelation: itChild->second)
7215 stack.push_back(childRelation);
7225 const auto itCache = m_entityToAsTargetsTravCache.find(key);
7226 if (itCache != m_entityToAsTargetsTravCache.end())
7227 return itCache->second;
7229 auto& cache = m_entityToAsTargetsTravCache[key];
7230 const auto it = m_entityToAsTargets.find(key);
7231 if (it == m_entityToAsTargets.end())
7236 stack.reserve((uint32_t)it->second.size());
7237 for (
auto target: it->second)
7238 stack.push_back(target);
7240 while (!stack.empty()) {
7241 const auto targetKey = stack.back();
7244 const auto target = targetKey.entity();
7245 cache.push_back(target);
7247 const auto itChild = m_entityToAsTargets.find(targetKey);
7248 if (itChild == m_entityToAsTargets.end())
7251 for (
auto childTarget: itChild->second)
7252 stack.push_back(childTarget);
7262 const auto itCache = m_targetsTravCache.find(key);
7263 if (itCache != m_targetsTravCache.end())
7264 return itCache->second;
7266 auto& cache = m_targetsTravCache[key];
7267 if (!valid(relation) || !valid(source))
7271 GAIA_FOR(MAX_TRAV_DEPTH) {
7272 const auto next = target(curr, relation);
7273 if (next == EntityBad || next == curr)
7276 cache.push_back(next);
7287 const auto itCache = m_targetsAllCache.find(key);
7288 if (itCache != m_targetsAllCache.end())
7289 return itCache->second;
7291 auto& cache = m_targetsAllCache[key];
7295 const auto visitStamp = next_entity_visit_stamp();
7297 const auto itAdjunctRels = m_srcToExclusiveAdjunctRel.find(key);
7298 if (itAdjunctRels != m_srcToExclusiveAdjunctRel.end()) {
7299 for (
auto rel: itAdjunctRels->second) {
7300 const auto* pStore = exclusive_adjunct_store(rel);
7301 if (pStore ==
nullptr)
7304 const auto target = exclusive_adjunct_target(*pStore, source);
7305 if (target != EntityBad && try_mark_entity_visited(target, visitStamp))
7306 cache.push_back(target);
7310 const auto& ec = fetch(source);
7311 const auto* pArchetype = ec.pArchetype;
7312 if (pArchetype->pairs() == 0)
7315 const auto ids = pArchetype->ids_view();
7316 for (
auto idsIdx: pArchetype->pair_indices()) {
7317 const auto id = ids[idsIdx];
7318 const auto target = pair_target_if_alive(
id);
7319 if (target == EntityBad)
7321 if (try_mark_entity_visited(target, visitStamp))
7322 cache.push_back(target);
7332 const auto itCache = m_sourcesAllCache.find(key);
7333 if (itCache != m_sourcesAllCache.end())
7334 return itCache->second;
7336 auto& cache = m_sourcesAllCache[key];
7340 const auto visitStamp = next_entity_visit_stamp();
7342 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
7344 const auto* pSources = exclusive_adjunct_sources(store, target);
7345 if (pSources ==
nullptr)
7348 for (
auto source: *pSources) {
7349 if (valid(source) && try_mark_entity_visited(source, visitStamp))
7350 cache.push_back(source);
7354 const auto pair =
Pair(All, target);
7356 if (it == m_entityToArchetypeMap.end())
7359 for (
const auto& record: it->second) {
7360 const auto* pArchetype = record.pArchetype;
7361 if (pArchetype->is_req_del())
7364 for (
const auto* pChunk: pArchetype->chunks()) {
7365 const auto entities = pChunk->entity_view();
7366 GAIA_EACH(entities) {
7367 const auto source = entities[i];
7370 if (try_mark_entity_visited(source, visitStamp))
7371 cache.push_back(source);
7381 template <
typename Func>
7383 if (!valid(relation) || !valid(source))
7387 GAIA_FOR(MAX_TRAV_DEPTH) {
7388 const auto next = target(curr, relation);
7389 if (next == EntityBad || next == curr)
7402 template <
typename Func>
7404 if (!valid(relation) || !valid(source))
7408 GAIA_FOR(MAX_TRAV_DEPTH) {
7409 const auto next = target(curr, relation);
7410 if (next == EntityBad || next == curr)
7427 const auto itCache = m_srcBfsTravCache.find(key);
7428 if (itCache != m_srcBfsTravCache.end())
7429 return itCache->second;
7431 auto& cache = m_srcBfsTravCache[key];
7432 if (!valid(relation) || !valid(rootTarget))
7436 queue.push_back(rootTarget);
7441 for (uint32_t i = 0; i < queue.size(); ++i) {
7442 const auto currTarget = queue[i];
7445 sources(relation, currTarget, [&](
Entity source) {
7447 const auto ins = visited.insert(keySource);
7451 children.push_back(source);
7455 return left.id() < right.id();
7458 for (
auto child: children) {
7459 cache.push_back(child);
7460 queue.push_back(child);
7472 const auto itCache = m_depthOrderCache.find(key);
7473 if (itCache != m_depthOrderCache.end()) {
7474 GAIA_ASSERT(itCache->second != GroupIdMax &&
"depth_order requires an acyclic relation graph");
7475 return itCache->second;
7478 if (!valid(relation) || !valid(sourceTarget))
7482 m_depthOrderCache[key] = GroupIdMax;
7485 targets(sourceTarget, relation, [&](
Entity next) {
7486 const auto nextDepth = depth_order_cache(relation, next);
7489 const auto candidate = nextDepth + 1;
7490 if (candidate > depth)
7494 m_depthOrderCache[key] = depth;
7502 template <
typename Func>
7507 const auto& relations = as_relations_trav_cache(target);
7508 for (
auto relation: relations)
7517 template <
typename Func>
7522 const auto& relations = as_relations_trav_cache(target);
7523 for (
auto relation: relations) {
7538 if (it == m_relToTgt.end())
7552 if (relation != All && !valid(relation))
7555 if (relation == All) {
7556 const auto& targets = targets_all_cache(entity);
7557 return targets.empty() ? EntityBad : targets[0];
7560 if (is_exclusive_dont_fragment_relation(relation)) {
7561 const auto* pStore = exclusive_adjunct_store(relation);
7562 if (pStore ==
nullptr)
7565 return exclusive_adjunct_target(*pStore, entity);
7568 const auto& ec = fetch(entity);
7569 const auto* pArchetype = ec.pArchetype;
7572 if (pArchetype->pairs() == 0)
7575 const auto ids = pArchetype->ids_view();
7576 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
7577 const auto e = ids[idsIdx];
7578 const auto target = pair_target_if_alive(e);
7579 if (target == EntityBad)
7592 template <
typename Func>
7596 if (relation != All && !valid(relation))
7599 if (relation == All) {
7600 for (
auto target: targets_all_cache(entity))
7605 if (is_exclusive_dont_fragment_relation(relation)) {
7606 const auto target = this->target(entity, relation);
7607 if (target != EntityBad)
7612 const auto& ec = fetch(entity);
7613 const auto* pArchetype = ec.pArchetype;
7616 if (pArchetype->pairs() == 0)
7619 const auto ids = pArchetype->ids_view();
7620 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
7621 const auto e = ids[idsIdx];
7622 const auto target = pair_target_if_alive(e);
7623 if (target == EntityBad)
7635 template <
typename Func>
7637 GAIA_ASSERT(valid(entity));
7638 if (relation != All && !valid(relation))
7641 if (relation == All) {
7642 for (
auto target: targets_all_cache(entity)) {
7649 if (is_exclusive_dont_fragment_relation(relation)) {
7650 const auto target = this->target(entity, relation);
7651 if (target != EntityBad)
7656 const auto& ec = fetch(entity);
7657 const auto* pArchetype = ec.pArchetype;
7660 if (pArchetype->pairs() == 0)
7663 const auto ids = pArchetype->ids_view();
7664 for (
auto idsIdx: pArchetype->pair_rel_indices(relation)) {
7665 const auto e = ids[idsIdx];
7666 const auto target = pair_target_if_alive(e);
7667 if (target == EntityBad)
7676 GAIA_ASSERT(
pair.pair());
7677 if (!valid_entity_id((EntityId)
pair.gen()))
7680 const auto& ecTarget = m_recs.entities[
pair.gen()];
7681 if (ecTarget.pEntity ==
nullptr)
7684 const auto target = *ecTarget.pEntity;
7685 return valid(target) ? target : EntityBad;
7693 template <
typename Func>
7695 if ((relation != All && !valid(relation)) || !valid(target))
7698 if (relation == All) {
7699 for (
auto source: sources_all_cache(target))
7704 if (is_exclusive_dont_fragment_relation(relation)) {
7705 const auto* pStore = exclusive_adjunct_store(relation);
7706 if (pStore ==
nullptr)
7709 const auto* pSources = exclusive_adjunct_sources(*pStore, target);
7710 if (pSources ==
nullptr)
7713 for (
auto source: *pSources) {
7722 const auto pair =
Pair(relation, target);
7724 if (it == m_entityToArchetypeMap.end())
7727 for (
const auto& record: it->second) {
7728 const auto* pArchetype = record.pArchetype;
7729 if (pArchetype->is_req_del())
7732 for (
const auto* pChunk: pArchetype->chunks()) {
7733 auto entities = pChunk->entity_view();
7734 GAIA_EACH(entities) {
7735 const auto source = entities[i];
7750 template <
typename Func>
7752 if ((relation != All && !valid(relation)) || !valid(target))
7755 if (relation == All) {
7756 for (
auto source: sources_all_cache(target)) {
7763 if (is_exclusive_dont_fragment_relation(relation)) {
7764 const auto* pStore = exclusive_adjunct_store(relation);
7765 if (pStore ==
nullptr)
7768 const auto* pSources = exclusive_adjunct_sources(*pStore, target);
7769 if (pSources ==
nullptr)
7772 for (
auto source: *pSources) {
7782 const auto pair =
Pair(relation, target);
7784 if (it == m_entityToArchetypeMap.end())
7787 for (
const auto& record: it->second) {
7788 const auto* pArchetype = record.pArchetype;
7789 if (pArchetype->is_req_del())
7792 for (
const auto* pChunk: pArchetype->chunks()) {
7793 auto entities = pChunk->entity_view();
7794 GAIA_EACH(entities) {
7795 const auto source = entities[i];
7806 GAIA_NODISCARD uint64_t next_entity_visit_stamp()
const {
7807 ++m_entityVisitStamp;
7808 if (m_entityVisitStamp != 0)
7809 return m_entityVisitStamp;
7811 const auto cnt = (uint32_t)m_entityVisitStamps.size();
7813 m_entityVisitStamps[i] = 0;
7816 m_entityVisitStamp = 1;
7817 return m_entityVisitStamp;
7820 GAIA_NODISCARD
bool try_mark_entity_visited(Entity entity, uint64_t stamp)
const {
7821 GAIA_ASSERT(!entity.pair());
7822 if (entity.id() >= m_entityVisitStamps.size())
7823 m_entityVisitStamps.resize(m_recs.entities.size(), 0);
7825 auto& slot = m_entityVisitStamps[entity.id()];
7833 template <
typename Func>
7834 GAIA_NODISCARD
bool for_each_inherited_term_entity(Entity term, Func&& func)
const {
7835 cnt::set<EntityLookupKey> seen;
7836 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
7837 if (it == m_entityToArchetypeMap.end())
7840 for (
const auto& record: it->second) {
7841 const auto* pArchetype = record.pArchetype;
7842 if (pArchetype->is_req_del())
7845 for (
const auto* pChunk: pArchetype->chunks()) {
7846 const auto entities = pChunk->entity_view();
7847 GAIA_EACH(entities) {
7848 const auto entity = entities[i];
7849 GAIA_ASSERT(valid(entity));
7850 const auto entityKey = EntityLookupKey(entity);
7851 if (seen.contains(entityKey))
7853 seen.insert(entityKey);
7858 const auto& descendants = as_relations_trav_cache(entity);
7859 for (
const auto descendant: descendants) {
7860 GAIA_ASSERT(valid(descendant));
7861 const auto descendantKey = EntityLookupKey(descendant);
7862 if (seen.contains(descendantKey))
7864 seen.insert(descendantKey);
7866 if (!func(descendant))
7878 GAIA_NODISCARD uint32_t count_direct_term_entities_inter(Entity term,
bool allowSemanticIs)
const {
7879 if (term == EntityBad)
7882 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
7883 const auto target = get(term.gen());
7887 return (uint32_t)as_relations_trav_cache(target).size() + 1;
7890 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
7892 (void)for_each_inherited_term_entity(term, [&](Entity) {
7899 if (term.pair() && is_exclusive_dont_fragment_relation(entity_from_id(*
this, term.id()))) {
7900 const auto relation = entity_from_id(*
this, term.id());
7901 const auto* pStore = exclusive_adjunct_store(relation);
7902 if (pStore ==
nullptr)
7905 if (is_wildcard(term.gen()))
7906 return pStore->srcToTgtCnt;
7908 const auto* pSources = exclusive_adjunct_sources(*pStore, entity_from_id(*
this, term.gen()));
7909 return pSources !=
nullptr ? (uint32_t)pSources->size() : 0;
7912 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
7913 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
7914 return it != m_sparseComponentsByComp.end() ? it->second.func_count(it->second.pStore) : 0;
7917 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
7918 if (it == m_entityToArchetypeMap.end())
7922 for (
const auto& record: it->second) {
7923 const auto* pArchetype = record.pArchetype;
7924 if (pArchetype->is_req_del())
7926 for (
const auto* pChunk: pArchetype->chunks())
7927 cnt += pChunk->size();
7934 void collect_direct_term_entities_inter(Entity term, cnt::darray<Entity>& out,
bool allowSemanticIs)
const {
7935 if (term == EntityBad)
7938 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
7939 const auto target = get(term.gen());
7943 out.push_back(target);
7944 const auto& relations = as_relations_trav_cache(target);
7945 out.reserve(out.size() + (uint32_t)relations.size());
7946 for (
auto relation: relations)
7947 out.push_back(relation);
7951 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
7952 (void)for_each_inherited_term_entity(term, [&](Entity entity) {
7953 out.push_back(entity);
7959 if (term.pair() && is_exclusive_dont_fragment_relation(entity_from_id(*
this, term.id()))) {
7960 const auto relation = entity_from_id(*
this, term.id());
7961 const auto* pStore = exclusive_adjunct_store(relation);
7962 if (pStore ==
nullptr)
7965 if (is_wildcard(term.gen())) {
7966 out.reserve(out.size() + pStore->srcToTgtCnt);
7967 const auto cnt = (uint32_t)pStore->srcToTgt.size();
7969 const auto target = pStore->srcToTgt[i];
7970 if (target == EntityBad)
7972 if (!m_recs.entities.has(i))
7974 out.push_back(EntityContainer::handle(m_recs.entities[i]));
7979 const auto* pSources = exclusive_adjunct_sources(*pStore, entity_from_id(*
this, term.gen()));
7980 if (pSources ==
nullptr)
7983 out.reserve(out.size() + (uint32_t)pSources->size());
7984 for (
auto source: *pSources)
7985 out.push_back(source);
7989 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
7990 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
7991 if (it != m_sparseComponentsByComp.end())
7992 it->second.func_collect_entities(it->second.pStore, out);
7996 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
7997 if (it == m_entityToArchetypeMap.end())
8000 for (
const auto& record: it->second) {
8001 const auto* pArchetype = record.pArchetype;
8002 if (pArchetype->is_req_del())
8005 for (
const auto* pChunk: pArchetype->chunks()) {
8006 const auto entities = pChunk->entity_view();
8007 out.reserve(out.size() + (uint32_t)entities.size());
8009 out.push_back(entities[i]);
8015 GAIA_NODISCARD
bool for_each_direct_term_entity_inter(
8016 Entity term,
void* ctx,
bool (*func)(
void*, Entity),
bool allowSemanticIs)
const {
8017 if (term == EntityBad)
8020 if (allowSemanticIs && term.pair() && term.id() == Is.id() && !is_wildcard(term.gen())) {
8021 const auto target = get(term.gen());
8025 if (!func(ctx, target))
8028 const auto& relations = as_relations_trav_cache(target);
8029 for (
auto relation: relations) {
8030 if (!func(ctx, relation))
8036 if (allowSemanticIs && !is_wildcard(term) && valid(term) && target(term, OnInstantiate) == Inherit) {
8037 return for_each_inherited_term_entity(term, [&](Entity entity) {
8038 return func(ctx, entity);
8042 if (term.pair() && is_exclusive_dont_fragment_relation(entity_from_id(*
this, term.id()))) {
8043 const auto relation = entity_from_id(*
this, term.id());
8044 const auto* pStore = exclusive_adjunct_store(relation);
8045 if (pStore ==
nullptr)
8048 if (is_wildcard(term.gen())) {
8049 const auto cnt = (uint32_t)pStore->srcToTgt.size();
8051 const auto target = pStore->srcToTgt[i];
8052 if (target == EntityBad)
8054 if (!m_recs.entities.has(i))
8056 if (!func(ctx, EntityContainer::handle(m_recs.entities[i])))
8062 const auto* pSources = exclusive_adjunct_sources(*pStore, entity_from_id(*
this, term.gen()));
8063 if (pSources ==
nullptr)
8066 for (
auto source: *pSources) {
8067 if (!func(ctx, source))
8073 if (!term.pair() && is_non_fragmenting_out_of_line_component(term)) {
8074 const auto it = m_sparseComponentsByComp.find(EntityLookupKey(term));
8075 if (it == m_sparseComponentsByComp.end())
8077 return it->second.func_for_each_entity(it->second.pStore, ctx, func);
8080 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(term));
8081 if (it == m_entityToArchetypeMap.end())
8084 for (
const auto& record: it->second) {
8085 const auto* pArchetype = record.pArchetype;
8086 if (pArchetype->is_req_del())
8089 for (
const auto* pChunk: pArchetype->chunks()) {
8090 const auto entities = pChunk->entity_view();
8091 GAIA_EACH(entities) {
8092 if (!func(ctx, entities[i]))
8106 return count_direct_term_entities_inter(term,
true);
8113 return count_direct_term_entities_inter(term,
false);
8120 collect_direct_term_entities_inter(term, out,
true);
8127 collect_direct_term_entities_inter(term, out,
false);
8136 return for_each_direct_term_entity_inter(term, ctx, func,
true);
8146 return for_each_direct_term_entity_inter(term, ctx, func,
false);
8154 template <
typename Func>
8156 if (!valid(relation) || !valid(rootTarget))
8160 queue.push_back(rootTarget);
8165 for (uint32_t i = 0; i < queue.size(); ++i) {
8166 const auto currTarget = queue[i];
8169 sources(relation, currTarget, [&](
Entity source) {
8171 const auto ins = visited.insert(key);
8175 children.push_back(source);
8179 return left.id() < right.id();
8182 for (
auto child: children) {
8183 if (!enabled(child))
8186 queue.push_back(child);
8198 template <
typename Func>
8200 if (!valid(relation) || !valid(rootTarget))
8204 queue.push_back(rootTarget);
8209 for (uint32_t i = 0; i < queue.size(); ++i) {
8210 const auto currTarget = queue[i];
8213 sources(relation, currTarget, [&](
Entity source) {
8215 const auto ins = visited.insert(key);
8219 children.push_back(source);
8223 return left.id() < right.id();
8226 for (
auto child: children) {
8227 if (!enabled(child))
8232 queue.push_back(child);
8242 template <
typename Func>
8244 sources(ChildOf, parent, func);
8251 template <
typename Func>
8253 sources_if(ChildOf, parent, func);
8259 template <
typename Func>
8261 sources_bfs(ChildOf, root, func);
8269 template <
typename Func>
8271 return sources_bfs_if(ChildOf, root, func);
8278 template <
typename Func>
8280 GAIA_ASSERT(valid(relation));
8281 if (!valid(relation))
8284 const auto& targets = as_targets_trav_cache(relation);
8285 for (
auto target: targets)
8294 template <
typename Func>
8296 GAIA_ASSERT(valid(relation));
8297 if (!valid(relation))
8300 const auto& targets = as_targets_trav_cache(relation);
8301 for (
auto target: targets)
8313 return *m_pCmdBufferST;
8319 return *m_pCmdBufferMT;
8324#if GAIA_SYSTEMS_ENABLED
8327 void systems_init();
8334 SystemBuilder system();
8338#if GAIA_OBSERVERS_ENABLED
8342 ObserverBuilder observer();
8345 ObserverRegistry& observers() {
8350 const ObserverRegistry& observers()
const {
8363 GAIA_ASSERT(valid(entity));
8365 auto& ec = m_recs.entities[entity.id()];
8366 auto& archetype = *ec.pArchetype;
8367 auto* pChunk = ec.pChunk;
8368 const bool wasEnabled = !ec.data.dis;
8369#if GAIA_ASSERT_ENABLED
8370 verify_enable(*
this, archetype, entity);
8372 archetype.enable_entity(ec.pChunk, ec.row, enable, m_recs);
8374 if (wasEnabled != enable) {
8375 pChunk->update_world_version();
8376 pChunk->update_entity_order_version();
8377 update_version(m_enabledHierarchyVersion);
8378 update_version(m_worldVersion);
8386 const bool entityStateInContainer = !ec.data.
dis;
8387#if GAIA_ASSERT_ENABLED
8389 GAIA_ASSERT(entityStateInChunk == entityStateInContainer);
8391 return entityStateInContainer;
8399 GAIA_ASSERT(valid(entity));
8401 const auto& ec = m_recs.entities[entity.id()];
8408 GAIA_ASSERT(valid(entity));
8409 GAIA_ASSERT(valid(relation));
8410 if (!valid(entity) || !valid(relation))
8412 if (!enabled(entity))
8416 GAIA_FOR(MAX_TRAV_DEPTH) {
8417 const auto next = target(curr, relation);
8418 if (next == EntityBad || next == curr)
8434 GAIA_ASSERT(entity.id() < m_recs.entities.size());
8435 const auto& ec = m_recs.entities[entity.id()];
8445 GAIA_ASSERT(entity.id() < m_recs.entities.size());
8446 const auto& ec = m_recs.entities[entity.id()];
8453 GAIA_NODISCARD uint32_t
size()
const {
8454 return m_recs.entities.item_count();
8459 uint32_t& outArchetypes, uint32_t& outChunks, uint32_t& outEntitiesTotal, uint32_t& outEntitiesActive)
const {
8460 outArchetypes = (uint32_t)m_archetypes.size();
8462 outEntitiesTotal = 0;
8463 outEntitiesActive = 0;
8465 for (
const auto* pArchetype: m_archetypes) {
8466 if (pArchetype ==
nullptr)
8468 const auto& chunks = pArchetype->chunks();
8469 outChunks += (uint32_t)chunks.size();
8470 for (
const auto* pChunk: chunks) {
8471 if (pChunk ==
nullptr)
8473 outEntitiesTotal += pChunk->size();
8474 outEntitiesActive += pChunk->size_enabled();
8482 return m_worldVersion;
8489 return it != m_relationVersions.end() ? it->second : 0;
8496 return m_enabledHierarchyVersion;
8500 friend uint32_t world_version(
const World& world);
8506 const auto it = m_srcEntityVersions.find(key);
8507 if (it == m_srcEntityVersions.end())
8510 update_version(it->second);
8526 auto& ec = fetch(entity);
8527 const auto prevLifespan = ec.pArchetype->max_lifespan();
8528 ec.pArchetype->set_max_lifespan(lifespan);
8530 if (prevLifespan == 0) {
8532 try_enqueue_archetype_for_deletion(*ec.pArchetype);
8559 if GAIA_UNLIKELY (m_teardownActive)
8561 m_teardownActive =
true;
8563 GAIA_PROF_SCOPE(World::teardown);
8565#if GAIA_SYSTEMS_ENABLED
8569#if GAIA_OBSERVERS_ENABLED
8570 m_observers.teardown();
8574 const auto prevReqArchetypes = m_reqArchetypesToDel.size();
8575 const auto prevReqEntities = m_reqEntitiesToDel.size();
8576 const auto prevChunks = m_chunksToDel.size();
8577 const auto prevArchetypes = m_archetypesToDel.size();
8582 if (m_reqArchetypesToDel.empty() && m_reqEntitiesToDel.empty() && m_chunksToDel.empty() &&
8583 m_archetypesToDel.empty())
8586 const bool madeProgress = m_reqArchetypesToDel.size() != prevReqArchetypes ||
8587 m_reqEntitiesToDel.size() != prevReqEntities ||
8588 m_chunksToDel.size() != prevChunks || m_archetypesToDel.size() != prevArchetypes;
8601 m_pRootArchetype =
nullptr;
8602 m_pEntityArchetype =
nullptr;
8603 m_pCompArchetype =
nullptr;
8604 m_nextArchetypeId = 0;
8605 m_defragLastArchetypeIdx = 0;
8607 m_enabledHierarchyVersion = 0;
8614 m_defragEntitiesPerTick = value;
8621 GAIA_LOG_N(
"Archetypes:%u", (uint32_t)m_archetypes.size());
8622 for (
auto* pArchetype: m_archetypes)
8623 Archetype::diag(*
this, *pArchetype);
8629 comp_cache().diag();
8635 validate_entities();
8637 GAIA_LOG_N(
"Deleted entities: %u", (uint32_t)m_recs.entities.get_free_items());
8638 if (m_recs.entities.get_free_items() != 0U) {
8639 GAIA_LOG_N(
" --> %u", (uint32_t)m_recs.entities.get_next_free_item());
8642 auto fe = m_recs.entities.next_free(m_recs.entities.get_next_free_item());
8643 while (fe != IdentifierIdBad) {
8644 GAIA_LOG_N(
" --> %u", m_recs.entities.next_free(fe));
8645 fe = m_recs.entities.next_free(fe);
8647 if (iters > m_recs.entities.get_free_items())
8651 if ((iters == 0U) || iters > m_recs.entities.get_free_items())
8652 GAIA_LOG_E(
" Entities recycle list contains inconsistent data!");
8665 void cleanup_inter() {
8666 GAIA_PROF_SCOPE(World::cleanup_inter);
8672 m_queryCache.clear_archetype_tracking();
8673 m_reqArchetypesToDel = {};
8674 m_reqEntitiesToDel = {};
8675 m_entitiesToDel = {};
8677 m_archetypesToDel = {};
8681 m_recs.entities = {};
8687 for (
auto* pArchetype: m_archetypes)
8688 Archetype::destroy(pArchetype);
8690 m_entityToAsRelations = {};
8691 m_entityToAsRelationsTravCache = {};
8692 m_entityToAsTargets = {};
8693 m_entityToAsTargetsTravCache = {};
8694 m_targetsTravCache = {};
8695 m_srcBfsTravCache = {};
8696 m_depthOrderCache = {};
8697 m_sourcesAllCache = {};
8698 m_targetsAllCache = {};
8701 m_exclusiveAdjunctByRel = {};
8702 m_srcToExclusiveAdjunctRel = {};
8703 for (
auto& [compKey, store]: m_sparseComponentsByComp) {
8705 store.func_clear_store(store.pStore);
8706 store.func_del_store(store.pStore);
8708 m_sparseComponentsByComp = {};
8709 m_relationVersions = {};
8710 m_srcEntityVersions = {};
8713 m_archetypesById = {};
8714 m_archetypesByHash = {};
8719 m_entityToArchetypeMap = {};
8720 m_queryCache.clear();
8721 for (
auto* pScratch: m_queryMatchScratchStack)
8723 m_queryMatchScratchStack = {};
8724 m_queryMatchScratchDepth = 0;
8726 m_nextQuerySerId = 0;
8731 for (
auto& pair: m_aliasToEntity) {
8732 if (!pair.first.owned())
8735 mem::mem_free((
void*)pair.first.str());
8737 m_aliasToEntity = {};
8742 for (
auto& pair: m_nameToEntity) {
8743 if (!pair.first.owned())
8746 mem::mem_free((
void*)pair.first.str());
8748 m_nameToEntity = {};
8752 m_compCache.clear();
8755 GAIA_NODISCARD
static bool valid(
const EntityContainer& ec, [[maybe_unused]] Entity entityExpected) {
8756 if ((ec.flags & EntityContainerFlags::Load) != 0) {
8757 return entityExpected.id() == ec.idx && entityExpected.gen() == ec.data.gen &&
8758 entityExpected.entity() == (bool)ec.data.ent && entityExpected.pair() == (bool)ec.data.pair &&
8759 entityExpected.kind() == (EntityKind)ec.data.kind;
8766 const auto* pChunk = ec.pChunk;
8767 if (pChunk ==
nullptr || ec.row >= pChunk->size())
8770 const auto entityPresent = pChunk->entity_view()[ec.row];
8773 return entityExpected == entityPresent;
8778 GAIA_NODISCARD
bool valid_pair(Entity entity)
const {
8779 if (entity == EntityBad)
8782 GAIA_ASSERT(entity.pair());
8787 if (is_wildcard(entity))
8790 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
8791 if (it == m_recs.pairs.end())
8794 const auto& ec = it->second;
8795 return valid(ec, entity);
8800 GAIA_NODISCARD
bool valid_entity(Entity entity)
const {
8801 if (entity == EntityBad)
8804 GAIA_ASSERT(!entity.pair());
8809 if (entity.id() >= m_recs.entities.size())
8812 const auto* pEc = m_recs.entities.try_get(entity.id());
8816 return valid(*pEc, entity);
8822 GAIA_NODISCARD
bool valid_entity_id(EntityId entityId)
const {
8823 if (entityId == EntityBad.id())
8827 if (entityId >= m_recs.entities.size())
8830 const auto* pEc = m_recs.entities.try_get(entityId);
8834 const auto& ec = *pEc;
8835 if (ec.data.pair != 0)
8839 ec, Entity(entityId, ec.data.gen, (
bool)ec.data.ent, (
bool)ec.data.pair, (EntityKind)ec.data.kind));
8846 GAIA_ASSERT(m_structuralChangesLocked != (uint32_t)-1);
8847 ++m_structuralChangesLocked;
8854 GAIA_ASSERT(m_structuralChangesLocked > 0);
8855 --m_structuralChangesLocked;
8858#if GAIA_SYSTEMS_ENABLED
8859 void systems_done();
8865 return m_structuralChangesLocked != 0;
8871 return m_teardownActive;
8875 static constexpr uint32_t WorldSerializerVersion = 2;
8876 static constexpr uint32_t WorldSerializerJSONVersion = 1;
8879 GAIA_ASSERT(s.valid());
8882 s.save((uint32_t)WorldSerializerVersion);
8886 const auto lastCoreComponentId = GAIA_ID(LastCoreComponent).id();
8887 s.save(lastCoreComponentId);
8895 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) == 0);
8898#if GAIA_USE_SAFE_ENTITY
8901 s.save((uint32_t)0);
8904 uint32_t archetypeIdx = ec.pArchetype->list_idx();
8905 s.save(archetypeIdx);
8906 uint32_t chunkIdx = ec.pChunk->idx();
8910 const auto recEntities = (uint32_t)m_recs.entities.size();
8911 const auto newEntities = recEntities - lastCoreComponentId;
8912 s.save(newEntities);
8913 GAIA_FOR2(lastCoreComponentId, recEntities) {
8914 const bool isAlive = m_recs.entities.has(i);
8917 saveEntityContainer(m_recs.entities[i]);
8919 s.save(m_recs.entities.handle(i).val);
8920 s.save(m_recs.entities.next_free(i));
8925 uint32_t pairsCnt = 0;
8926 for (
const auto& pair: m_recs.pairs) {
8928 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
8936 for (
const auto& pair: m_recs.pairs) {
8938 if (pair.first.entity().id() < lastCoreComponentId && pair.first.entity().gen() < lastCoreComponentId)
8941 saveEntityContainer(pair.second);
8945 s.save(m_recs.entities.m_nextFreeIdx);
8946 s.save(m_recs.entities.m_freeItems);
8951 s.save((uint32_t)m_archetypes.size());
8952 for (
auto* pArchetype: m_archetypes) {
8953 s.save((uint32_t)pArchetype->ids_view().size());
8954 for (
auto e: pArchetype->ids_view())
8957 pArchetype->save(s);
8960 s.save(m_worldVersion);
8965 s.save((uint32_t)m_nameToEntity.size());
8966 for (
const auto& pair: m_nameToEntity) {
8967 s.save(pair.second);
8968 const bool isOwnedStr = pair.first.owned();
8973 const auto* str = pair.first.str();
8974 const uint32_t len = pair.first.len();
8976 s.save_raw(str, len, ser::serialization_type_id::c8);
8981 else if (!pair.second.comp()) {
8982 const auto* str = pair.first.str();
8983 const uint32_t len = pair.first.len();
8985 const auto ptr_val = (uint64_t)str;
8986 s.save_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
8993 uint32_t aliasCnt = 0;
8994 GAIA_FOR((uint32_t)m_recs.entities.size()) {
8995 const auto entity = get((EntityId)i);
8996 if (!valid(entity) || entity.pair())
8999 const auto& ec = m_recs.entities[i];
9000 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
9001 if (compIdx == BadIndex)
9004 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
9005 if (pDesc->alias !=
nullptr)
9010 GAIA_FOR((uint32_t)m_recs.entities.size()) {
9011 const auto entity = get((EntityId)i);
9012 if (!valid(entity) || entity.pair())
9015 const auto& ec = m_recs.entities[i];
9016 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
9017 if (compIdx == BadIndex)
9020 const auto* pDesc =
reinterpret_cast<const EntityDesc*
>(ec.pChunk->comp_ptr(compIdx, ec.row));
9021 if (pDesc->alias ==
nullptr)
9025 s.save(pDesc->alias_len);
9026 s.save_raw(pDesc->alias, pDesc->alias_len, ser::serialization_type_id::c8);
9039 auto s = m_serializer;
9040 GAIA_ASSERT(s.valid());
9050 bool save_json(
ser::ser_json& writer, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default)
const;
9053 ser::json_str save_json(
bool& ok, ser::JsonSaveFlags flags = ser::JsonSaveFlags::Default)
const;
9064 bool load_json(
const char* json, uint32_t len);
9084 auto s = inputSerializer.valid() ? inputSerializer : m_serializer;
9085 GAIA_ASSERT(s.valid());
9091 uint32_t version = 0;
9093 if (version != WorldSerializerVersion) {
9094 GAIA_LOG_E(
"Unsupported world version %u. Expected %u.", version, WorldSerializerVersion);
9099 uint32_t lastCoreComponentId = 0;
9100 s.load(lastCoreComponentId);
9104 const auto currLastCoreComponentId = GAIA_ID(LastCoreComponent).id();
9105 if (lastCoreComponentId > currLastCoreComponentId) {
9107 "Unsupported world core boundary %u. Current runtime supports up to %u.", lastCoreComponentId,
9108 currLastCoreComponentId);
9114 const detail::EntityLoadRemapGuard entityLoadRemapGuard(lastCoreComponentId, currLastCoreComponentId);
9115 auto remapLoadedEntityId = [&](uint32_t id) {
9116 return detail::remap_loaded_entity_id(
id, lastCoreComponentId, currLastCoreComponentId);
9121 auto loadEntityContainer = [&](EntityContainer& ec) {
9126 ec.flags |= EntityContainerFlags::Load;
9128 ec.idx = remapLoadedEntityId(ec.idx);
9129 if (ec.data.pair != 0)
9130 ec.data.gen = remapLoadedEntityId(ec.data.gen);
9132#if GAIA_USE_SAFE_ENTITY
9139 GAIA_ASSERT(ec.unused == 0);
9142 uint32_t archetypeIdx = 0;
9143 s.load(archetypeIdx);
9144 ec.pArchetype = (Archetype*)((uintptr_t)archetypeIdx);
9146 uint32_t chunkIdx = 0;
9148 ec.pChunk = (Chunk*)((uintptr_t)chunkIdx);
9151 uint32_t newEntities = 0;
9152 s.load(newEntities);
9153 GAIA_FOR(newEntities) {
9154 bool isAlive =
false;
9157 EntityContainer ec{};
9158 loadEntityContainer(ec);
9159 m_recs.entities.add_live(GAIA_MOV(ec));
9161 Identifier
id = IdentifierBad;
9162 uint32_t nextFreeIdx = Entity::IdMask;
9164 s.load(nextFreeIdx);
9165 auto entity = Entity(
id);
9166 entity = detail::remap_loaded_entity(entity, lastCoreComponentId, currLastCoreComponentId);
9167 nextFreeIdx = remapLoadedEntityId(nextFreeIdx);
9168 GAIA_ASSERT(entity.id() == remapLoadedEntityId(lastCoreComponentId + i));
9169 m_recs.entities.add_free(entity, nextFreeIdx);
9173 uint32_t pairsCnt = 0;
9175 GAIA_FOR(pairsCnt) {
9176 EntityContainer ec{};
9177 loadEntityContainer(ec);
9178 Entity pair(ec.idx, ec.data.gen);
9179 m_recs.pairs.emplace(EntityLookupKey(pair), GAIA_MOV(ec));
9182 s.load(m_recs.entities.m_nextFreeIdx);
9183 m_recs.entities.m_nextFreeIdx = remapLoadedEntityId(m_recs.entities.m_nextFreeIdx);
9184 s.load(m_recs.entities.m_freeItems);
9189 uint32_t archetypesSize = 0;
9190 s.load(archetypesSize);
9191 m_archetypes.reserve(archetypesSize);
9192 GAIA_FOR(archetypesSize) {
9193 uint32_t idsSize = 0;
9195 Entity ids[ChunkHeader::MAX_COMPONENTS];
9196 GAIA_FOR_(idsSize, j) {
9201 const auto hashLookup = calc_lookup_hash({&ids[0], idsSize}).hash;
9203 auto* pArchetype = find_archetype({hashLookup}, {&ids[0], idsSize});
9204 if (pArchetype ==
nullptr) {
9206 pArchetype = create_archetype({&ids[0], idsSize});
9207 pArchetype->set_hashes({hashLookup});
9213 reg_archetype(pArchetype);
9217 pArchetype->load(s);
9220 s.load(m_worldVersion);
9227 for (
auto& ec: m_recs.entities) {
9228 if ((ec.flags & EntityContainerFlags::Load) == 0)
9230 ec.flags &= ~EntityContainerFlags::Load;
9232 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
9233 ec.pArchetype = m_archetypes[archetypeIdx];
9234 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
9235 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
9236 ec.pEntity = &ec.pChunk->entity_view()[ec.row];
9239 for (
auto& pair: m_recs.pairs) {
9240 auto& ec = pair.second;
9243 if ((ec.flags & EntityContainerFlags::Load) == 0)
9246 GAIA_ASSERT((ec.flags & EntityContainerFlags::Load) != 0);
9247 ec.flags &= ~EntityContainerFlags::Load;
9249 const auto archetypeIdx = (ArchetypeId)((uintptr_t)ec.pArchetype);
9250 ec.pArchetype = m_archetypes[archetypeIdx];
9251 const uint32_t chunkIdx = (uint32_t)((uintptr_t)ec.pChunk);
9252 ec.pChunk = ec.pArchetype->chunks()[chunkIdx];
9253 ec.pEntity = &ec.pChunk->entity_view()[ec.row];
9257#if GAIA_ASSERT_ENABLED
9258 for (
const auto& ec: m_recs.entities) {
9259 GAIA_ASSERT(ec.idx < m_recs.entities.size());
9260 GAIA_ASSERT(m_recs.entities.handle(ec.idx) == EntityContainer::handle(ec));
9261 GAIA_ASSERT(ec.pArchetype !=
nullptr);
9262 GAIA_ASSERT(ec.pChunk !=
nullptr);
9263 GAIA_ASSERT(ec.pEntity !=
nullptr);
9268 m_nameToEntity = {};
9276 const auto& ec = fetch(entity);
9277 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
9278 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
9279 GAIA_ASSERT(core::check_alignment(pDesc));
9281 bool isOwned =
false;
9284 if (entity.comp()) {
9287 const auto& ci = comp_cache().
get(entity);
9288 pDesc->
name = ci.name.str();
9290 GAIA_ASSERT(pDesc->name_len == ci.name.len());
9291 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
9295 uint64_t ptr_val = 0;
9296 s.load_raw(&ptr_val,
sizeof(ptr_val), ser::serialization_type_id::u64);
9299 pDesc->name = (
const char*)ptr_val;
9300 pDesc->name_len = len;
9301 m_nameToEntity.try_emplace(EntityNameLookupKey(pDesc->name, pDesc->name_len, 0), entity);
9311 const char* entityStr = (
const char*)(s.data() + s.tell());
9312 s.seek(s.tell() + len);
9316 pDesc->name =
nullptr;
9317 pDesc->name_len = 0;
9321 name(entity, entityStr, len);
9327 m_aliasToEntity = {};
9328 for (
auto& ec: m_recs.entities) {
9329 const auto entity = EntityContainer::handle(ec);
9333 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
9334 if (compIdx == BadIndex)
9337 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
9338 GAIA_ASSERT(core::check_alignment(pDesc));
9339 pDesc->alias =
nullptr;
9340 pDesc->alias_len = 0;
9349 const auto& ec = fetch(entity);
9350 const auto compIdx = core::get_index(ec.pChunk->ids_view(), GAIA_ID(EntityDesc));
9351 auto* pDesc =
reinterpret_cast<EntityDesc*
>(ec.pChunk->comp_ptr_mut(compIdx, ec.row));
9352 GAIA_ASSERT(core::check_alignment(pDesc));
9358 const char* aliasStr = (
const char*)(s.data() + s.tell());
9359 s.seek(s.tell() + len);
9361 pDesc->alias =
nullptr;
9362 pDesc->alias_len = 0;
9363 alias(entity, aliasStr, len);
9373 template <
typename TSerializer>
9374 bool load(TSerializer& inputSerializer) {
9375 return load(ser::make_serializer(inputSerializer));
9380 void sort_archetypes() {
9383 return a->id() < b->id();
9387 core::sort(m_archetypes, sort_cond{}, [&](uint32_t left, uint32_t right) {
9388 Archetype* tmp = m_archetypes[left];
9390 m_archetypes[right]->list_idx(left);
9391 m_archetypes[left]->list_idx(right);
9393 m_archetypes.data()[left] = (Archetype*)m_archetypes[right];
9394 m_archetypes.data()[right] = tmp;
9401 void remove_chunk(Archetype& archetype, Chunk& chunk) {
9402 archetype.del(&chunk);
9403 try_enqueue_archetype_for_deletion(archetype);
9407 void remove_chunk_from_delete_queue(uint32_t idx) {
9408 GAIA_ASSERT(idx < m_chunksToDel.size());
9410 auto* pChunk = m_chunksToDel[idx].pChunk;
9411 pChunk->clear_delete_queue_index();
9413 const auto lastIdx = (uint32_t)m_chunksToDel.size() - 1;
9414 if (idx != lastIdx) {
9415 auto* pMovedChunk = m_chunksToDel[lastIdx].pChunk;
9416 pMovedChunk->delete_queue_index(idx);
9419 core::swap_erase(m_chunksToDel, idx);
9426 void remove_entity(Archetype& archetype, Chunk& chunk, uint16_t row) {
9427 archetype.remove_entity(chunk, row, m_recs);
9428 try_enqueue_chunk_for_deletion(archetype, chunk);
9432 void del_empty_chunks() {
9433 GAIA_PROF_SCOPE(World::del_empty_chunks);
9435 for (uint32_t i = 0; i < m_chunksToDel.size();) {
9436 auto* pArchetype = m_chunksToDel[i].pArchetype;
9437 auto* pChunk = m_chunksToDel[i].pChunk;
9440 if (!pChunk->empty()) {
9442 revive_archetype(*pArchetype);
9443 remove_chunk_from_delete_queue(i);
9448 if (pChunk->progress_death()) {
9454 remove_chunk(*pArchetype, *pChunk);
9455 remove_chunk_from_delete_queue(i);
9460 void del_empty_archetype(Archetype* pArchetype) {
9461 GAIA_PROF_SCOPE(World::del_empty_archetype);
9463 GAIA_ASSERT(pArchetype !=
nullptr);
9464 GAIA_ASSERT(pArchetype->empty() || pArchetype->is_req_del());
9465 GAIA_ASSERT(!pArchetype->dying() || pArchetype->is_req_del());
9467 unreg_archetype(pArchetype);
9468 for (
auto& ec: m_recs.entities) {
9469 if (ec.pArchetype != pArchetype)
9472 ec.pArchetype =
nullptr;
9473 ec.pChunk =
nullptr;
9474 ec.pEntity =
nullptr;
9476 for (
auto& [_, ec]: m_recs.pairs) {
9477 if (ec.pArchetype != pArchetype)
9480 ec.pArchetype =
nullptr;
9481 ec.pChunk =
nullptr;
9482 ec.pEntity =
nullptr;
9484 Archetype::destroy(pArchetype);
9488 void del_empty_archetypes() {
9489 GAIA_PROF_SCOPE(World::del_empty_archetypes);
9491 cnt::sarray_ext<Archetype*, 512> tmp;
9499 auto remove_from_queries = [&]() {
9503 for (
auto* pArchetype: tmp) {
9504 m_queryCache.remove_archetype_from_queries(pArchetype);
9505 del_empty_archetype(pArchetype);
9510 for (uint32_t i = 0; i < m_archetypesToDel.size();) {
9511 auto* pArchetype = m_archetypesToDel[i];
9514 if (!pArchetype->empty() || pArchetype->max_lifespan() == 0) {
9515 revive_archetype(*pArchetype);
9516 core::swap_erase(m_archetypesToDel, i);
9522 if (!pArchetype->is_req_del() && pArchetype->progress_death()) {
9527 tmp.push_back(pArchetype);
9530 core::swap_erase(m_archetypesToDel, i);
9533 if (tmp.size() == tmp.max_size())
9534 remove_from_queries();
9537 remove_from_queries();
9540 void revive_archetype(Archetype& archetype) {
9542 m_reqArchetypesToDel.erase(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
9545 void try_enqueue_chunk_for_deletion(Archetype& archetype, Chunk& chunk) {
9546 if (chunk.dying() || !chunk.empty())
9561 chunk.start_dying();
9563 m_chunksToDel.push_back({&archetype, &chunk});
9564 chunk.delete_queue_index((uint32_t)m_chunksToDel.size() - 1);
9567 void try_enqueue_archetype_for_deletion(Archetype& archetype) {
9568 if (!archetype.ready_to_die())
9583 archetype.start_dying();
9585 m_archetypesToDel.push_back(&archetype);
9590 void defrag_chunks(uint32_t maxEntities) {
9591 GAIA_PROF_SCOPE(World::defrag_chunks);
9593 const auto maxIters = m_archetypes.size();
9595 GAIA_ASSERT(maxIters > 0);
9597 GAIA_FOR(maxIters) {
9598 const auto idx = (m_defragLastArchetypeIdx + 1) % maxIters;
9599 auto* pArchetype = m_archetypes[idx];
9600 defrag_archetype(*pArchetype, maxEntities);
9601 if (maxEntities == 0)
9604 m_defragLastArchetypeIdx = idx;
9612 void defrag_archetype(Archetype& archetype, uint32_t& maxEntities) {
9636 if (maxEntities == 0)
9639 const auto& chunks = archetype.chunks();
9640 if (chunks.size() < 2)
9644 uint32_t back = chunks.size() - 1;
9646 auto* pDstChunk = chunks[front];
9647 auto* pSrcChunk = chunks[back];
9650 while (front < back && (pDstChunk->full() || !pDstChunk->is_semi()))
9651 pDstChunk = chunks[++front];
9653 while (front < back && (pSrcChunk->empty() || !pSrcChunk->is_semi()))
9654 pSrcChunk = chunks[--back];
9656 const auto& props = archetype.props();
9657 const bool hasUniEnts =
9658 props.cntEntities > 0 && archetype.ids_view()[props.cntEntities - 1].kind() == EntityKind::EK_Uni;
9661 while (front < back) {
9662 pDstChunk = chunks[front];
9663 pSrcChunk = chunks[back];
9665 const uint32_t entitiesInSrcChunk = pSrcChunk->size();
9666 const uint32_t spaceInDstChunk = pDstChunk->capacity() - pDstChunk->size();
9667 const uint32_t entitiesToMoveSrc = core::get_min(entitiesInSrcChunk, maxEntities);
9668 const uint32_t entitiesToMove = core::get_min(entitiesToMoveSrc, spaceInDstChunk);
9672 auto rec = pSrcChunk->comp_rec_view();
9674 GAIA_FOR2(props.genEntities, props.cntEntities) {
9675 const auto* pSrcVal = (
const void*)pSrcChunk->comp_ptr(i, 0);
9676 const auto* pDstVal = (
const void*)pDstChunk->comp_ptr(i, 0);
9677 if (rec[i].pItem->cmp(pSrcVal, pDstVal)) {
9685 pDstChunk = chunks[++front];
9686 goto next_iteration;
9690 GAIA_FOR(entitiesToMove) {
9691 const auto lastSrcEntityIdx = entitiesInSrcChunk - i - 1;
9692 const auto entity = pSrcChunk->entity_view()[lastSrcEntityIdx];
9694 auto& ec = m_recs[entity];
9696 const auto srcRow = ec.row;
9697 const auto dstRow = pDstChunk->add_entity(entity);
9698 const bool wasEnabled = !ec.data.dis;
9701 archetype.enable_entity(pSrcChunk, srcRow,
true, m_recs);
9703 GAIA_ASSERT(srcRow == ec.row);
9706 pDstChunk->move_entity_data(entity, dstRow, m_recs);
9713 archetype.remove_entity_raw(*pSrcChunk, srcRow, m_recs);
9714 try_enqueue_chunk_for_deletion(archetype, *pSrcChunk);
9717 ec.pChunk = pDstChunk;
9718 ec.row = (uint16_t)dstRow;
9719 ec.pEntity = &pDstChunk->entity_view()[dstRow];
9722 archetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
9726 if (entitiesToMove > 0) {
9727 pSrcChunk->update_world_version();
9728 pDstChunk->update_world_version();
9729 pSrcChunk->update_entity_order_version();
9730 pDstChunk->update_entity_order_version();
9731 update_version(m_worldVersion);
9734 maxEntities -= entitiesToMove;
9735 if (maxEntities == 0)
9739 if (pSrcChunk->empty()) {
9740 while (front < back) {
9741 if (chunks[--back]->is_semi())
9749 while (front < back && pDstChunk->full())
9750 pDstChunk = chunks[++front];
9758 GAIA_NODISCARD Archetype* find_archetype(Archetype::LookupHash hashLookup, EntitySpan ids) {
9759 auto tmpArchetype = ArchetypeLookupChecker(ids);
9760 ArchetypeLookupKey key(hashLookup, &tmpArchetype);
9763 const auto it = m_archetypesByHash.find(key);
9764 if (it == m_archetypesByHash.end())
9767 auto* pArchetype = it->second;
9771 GAIA_NODISCARD
static auto
9772 find_component_index_record(ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
9773 return core::get_index_if(records, [&](
const auto& record) {
9774 return record.matches(pArchetype);
9778 GAIA_NODISCARD
static auto
9779 find_component_index_record(
const ComponentIndexEntryArray& records,
const Archetype* pArchetype) {
9780 return core::get_index_if(records, [&](
const auto& record) {
9781 return record.matches(pArchetype);
9787 void add_entity_archetype_pair(
9788 Entity entity, Archetype* pArchetype, uint16_t compIdx = ComponentIndexBad, uint16_t matchCount = 1) {
9789 GAIA_ASSERT(pArchetype !=
nullptr);
9790 GAIA_ASSERT(matchCount > 0);
9792 EntityLookupKey entityKey(entity);
9793 const auto it = m_entityToArchetypeMap.find(entityKey);
9794 if (it == m_entityToArchetypeMap.end()) {
9795 ComponentIndexEntryArray records;
9796 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
9797 m_entityToArchetypeMap.try_emplace(entityKey, GAIA_MOV(records));
9801 auto& records = it->second;
9802 const auto idx = find_component_index_record(records, pArchetype);
9803 if (idx == BadIndex) {
9804 records.push_back(ComponentIndexEntry{pArchetype, compIdx, matchCount});
9808 auto& record = records[idx];
9809 record.matchCount = (uint16_t)(record.matchCount + matchCount);
9810 if (compIdx != ComponentIndexBad)
9811 record.compIdx = compIdx;
9814 void add_pair_archetype_query_pairs(Entity pair, Archetype* pArchetype, uint16_t matchCount = 1) {
9815 GAIA_ASSERT(pair.pair());
9816 GAIA_ASSERT(pArchetype !=
nullptr);
9817 GAIA_ASSERT(matchCount > 0);
9819 const auto first = get(pair.id());
9820 const auto second = get(pair.gen());
9822 add_entity_archetype_pair(Pair(All, second), pArchetype, ComponentIndexBad, matchCount);
9823 add_entity_archetype_pair(Pair(first, All), pArchetype, ComponentIndexBad, matchCount);
9824 add_entity_archetype_pair(Pair(All, All), pArchetype, ComponentIndexBad, matchCount);
9830 void del_entity_query_pair(Pair pair, Entity entityToRemove) {
9831 auto it = m_entityToArchetypeMap.find(EntityLookupKey(pair));
9832 if (it == m_entityToArchetypeMap.end())
9834 auto& records = it->second;
9838 for (uint32_t i = records.size() - 1; i != (uint32_t)-1; --i) {
9839 auto& record = records[i];
9840 const auto* pArchetype = record.pArchetype;
9841 if (!pArchetype->has(entityToRemove))
9844 if ((!is_wildcard(pair.first()) && !is_wildcard(pair.second())) || record.matchCount <= 1)
9845 core::swap_erase_unsafe(records, i);
9847 --record.matchCount;
9850 if (records.empty())
9851 m_entityToArchetypeMap.erase(it);
9856 void del_entity_query_pair(Pair pair, Archetype* pArchetypeToRemove) {
9857 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
9859 auto it = m_entityToArchetypeMap.find(EntityLookupKey(pair));
9860 if (it == m_entityToArchetypeMap.end())
9863 auto& records = it->second;
9864 const auto idx = find_component_index_record(records, pArchetypeToRemove);
9865 if (idx != BadIndex)
9866 core::swap_erase_unsafe(records, idx);
9868 if (records.empty())
9869 m_entityToArchetypeMap.erase(it);
9872 void del_pair_archetype_query_pairs(Entity pair, Archetype* pArchetypeToRemove) {
9873 GAIA_ASSERT(pair.pair());
9874 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
9876 GAIA_ASSERT(pair.id() < m_recs.entities.size());
9877 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
9878 const auto first = m_recs.entities.handle(pair.id());
9879 const auto second = m_recs.entities.handle(pair.gen());
9881 del_entity_query_pair(Pair(All, second), pArchetypeToRemove);
9882 del_entity_query_pair(Pair(first, All), pArchetypeToRemove);
9883 del_entity_query_pair(Pair(All, All), pArchetypeToRemove);
9886 void del_pair_archetype_query_pairs(Entity pair, Entity entityToRemove) {
9887 GAIA_ASSERT(pair.pair());
9889 GAIA_ASSERT(pair.id() < m_recs.entities.size());
9890 GAIA_ASSERT(pair.gen() < m_recs.entities.size());
9891 const auto first = m_recs.entities.handle(pair.id());
9892 const auto second = m_recs.entities.handle(pair.gen());
9894 del_entity_query_pair(Pair(All, second), entityToRemove);
9895 del_entity_query_pair(Pair(first, All), entityToRemove);
9896 del_entity_query_pair(Pair(All, All), entityToRemove);
9901 void del_entity_archetype_pair(Entity entity, Archetype* pArchetypeToRemove) {
9902 GAIA_ASSERT(entity != Pair(All, All));
9903 GAIA_ASSERT(pArchetypeToRemove !=
nullptr);
9905 auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
9906 if (it == m_entityToArchetypeMap.end())
9909 auto& records = it->second;
9910 const auto idx = find_component_index_record(records, pArchetypeToRemove);
9911 if (idx != BadIndex)
9912 core::swap_erase_unsafe(records, idx);
9914 if (records.empty())
9915 m_entityToArchetypeMap.erase(it);
9919 void del_archetype_entity_pairs(Archetype* pArchetype) {
9920 GAIA_ASSERT(pArchetype !=
nullptr);
9922 for (
const auto entity: pArchetype->ids_view()) {
9923 del_entity_archetype_pair(entity, pArchetype);
9931 GAIA_ASSERT(entity.id() < m_recs.entities.size());
9932 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
9933 del_pair_archetype_query_pairs(entity, pArchetype);
9939 void del_entity_archetype_pairs(Entity entity, Archetype* pArchetype) {
9940 GAIA_ASSERT(entity != Pair(All, All));
9942 m_entityToArchetypeMap.erase(EntityLookupKey(entity));
9944 if (entity.pair()) {
9945 if (pArchetype !=
nullptr) {
9946 del_pair_archetype_query_pairs(entity, pArchetype);
9948 del_pair_archetype_query_pairs(entity, entity);
9956 GAIA_NODISCARD Archetype* create_archetype(EntitySpan entities) {
9957 GAIA_ASSERT(m_nextArchetypeId < (
decltype(m_nextArchetypeId))-1);
9958 auto* pArchetype = Archetype::create(*
this, m_nextArchetypeId++, m_worldVersion, entities);
9960 const auto entityCnt = (uint32_t)entities.size();
9961 GAIA_FOR(entityCnt) {
9962 auto entity = entities[i];
9963 add_entity_archetype_pair(entity, pArchetype, (uint16_t)i);
9965#if GAIA_OBSERVERS_ENABLED
9966 auto& ec = fetch(entity);
9967 if ((ec.flags & EntityContainerFlags::IsObserved) != 0 || m_observers.has_observers(entity)) {
9968 ec.flags |= EntityContainerFlags::IsObserved;
9969 pArchetype->observed_terms_inc();
9975 if (entity.pair()) {
9976 add_pair_archetype_query_pairs(entity, pArchetype);
9985 void reg_archetype(Archetype* pArchetype) {
9986 GAIA_ASSERT(pArchetype !=
nullptr);
9993 GAIA_ASSERT(pArchetype->list_idx() == BadIndex);
9996 [[maybe_unused]]
const auto it0 =
9997 m_archetypesById.emplace(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()), pArchetype);
9998 [[maybe_unused]]
const auto it1 =
9999 m_archetypesByHash.emplace(ArchetypeLookupKey(pArchetype->lookup_hash(), pArchetype), pArchetype);
10001 GAIA_ASSERT(it0.second);
10002 GAIA_ASSERT(it1.second);
10004 pArchetype->list_idx(m_archetypes.size());
10005 m_archetypes.emplace_back(pArchetype);
10007 m_queryCache.register_archetype_with_queries(pArchetype);
10012 void unreg_archetype(Archetype* pArchetype) {
10013 GAIA_ASSERT(pArchetype !=
nullptr);
10017 (m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));
10020 GAIA_ASSERT(pArchetype->list_idx() != BadIndex);
10025 del_archetype_entity_pairs(pArchetype);
10029 auto& edgeLefts = pArchetype->left_edges();
10030 for (
auto& itLeft: edgeLefts)
10031 remove_edge_from_archetype(pArchetype, itLeft.second, itLeft.first.entity());
10034 auto tmpArchetype = ArchetypeLookupChecker(pArchetype->ids_view());
10035 [[maybe_unused]]
const auto res0 =
10036 m_archetypesById.erase(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()));
10037 [[maybe_unused]]
const auto res1 =
10038 m_archetypesByHash.erase(ArchetypeLookupKey(pArchetype->lookup_hash(), &tmpArchetype));
10039 GAIA_ASSERT(res0 != 0);
10040 GAIA_ASSERT(res1 != 0);
10042 const auto idx = pArchetype->list_idx();
10043 GAIA_ASSERT(idx == core::get_index(m_archetypes, pArchetype));
10044 core::swap_erase(m_archetypes, idx);
10045 if (!m_archetypes.empty() && idx != m_archetypes.size())
10046 m_archetypes[idx]->list_idx(idx);
10049#if GAIA_ASSERT_ENABLED
10050 static void print_archetype_entities(
const World& world,
const Archetype& archetype, Entity entity,
bool adding) {
10051 auto ids = archetype.ids_view();
10053 GAIA_LOG_W(
"Currently present:");
10055 const auto name = entity_name(world, ids[i]);
10057 "> [%u] %.*s [%s]", i, (
int)name.size(), name.empty() ?
"" : name.data(),
10058 EntityKindString[(uint32_t)ids[i].kind()]);
10061 GAIA_LOG_W(
"Trying to %s:", adding ?
"add" :
"del");
10062 const auto name = entity_name(world, entity);
10064 "> %.*s [%s]", (
int)name.size(), name.empty() ?
"" : name.data(),
10065 EntityKindString[(uint32_t)entity.kind()]);
10068 static void verify_add(
const World& world, Archetype& archetype, Entity entity, Entity addEntity) {
10070 if (world.locked()) {
10071 GAIA_ASSERT2(
false,
"Trying to add an entity while the world is locked");
10072 GAIA_LOG_W(
"Trying to add an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
10073 print_archetype_entities(world, archetype, entity,
false);
10078 if (is_wildcard(addEntity)) {
10079 GAIA_ASSERT2(
false,
"Adding wildcard pairs is not supported");
10080 print_archetype_entities(world, archetype, addEntity,
true);
10085 auto ids = archetype.ids_view();
10086 if GAIA_UNLIKELY (ids.size() + 1 >= ChunkHeader::MAX_COMPONENTS) {
10087 GAIA_ASSERT2(
false,
"Trying to add too many entities to entity!");
10088 GAIA_LOG_W(
"Trying to add an entity to entity [%u:%u] but there's no space left!", entity.id(), entity.gen());
10089 print_archetype_entities(world, archetype, addEntity,
true);
10094 static void verify_del(
const World& world, Archetype& archetype, Entity entity, Entity func_del) {
10096 if (world.locked()) {
10097 GAIA_ASSERT2(
false,
"Trying to delete an entity while the world is locked");
10098 GAIA_LOG_W(
"Trying to delete an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
10099 print_archetype_entities(world, archetype, entity,
false);
10104 if GAIA_UNLIKELY (!archetype.has(func_del)) {
10105 GAIA_ASSERT2(
false,
"Trying to remove an entity which wasn't added");
10106 GAIA_LOG_W(
"Trying to del an entity from entity [%u:%u] but it was never added", entity.id(), entity.gen());
10107 print_archetype_entities(world, archetype, func_del,
false);
10112 static void verify_enable(
const World& world, Archetype& archetype, Entity entity) {
10113 if (world.locked()) {
10114 GAIA_ASSERT2(
false,
"Trying to enable/disable an entity while the world is locked");
10115 GAIA_LOG_W(
"Trying to enable/disable an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
10116 print_archetype_entities(world, archetype, entity,
false);
10120 static void verify_move(
const World& world, Archetype& archetype, Entity entity) {
10121 if (world.locked()) {
10122 GAIA_ASSERT2(
false,
"Trying to move an entity while the world is locked");
10123 GAIA_LOG_W(
"Trying to move an entity [%u:%u] while the world is locked", entity.id(), entity.gen());
10124 print_archetype_entities(world, archetype, entity,
false);
10134 GAIA_NODISCARD Archetype* foc_archetype_add(Archetype* pArchetypeLeft, Entity entity) {
10136 bool edgeNeedsRebuild =
false;
10138 const auto edge = pArchetypeLeft->find_edge_right(entity);
10139 if (edge != ArchetypeIdHashPairBad) {
10140 auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
10141 if (it != m_archetypesById.end() && it->second !=
nullptr)
10145 pArchetypeLeft->del_graph_edge_right_local(entity);
10146 edgeNeedsRebuild =
true;
10151 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
10153 auto entsOld = pArchetypeLeft->ids_view();
10154 const auto entsOldCnt = entsOld.size();
10155 entsNew.resize((uint32_t)entsOld.size() + 1);
10156 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
10157 entsNew[(uint32_t)entsOld.size()] = entity;
10162 sort(entsNew, SortComponentCond{});
10165 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
10166 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
10167 if (pArchetypeRight ==
nullptr) {
10168 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
10169 pArchetypeRight->set_hashes({hashLookup});
10170 reg_archetype(pArchetypeRight);
10171 edgeNeedsRebuild =
true;
10174 if (edgeNeedsRebuild)
10175 pArchetypeLeft->build_graph_edges(pArchetypeRight, entity);
10177 return pArchetypeRight;
10182 GAIA_NODISCARD Archetype* foc_archetype_add_no_graph(Archetype* pArchetypeLeft, Entity entity) {
10183 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
10185 auto entsOld = pArchetypeLeft->ids_view();
10186 const auto entsOldCnt = entsOld.size();
10187 entsNew.resize((uint32_t)entsOld.size() + 1);
10188 GAIA_FOR(entsOldCnt) entsNew[i] = entsOld[i];
10189 entsNew[(uint32_t)entsOld.size()] = entity;
10192 sort(entsNew, SortComponentCond{});
10194 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
10195 auto* pArchetypeRight = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
10196 if (pArchetypeRight !=
nullptr)
10197 return pArchetypeRight;
10199 pArchetypeRight = create_archetype({entsNew.data(), entsNew.size()});
10200 pArchetypeRight->set_hashes({hashLookup});
10201 reg_archetype(pArchetypeRight);
10202 return pArchetypeRight;
10210 GAIA_NODISCARD Archetype* foc_archetype_del(Archetype* pArchetypeRight, Entity entity) {
10212 bool edgeNeedsRebuild =
false;
10214 const auto edge = pArchetypeRight->find_edge_left(entity);
10215 if (edge != ArchetypeIdHashPairBad) {
10216 const auto it = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
10217 if (it != m_archetypesById.end()) {
10218 auto* pArchetypeLeft = it->second;
10219 if (pArchetypeLeft !=
nullptr)
10220 return pArchetypeLeft;
10224 pArchetypeRight->del_graph_edge_left_local(entity);
10225 edgeNeedsRebuild =
true;
10229 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
10230 auto entsOld = pArchetypeRight->ids_view();
10233 for (
const auto e: entsOld) {
10237 entsNew.push_back(e);
10241 GAIA_ASSERT(entsNew.size() != entsOld.size());
10244 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
10245 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
10246 if (pArchetype ==
nullptr) {
10247 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
10248 pArchetype->set_hashes({hashLookup});
10249 reg_archetype(pArchetype);
10250 edgeNeedsRebuild =
true;
10253 if (edgeNeedsRebuild)
10254 pArchetype->build_graph_edges(pArchetypeRight, entity);
10261 GAIA_NODISCARD Archetype* foc_archetype_del_no_graph(Archetype* pArchetypeRight, Entity entity) {
10262 cnt::sarray_ext<Entity, ChunkHeader::MAX_COMPONENTS> entsNew;
10263 auto entsOld = pArchetypeRight->ids_view();
10265 for (
const auto e: entsOld) {
10269 entsNew.push_back(e);
10272 GAIA_ASSERT(entsNew.size() != entsOld.size());
10274 const auto hashLookup = calc_lookup_hash({entsNew.data(), entsNew.size()}).hash;
10275 auto* pArchetype = find_archetype({hashLookup}, {entsNew.data(), entsNew.size()});
10276 if (pArchetype !=
nullptr)
10279 pArchetype = create_archetype({entsNew.data(), entsNew.size()});
10280 pArchetype->set_hashes({hashLookup});
10281 reg_archetype(pArchetype);
10287 GAIA_NODISCARD
const auto& archetypes()
const {
10288 return m_archetypes;
10294 GAIA_NODISCARD Archetype& archetype(Entity entity) {
10295 const auto& ec = fetch(entity);
10296 return *ec.pArchetype;
10301 void del_name(EntityContainer& ec, Entity entity) {
10302 EntityBuilder(*
this, entity, ec).
del_name();
10307 void del_name(Entity entity) {
10308 EntityBuilder(*
this, entity).
del_name();
10315 void del_entity(Entity entity,
bool invalidate) {
10316 if (entity.pair() || entity == EntityBad)
10319 auto& ec = fetch(entity);
10320 del_entity_inter(ec, entity, invalidate);
10328 void del_entity(EntityContainer& ec, Entity entity,
bool invalidate) {
10329 if (entity.pair() || entity == EntityBad)
10332 del_entity_inter(ec, entity, invalidate);
10339 void del_entity_inter(EntityContainer& ec, Entity entity,
bool invalidate) {
10340 GAIA_ASSERT(entity.id() > GAIA_ID(LastCoreComponent).
id());
10344 if (m_recs.entities.item_count() == 0)
10347#if GAIA_ASSERT_ENABLED
10348 auto* pChunk = ec.pChunk;
10349 GAIA_ASSERT(pChunk !=
nullptr);
10356 del_name(ec, entity);
10357 remove_entity(*ec.pArchetype, *ec.pChunk, ec.row);
10358 remove_src_entity_version(entity);
10364 invalidate_entity(entity);
10371 void del_entities(Archetype& archetype) {
10372 for (
auto* pChunk: archetype.chunks()) {
10373 auto ids = pChunk->entity_view();
10374 for (
auto e: ids) {
10378#if GAIA_ASSERT_ENABLED
10379 const auto& ec = fetch(e);
10382 GAIA_ASSERT((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) == 0);
10385 del_entity(e,
true);
10388 validate_chunk(pChunk);
10392 if (pChunk->queued_for_deletion())
10393 remove_chunk_from_delete_queue(pChunk->delete_queue_index());
10395 remove_chunk(archetype, *pChunk);
10398 validate_entities();
10403 void del_inter(Entity entity) {
10404 auto on_delete = [
this](Entity entityToDel) {
10405 auto& ec = fetch(entityToDel);
10406 handle_del_entity(ec, entityToDel);
10409 if (is_wildcard(entity)) {
10410 const auto rel = get(entity.id());
10411 const auto tgt = get(entity.gen());
10414 if (rel == All && tgt == All) {
10415 GAIA_ASSERT2(
false,
"Not supported yet");
10418 else if (rel == All) {
10419 if (
const auto* pTargets = relations(tgt)) {
10422 cnt::darray_ext<Entity, 64> tmp;
10423 for (
auto key: *pTargets)
10424 tmp.push_back(key.entity());
10426 on_delete(Pair(e, tgt));
10430 else if (tgt == All) {
10431 if (
const auto* pRelations = targets(rel)) {
10434 cnt::darray_ext<Entity, 64> tmp;
10435 for (
auto key: *pRelations)
10436 tmp.push_back(key.entity());
10438 on_delete(Pair(rel, e));
10447 void del_finalize_archetypes() {
10448 GAIA_PROF_SCOPE(World::del_finalize_archetypes);
10450 for (
auto& key: m_reqArchetypesToDel) {
10451 auto* pArchetype = key.archetype();
10452 if (pArchetype ==
nullptr)
10455 del_entities(*pArchetype);
10462 m_reqArchetypesToDel.clear();
10466 void del_finalize_entities() {
10467 GAIA_PROF_SCOPE(World::del_finalize_entities);
10469 for (
auto it = m_reqEntitiesToDel.begin(); it != m_reqEntitiesToDel.end();) {
10470 const auto e = it->entity();
10473 if (m_entityToArchetypeMap.contains(*it)) {
10479 invalidate_entity(e);
10481 it = m_reqEntitiesToDel.erase(it);
10486 void del_finalize() {
10487 GAIA_PROF_SCOPE(World::del_finalize);
10489 del_finalize_archetypes();
10490 del_finalize_entities();
10493 GAIA_NODISCARD
bool archetype_cond_match(Archetype& archetype, Pair cond, Entity target)
const {
10498 auto ids = archetype.ids_view();
10500 if (target.pair()) {
10501 for (
auto e: ids) {
10505 if (e.gen() != target.gen())
10508 const auto& ec = m_recs.entities[e.id()];
10509 const auto entity = ec.pChunk->entity_view()[ec.row];
10510 if (!has(entity, cond))
10516 for (
auto e: ids) {
10532 void move_to_archetype(Archetype& srcArchetype, Archetype& dstArchetype) {
10533 GAIA_ASSERT(&srcArchetype != &dstArchetype);
10535 bool updated =
false;
10537 for (
auto* pSrcChunk: srcArchetype.chunks()) {
10538 auto srcEnts = pSrcChunk->entity_view();
10539 if (srcEnts.empty())
10552 uint32_t i = (uint32_t)srcEnts.size();
10554 auto* pDstChunk = dstArchetype.foc_free_chunk();
10555 const uint32_t dstSpaceLeft = pDstChunk->capacity() - pDstChunk->size();
10556 const uint32_t cnt = core::get_min(dstSpaceLeft, i);
10557 for (uint32_t j = 0; j < cnt; ++j) {
10558 auto e = srcEnts[i - j - 1];
10559 move_entity(e, fetch(e), dstArchetype, *pDstChunk);
10562 pDstChunk->update_world_version();
10563 pDstChunk->update_entity_order_version();
10565 GAIA_ASSERT(cnt <= i);
10569 pSrcChunk->update_world_version();
10570 pSrcChunk->update_entity_order_version();
10575 update_version(m_worldVersion);
10580 GAIA_NODISCARD Archetype* calc_dst_archetype_ent(Archetype* pArchetype, Entity entity) {
10581 GAIA_ASSERT(!is_wildcard(entity));
10583 auto ids = pArchetype->ids_view();
10584 for (
auto id: ids) {
10588 return foc_archetype_del(pArchetype,
id);
10597 GAIA_NODISCARD Archetype* calc_dst_archetype_all_ent(Archetype* pArchetype, Entity entity) {
10598 GAIA_ASSERT(is_wildcard(entity));
10600 Archetype* pDstArchetype = pArchetype;
10602 auto ids = pArchetype->ids_view();
10603 for (
auto id: ids) {
10604 if (!
id.pair() ||
id.gen() != entity.gen())
10607 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
10610 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
10616 GAIA_NODISCARD Archetype* calc_dst_archetype_ent_all(Archetype* pArchetype, Entity entity) {
10617 GAIA_ASSERT(is_wildcard(entity));
10619 Archetype* pDstArchetype = pArchetype;
10621 auto ids = pArchetype->ids_view();
10622 for (
auto id: ids) {
10623 if (!
id.pair() ||
id.
id() != entity.id())
10626 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
10629 return pArchetype != pDstArchetype ? pDstArchetype :
nullptr;
10635 GAIA_NODISCARD Archetype* calc_dst_archetype_all_all(Archetype* pArchetype, [[maybe_unused]] Entity entity) {
10636 GAIA_ASSERT(is_wildcard(entity));
10638 Archetype* pDstArchetype = pArchetype;
10639 bool found =
false;
10641 auto ids = pArchetype->ids_view();
10642 for (
auto id: ids) {
10646 pDstArchetype = foc_archetype_del(pDstArchetype,
id);
10650 return found ? pDstArchetype :
nullptr;
10656 GAIA_NODISCARD Archetype* calc_dst_archetype(Archetype* pArchetype, Entity entity) {
10657 if (entity.pair()) {
10658 auto rel = entity.id();
10659 auto tgt = entity.gen();
10662 if (rel == All.id() || tgt == All.id()) {
10664 if (rel != All.id() && tgt == All.id())
10665 return calc_dst_archetype_ent_all(pArchetype, entity);
10668 if (rel == All.id() && tgt != All.id())
10669 return calc_dst_archetype_all_ent(pArchetype, entity);
10672 return calc_dst_archetype_all_all(pArchetype, EntityBad);
10677 return calc_dst_archetype_ent(pArchetype, entity);
10680 void req_del(Archetype& archetype) {
10681 if (archetype.is_req_del())
10684 archetype.req_del();
10685 m_reqArchetypesToDel.insert(ArchetypeLookupKey(archetype.lookup_hash(), &archetype));
10688 void req_del(EntityContainer& ec, Entity entity) {
10689 if (is_req_del(ec))
10692#if GAIA_OBSERVERS_ENABLED
10694 m_observers.prepare_diff(*
this, ObserverEvent::OnDel, EntitySpan{&entity, 1}, EntitySpan{&entity, 1});
10696 del_entity(ec, entity,
false);
10697#if GAIA_OBSERVERS_ENABLED
10698 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
10702 m_reqEntitiesToDel.insert(EntityLookupKey(entity));
10705 void invalidate_pair_removal_caches(Entity entity) {
10706 if (!entity.pair())
10709 auto invalidate_relation = [
this](Entity relation) {
10710 if (relation == EntityBad)
10712 touch_rel_version(relation);
10713 invalidate_queries_for_rel(relation);
10716 if (entity.id() != All.id()) {
10717 invalidate_relation(try_get(entity.id()));
10718 }
else if (entity.gen() != All.id()) {
10719 const auto target = try_get(entity.gen());
10720 if (target != EntityBad) {
10721 if (
const auto* pRelations = relations(target)) {
10722 for (
auto relationKey: *pRelations)
10723 invalidate_relation(relationKey.entity());
10727 for (
const auto& [relationKey, _]: m_relToTgt)
10728 invalidate_relation(relationKey.entity());
10731 m_targetsTravCache = {};
10732 m_srcBfsTravCache = {};
10733 m_depthOrderCache = {};
10734 m_sourcesAllCache = {};
10735 m_targetsAllCache = {};
10738 void unlink_live_is_relation(Entity source, Entity target) {
10739 const auto sourceKey = EntityLookupKey(source);
10740 const auto targetKey = EntityLookupKey(target);
10742 invalidate_queries_for_entity({Is, target});
10744 if (
const auto itTargets = m_entityToAsTargets.find(sourceKey); itTargets != m_entityToAsTargets.end()) {
10745 itTargets->second.erase(targetKey);
10746 if (itTargets->second.empty())
10747 m_entityToAsTargets.erase(itTargets);
10749 m_entityToAsTargetsTravCache = {};
10751 if (
const auto itRelations = m_entityToAsRelations.find(targetKey);
10752 itRelations != m_entityToAsRelations.end()) {
10753 itRelations->second.erase(sourceKey);
10754 if (itRelations->second.empty())
10755 m_entityToAsRelations.erase(itRelations);
10757 m_entityToAsRelationsTravCache = {};
10760 void unlink_stale_is_relations_by_target_id(Entity source, EntityId targetId) {
10761 const auto sourceKey = EntityLookupKey(source);
10762 const auto itTargets = m_entityToAsTargets.find(sourceKey);
10763 if (itTargets == m_entityToAsTargets.end())
10766 cnt::darray_ext<EntityLookupKey, 4> removedTargets;
10767 for (
auto targetKey: itTargets->second) {
10768 if (targetKey.entity().id() == targetId)
10769 removedTargets.push_back(targetKey);
10772 for (
auto targetKey: removedTargets) {
10773 invalidate_queries_for_structural_entity(EntityLookupKey(Pair{Is, targetKey.entity()}));
10774 itTargets->second.erase(targetKey);
10776 const auto itRelations = m_entityToAsRelations.find(targetKey);
10777 if (itRelations != m_entityToAsRelations.end()) {
10778 itRelations->second.erase(sourceKey);
10779 if (itRelations->second.empty())
10780 m_entityToAsRelations.erase(itRelations);
10784 if (itTargets->second.empty())
10785 m_entityToAsTargets.erase(itTargets);
10787 m_entityToAsTargetsTravCache = {};
10788 m_entityToAsRelationsTravCache = {};
10791 template <
typename Func>
10792 void each_delete_cascade_direct_source(Entity target, Pair cond, Func&& func) {
10793 GAIA_ASSERT(!target.pair());
10795 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
10796 const auto relation = relKey.entity();
10797 if (!has(relation, cond))
10800 const auto* pSources = exclusive_adjunct_sources(store, target);
10801 if (pSources ==
nullptr)
10804 for (
auto source: *pSources)
10808 const auto pairEntity = Pair(All, target);
10809 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(pairEntity));
10810 if (it != m_entityToArchetypeMap.end()) {
10811 for (
const auto& record: it->second) {
10812 auto* pArchetype = record.pArchetype;
10813 if (pArchetype ==
nullptr || pArchetype->is_req_del())
10815 if (!archetype_cond_match(*pArchetype, cond, pairEntity))
10818 for (
const auto* pChunk: pArchetype->chunks()) {
10819 const auto entities = pChunk->entity_view();
10820 GAIA_EACH(entities)
10827 void collect_delete_cascade_direct_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
10828 GAIA_ASSERT(!target.pair());
10829 const auto visitStamp = next_entity_visit_stamp();
10830 each_delete_cascade_direct_source(target, cond, [&](Entity source) {
10831 if (!valid(source))
10833 if (!try_mark_entity_visited(source, visitStamp))
10835 out.push_back(source);
10839 void collect_delete_cascade_sources(Entity target, Pair cond, cnt::darray<Entity>& out) {
10840 GAIA_ASSERT(!target.pair());
10841 const auto visitStamp = next_entity_visit_stamp();
10842 cnt::darray_ext<Entity, 32> targetsToVisit;
10843 (void)try_mark_entity_visited(target, visitStamp);
10844 targetsToVisit.push_back(target);
10846 for (uint32_t i = 0; i < targetsToVisit.size(); ++i) {
10847 const auto currTarget = targetsToVisit[i];
10848 each_delete_cascade_direct_source(currTarget, cond, [&](Entity source) {
10849 if (!valid(source))
10851 if (!try_mark_entity_visited(source, visitStamp))
10854 out.push_back(source);
10855 targetsToVisit.push_back(source);
10861 void req_del_entities_with(Entity entity) {
10862 GAIA_PROF_SCOPE(World::req_del_entities_with);
10864 GAIA_ASSERT(entity != Pair(All, All));
10866 auto req_del_adjunct_pair = [&](Entity relation, Entity target) {
10867 cnt::darray<Entity> sourcesToDel;
10868 sources(relation, target, [&](Entity source) {
10869 sourcesToDel.push_back(source);
10872 for (
auto source: sourcesToDel)
10873 req_del(fetch(source), source);
10876 if (entity.pair()) {
10877 if (entity.id() != All.id()) {
10878 const auto relation = try_get(entity.id());
10879 const auto target = try_get(entity.gen());
10880 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
10881 req_del_adjunct_pair(relation, target);
10883 const auto target = try_get(entity.gen());
10884 if (target == EntityBad)
10885 goto skip_req_del_all_target;
10886 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
10887 if (exclusive_adjunct_sources(store, target) ==
nullptr)
10890 req_del_adjunct_pair(relKey.entity(), target);
10892 skip_req_del_all_target:;
10894 }
else if (is_exclusive_dont_fragment_relation(entity)) {
10895 if (
const auto* pTargets = targets(entity)) {
10896 for (
auto targetKey: *pTargets)
10897 req_del_adjunct_pair(entity, targetKey.entity());
10901 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
10902 if (it == m_entityToArchetypeMap.end())
10905 for (
const auto& record: it->second)
10906 req_del(*record.pArchetype);
10911 void req_del_entities_with(Entity entity, Pair cond) {
10912 cnt::set<EntityLookupKey> visited;
10913 req_del_entities_with(entity, cond, visited);
10916 void req_del_entities_with(Entity entity, Pair cond, cnt::set<EntityLookupKey>& visited) {
10917 GAIA_PROF_SCOPE(World::req_del_entities_with);
10919 GAIA_ASSERT(entity != Pair(All, All));
10920 if (!visited.insert(EntityLookupKey(entity)).second)
10923 auto req_del_adjunct_pair = [&](Entity relation, Entity target) {
10924 if (!has(relation, cond))
10927 cnt::darray<Entity> sourcesToDel;
10928 sources(relation, target, [&](Entity source) {
10929 sourcesToDel.push_back(source);
10932 for (
auto source: sourcesToDel)
10933 req_del(fetch(source), source);
10936 if (entity.pair()) {
10937 if (entity.id() != All.id()) {
10938 const auto relation = try_get(entity.id());
10939 const auto target = try_get(entity.gen());
10940 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
10941 req_del_adjunct_pair(relation, target);
10943 const auto target = try_get(entity.gen());
10944 if (target == EntityBad)
10945 goto skip_req_del_all_target_cond;
10946 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
10947 if (exclusive_adjunct_sources(store, target) ==
nullptr)
10950 req_del_adjunct_pair(relKey.entity(), target);
10952 skip_req_del_all_target_cond:;
10954 }
else if (is_exclusive_dont_fragment_relation(entity)) {
10955 if (
const auto* pTargets = targets(entity)) {
10956 for (
auto targetKey: *pTargets)
10957 req_del_adjunct_pair(entity, targetKey.entity());
10961 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
10962 if (it == m_entityToArchetypeMap.end())
10965 cnt::darray<Entity> cascadeTargets;
10966 if (entity.pair() && entity.id() == All.id()) {
10967 const auto target = try_get(entity.gen());
10968 if (target != EntityBad)
10969 collect_delete_cascade_direct_sources(target, cond, cascadeTargets);
10972 for (
auto source: cascadeTargets)
10973 req_del_entities_with(Pair(All, source), cond, visited);
10975 for (
const auto& record: it->second) {
10976 auto* pArchetype = record.pArchetype;
10978 if (!archetype_cond_match(*pArchetype, cond, entity))
10981 req_del(*pArchetype);
10986 void rem_from_entities(Entity entity) {
10987 GAIA_PROF_SCOPE(World::rem_from_entities);
10989 invalidate_pair_removal_caches(entity);
10991 auto rem_adjunct_pair = [&](Entity relation, Entity target) {
10992 cnt::darray<Entity> sourcesToRem;
10993 sources(relation, target, [&](Entity source) {
10994 sourcesToRem.push_back(source);
10997 for (
auto source: sourcesToRem)
10998 del(source, Pair(relation, target));
11001 if (entity.pair()) {
11002 if (entity.id() != All.id()) {
11003 const auto relation = try_get(entity.id());
11004 const auto target = try_get(entity.gen());
11005 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
11006 rem_adjunct_pair(relation, target);
11008 const auto target = try_get(entity.gen());
11009 if (target == EntityBad)
11010 goto skip_rem_all_target;
11011 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
11012 if (exclusive_adjunct_sources(store, target) ==
nullptr)
11015 rem_adjunct_pair(relKey.entity(), target);
11017 skip_rem_all_target:;
11019 }
else if (is_exclusive_dont_fragment_relation(entity)) {
11020 if (
const auto* pTargets = targets(entity)) {
11021 for (
auto targetKey: *pTargets)
11022 rem_adjunct_pair(entity, targetKey.entity());
11026 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
11027 if (it == m_entityToArchetypeMap.end())
11031 if (!entity.pair()) {
11032 auto& ec = fetch(entity);
11033 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
11034 auto ids = ec.pArchetype->ids_view();
11035 const auto idx = core::get_index(ids, entity);
11036 if (idx != BadIndex)
11037 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
11041#if GAIA_OBSERVERS_ENABLED
11042 cnt::set<EntityLookupKey> diffTermSet;
11043 cnt::darray<Entity> diffTerms;
11044 cnt::darray<Entity> diffTargets;
11045 for (
const auto& record: it->second) {
11046 auto* pArchetype = record.pArchetype;
11047 if (pArchetype->is_req_del())
11050 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
11051 if (pDstArchetype ==
nullptr)
11054 for (
auto id: pArchetype->ids_view()) {
11055 bool matches =
false;
11056 if (entity.pair()) {
11057 if (entity.id() == All.id() && entity.gen() == All.id())
11058 matches =
id.pair();
11059 else if (entity.id() == All.id())
11060 matches =
id.pair() &&
id.gen() == entity.gen();
11061 else if (entity.gen() == All.id())
11062 matches =
id.pair() &&
id.id() == entity.id();
11064 matches =
id == entity;
11066 matches =
id == entity;
11071 if (diffTermSet.insert(EntityLookupKey(
id)).second)
11072 diffTerms.push_back(
id);
11075 for (
const auto* pChunk: pArchetype->chunks()) {
11076 const auto entities = pChunk->entity_view();
11077 GAIA_EACH(entities)
11078 diffTargets.push_back(entities[i]);
11081 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
11082 ? ObserverRegistry::DiffDispatchCtx{}
11083 : m_observers.prepare_diff(
11084 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
11085 EntitySpan{diffTargets.data(), diffTargets.size()});
11089 for (
const auto& record: it->second) {
11090 auto* pArchetype = record.pArchetype;
11091 if (pArchetype->is_req_del())
11094 if (entity.pair()) {
11095 cnt::darray_ext<Entity, 16> removedIsTargets;
11096 for (
auto id: pArchetype->ids_view()) {
11097 bool matches =
false;
11098 if (entity.id() == All.id() && entity.gen() == All.id())
11099 matches =
id.pair();
11100 else if (entity.id() == All.id())
11101 matches =
id.pair() &&
id.gen() == entity.gen();
11102 else if (entity.gen() == All.id())
11103 matches =
id.pair() &&
id.id() == entity.id();
11105 matches =
id == entity;
11107 if (!matches || !
id.pair() ||
id.
id() != Is.id())
11110 const auto target = try_get(
id.gen());
11111 if (target != EntityBad)
11112 removedIsTargets.push_back(target);
11115 if (!removedIsTargets.empty()) {
11116 for (
const auto* pChunk: pArchetype->chunks()) {
11117 auto entities = pChunk->entity_view();
11118 GAIA_EACH(entities) {
11119 for (
const auto target: removedIsTargets)
11120 unlink_live_is_relation(entities[i], target);
11126 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
11127 if (pDstArchetype !=
nullptr)
11128 move_to_archetype(*pArchetype, *pDstArchetype);
11131#if GAIA_OBSERVERS_ENABLED
11132 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
11138 void rem_from_entities(Entity entity, Pair cond) {
11139 GAIA_PROF_SCOPE(World::rem_from_entities);
11141 invalidate_pair_removal_caches(entity);
11143 auto rem_adjunct_pair = [&](Entity relation, Entity target) {
11144 if (!has(relation, cond))
11147 cnt::darray<Entity> sourcesToRem;
11148 sources(relation, target, [&](Entity source) {
11149 sourcesToRem.push_back(source);
11152 for (
auto source: sourcesToRem)
11153 del(source, Pair(relation, target));
11156 if (entity.pair()) {
11157 if (entity.id() != All.id()) {
11158 const auto relation = try_get(entity.id());
11159 const auto target = try_get(entity.gen());
11160 if (relation != EntityBad && target != EntityBad && is_exclusive_dont_fragment_relation(relation))
11161 rem_adjunct_pair(relation, target);
11163 const auto target = try_get(entity.gen());
11164 if (target == EntityBad)
11165 goto skip_rem_all_target_cond;
11166 for (
const auto& [relKey, store]: m_exclusiveAdjunctByRel) {
11167 if (exclusive_adjunct_sources(store, target) ==
nullptr)
11170 rem_adjunct_pair(relKey.entity(), target);
11172 skip_rem_all_target_cond:;
11174 }
else if (is_exclusive_dont_fragment_relation(entity)) {
11175 if (
const auto* pTargets = targets(entity)) {
11176 for (
auto targetKey: *pTargets)
11177 rem_adjunct_pair(entity, targetKey.entity());
11181 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entity));
11182 if (it == m_entityToArchetypeMap.end())
11186 if (!entity.pair()) {
11187 auto& ec = fetch(entity);
11188 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0) {
11189 auto ids = ec.pArchetype->ids_view();
11190 const auto idx = core::get_index(ids, entity);
11191 if (idx != BadIndex)
11192 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::IsSingleton,
false);
11196#if GAIA_OBSERVERS_ENABLED
11197 cnt::set<EntityLookupKey> diffTermSet;
11198 cnt::darray<Entity> diffTerms;
11199 cnt::darray<Entity> diffTargets;
11200 for (
const auto& record: it->second) {
11201 auto* pArchetype = record.pArchetype;
11202 if (pArchetype->is_req_del())
11205 if (!archetype_cond_match(*pArchetype, cond, entity))
11208 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
11209 if (pDstArchetype ==
nullptr)
11212 for (
auto id: pArchetype->ids_view()) {
11213 bool matches =
false;
11214 if (entity.pair()) {
11215 if (entity.id() == All.id() && entity.gen() == All.id())
11216 matches =
id.pair();
11217 else if (entity.id() == All.id())
11218 matches =
id.pair() &&
id.gen() == entity.gen();
11219 else if (entity.gen() == All.id())
11220 matches =
id.pair() &&
id.id() == entity.id();
11222 matches =
id == entity;
11224 matches =
id == entity;
11229 if (diffTermSet.insert(EntityLookupKey(
id)).second)
11230 diffTerms.push_back(
id);
11233 for (
const auto* pChunk: pArchetype->chunks()) {
11234 const auto entities = pChunk->entity_view();
11235 GAIA_EACH(entities)
11236 diffTargets.push_back(entities[i]);
11239 auto delDiffCtx = diffTargets.empty() || diffTerms.empty()
11240 ? ObserverRegistry::DiffDispatchCtx{}
11241 : m_observers.prepare_diff(
11242 *
this, ObserverEvent::OnDel, EntitySpan{diffTerms.data(), diffTerms.size()},
11243 EntitySpan{diffTargets.data(), diffTargets.size()});
11246 for (
const auto& record: it->second) {
11247 auto* pArchetype = record.pArchetype;
11248 if (pArchetype->is_req_del())
11252 if (!archetype_cond_match(*pArchetype, cond, entity))
11255 auto* pDstArchetype = calc_dst_archetype(pArchetype, entity);
11256 if (pDstArchetype !=
nullptr)
11257 move_to_archetype(*pArchetype, *pDstArchetype);
11260#if GAIA_OBSERVERS_ENABLED
11261 m_observers.finish_diff(*
this, GAIA_MOV(delDiffCtx));
11275 void handle_del_entity(EntityContainer& ec, Entity entity) {
11276 GAIA_PROF_SCOPE(World::handle_del_entity);
11278 GAIA_ASSERT(!is_wildcard(entity));
11280 if (entity.pair()) {
11281 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
11282 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
11284 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
11285 entity.gen(), name(entity), EntityKindString[entity.kind()]);
11289 const auto tgt = try_get(entity.gen());
11290 const bool hasLiveTarget = tgt != EntityBad;
11291 if (hasLiveTarget) {
11292 const auto& ecTgt = fetch(tgt);
11293 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
11294 has_exclusive_adjunct_target_cond(tgt, Pair(OnDeleteTarget, Error))) {
11296 false,
"Trying to delete an entity that is forbidden from being deleted (target restriction)");
11298 "Trying to delete a pair [%u.%u] %s [%s] that is forbidden from being deleted (target restriction)",
11299 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
11304#if GAIA_USE_SAFE_ENTITY
11306 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
11308 ec.flags |= EntityContainerFlags::RefDecreased;
11312 if (ec.refCnt != 0)
11316 if (hasLiveTarget) {
11317 const auto& ecTgt = fetch(tgt);
11318 if ((ecTgt.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0 ||
11319 has_exclusive_adjunct_target_cond(tgt, Pair(OnDeleteTarget, Delete))) {
11320#if GAIA_OBSERVERS_ENABLED
11321 cnt::darray<Entity> cascadeTargets;
11322 collect_delete_cascade_sources(tgt, Pair(OnDeleteTarget, Delete), cascadeTargets);
11323 auto cascadeDelDiffCtx =
11324 cascadeTargets.empty()
11325 ? ObserverRegistry::DiffDispatchCtx{}
11326 : m_observers.prepare_diff(
11327 *
this, ObserverEvent::OnDel, EntitySpan{cascadeTargets.data(), cascadeTargets.size()},
11328 EntitySpan{cascadeTargets.data(), cascadeTargets.size()});
11331 req_del_entities_with(Pair(All, tgt), Pair(OnDeleteTarget, Delete));
11332#if GAIA_OBSERVERS_ENABLED
11333 m_observers.finish_diff(*
this, GAIA_MOV(cascadeDelDiffCtx));
11337 rem_from_entities(Pair(All, tgt));
11342 if (is_req_del(ec))
11345#if GAIA_OBSERVERS_ENABLED
11346 observers().del(*
this, entity);
11349 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
11351 req_del_entities_with(entity);
11354 rem_from_entities(entity);
11357 if ((ec.flags & EntityContainerFlags::OnDelete_Error) != 0) {
11358 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted");
11360 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted", entity.id(),
11361 entity.gen(), name(entity), EntityKindString[entity.kind()]);
11365 if ((ec.flags & EntityContainerFlags::OnDeleteTarget_Error) != 0 ||
11366 has_exclusive_adjunct_target_cond(entity, Pair(OnDeleteTarget, Error))) {
11367 GAIA_ASSERT2(
false,
"Trying to delete an entity that is forbidden from being deleted (a pair's target)");
11369 "Trying to delete an entity [%u.%u] %s [%s] that is forbidden from being deleted (a pair's target)",
11370 entity.id(), entity.gen(), name(entity), EntityKindString[entity.kind()]);
11374#if GAIA_USE_SAFE_ENTITY
11376 if ((ec.flags & EntityContainerFlags::RefDecreased) == 0) {
11378 ec.flags |= EntityContainerFlags::RefDecreased;
11382 if (ec.refCnt != 0)
11386 const bool deleteTargets = (ec.flags & EntityContainerFlags::OnDeleteTarget_Delete) != 0 ||
11387 has_exclusive_adjunct_target_cond(entity, Pair(OnDeleteTarget, Delete));
11388 cnt::darray<Entity> cascadeTargets;
11390 collect_delete_cascade_sources(entity, Pair(OnDeleteTarget, Delete), cascadeTargets);
11391#if GAIA_OBSERVERS_ENABLED
11392 auto cascadeDelDiffCtx = ObserverRegistry::DiffDispatchCtx{};
11393 if (!cascadeTargets.empty()) {
11394 cascadeDelDiffCtx = m_observers.prepare_diff(
11395 *
this, ObserverEvent::OnDel, EntitySpan{cascadeTargets.data(), cascadeTargets.size()},
11396 EntitySpan{cascadeTargets.data(), cascadeTargets.size()});
11400 if (deleteTargets) {
11402 req_del_entities_with(Pair(All, entity), Pair(OnDeleteTarget, Delete));
11405 rem_from_entities(Pair(All, entity));
11408#if GAIA_OBSERVERS_ENABLED
11409 m_observers.finish_diff(*
this, GAIA_MOV(cascadeDelDiffCtx));
11413 if (is_req_del(ec))
11416#if GAIA_OBSERVERS_ENABLED
11417 observers().del(*
this, entity);
11420 if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
11422 req_del_entities_with(entity);
11425 rem_from_entities(entity);
11430 req_del(ec, entity);
11432#if GAIA_USE_WEAK_ENTITY
11434 while (ec.pWeakTracker !=
nullptr) {
11435 auto* pTracker = ec.pWeakTracker;
11436 ec.pWeakTracker = pTracker->next;
11437 if (ec.pWeakTracker !=
nullptr)
11438 ec.pWeakTracker->prev =
nullptr;
11440 auto* pWeakEntity = pTracker->pWeakEntity;
11441 GAIA_ASSERT(pWeakEntity !=
nullptr);
11442 GAIA_ASSERT(pWeakEntity->m_pTracker == pTracker);
11443 pWeakEntity->m_pTracker =
nullptr;
11444 pWeakEntity->m_entity = EntityBad;
11454 void remove_edge_from_archetype(Archetype* pArchetype, ArchetypeGraphEdge edgeLeft, Entity edgeEntity) {
11455 GAIA_ASSERT(pArchetype !=
nullptr);
11457 const auto edgeLeftIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
11458 if (edgeLeftIt == m_archetypesById.end())
11461 auto* pArchetypeLeft = edgeLeftIt->second;
11462 GAIA_ASSERT(pArchetypeLeft !=
nullptr);
11465 pArchetypeLeft->del_graph_edges(pArchetype, edgeEntity);
11468 auto& archetypesRight = pArchetype->right_edges();
11469 for (
auto& it: archetypesRight) {
11470 const auto& edgeRight = it.second;
11471 const auto edgeRightIt = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
11472 if (edgeRightIt == m_archetypesById.end())
11475 auto* pArchetypeRight = edgeRightIt->second;
11478 pArchetype->del_graph_edges(pArchetypeRight, it.first.entity());
11482 void remove_edges(Entity entityToRemove) {
11483 const auto it = m_entityToArchetypeMap.find(EntityLookupKey(entityToRemove));
11484 if (it == m_entityToArchetypeMap.end())
11487 for (
const auto& record: it->second) {
11488 auto* pArchetype = record.pArchetype;
11489 remove_edge_from_archetype(pArchetype, pArchetype->find_edge_left(entityToRemove), entityToRemove);
11493 void remove_edges_from_pairs(Entity entity) {
11499 const auto* tgts = targets(entity);
11500 if (tgts !=
nullptr) {
11501 for (
auto target: *tgts)
11502 remove_edges(Pair(entity, target.entity()));
11505 const auto* rels = relations(entity);
11506 if (rels !=
nullptr) {
11507 for (
auto relation: *rels)
11508 remove_edges(Pair(relation.entity(), entity));
11514 void del_graph_edges(Entity entity) {
11515 remove_edges(entity);
11516 remove_edges_from_pairs(entity);
11519 void touch_rel_version(Entity relation) {
11520 const EntityLookupKey key(relation);
11521 auto it = m_relationVersions.find(key);
11522 if (it == m_relationVersions.end())
11523 m_relationVersions.emplace(key, 1);
11526 if (it->second == 0)
11531 void del_reltgt_tgtrel_pairs(Entity entity) {
11532 auto delPair = [](PairMap& map, Entity source, Entity remove) {
11533 auto itTargets = map.find(EntityLookupKey(source));
11534 if (itTargets != map.end()) {
11535 auto& targets = itTargets->second;
11536 targets.erase(EntityLookupKey(remove));
11540 Archetype* pArchetype =
nullptr;
11542 if (entity.pair()) {
11543 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
11544 if (it != m_recs.pairs.end()) {
11545 pArchetype = it->second.pArchetype;
11547 m_recs.pairs.erase(it);
11552 GAIA_ASSERT(entity.id() < m_recs.entities.size());
11553 GAIA_ASSERT(entity.gen() < m_recs.entities.size());
11554 auto rel = m_recs.entities.handle(entity.id());
11555 auto tgt = m_recs.entities.handle(entity.gen());
11557 delPair(m_relToTgt, rel, tgt);
11558 delPair(m_relToTgt, All, tgt);
11559 delPair(m_tgtToRel, tgt, rel);
11560 delPair(m_tgtToRel, All, rel);
11564 auto ec = m_recs.entities[entity.id()];
11565 m_recs.entities.free(entity);
11568 del_sparse_components(entity);
11570 del_exclusive_adjunct_source(entity);
11572 del_exclusive_adjunct_relation(entity);
11574 del_sparse_component_store(entity);
11577 if ((ec.flags & EntityContainerFlags::IsSingleton) != 0)
11578 req_del(*ec.pArchetype);
11580 ec.pArchetype =
nullptr;
11581 ec.pChunk =
nullptr;
11582 ec.pEntity =
nullptr;
11583 EntityBuilder::set_flag(ec.flags, EntityContainerFlags::DeleteRequested,
false);
11586 delPair(m_relToTgt, All, entity);
11587 delPair(m_tgtToRel, All, entity);
11588 m_relToTgt.erase(EntityLookupKey(entity));
11589 m_tgtToRel.erase(EntityLookupKey(entity));
11592 del_entity_archetype_pairs(entity, pArchetype);
11597 void invalidate_entity(Entity entity) {
11598 del_graph_edges(entity);
11599 del_reltgt_tgtrel_pairs(entity);
11606 void store_entity(EntityContainer& ec, Entity entity, Archetype* pArchetype, Chunk* pChunk) {
11607 GAIA_ASSERT(pArchetype !=
nullptr);
11608 GAIA_ASSERT(pChunk !=
nullptr);
11610 !locked() &&
"Entities can't be stored while the world is locked "
11611 "(structural changes are forbidden during this time!)");
11613 ec.pArchetype = pArchetype;
11614 ec.pChunk = pChunk;
11615 ec.row = pChunk->add_entity(entity);
11616 ec.pEntity = &pChunk->entity_view()[ec.row];
11617 GAIA_ASSERT(entity.pair() || ec.data.gen == entity.gen());
11626 void move_entity(Entity entity, EntityContainer& ec, Archetype& dstArchetype, Chunk& dstChunk) {
11627 GAIA_PROF_SCOPE(World::move_entity);
11629 auto* pDstChunk = &dstChunk;
11630 auto* pSrcChunk = ec.pChunk;
11632 GAIA_ASSERT(pDstChunk != pSrcChunk);
11634 const auto srcRow0 = ec.row;
11635 const auto dstRow = pDstChunk->add_entity(entity);
11636 const bool wasEnabled = !ec.data.dis;
11638 auto& srcArchetype = *ec.pArchetype;
11639 const bool archetypeChanged = srcArchetype.id() != dstArchetype.id();
11640#if GAIA_ASSERT_ENABLED
11641 verify_move(*
this, srcArchetype, entity);
11645 srcArchetype.enable_entity(pSrcChunk, srcRow0,
true, m_recs);
11647 const auto srcRow = ec.row;
11650 if (dstArchetype.id() == srcArchetype.id()) {
11651 pDstChunk->move_entity_data(entity, dstRow, m_recs);
11653 pDstChunk->move_foreign_entity_data(pSrcChunk, srcRow, pDstChunk, dstRow);
11657 remove_entity(srcArchetype, *pSrcChunk, srcRow);
11660 dstArchetype.try_update_free_chunk_idx();
11663 ec.pArchetype = &dstArchetype;
11664 ec.pChunk = pDstChunk;
11665 ec.row = (uint16_t)dstRow;
11666 ec.pEntity = &pDstChunk->entity_view()[dstRow];
11667 if (archetypeChanged)
11668 update_src_entity_version(entity);
11671 dstArchetype.enable_entity(pDstChunk, dstRow, wasEnabled, m_recs);
11674 GAIA_ASSERT(valid(entity));
11675 validate_chunk(pSrcChunk);
11676 validate_chunk(pDstChunk);
11677 validate_entities();
11682 void move_entity_raw(Entity entity, EntityContainer& ec, Archetype& dstArchetype) {
11684 ec.pChunk->update_world_version();
11685 ec.pChunk->update_entity_order_version();
11687 auto* pDstChunk = dstArchetype.foc_free_chunk();
11688 move_entity(entity, ec, dstArchetype, *pDstChunk);
11691 pDstChunk->update_world_version();
11692 pDstChunk->update_entity_order_version();
11693 update_version(m_worldVersion);
11698 Chunk* move_entity(Entity entity, Archetype& dstArchetype) {
11700 auto& ec = fetch(entity);
11701 if (ec.pArchetype == &dstArchetype)
11705 ec.pChunk->update_world_version();
11706 ec.pChunk->update_entity_order_version();
11708 auto* pDstChunk = dstArchetype.foc_free_chunk();
11709 move_entity(entity, ec, dstArchetype, *pDstChunk);
11712 pDstChunk->update_world_version();
11713 pDstChunk->update_entity_order_version();
11714 update_version(m_worldVersion);
11719 void validate_archetype_edges([[maybe_unused]]
const Archetype* pArchetype)
const {
11720#if GAIA_ECS_VALIDATE_ARCHETYPE_GRAPH && GAIA_ASSERT_ENABLED
11721 GAIA_ASSERT(pArchetype !=
nullptr);
11724 const auto& archetypesLeft = pArchetype->left_edges();
11725 for (
const auto& it: archetypesLeft) {
11726 const auto& edge = it.second;
11727 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
11728 if (edgeIt == m_archetypesById.end())
11731 const auto entity = it.first.entity();
11732 const auto* pArchetypeRight = edgeIt->second;
11735 const auto edgeRight = pArchetypeRight->find_edge_right(entity);
11736 GAIA_ASSERT(edgeRight != ArchetypeIdHashPairBad);
11739 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeRight.id, edgeRight.hash));
11740 GAIA_ASSERT(it2 != m_archetypesById.end());
11741 const auto* pArchetype2 = it2->second;
11742 GAIA_ASSERT(pArchetype2 == pArchetype);
11746 const auto& archetypesRight = pArchetype->right_edges();
11747 for (
const auto& it: archetypesRight) {
11748 const auto& edge = it.second;
11749 const auto edgeIt = m_archetypesById.find(ArchetypeIdLookupKey(edge.id, edge.hash));
11750 if (edgeIt == m_archetypesById.end())
11753 const auto entity = it.first.entity();
11754 const auto* pArchetypeRight = edgeIt->second;
11757 const auto edgeLeft = pArchetypeRight->find_edge_left(entity);
11758 GAIA_ASSERT(edgeLeft != ArchetypeIdHashPairBad);
11761 const auto it2 = m_archetypesById.find(ArchetypeIdLookupKey(edgeLeft.id, edgeLeft.hash));
11762 GAIA_ASSERT(it2 != m_archetypesById.end());
11763 const auto* pArchetype2 = it2->second;
11764 GAIA_ASSERT(pArchetype2 == pArchetype);
11770 void validate_entities()
const {
11771#if GAIA_ECS_VALIDATE_ENTITY_LIST
11772 m_recs.entities.validate();
11777 void validate_chunk([[maybe_unused]] Chunk* pChunk)
const {
11778#if GAIA_ECS_VALIDATE_CHUNKS && GAIA_ASSERT_ENABLED
11779 GAIA_ASSERT(pChunk !=
nullptr);
11781 if (!pChunk->empty()) {
11784 for (
const auto& ec: m_recs.entities) {
11785 if (ec.pChunk != pChunk)
11789 for (
const auto& pair: m_recs.pairs) {
11790 if (pair.second.pChunk != pChunk)
11794 GAIA_ASSERT(cnt == pChunk->size());
11797 for (
const auto& ec: m_recs.entities) {
11798 GAIA_ASSERT(ec.pChunk != pChunk);
11800 for (
const auto& pair: m_recs.pairs) {
11801 GAIA_ASSERT(pair.second.pChunk != pChunk);
11810 template <
bool CheckIn>
11811 GAIA_NODISCARD
bool is_inter(Entity entity, Entity entityBase)
const {
11812 GAIA_ASSERT(valid_entity(entity));
11813 GAIA_ASSERT(valid_entity(entityBase));
11816 if (entity.pair() || entityBase.pair())
11819 if constexpr (!CheckIn) {
11820 if (entity == entityBase)
11824 const auto& targets = as_targets_trav_cache(entity);
11825 for (
auto target: targets) {
11826 if (target == entityBase)
11834 template <
bool CheckIn,
typename Func>
11835 void as_up_trav(Entity entity, Func func) {
11836 GAIA_ASSERT(valid_entity(entity));
11842 if constexpr (!CheckIn) {
11846 const auto& ec = m_recs.entities[entity.id()];
11847 const auto* pArchetype = ec.pArchetype;
11850 if (pArchetype->pairs_is() == 0)
11853 for (uint32_t i = 0; i < pArchetype->pairs_is(); ++i) {
11854 auto e = pArchetype->entity_from_pairs_as_idx(i);
11855 const auto& ecTarget = m_recs.entities[e.gen()];
11856 auto target = *ecTarget.pEntity;
11859 as_up_trav<CheckIn>(target, func);
11863 template <
typename T>
11864 const ComponentCacheItem& reg_core_entity(Entity
id, Archetype* pArchetype) {
11865 auto comp = add(*pArchetype,
id.entity(),
id.pair(),
id.kind());
11866 const auto& ci = comp_cache_mut().
add<T>(id);
11867 GAIA_ASSERT(ci.entity ==
id);
11868 GAIA_ASSERT(comp ==
id);
11873 template <
typename T>
11874 const ComponentCacheItem& reg_core_entity(Entity
id) {
11875 return reg_core_entity<T>(
id, m_pRootArchetype);
11878#if GAIA_ECS_AUTO_COMPONENT_SCHEMA
11879 template <
typename T>
11880 static void auto_populate_component_schema(ComponentCacheItem& item) {
11881 if (!item.fields_empty())
11884 using U = core::raw_t<T>;
11885 if constexpr (std::is_empty_v<U>)
11887 if constexpr (mem::is_soa_layout_v<U>)
11890 if constexpr (!std::is_class_v<U>) {
11891 (void)item.set_field(
"value", 5, ser::type_id<U>(), 0, (uint32_t)
sizeof(U));
11893 }
else if constexpr (!std::is_aggregate_v<U>) {
11899 const auto* pBase =
reinterpret_cast<const uint8_t*
>(&tmp);
11900 uint32_t fieldIdx = 0;
11901 meta::each_member(tmp, [&](
auto&... fields) {
11902 auto add_field = [&](
auto& field) {
11903 using F = core::raw_t<
decltype(field)>;
11904 char fieldName[24]{};
11905 (void)GAIA_STRFMT(fieldName,
sizeof(fieldName),
"f%u", fieldIdx++);
11906 const auto* pField =
reinterpret_cast<const uint8_t*
>(&field);
11907 const auto offset = (uint32_t)(pField - pBase);
11908 (void)item.set_field(fieldName, 0, ser::type_id<F>(), offset, (uint32_t)
sizeof(F));
11910 (add(fields), ...);
11921#if GAIA_ECS_CHUNK_ALLOCATOR
11922 ChunkAllocator::get().flush();
11929 void assign_entity(Entity entity, Archetype& archetype) {
11930 GAIA_ASSERT(!entity.pair());
11932 auto* pChunk = archetype.foc_free_chunk();
11933 store_entity(m_recs.entities[entity.id()], entity, &archetype, pChunk);
11934 pChunk->update_versions();
11935 archetype.try_update_free_chunk_idx();
11938 pChunk->call_gen_ctors(pChunk->size() - 1, 1);
11940#if GAIA_ASSERT_ENABLED
11941 const auto& ec = m_recs.entities[entity.id()];
11942 GAIA_ASSERT(ec.pChunk == pChunk);
11943 auto entityExpected = pChunk->entity_view()[ec.row];
11944 GAIA_ASSERT(entityExpected == entity);
11951 void assign_pair(Entity entity, Archetype& archetype) {
11952 GAIA_ASSERT(entity.pair());
11955 GAIA_ASSERT(&archetype == m_pEntityArchetype);
11957 const auto it = m_recs.pairs.find(EntityLookupKey(entity));
11958 if (it != m_recs.pairs.end())
11962 EntityContainer ec{};
11963 ec.idx = entity.id();
11964 ec.data.gen = entity.gen();
11967 ec.data.kind = EntityKind::EK_Gen;
11969 auto* pChunk = archetype.foc_free_chunk();
11970 store_entity(ec, entity, &archetype, pChunk);
11971 pChunk->update_versions();
11972 archetype.try_update_free_chunk_idx();
11974 m_recs.pairs.emplace(EntityLookupKey(entity), GAIA_MOV(ec));
11977 const auto rel = get(entity.id());
11978 const auto tgt = get(entity.gen());
11980 auto addPair = [](PairMap& map, Entity source, Entity add) {
11981 auto& ents = map[EntityLookupKey(source)];
11982 ents.insert(EntityLookupKey(add));
11985 addPair(m_relToTgt, rel, tgt);
11986 addPair(m_relToTgt, All, tgt);
11987 addPair(m_tgtToRel, tgt, rel);
11988 addPair(m_tgtToRel, All, rel);
11990#if GAIA_OBSERVERS_ENABLED
11991 m_observers.try_mark_term_observed(*
this, entity);
12001 GAIA_NODISCARD Entity add(Archetype& archetype,
bool isEntity,
bool isPair, EntityKind kind) {
12002 EntityContainerCtx ctx{isEntity, isPair, kind};
12003 const auto entity = m_recs.entities.alloc(&ctx);
12004 assign_entity(entity, archetype);
12013 template <
typename Func>
12014 void add_entity_n(Archetype& archetype, uint32_t count, Func func) {
12015 EntityContainerCtx ctx{
true,
false, EntityKind::EK_Gen};
12016#if GAIA_OBSERVERS_ENABLED
12017 const auto addedIds = EntitySpan{archetype.ids_view()};
12018 ObserverRegistry::DiffDispatchCtx addDiffCtx{};
12019 if (!addedIds.empty())
12020 addDiffCtx = m_observers.prepare_diff_add_new(*
this, addedIds);
12023 uint32_t left = count;
12025 auto* pChunk = archetype.foc_free_chunk();
12026 const uint32_t originalChunkSize = pChunk->size();
12027 const uint32_t freeSlotsInChunk = pChunk->capacity() - originalChunkSize;
12028 const uint32_t toCreate = core::get_min(freeSlotsInChunk, left);
12030 GAIA_FOR(toCreate) {
12031 const auto entityNew = m_recs.entities.alloc(&ctx);
12032 auto& ecNew = m_recs.entities[entityNew.id()];
12033 store_entity(ecNew, entityNew, &archetype, pChunk);
12035#if GAIA_ASSERT_ENABLED
12036 GAIA_ASSERT(ecNew.pChunk == pChunk);
12037 auto entityExpected = pChunk->entity_view()[ecNew.row];
12038 GAIA_ASSERT(entityExpected == entityNew);
12043 archetype.try_update_free_chunk_idx();
12046 pChunk->call_gen_ctors(originalChunkSize, toCreate);
12050 auto entities = pChunk->entity_view();
12051 GAIA_FOR2(originalChunkSize, pChunk->size()) func(entities[i]);
12054 pChunk->update_versions();
12056#if GAIA_OBSERVERS_ENABLED
12057 if (!addedIds.empty()) {
12058 auto entities = pChunk->entity_view();
12059 const auto targets = EntitySpan{entities.data() + originalChunkSize, toCreate};
12060 m_observers.append_diff_targets(*
this, addDiffCtx, targets);
12061 m_observers.on_add(*
this, archetype, addedIds, targets);
12066 }
while (left > 0);
12068#if GAIA_OBSERVERS_ENABLED
12069 if (!addedIds.empty())
12070 m_observers.finish_diff(*
this, GAIA_MOV(addDiffCtx));
12076 GAIA_PROF_SCOPE(World::gc);
12078 del_empty_chunks();
12079 defrag_chunks(m_defragEntitiesPerTick);
12080 del_empty_archetypes();
12090 if GAIA_UNLIKELY (serId == QueryIdBad) {
12091#if GAIA_ASSERT_ENABLED
12092 uint32_t safetyCounter = 0;
12096#if GAIA_ASSERT_ENABLED
12099 GAIA_ASSERT(safetyCounter < 100000);
12102 serId = ++m_nextQuerySerId;
12104 GAIA_ASSERT(serId != 0);
12109 auto ret = m_querySerMap.try_emplace(serId);
12113 return ret.first->second;
12117 return m_querySerMap[serId];
12123 auto it = m_querySerMap.find(serId);
12124 if (it == m_querySerMap.end())
12127 m_querySerMap.erase(it);
12128 serId = QueryIdBad;
12134 m_queryCache.invalidate_queries_for_entity(entityKey, QueryCache::ChangeKind::Structural);
12140 m_queryCache.invalidate_queries_for_rel(relation, QueryCache::ChangeKind::DynamicResult);
12146 m_queryCache.invalidate_sorted_queries_for_entity(entity);
12151 m_queryCache.invalidate_sorted_queries();
12157 GAIA_ASSERT(
is_pair.first() == Is);
12171 as_up_trav<false>(e, [&](
Entity target) {
12182 auto expr = util::trim(exprRaw);
12184 if (expr[0] ==
'(') {
12185 if (expr.back() !=
')') {
12186 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
12190 const auto idStr = expr.subspan(1, expr.size() - 2);
12191 const auto commaIdx = core::get_index(idStr,
',');
12193 const auto first = name_to_entity(idStr.subspan(0, commaIdx));
12194 if (first == EntityBad)
12196 const auto second = name_to_entity(idStr.subspan(commaIdx + 1));
12197 if (second == EntityBad)
12204 auto idStr = util::trim(expr);
12207 if (idStr.size() == 1 && idStr[0] ==
'*')
12210 return get_inter(idStr.data(), (uint32_t)idStr.size());
12220 auto expr = util::trim(exprRaw);
12222 if (expr[0] ==
'%') {
12223 if (expr[1] !=
'e') {
12224 GAIA_ASSERT2(
false,
"Expression '%' not terminated");
12228 auto id = (Identifier)va_arg(args,
unsigned long long);
12232 if (expr[0] ==
'(') {
12233 if (expr.back() !=
')') {
12234 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
12238 const auto idStr = expr.subspan(1, expr.size() - 2);
12239 const auto commaIdx = core::get_index(idStr,
',');
12241 const auto first = expr_to_entity(args, idStr.subspan(0, commaIdx));
12242 if (first == EntityBad)
12244 const auto second = expr_to_entity(args, idStr.subspan(commaIdx + 1));
12245 if (second == EntityBad)
12252 auto idStr = util::trim(expr);
12255 if (idStr.size() == 1 && idStr[0] ==
'*')
12259 const auto* pItem = resolve_component_name_inter(idStr.data(), (uint32_t)idStr.size());
12260 if (pItem ==
nullptr) {
12261 GAIA_ASSERT2(
false,
"Component not found");
12262 GAIA_LOG_W(
"Component '%.*s' not found", (uint32_t)idStr.size(), idStr.data());
12266 return pItem->entity;