#pragma once #include namespace lt::ecs { using EntityId = uint32_t; constexpr auto null_entity = std::numeric_limits::max(); /** A registry of components, the heart of an ECS architecture. * * @todo(Light): Implement grouping * @todo(Light): Implement identifier recycling * @todo(Light): Optimize views/each * @todo(Light): Support >2 component views * @todo(Light): Handle more edge cases or specify the undefined behaviors * * @ref https://skypjack.github.io/ * @ref https://github.com/alecthomas/entityx * @ref https://github.com/skypjack/entt * @ref https://github.com/SanderMertens/flecs */ class Registry { public: using UnderlyingSparseSet_T = TypeErasedSparseSet; using Callback_T = std::function; template void connect_on_construct(Callback_T callback) { m_on_construct_hooks[get_type_id()] = callback; } template void connect_on_destruct(Callback_T callback) { m_on_destruct_hooks[get_type_id()] = callback; } template void disconnect_on_construct() { m_on_construct_hooks.erase(get_type_id()); } template void disconnect_on_destruct() { m_on_destruct_hooks.erase(get_type_id()); } auto create_entity() -> EntityId { ++m_entity_count; return m_current++; } void destroy_entity(EntityId entity) { for (const auto &[key, set] : m_sparsed_sets) { set->remove(entity); } --m_entity_count; } template auto get(EntityId entity) const -> const Component_T & { auto &derived_set = get_derived_set(); return derived_set.at(entity).second; } template auto get(EntityId entity) -> Component_T & { auto &derived_set = get_derived_set(); return derived_set.at(entity).second; } template auto add(EntityId entity, Component_T component) -> Component_T & { auto &derived_set = get_derived_set(); auto &added_component = derived_set.insert(entity, std::move(component)).second; if (m_on_construct_hooks.contains(get_type_id())) { m_on_construct_hooks[get_type_id()](*this, entity); } return added_component; }; template void remove(EntityId entity) { if (m_on_destruct_hooks.contains(get_type_id())) { m_on_destruct_hooks[get_type_id()](*this, entity); } auto &derived_set = get_derived_set(); derived_set.remove(entity); } template auto view() -> SparseSet & { return get_derived_set(); }; template requires(!std::is_same_v) auto view() -> std::vector> { auto &set_a = get_derived_set(); auto &set_b = get_derived_set(); auto view = std::vector> {}; /* iterate over the "smaller" component-set, and check if its entities have the other * component */ if (set_a.get_size() > set_b.get_size()) { for (auto &[entity, component_b] : set_b) { if (set_a.contains(entity)) { view.emplace_back(std::tie(entity, set_a.at(entity).second, component_b)); } } } else { for (auto &[entity, component_a] : set_a) { if (set_b.contains(entity)) { view.emplace_back(std::tie(entity, component_a, set_b.at(entity).second)); } } } return view; }; template void each(std::function functor) { for (auto &[entity, component] : get_derived_set()) { functor(entity, component); } }; template requires(!std::is_same_v) void each(std::function functor) { auto &set_a = get_derived_set(); auto &set_b = get_derived_set(); /* iterate over the "smaller" component-set, and check if its entities have the other * component */ if (set_a.get_size() > set_b.get_size()) { for (auto &[entity, component_b] : set_b) { if (set_a.contains(entity)) { functor(entity, set_a.at(entity).second, component_b); } } return; } for (auto &[entity, component_a] : set_a) { if (set_b.contains(entity)) { functor(entity, component_a, set_b.at(entity).second); } } }; [[nodiscard]] auto get_entity_count() const -> size_t { return static_cast(m_entity_count); } private: using TypeId = size_t; static consteval auto hash_cstr(const char *str) -> TypeId { constexpr auto fnv_offset_basis = size_t { 14695981039346656037ull }; constexpr auto fnv_prime = size_t { 1099511628211ull }; auto hash = fnv_offset_basis; for (const auto &ch : std::string_view { str }) { hash *= fnv_prime; hash ^= static_cast(ch); } return hash; } template static consteval auto get_type_id() -> TypeId { #if defined _MSC_VER #define GENERATOR_PRETTY_FUNCTION __FUNCSIG__ #elif defined __clang__ || (defined __GNUC__) #define GENERATOR_PRETTY_FUNCTION __PRETTY_FUNCTION__ #else #error "Compiler not supported" #endif constexpr auto value = hash_cstr(GENERATOR_PRETTY_FUNCTION); #undef GENERATOR_PRETTY_FUNCTION return value; } template auto get_derived_set() -> SparseSet & { constexpr auto type_id = get_type_id(); if (!m_sparsed_sets.contains(type_id)) { m_sparsed_sets[type_id] = create_scope>(); } auto *base_set = m_sparsed_sets[type_id].get(); auto *derived_set = dynamic_cast *>(base_set); ensure(derived_set, "Failed to downcast to derived set"); return *derived_set; } EntityId m_current; TypeId m_entity_count; std::flat_map> m_sparsed_sets; std::flat_map m_on_construct_hooks; std::flat_map m_on_destruct_hooks; }; } // namespace lt::ecs