2#include "gaia/config/config.h"
9#include "gaia/cnt/map.h"
10#include "gaia/core/hashing_string.h"
11#include "gaia/ecs/component.h"
12#include "gaia/ecs/component_cache_item.h"
13#include "gaia/ecs/component_desc.h"
14#include "gaia/ecs/id.h"
15#include "gaia/meta/type_info.h"
16#include "gaia/util/logging.h"
17#include "gaia/util/str.h"
25 class GAIA_API ComponentCache final {
28 struct ResolvedLookupEntry {
29 const ComponentCacheItem* pItem =
nullptr;
34 cnt::map<uint32_t, const ComponentCacheItem*> m_compByEntityId;
36 SymbolTable m_symbols;
38 cnt::map<ComponentLookupHash, const ComponentCacheItem*> m_compByTypeHash;
41 cnt::map<ComponentCacheItem::SymbolLookupKey, const ComponentCacheItem*> m_compBySymbol;
44 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry> m_compByPath;
47 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry> m_compByShortSymbol;
50 bool m_hooksAccessed =
false;
60 m_compByTypeHash.clear();
61 m_compBySymbol.clear();
63 m_compByShortSymbol.clear();
65 for (
auto [entityId, pItem]: m_compByEntityId) {
67 ComponentCacheItem::destroy(
const_cast<ComponentCacheItem*
>(pItem));
70 m_compByEntityId.clear();
73 m_hooksAccessed =
false;
80 GAIA_NODISCARD
bool hooks_accessed() const noexcept {
81 return m_hooksAccessed;
85 GAIA_NODISCARD
static bool is_internal_symbol(util::str_view symbol)
noexcept {
86 constexpr char InternalPrefix[] =
"gaia::ecs::";
87 constexpr uint32_t InternalPrefixLen = (uint32_t)(
sizeof(InternalPrefix) - 1);
88 return symbol.size() >= InternalPrefixLen && memcmp(symbol.data(), InternalPrefix, InternalPrefixLen) == 0;
91 static ComponentCacheItem::SymbolLookupKey short_name_key(
const ComponentCacheItem& item)
noexcept {
92 const auto symbol = item.symbol_name();
93 const auto* pName = symbol.data();
94 const auto len = symbol.size();
95 for (uint32_t i = len; i > 1; --i) {
96 const auto idx = i - 1;
97 if (pName[idx] !=
':' || pName[idx - 1] !=
':')
100 return ComponentCacheItem::SymbolLookupKey(pName + idx + 1, len - idx - 1, 0);
103 return ComponentCacheItem::SymbolLookupKey();
106 GAIA_NODISCARD
static util::str_view short_symbol_view(
const ComponentCacheItem& item)
noexcept {
107 const auto symbol = item.symbol_name();
108 if (is_internal_symbol(symbol))
111 const auto shortName = short_name_key(item);
112 if (shortName.str() ==
nullptr || shortName.len() == 0)
115 return util::str_view(shortName.str(), shortName.len());
118 GAIA_NODISCARD
static ComponentCacheItem::SymbolLookupKey lookup_key(util::str_view value)
noexcept {
119 return value.empty() ? ComponentCacheItem::SymbolLookupKey()
120 : ComponentCacheItem::SymbolLookupKey(value.data(), value.size(), 0);
123 GAIA_NODISCARD
static util::str_view normalize_name_view(
const char* name, uint32_t len = 0) noexcept {
127 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
128 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
129 return util::str_view(name, l);
132#if GAIA_ASSERT_ENABLED
135 void validate_runtime_type(
const ecs::ComponentDesc& desc)
const noexcept {
136 const auto& runtimeType = desc.runtimeType;
137 ser::serialization_type_id type = ser::serialization_type_id::ignore;
138 if (runtimeType.typeKind == RuntimeTypeKind::Enum || runtimeType.typeKind == RuntimeTypeKind::Bitmask) {
139 GAIA_ASSERT(runtime_primitive_serialization_type(runtimeType.underlyingType, type));
140 GAIA_ASSERT(runtimeType.elementType == EntityBad);
141 GAIA_ASSERT(runtimeType.elementCount == 0);
142 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
146 if (runtimeType.typeKind == RuntimeTypeKind::Array) {
147 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
148 GAIA_ASSERT(runtimeType.elementType != EntityBad);
149 GAIA_ASSERT(runtimeType.elementCount > 0);
150 GAIA_ASSERT(runtimeType.fields ==
nullptr);
151 GAIA_ASSERT(runtimeType.fieldCount == 0);
152 GAIA_ASSERT(runtimeType.constants ==
nullptr);
153 GAIA_ASSERT(runtimeType.constantCount == 0);
154 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
155 const auto* pElementType = find(runtimeType.elementType);
156 GAIA_ASSERT(pElementType !=
nullptr);
157 const auto elementSize = pElementType !=
nullptr ? pElementType->comp.size() : 0;
158 const auto elementAlignment = pElementType !=
nullptr ? pElementType->comp.alig() : 0;
159 GAIA_ASSERT(elementSize > 0);
160 if (elementSize > 0) {
161 GAIA_ASSERT(runtimeType.elementCount <= UINT32_MAX / elementSize);
162 GAIA_ASSERT(desc.size == elementSize * runtimeType.elementCount);
164 GAIA_ASSERT(desc.alig == elementAlignment);
168 if (runtimeType.typeKind == RuntimeTypeKind::Vector) {
169 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
170 GAIA_ASSERT(runtimeType.elementType != EntityBad);
171 GAIA_ASSERT(runtimeType.elementCount == 0);
172 GAIA_ASSERT(runtimeType.fields ==
nullptr);
173 GAIA_ASSERT(runtimeType.fieldCount == 0);
174 GAIA_ASSERT(runtimeType.constants ==
nullptr);
175 GAIA_ASSERT(runtimeType.constantCount == 0);
176 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
177 if (runtimeType.sequenceAdapter ==
nullptr) {
178 GAIA_ASSERT(desc.size == 0);
179 GAIA_ASSERT(desc.soa == 0);
181 GAIA_ASSERT(runtimeType.sequenceAdapter->count !=
nullptr);
182 GAIA_ASSERT(runtimeType.sequenceAdapter->element !=
nullptr);
184 GAIA_ASSERT(find(runtimeType.elementType) !=
nullptr);
185 #if GAIA_JSON_ENABLED
186 GAIA_ASSERT(runtimeType.jsonEncoding != RuntimeJsonEncoding::Utf8String || runtimeType.elementType == Char8);
190 #if GAIA_JSON_ENABLED
191 GAIA_ASSERT(runtimeType.jsonEncoding == RuntimeJsonEncoding::Default);
194 if (runtimeType.typeKind == RuntimeTypeKind::Opaque) {
195 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
196 GAIA_ASSERT(runtimeType.elementType == EntityBad);
197 GAIA_ASSERT(runtimeType.elementCount == 0);
198 GAIA_ASSERT(runtimeType.fields ==
nullptr);
199 GAIA_ASSERT(runtimeType.fieldCount == 0);
200 GAIA_ASSERT(runtimeType.constants ==
nullptr);
201 GAIA_ASSERT(runtimeType.constantCount == 0);
202 GAIA_ASSERT(runtimeType.opaqueAsType != EntityBad);
203 if (runtimeType.opaqueAdapter !=
nullptr)
204 GAIA_ASSERT(runtimeType.opaqueAdapter->project !=
nullptr);
205 GAIA_ASSERT(find(runtimeType.opaqueAsType) !=
nullptr);
209 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
210 GAIA_ASSERT(runtimeType.elementType == EntityBad);
211 GAIA_ASSERT(runtimeType.elementCount == 0);
212 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
213 GAIA_ASSERT(runtimeType.sequenceAdapter ==
nullptr);
214 GAIA_ASSERT(runtimeType.opaqueAdapter ==
nullptr);
219 void validate_runtime_fields(
const ecs::ComponentDesc& desc)
const noexcept {
220 const auto& runtimeType = desc.runtimeType;
221 if (runtimeType.fieldCount == 0)
224 GAIA_ASSERT(runtimeType.fields !=
nullptr);
225 if (runtimeType.fields ==
nullptr)
228 GAIA_FOR(runtimeType.fieldCount) {
229 const auto& field = runtimeType.fields[i];
230 GAIA_ASSERT(!field.name.empty());
231 GAIA_ASSERT(field.name.size() < ComponentCacheItem::MaxNameLength);
232 GAIA_ASSERT(field.unit.size() < RuntimeFieldDesc::MaxUnitLength);
233 GAIA_ASSERT(field.type != EntityBad);
235 (field.flags & (RuntimeFieldFlag_HasMinimum | RuntimeFieldFlag_HasMaximum)) !=
236 (RuntimeFieldFlag_HasMinimum | RuntimeFieldFlag_HasMaximum) ||
237 field.minimum <= field.maximum);
238 GAIA_ASSERT((field.flags & RuntimeFieldFlag_HasStep) == 0 || field.step > 0.0);
239 #if GAIA_JSON_ENABLED
241 field.jsonEncoding != RuntimeJsonEncoding::Utf8String || (field.type == Char8 && field.count != 0));
244 const auto* pType = find(field.type);
245 GAIA_ASSERT(pType !=
nullptr);
246 GAIA_ASSERT(pType ==
nullptr || pType->typeKind != RuntimeTypeKind::Array || field.count == 0);
247 const auto elementSize = pType !=
nullptr ? pType->comp.size() : 0;
248 GAIA_ASSERT(elementSize > 0);
249 const auto elementCount = ComponentCacheItem::field_element_count(field);
250 GAIA_ASSERT(elementCount > 0);
251 GAIA_ASSERT(field.offset <= desc.size);
252 const auto availableSize = field.offset <= desc.size ? desc.size - field.offset : 0;
253 GAIA_ASSERT(elementSize <= availableSize);
254 GAIA_ASSERT(elementCount <= availableSize / elementSize);
256 GAIA_FOR2_(i + 1, runtimeType.fieldCount, otherIdx) {
257 const auto& other = runtimeType.fields[otherIdx];
258 const bool sameLen = field.name.size() == other.name.size();
259 const bool sameName = sameLen && field.name.data() !=
nullptr && other.name.data() !=
nullptr &&
260 strncmp(field.name.data(), other.name.data(), field.name.size()) == 0;
261 GAIA_ASSERT(!sameName);
268 static void validate_runtime_constants(
const ecs::ComponentDesc& desc)
noexcept {
269 const auto& runtimeType = desc.runtimeType;
270 if (runtimeType.constantCount == 0)
273 GAIA_ASSERT(runtimeType.typeKind == RuntimeTypeKind::Enum || runtimeType.typeKind == RuntimeTypeKind::Bitmask);
274 GAIA_ASSERT(runtimeType.constants !=
nullptr);
275 if (runtimeType.constants ==
nullptr)
278 GAIA_FOR(runtimeType.constantCount) {
279 const auto& constant = runtimeType.constants[i];
280 GAIA_ASSERT(!constant.name.empty());
281 GAIA_ASSERT(constant.name.size() < ComponentCacheItem::MaxNameLength);
283 GAIA_FOR2_(i + 1, runtimeType.constantCount, otherIdx) {
284 const auto& other = runtimeType.constants[otherIdx];
285 const bool sameLen = constant.name.size() == other.name.size();
286 const bool sameName = sameLen && constant.name.data() !=
nullptr && other.name.data() !=
nullptr &&
287 strncmp(constant.name.data(), other.name.data(), constant.name.size()) == 0;
288 GAIA_ASSERT(!sameName);
289 GAIA_ASSERT(constant.value != other.value);
298 validate_typed_runtime_type(
const ComponentCacheItem& item,
const RuntimeTypeDesc& runtimeType)
const noexcept {
299 ecs::ComponentDesc desc{};
300 desc.size = item.comp.size();
301 desc.alig = item.comp.alig();
302 desc.storageType = item.comp.storage_type();
303 desc.soa = item.comp.soa();
304 desc.pSoaSizes = item.soaSizes;
305 desc.runtimeType = runtimeType;
306 validate_runtime_type(desc);
307 validate_runtime_fields(desc);
308 validate_runtime_constants(desc);
312 static bool build_default_path(util::str& out, util::str_view symbol) {
314 bool changed =
false;
315 out.reserve(symbol.size());
316 for (uint32_t i = 0; i < symbol.size(); ++i) {
317 if (i + 1 < symbol.size() && symbol.data()[i] ==
':' && symbol.data()[i + 1] ==
':') {
324 out.append(symbol.data()[i]);
338 static bool build_scoped_path(util::str& out, util::str_view scopePath, util::str_view leaf) {
340 if (scopePath.empty() || leaf.empty())
343 out.reserve(scopePath.size() + 1 + leaf.size());
344 out.append(scopePath);
353 static void initialize_names(ComponentCacheItem& item, util::str_view scopePath = {}) {
355 const auto symbol = item.symbol_name();
356 if (is_internal_symbol(symbol))
359 const auto shortName = short_name_key(item);
360 const bool hasShortName =
361 shortName.str() !=
nullptr && shortName.len() != 0 && shortName.len() != symbol.size();
362 const auto leaf = hasShortName ? util::str_view{shortName.str(), shortName.len()} : symbol;
364 if (!build_scoped_path(item.path, scopePath, leaf))
365 (void)build_default_path(item.path, symbol);
368 static void add_lookup_mapping(
369 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry>& map, util::str_view view,
370 const ComponentCacheItem& item) {
374 const auto key = lookup_key(view);
375 const auto it = map.find(key);
376 if (it == map.end()) {
377 map.emplace(key, ResolvedLookupEntry{&item, 1});
381 auto& entry = it->second;
382 GAIA_ASSERT(entry.matches != 0);
386 void add_path_mapping(
const ComponentCacheItem& item) {
387 add_lookup_mapping(m_compByPath, item.path.view(), item);
390 static void push_unique_entity(cnt::darray<Entity>& out, Entity entity) {
391 if (entity == EntityBad)
394 for (
const auto existing: out) {
395 if (existing == entity)
399 out.push_back(entity);
402 void remove_path_mapping(
const ComponentCacheItem& item) {
403 const auto pathValue = item.path.view();
404 if (pathValue.empty())
407 const auto key = lookup_key(pathValue);
408 const auto it = m_compByPath.find(key);
409 if (it == m_compByPath.end())
412 auto& entry = it->second;
413 GAIA_ASSERT(entry.matches != 0);
415 if (entry.matches == 1) {
416 GAIA_ASSERT(entry.pItem == &item);
417 m_compByPath.erase(it);
421 const auto remainingMatches = entry.matches - 1;
422 if (entry.pItem != &item) {
423 entry.matches = remainingMatches;
427 const ComponentCacheItem* pReplacement =
nullptr;
428 for (
const auto& [entityId, pItem]: m_compByEntityId) {
430 GAIA_ASSERT(pItem !=
nullptr);
434 if (pItem->path.view() != pathValue)
437 pReplacement = pItem;
441 GAIA_ASSERT(pReplacement !=
nullptr);
443 m_compByPath.erase(it);
444 if (pReplacement !=
nullptr)
445 m_compByPath.emplace(
446 lookup_key(pReplacement->path.view()), ResolvedLookupEntry{pReplacement, remainingMatches});
452 void add_name_mappings(ComponentCacheItem& item, util::str_view scopePath) {
453 m_compBySymbol.emplace(lookup_key(item.symbol_name()), &item);
454 initialize_names(item, scopePath);
455 add_path_mapping(item);
456 add_lookup_mapping(m_compByShortSymbol, short_symbol_view(item), item);
463 ComponentCacheItem& add_item(
const ComponentCacheItem* pItem, util::str_view scopePath) {
464 GAIA_ASSERT(pItem !=
nullptr);
465 GAIA_ASSERT(pItem->entity.id() == pItem->comp.id());
466 m_compByEntityId.emplace(pItem->entity.id(), pItem);
468 auto& item = *
const_cast<ComponentCacheItem*
>(pItem);
469 item.m_ownerCache =
this;
471 item.m_pHooksAccessed = &m_hooksAccessed;
473 add_name_mappings(item, scopePath);
480 GAIA_NODISCARD
const ComponentCacheItem* find_item(ComponentLookupHash hash)
const noexcept {
481 const auto it = m_compByTypeHash.find(hash);
482 return it != m_compByTypeHash.end() ? it->second :
nullptr;
488 template <
typename T>
489 void add_hash_mapping(
const ComponentCacheItem& item) {
490 const auto hash = detail::ComponentDesc<T>::hash_lookup();
491 const auto it = m_compByTypeHash.find(hash);
492 if (it == m_compByTypeHash.end()) {
493 m_compByTypeHash.emplace(hash, &item);
497 GAIA_ASSERT(it->second == &item);
501 ComponentCache() =
default;
507 ComponentCache(ComponentCache&&) =
delete;
508 ComponentCache(
const ComponentCache&) =
delete;
509 ComponentCache& operator=(ComponentCache&&) =
delete;
510 ComponentCache& operator=(
const ComponentCache&) =
delete;
514 template <
typename T>
515 GAIA_NODISCARD GAIA_FORCEINLINE
const ComponentCacheItem& add(Entity entity, util::str_view scopePath = {}) {
516 static_assert(!is_pair<T>::value);
517 GAIA_ASSERT(!entity.pair());
519 const auto* pItem = find_item(detail::ComponentDesc<T>::hash_lookup());
520 if (pItem !=
nullptr)
523 const auto* pNewItem = ComponentCacheItem::create<T>(entity, m_symbols);
524 GAIA_ASSERT(entity.id() == pNewItem->comp.id());
525 const auto& item = add_item(pNewItem, scopePath);
526 add_hash_mapping<T>(item);
537 template <
typename T>
538 GAIA_NODISCARD GAIA_FORCEINLINE
const ComponentCacheItem&
539 add(Entity entity,
const RuntimeTypeDesc& runtimeType, util::str_view scopePath = {}) {
540 static_assert(!is_pair<T>::value);
541 GAIA_ASSERT(!entity.pair());
543 const auto* pItem = find_item(detail::ComponentDesc<T>::hash_lookup());
544 if (pItem !=
nullptr)
547 const auto* pNewItem = ComponentCacheItem::create<T>(entity, m_symbols, runtimeType);
548 GAIA_ASSERT(entity.id() == pNewItem->comp.id());
549#if GAIA_ASSERT_ENABLED
550 validate_typed_runtime_type(*pNewItem, runtimeType);
552 const auto& item = add_item(pNewItem, scopePath);
553 add_hash_mapping<T>(item);
562 GAIA_NODISCARD ComponentCacheItem&
563 add(Entity entity,
const ecs::ComponentDesc& desc, util::str_view scopePath = {}) {
564 GAIA_ASSERT(entity != EntityBad);
565 GAIA_ASSERT(!entity.pair());
566 GAIA_ASSERT(!desc.name.empty());
567 GAIA_ASSERT(desc.name.size() < ComponentCacheItem::MaxNameLength);
570 const auto* pExisting = symbol(desc.name);
571 if (pExisting !=
nullptr)
572 return *find(pExisting->entity);
575#if GAIA_ASSERT_ENABLED
576 validate_runtime_type(desc);
577 validate_runtime_fields(desc);
578 validate_runtime_constants(desc);
581 const auto* pItem = ComponentCacheItem::create(entity, m_symbols, desc);
582 GAIA_ASSERT(entity.id() == pItem->comp.id());
583 return add_item(pItem, scopePath);
589 GAIA_NODISCARD
const ComponentCacheItem* find(Entity entity)
const noexcept {
593 const auto it = m_compByEntityId.find(entity.id());
594 return it != m_compByEntityId.end() ? it->second :
nullptr;
601 GAIA_NODISCARD
const ComponentCacheItem* find_pair_payload(Entity pair)
const noexcept {
605 const auto relationIt = m_compByEntityId.find(pair.id());
606 const auto* pRelation = relationIt != m_compByEntityId.end() ? relationIt->second :
nullptr;
607 if (pRelation !=
nullptr && pRelation->comp.size() != 0U)
610 const auto targetIt = m_compByEntityId.find(pair.gen());
611 const auto* pTarget = targetIt != m_compByEntityId.end() ? targetIt->second :
nullptr;
612 return pTarget !=
nullptr && pTarget->comp.size() != 0U ? pTarget : pRelation;
618 GAIA_NODISCARD ComponentCacheItem* find(Entity entity)
noexcept {
619 return const_cast<ComponentCacheItem*
>(
const_cast<const ComponentCache*
>(
this)->find(entity));
626 GAIA_NODISCARD
const ComponentCacheItem& get(Entity entity)
const noexcept {
627 GAIA_ASSERT(!entity.pair());
628 const auto* pItem = find(entity);
629 GAIA_ASSERT(pItem !=
nullptr);
637 GAIA_NODISCARD ComponentCacheItem& get(Entity entity)
noexcept {
638 auto* pItem = find(entity);
639 GAIA_ASSERT(pItem !=
nullptr);
647 GAIA_NODISCARD util::str_view symbol_name(
const ComponentCacheItem& item)
const noexcept {
648 return item.symbol_name();
654 GAIA_NODISCARD util::str_view path_name(
const ComponentCacheItem& item)
const noexcept {
655 return item.path.view();
663 bool path(ComponentCacheItem& item, util::str_view name)
noexcept {
664 if (path_name(item) == name)
668 remove_path_mapping(item);
673 if (name.size() >= ComponentCacheItem::MaxNameLength)
676 remove_path_mapping(item);
677 item.path.assign(name);
678 add_path_mapping(item);
688 bool path(ComponentCacheItem& item,
const char* name, uint32_t len = 0) noexcept {
689 return path(item, normalize_name_view(name, len));
695 GAIA_NODISCARD
const ComponentCacheItem* symbol(util::str_view name)
const noexcept {
696 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
697 const auto it = m_compBySymbol.find(lookup_key(name));
698 return it != m_compBySymbol.end() ? it->second :
nullptr;
705 GAIA_NODISCARD
const ComponentCacheItem* symbol(
const char* name, uint32_t len = 0) const noexcept {
706 GAIA_ASSERT(name !=
nullptr);
707 return symbol(normalize_name_view(name, len));
713 GAIA_NODISCARD
const ComponentCacheItem* path(util::str_view name)
const noexcept {
714 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
715 const auto it = m_compByPath.find(lookup_key(name));
716 if (it == m_compByPath.end())
719 return it->second.matches == 1 ? it->second.pItem :
nullptr;
726 GAIA_NODISCARD
const ComponentCacheItem* path(
const char* name, uint32_t len = 0) const noexcept {
727 GAIA_ASSERT(name !=
nullptr);
728 return path(normalize_name_view(name, len));
735 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(util::str_view name)
const noexcept {
736 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
737 const auto it = m_compByShortSymbol.find(lookup_key(name));
738 if (it == m_compByShortSymbol.end())
741 return it->second.matches == 1 ? it->second.pItem :
nullptr;
749 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(
const char* name, uint32_t len = 0) const noexcept {
750 GAIA_ASSERT(name !=
nullptr);
751 return short_symbol(normalize_name_view(name, len));
758 void add_path_matches(cnt::darray<Entity>& out, util::str_view name)
const {
762 const auto it = m_compByPath.find(lookup_key(name));
763 if (it == m_compByPath.end())
766 const auto& entry = it->second;
767 GAIA_ASSERT(entry.matches != 0);
769 if (entry.matches == 1) {
770 GAIA_ASSERT(entry.pItem !=
nullptr);
771 push_unique_entity(out, entry.pItem->entity);
775 for (
const auto& [entityId, pItem]: m_compByEntityId) {
777 GAIA_ASSERT(pItem !=
nullptr);
778 if (pItem->path.view() == name)
779 push_unique_entity(out, pItem->entity);
787 template <
typename T>
788 GAIA_NODISCARD
const ComponentCacheItem* find() const noexcept {
789 static_assert(!is_pair<T>::value);
790 return find_item(detail::ComponentDesc<T>::hash_lookup());
796 template <
typename T>
797 GAIA_NODISCARD
const ComponentCacheItem& get() const noexcept {
798 static_assert(!is_pair<T>::value);
799 const auto* pItem = find<T>();
800 GAIA_ASSERT(pItem !=
nullptr);
809 static ComponentCacheItem::Hooks& hooks(
const ComponentCacheItem& cacheItem)
noexcept {
810 return const_cast<ComponentCacheItem&
>(cacheItem).hooks();
816 const auto registeredTypes = m_compByEntityId.size();
817 GAIA_LOG_N(
"Registered components: %u", registeredTypes);
819 auto logDesc = [](
const ComponentCacheItem& item) {
820 const auto symbol = item.symbol_name();
822 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %.*s", item.hashLookup.hash,
823 item.comp.size(), item.comp.alig(), item.entity.id(), item.entity.gen(), (
int)symbol.size(),
826 for (
const auto& [entityId, pItem]: m_compByEntityId) {
828 GAIA_ASSERT(pItem !=
nullptr);