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"
27 struct ResolvedLookupEntry {
53 for (
auto [entityId, pItem]: m_compByEntityId) {
58 m_compByEntityId.clear();
59 m_compByTypeHash.clear();
60 m_compBySymbol.clear();
62 m_compByShortSymbol.clear();
65 GAIA_NODISCARD
static bool is_internal_symbol(
util::str_view symbol)
noexcept {
66 constexpr char InternalPrefix[] =
"gaia::ecs::";
67 constexpr uint32_t InternalPrefixLen = (uint32_t)(
sizeof(InternalPrefix) - 1);
68 return symbol.
size() >= InternalPrefixLen && memcmp(symbol.
data(), InternalPrefix, InternalPrefixLen) == 0;
72 const auto* pName = item.
name.str();
73 const auto len = item.
name.len();
74 for (uint32_t i = len; i > 1; --i) {
75 const auto idx = i - 1;
76 if (pName[idx] !=
':' || pName[idx - 1] !=
':')
87 if (is_internal_symbol(symbol))
90 const auto shortName = short_name_key(item);
91 if (shortName.str() ==
nullptr || shortName.len() == 0)
102 GAIA_NODISCARD
static util::str_view normalize_name_view(
const char* name, uint32_t len = 0)
noexcept {
106 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
107 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
113 bool changed =
false;
115 for (uint32_t i = 0; i < symbol.
size(); ++i) {
116 if (i + 1 < symbol.
size() && symbol.
data()[i] ==
':' && symbol.
data()[i + 1] ==
':') {
155 if (is_internal_symbol(symbol))
158 const auto shortName = short_name_key(item);
159 const bool hasShortName =
160 shortName.str() !=
nullptr && shortName.len() != 0 && shortName.len() != item.
name.len();
161 const auto leaf = hasShortName ?
util::str_view{shortName.str(), shortName.len()} : symbol;
163 if (!build_scoped_path(item.
path, scopePath, leaf))
164 (void)build_default_path(item.
path, symbol);
167 static void add_lookup_mapping(
173 const auto key = lookup_key(view);
174 const auto it = map.find(key);
175 if (it == map.end()) {
176 map.emplace(key, ResolvedLookupEntry{&item, 1});
180 auto& entry = it->second;
181 GAIA_ASSERT(entry.matches != 0);
186 add_lookup_mapping(m_compByPath, item.
path.
view(), item);
190 if (entity == EntityBad)
193 for (
const auto existing: out) {
194 if (existing == entity)
198 out.push_back(entity);
202 const auto pathValue = item.
path.
view();
203 if (pathValue.empty())
206 const auto key = lookup_key(pathValue);
207 const auto it = m_compByPath.find(key);
208 if (it == m_compByPath.end())
211 auto& entry = it->second;
212 GAIA_ASSERT(entry.matches != 0);
214 if (entry.matches == 1) {
215 GAIA_ASSERT(entry.pItem == &item);
216 m_compByPath.
erase(it);
221 if (entry.pItem != &item)
224 entry.pItem =
nullptr;
225 for (
const auto& [entityId, pItem]: m_compByEntityId) {
227 GAIA_ASSERT(pItem !=
nullptr);
231 if (pItem->path.view() != pathValue)
245 m_compBySymbol.emplace(item.
name, &item);
246 initialize_names(item, scopePath);
247 add_path_mapping(item);
248 add_lookup_mapping(m_compByShortSymbol, short_symbol_view(item), item);
256 GAIA_ASSERT(pItem !=
nullptr);
257 GAIA_ASSERT(pItem->
entity.id() == pItem->
comp.id());
258 m_compByEntityId.emplace(pItem->
entity.id(), pItem);
268 const auto it = m_compByTypeHash.find(hash);
269 return it != m_compByTypeHash.end() ? it->second :
nullptr;
275 template <
typename T>
278 const auto it = m_compByTypeHash.find(hash);
279 if (it == m_compByTypeHash.end()) {
280 m_compByTypeHash.emplace(hash, &item);
284 GAIA_ASSERT(it->second == &item);
301 template <
typename T>
304 GAIA_ASSERT(!entity.pair());
307 if (pItem !=
nullptr)
310 const auto* pNewItem = ComponentCacheItem::create<T>(entity);
311 GAIA_ASSERT(entity.id() == pNewItem->comp.id());
312 const auto& item = add_item(pNewItem, scopePath);
313 add_hash_mapping<T>(item);
322 GAIA_NODISCARD
const ComponentCacheItem&
324 GAIA_ASSERT(entity != EntityBad);
325 GAIA_ASSERT(!entity.pair());
327 GAIA_ASSERT(desc.
name.
size() < ComponentCacheItem::MaxNameLength);
330 const auto* pExisting = symbol(desc.
name);
331 if (pExisting !=
nullptr)
335 const auto* pItem = ComponentCacheItem::create(entity, desc);
336 GAIA_ASSERT(entity.id() == pItem->
comp.id());
337 return add_item(pItem, scopePath);
345 GAIA_NODISCARD
const ComponentCacheItem&
358 desc.funcMoveCtor = item.funcMoveCtor;
359 desc.funcCopyCtor = item.funcCopyCtor;
360 desc.funcDtor = item.funcDtor;
361 desc.funcCopy = item.funcCopy;
362 desc.funcMove = item.funcMove;
363 desc.funcSwap = item.funcSwap;
364 desc.funcCmp = item.funcCmp;
365 desc.funcSave = item.funcSave;
366 desc.funcLoad = item.funcLoad;
367 return add(entity, desc, scopePath);
375 auto* pItem = find(component);
376 if (pItem ==
nullptr)
379 const auto* pType = find(field.
type);
380 if (pType ==
nullptr)
383 return pItem->add_field(field, pType->comp.size());
393 const auto it = m_compByEntityId.find(entity.id());
394 return it != m_compByEntityId.end() ? it->second :
nullptr;
409 GAIA_ASSERT(!entity.pair());
410 const auto* pItem = find(entity);
411 GAIA_ASSERT(pItem !=
nullptr);
420 auto* pItem = find(entity);
421 GAIA_ASSERT(pItem !=
nullptr);
430 return {item.
name.str(), item.
name.len()};
436 GAIA_NODISCARD util::str_view path_name(
const ComponentCacheItem& item)
const noexcept {
437 return item.path.view();
445 bool path(ComponentCacheItem& item, util::str_view name)
noexcept {
446 if (path_name(item) == name)
450 remove_path_mapping(item);
455 if (name.size() >= ComponentCacheItem::MaxNameLength)
458 remove_path_mapping(item);
459 item.path.assign(name);
460 add_path_mapping(item);
470 bool path(ComponentCacheItem& item,
const char* name, uint32_t len = 0) noexcept {
471 return path(item, normalize_name_view(name, len));
477 GAIA_NODISCARD
const ComponentCacheItem* symbol(util::str_view name)
const noexcept {
478 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
479 const auto it = m_compBySymbol.find(lookup_key(name));
480 return it != m_compBySymbol.end() ? it->second :
nullptr;
487 GAIA_NODISCARD
const ComponentCacheItem* symbol(
const char* name, uint32_t len = 0) const noexcept {
488 GAIA_ASSERT(name !=
nullptr);
489 return symbol(normalize_name_view(name, len));
495 GAIA_NODISCARD
const ComponentCacheItem* path(util::str_view name)
const noexcept {
496 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
497 const auto it = m_compByPath.find(lookup_key(name));
498 if (it == m_compByPath.end())
501 return it->second.matches == 1 ? it->second.pItem :
nullptr;
508 GAIA_NODISCARD
const ComponentCacheItem* path(
const char* name, uint32_t len = 0) const noexcept {
509 GAIA_ASSERT(name !=
nullptr);
510 return path(normalize_name_view(name, len));
517 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(util::str_view name)
const noexcept {
518 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
519 const auto it = m_compByShortSymbol.find(lookup_key(name));
520 if (it == m_compByShortSymbol.end())
523 return it->second.matches == 1 ? it->second.pItem :
nullptr;
531 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(
const char* name, uint32_t len = 0) const noexcept {
532 GAIA_ASSERT(name !=
nullptr);
533 return short_symbol(normalize_name_view(name, len));
540 void add_path_matches(cnt::darray<Entity>& out, util::str_view name)
const {
544 const auto it = m_compByPath.find(lookup_key(name));
545 if (it == m_compByPath.end())
548 const auto& entry = it->second;
549 GAIA_ASSERT(entry.matches != 0);
551 if (entry.matches == 1) {
552 GAIA_ASSERT(entry.pItem !=
nullptr);
553 push_unique_entity(out, entry.pItem->entity);
557 for (
const auto& [entityId, pItem]: m_compByEntityId) {
559 GAIA_ASSERT(pItem !=
nullptr);
561 push_unique_entity(out, pItem->
entity);
569 template <
typename T>
578 template <
typename T>
581 const auto* pItem = find<T>();
582 GAIA_ASSERT(pItem !=
nullptr);
591 static ComponentCacheItem::Hooks& hooks(
const ComponentCacheItem& cacheItem)
noexcept {
598 const auto registeredTypes = m_compByEntityId.size();
599 GAIA_LOG_N(
"Registered components: %u", registeredTypes);
601 auto logDesc = [](
const ComponentCacheItem& item) {
603 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %s [%s]", item.hashLookup.hash,
604 item.comp.size(), item.comp.alig(), item.entity.id(), item.entity.gen(), item.name.str(),
605 EntityKindString[item.entity.kind()]);
607 for (
const auto& [entityId, pItem]: m_compByEntityId) {
609 GAIA_ASSERT(pItem !=
nullptr);
Array with variable size of elements of type.
Definition darray_impl.h:25
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_impl.h:331
Cache for compile-time defined components.
Definition component_cache.h:24
GAIA_NODISCARD bool add_field(Entity component, const RuntimeFieldDesc &field)
Adds runtime field metadata to a registered component.
Definition component_cache.h:374
GAIA_NODISCARD ComponentCacheItem & get(Entity entity) noexcept
Returns the component cache item.
Definition component_cache.h:419
GAIA_NODISCARD const ComponentCacheItem & get(Entity entity) const noexcept
Returns the component cache item.
Definition component_cache.h:408
GAIA_NODISCARD const ComponentCacheItem & get() const noexcept
Returns the component item for.
Definition component_cache.h:579
GAIA_NODISCARD ComponentCacheItem * find(Entity entity) noexcept
Searches for the component cache item.
Definition component_cache.h:400
GAIA_NODISCARD const ComponentCacheItem * find(Entity entity) const noexcept
Searches for the component cache item.
Definition component_cache.h:389
GAIA_NODISCARD const ComponentCacheItem * find() const noexcept
Searches for the component item for.
Definition component_cache.h:570
GAIA_NODISCARD const ComponentCacheItem & add(Entity entity, const ComponentCacheItem::ComponentCacheItemCtx &item, util::str_view scopePath={})
Registers a runtime-defined component.
Definition component_cache.h:346
GAIA_NODISCARD GAIA_FORCEINLINE const ComponentCacheItem & add(Entity entity, util::str_view scopePath={})
Registers the component item for.
Definition component_cache.h:302
GAIA_NODISCARD const ComponentCacheItem & add(Entity entity, const ecs::ComponentDesc &desc, util::str_view scopePath={})
Registers a runtime-defined component.
Definition component_cache.h:323
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Component item registration context.
Definition component_cache_item.h:51
ComponentLookupHash hashLookup
Optional explicit lookup hash. When empty, the symbol hash is used.
Definition component_cache_item.h:65
RuntimePrimitiveKind primitiveKind
Runtime primitive kind. Only valid when typeKind is Primitive.
Definition component_cache_item.h:69
uint32_t size
Component payload size in bytes.
Definition component_cache_item.h:55
uint32_t alig
Component payload alignment in bytes.
Definition component_cache_item.h:57
DataStorageType storageType
Component storage mode.
Definition component_cache_item.h:59
FuncCtor * funcCtor
Optional lifecycle and serialization callbacks.
Definition component_cache_item.h:71
util::str_view name
Registered component symbol.
Definition component_cache_item.h:53
RuntimeTypeKind typeKind
Runtime reflection type kind.
Definition component_cache_item.h:67
uint32_t soa
Number of SoA elements, 0 means AoS.
Definition component_cache_item.h:61
const uint8_t * pSoaSizes
Per-element SoA sizes when soa is non-zero.
Definition component_cache_item.h:63
Definition component_cache_item.h:26
Entity entity
Component entity.
Definition component_cache_item.h:84
Component comp
Unique component identifier.
Definition component_cache_item.h:86
SymbolLookupKey name
Component name.
Definition component_cache_item.h:93
util::str path
User-facing path name, e.g. "Gameplay.Device".
Definition component_cache_item.h:95
Plain component registration descriptor shared by typed and runtime component paths....
Definition component_desc.h:75
FuncCtor * funcCtor
Optional lifecycle and serialization callbacks.
Definition component_desc.h:105
const uint8_t * pSoaSizes
Per-element SoA sizes when soa is non-zero.
Definition component_desc.h:97
RuntimeTypeKind typeKind
Runtime reflection type kind.
Definition component_desc.h:101
uint32_t soa
Number of SoA elements, 0 means AoS.
Definition component_desc.h:95
DataStorageType storageType
Component storage mode.
Definition component_desc.h:93
RuntimePrimitiveKind primitiveKind
Runtime primitive kind. Only valid when typeKind is Primitive.
Definition component_desc.h:103
uint32_t alig
Component payload alignment in bytes.
Definition component_desc.h:91
uint32_t size
Component payload size in bytes.
Definition component_desc.h:89
ComponentLookupHash hashLookup
Optional explicit lookup hash. When empty, the symbol hash is used.
Definition component_desc.h:99
util::str_view name
Registered component symbol.
Definition component_desc.h:87
User-authored runtime field descriptor. A count of 0 means scalar; positive values describe a fixed i...
Definition component_desc.h:53
Entity type
Entity describing the field type.
Definition component_desc.h:57
Definition component_desc.h:119
Lightweight non-owning string view over a character sequence.
Definition str.h:13
GAIA_NODISCARD constexpr uint32_t size() const
Returns the number of characters in the view.
Definition str.h:41
GAIA_NODISCARD constexpr bool empty() const
Checks whether the view contains no characters.
Definition str.h:47
GAIA_NODISCARD constexpr const char * data() const
Returns the underlying character pointer.
Definition str.h:35
Lightweight owning string container with explicit length semantics (no implicit null terminator).
Definition str.h:331
void append(const char *data, uint32_t size)
Appends size characters from data.
Definition str.h:389
void clear()
Removes all characters from the string.
Definition str.h:352
GAIA_NODISCARD str_view view() const
Returns a non-owning view over the current contents.
Definition str.h:443
void reserve(uint32_t len)
Reserves capacity for at least len characters.
Definition str.h:358