diff --git a/libs/ecs/include/psemek/ecs/entity_container.hpp b/libs/ecs/include/psemek/ecs/entity_container.hpp index 0e751bc7..a86c7a24 100644 --- a/libs/ecs/include/psemek/ecs/entity_container.hpp +++ b/libs/ecs/include/psemek/ecs/entity_container.hpp @@ -15,93 +15,37 @@ namespace psemek::ecs struct entity_container { + // Create an entity with the specified components + // NB: equivalent to create() followed by a + // series of attach() calls, but faster template - entity_handle create(Components && ... components) - { - detail::component_uuid_holder uuids; - detail::component_mask mask = component_index_.make_component_mask(uuids.get()); + entity_handle create(Components && ... components); - auto insert_result = table_container_.insert(mask, uuids.get()); - auto table = insert_result.first; - bool created = insert_result.second; + // Check if an entity handle refers to an active entity + // UB if the handle wasn't obtained by a create() call + bool alive(entity_handle const & entity) const; - if (created) - { - query_cache_container_.apply([table](detail::query_cache & cache){ - cache.add(table); - }, mask); - } + // Destroy an entity + // UB if the handle wasn't obtained by a create() call + // or if the entity was already destroyed + void destroy(entity_handle const & entity); - auto id = entity_list_.create(table, table->row_count()); - entity_handle handle{id, entity_list_.get_entities()[id].epoch}; - [[maybe_unused]] entity_accessor accessor = get(handle); - - table->push_row(id); - ((accessor.get() = std::move(components)), ...); - - return handle; - } - - bool alive(entity_handle const & entity) const - { - return entity_list_.get_entities()[entity.id].epoch == entity.epoch; - } - - void destroy(entity_handle const & entity) - { - // Swap with the last row in that table - auto entities = entity_list_.get_entities(); - auto & data = entities[entity.id]; - auto table_entity_ids = data.table->get_entity_ids(); - data.table->swap_rows(data.row, table_entity_ids.size() - 1); - data.table->pop_row(); - auto swap_id = table_entity_ids[data.row]; - auto & swap_data = entities[swap_id]; - swap_data.row = data.row; - entity_list_.destroy(entity.id); - } + // Get an entity accessor for an entity + // UB if the handle wasn't obtained by a create() call + // or if the entity isn't active + entity_accessor get(entity_handle const & entity); + // Create a query cache that can be used for the + // apply() call to speed it up template - query_cache cache() - { - detail::component_uuid_holder uuids; - detail::component_mask mask = component_index_.make_component_mask(uuids.get()); - - auto result = query_cache_container_.create(mask, uuids.get()); - - table_container_.apply([&](detail::table & table){ - result->add(&table); - }, mask); - - return result; - } + query_cache cache(); + // Apply a function to all entities having the specified + // components. The function signature is void(entity_handle, components...) + // An optional query cache can be supplied to speed up the call + // UB if the cache wasn't created with the exact same component sequence template - void apply(Function && function, query_cache cache = {}) - { - if (!cache) - cache = this->cache(); - - for (auto const & entry : cache->tables) - { - detail::static_apply_helper apply_helper(entry.table->get_entity_ids(), entity_list_.get_entities()); - - for (std::size_t i = 0; i < sizeof...(Components); ++i) - apply_helper.pointers[i] = entry.table->get_component_pointers()[entry.column_ids[i]]; - - for (std::size_t i = 0; i < apply_helper.size(); ++i) - { - apply_helper.apply(function); - apply_helper.advance(); - } - } - } - - entity_accessor get(entity_handle const & entity) - { - auto const & data = entity_list_.get_entities()[entity.id]; - return {data.table, data.row}; - } + void apply(Function && function, query_cache cache = {}); private: detail::entity_list entity_list_; @@ -110,4 +54,66 @@ namespace psemek::ecs detail::query_cache_container query_cache_container_; }; + template + entity_handle entity_container::create(Components && ... components) + { + detail::component_uuid_holder uuids; + detail::component_mask mask = component_index_.make_component_mask(uuids.get()); + + auto insert_result = table_container_.insert(mask, uuids.get()); + auto table = insert_result.first; + bool created = insert_result.second; + + if (created) + { + query_cache_container_.apply([table](detail::query_cache & cache){ + cache.add(table); + }, mask); + } + + auto id = entity_list_.create(table, table->row_count()); + entity_handle handle{id, entity_list_.get_entities()[id].epoch}; + [[maybe_unused]] entity_accessor accessor = get(handle); + + table->push_row(id); + ((accessor.get() = std::move(components)), ...); + + return handle; + } + + template + query_cache entity_container::cache() + { + detail::component_uuid_holder uuids; + detail::component_mask mask = component_index_.make_component_mask(uuids.get()); + + auto result = query_cache_container_.create(mask, uuids.get()); + + table_container_.apply([&](detail::table & table){ + result->add(&table); + }, mask); + + return result; + } + + template + void entity_container::apply(Function && function, query_cache cache) + { + if (!cache) + cache = this->cache(); + + for (auto const & entry : cache->tables) + { + detail::static_apply_helper apply_helper(entry.table->get_entity_ids(), entity_list_.get_entities()); + + for (std::size_t i = 0; i < sizeof...(Components); ++i) + apply_helper.pointers[i] = entry.table->get_component_pointers()[entry.column_ids[i]]; + + for (std::size_t i = 0; i < apply_helper.size(); ++i) + { + apply_helper.apply(function); + apply_helper.advance(); + } + } + } } diff --git a/libs/ecs/source/entity_container.cpp b/libs/ecs/source/entity_container.cpp new file mode 100644 index 00000000..94324933 --- /dev/null +++ b/libs/ecs/source/entity_container.cpp @@ -0,0 +1,31 @@ +#include + +namespace psemek::ecs +{ + + bool entity_container::alive(entity_handle const & entity) const + { + return entity_list_.get_entities()[entity.id].epoch == entity.epoch; + } + + void entity_container::destroy(entity_handle const & entity) + { + // Swap with the last row in that table + auto entities = entity_list_.get_entities(); + auto & data = entities[entity.id]; + auto table_entity_ids = data.table->get_entity_ids(); + data.table->swap_rows(data.row, table_entity_ids.size() - 1); + data.table->pop_row(); + auto swap_id = table_entity_ids[data.row]; + auto & swap_data = entities[swap_id]; + swap_data.row = data.row; + entity_list_.destroy(entity.id); + } + + entity_accessor entity_container::get(entity_handle const & entity) + { + auto const & data = entity_list_.get_entities()[entity.id]; + return {data.table, data.row}; + } + +}