Support registering components in ecs::container

This commit is contained in:
Nikita Lisitsa 2023-12-17 15:44:38 +03:00
parent 3b5e649a31
commit bda4a156e7
2 changed files with 60 additions and 1 deletions

View file

@ -7,6 +7,7 @@
#include <psemek/ecs/detail/all_different_types.hpp>
#include <psemek/ecs/detail/component_uuid_helper.hpp>
#include <psemek/ecs/detail/without.hpp>
#include <psemek/ecs/detail/component_registry.hpp>
#include <psemek/ecs/accessor.hpp>
#include <psemek/ecs/exceptions.hpp>
#include <psemek/util/range.hpp>
@ -24,7 +25,6 @@ namespace psemek::ecs
// - Fully document which functions can be called from which callbacks
// - Constructors & destructors implementation (const-only)
// - Modification callbacks implementation (const-only)
// - Registering components
// - Tables serialization
// - Refactor query caches
// - Index API
@ -32,6 +32,18 @@ namespace psemek::ecs
struct container
{
/** Register a component type within this container. This is used to properly deserialize
* a serialized container, otherwise it wouldn't know how to create entity tables from
* serialized data.
*
* Note that creating entities or attaching components to them automatically registers all
* referenced components.
*
* @tparam Component The component type to register
*/
template <typename Component>
void register_component();
/** Create an entity with the specified components. It is faster to create an entity
* with all the components at once than to create it with no components and `attach()`
* them one-by-one.
@ -41,6 +53,8 @@ namespace psemek::ecs
* If the entity is created during iteration (inside an `apply()` call), it is unspecified
* whether the created entity will be visited by iteration or not.
*
* This function automatically registers all the entity's component types by calling `register_component()`.
*
* @param components The components to initialize the entity with
* @return A unique handle to the created entity
* @warning If any two of the passed component types are equal, the call fails with
@ -124,6 +138,8 @@ namespace psemek::ecs
* For the purposes of `apply()` semantics, attaching behaves as if
* the entity was destroyed and then recreated with the same handle.
*
* This function automatically registers all the new component types by calling `register_component()`.
*
* @param entity A handle to the entity to attach components to
* @param components The components to attach to the entity
* @pre The entity was previously obtained by a `create()` call and is `alive()`
@ -366,6 +382,7 @@ namespace psemek::ecs
detail::entity_list entity_list_;
detail::table_container table_container_;
detail::query_cache_container query_cache_container_;
detail::component_registry component_registry_;
std::vector<util::uuid> uuid_helper_;
util::hash_set<util::uuid> uuid_set_helper_;
@ -375,11 +392,19 @@ namespace psemek::ecs
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
};
template <typename Component>
void container::register_component()
{
component_registry_.register_component<Component>();
}
template <typename ... Components>
handle container::create(Components && ... components)
{
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
(register_component<Components>(), ...);
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
auto table = table_container_.get(uuids.get());
@ -410,6 +435,8 @@ namespace psemek::ecs
{
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
(register_component<Components>(), ...);
auto & data = entity_list_.get_entities()[entity.id];
for (auto const & column : data.table->columns())
uuid_helper_.push_back(column->uuid());

View file

@ -0,0 +1,32 @@
#pragma once
#include <psemek/ecs/detail/column.hpp>
#include <psemek/util/uuid.hpp>
#include <psemek/util/hash_table.hpp>
#include <psemek/util/function.hpp>
namespace psemek::ecs::detail
{
struct component_registry
{
template <typename Component>
void register_component()
{
column_factories_.insert({Component::uuid(), []{
return std::make_unique<column_impl<Component>>();
}});
}
std::unique_ptr<column> create_column(util::uuid const & uuid) const
{
if (auto it = column_factories_.find(uuid); it != column_factories_.end())
return it->second();
return nullptr;
}
private:
util::hash_map<util::uuid, util::function<std::unique_ptr<column>()>> column_factories_;
};
}