ECS handles refactor: handle includes species & version

This commit is contained in:
Nikita Lisitsa 2022-08-15 19:40:50 +03:00
parent b05e209c6d
commit f22b45c5e8

View file

@ -10,6 +10,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <functional>
#include <experimental/type_traits> #include <experimental/type_traits>
@ -19,11 +20,43 @@ namespace psemek::util
namespace ecs_detail namespace ecs_detail
{ {
using handle = std::uint32_t; using species_handle = std::uint16_t;
// Entity id within it's species
using entity_id = std::uint32_t;
using entity_version = std::uint32_t;
// From highest to lowest bits:
// 12: species id
// 20: entity id (within species)
// 32: entity version
using entity_handle = std::uint64_t;
struct unpacked_handle
{
species_handle species;
entity_id entity;
entity_version version;
};
inline unpacked_handle unpack(entity_handle h)
{
return {
h >> 52,
(h >> 32) & 0xFFFFFu,
h & 0xFFFFFFFFu
};
}
inline entity_handle pack(species_handle species, entity_id id, entity_version version)
{
return (static_cast<entity_handle>(species) << 52) | (static_cast<entity_handle>(id) << 32) | static_cast<entity_handle>(version);
}
struct species_base struct species_base
{ {
species_base(std::string name, handle id) species_base(std::string name, species_handle id)
: name_(std::move(name)) : name_(std::move(name))
, id_(id) , id_(id)
{} {}
@ -45,15 +78,12 @@ namespace psemek::util
return reinterpret_cast<typename Component::data *>(get_entity_component(typeid(Component))); return reinterpret_cast<typename Component::data *>(get_entity_component(typeid(Component)));
} }
virtual handle entity_count() const = 0; virtual entity_id entity_count() const = 0;
virtual handle add_entity() = 0; virtual entity_handle add_entity() = 0;
virtual void remove_entity(handle h) = 0; virtual void remove_entity(entity_handle h) = 0;
bool entity_active(handle h) virtual bool entity_active(entity_handle h) const = 0;
{
return get_free_list()[h] == h;
}
template <typename Behavior> template <typename Behavior>
void apply(Behavior & behavior) void apply(Behavior & behavior)
@ -63,12 +93,14 @@ namespace psemek::util
virtual ~species_base() {} virtual ~species_base() {}
virtual handle const * get_free_list() = 0; virtual entity_id const * get_free_list() const = 0;
virtual std::size_t list_size() const = 0; virtual entity_id list_size() const = 0;
private: virtual entity_version const * get_version_list() const = 0;
protected:
std::string name_; std::string name_;
handle id_; species_handle id_;
template <typename Behavior, typename ... Components, std::size_t ... Is> template <typename Behavior, typename ... Components, std::size_t ... Is>
void apply_impl(Behavior & behavior, std::tuple<Components...> *, std::index_sequence<Is...>) void apply_impl(Behavior & behavior, std::tuple<Components...> *, std::index_sequence<Is...>)
@ -86,8 +118,6 @@ namespace psemek::util
return; return;
typename Behavior::context ctx; typename Behavior::context ctx;
ctx.species = id_;
ctx.species_name = name_;
((std::get<Components *>(ctx.components) = get_species_component<Components>()), ...); ((std::get<Components *>(ctx.components) = get_species_component<Components>()), ...);
@ -113,19 +143,21 @@ namespace psemek::util
if (list) if (list)
{ {
// sparse // sparse
std::size_t const size = list_size(); auto const size = list_size();
for (std::size_t i = 0; i < size; ++i) auto version = get_version_list();
for (entity_id i = 0; i < size; ++i)
{ {
if (*list == i) if (*list == i)
{ {
ctx.entity = i; ctx.entity.value = pack(id_, i, *version);
ctx.remove = false; ctx.remove = false;
std::apply(visit, cptrs); std::apply(visit, cptrs);
if (ctx.remove) if (ctx.remove)
remove_entity(i); remove_entity(ctx.entity.value);
} }
std::apply(increment, cptrs); std::apply(increment, cptrs);
++list; ++list;
++version;
} }
} }
else else
@ -133,11 +165,11 @@ namespace psemek::util
// packed // packed
for (std::size_t i = 0; i < entity_count();) for (std::size_t i = 0; i < entity_count();)
{ {
ctx.entity = i; ctx.entity.value = pack(id_, i, 0);
ctx.remove = false; ctx.remove = false;
std::apply(visit, cptrs); std::apply(visit, cptrs);
if (ctx.remove) if (ctx.remove)
remove_entity(i); remove_entity(ctx.entity.value);
else else
{ {
std::apply(increment, cptrs); std::apply(increment, cptrs);
@ -152,7 +184,7 @@ namespace psemek::util
struct species_impl_base struct species_impl_base
: species_base : species_base
{ {
species_impl_base(std::string name, handle id, Components && ... components) species_impl_base(std::string name, species_handle id, Components && ... components)
: species_base(std::move(name), id) : species_base(std::move(name), id)
, species_components_{std::move(components)...} , species_components_{std::move(components)...}
{} {}
@ -199,42 +231,58 @@ namespace psemek::util
{ {
using species_impl_base<Components...>::species_impl_base; using species_impl_base<Components...>::species_impl_base;
static constexpr handle null = static_cast<handle>(-1); static constexpr entity_id null = static_cast<entity_id>(-1);
handle entity_count() const override entity_id entity_count() const override
{ {
return entity_count_; return entity_count_;
} }
handle add_entity() override entity_handle add_entity() override
{ {
return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{}); return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{});
} }
void remove_entity(handle h) override void remove_entity(entity_handle h) override
{ {
list_[h] = list_head_; auto id = unpack(h).entity;
list_head_ = h; list_[id] = list_head_;
list_head_ = id;
destroyed_at_[id] = ++version_;
--entity_count_; --entity_count_;
} }
handle const * get_free_list() override bool entity_active(entity_handle h) const override
{
auto u = unpack(h);
return list_[u.entity] == u.entity && u.version >= destroyed_at_[u.entity];
}
entity_id const * get_free_list() const override
{ {
return list_.data(); return list_.data();
} }
std::size_t list_size() const override entity_id list_size() const override
{ {
return list_.size(); return list_.size();
} }
entity_version const * get_version_list() const override
{
return created_at_.data();
}
private: private:
std::vector<handle> list_; std::vector<entity_id> list_;
handle list_head_ = null; entity_id list_head_ = null;
std::size_t entity_count_ = 0; entity_id entity_count_ = 0;
std::vector<entity_version> created_at_;
std::vector<entity_version> destroyed_at_;
entity_version version_ = 0;
template <std::size_t ... I> template <std::size_t ... I>
handle add_entity_impl(std::index_sequence<I...>) entity_handle add_entity_impl(std::index_sequence<I...>)
{ {
if (list_head_ == null) if (list_head_ == null)
{ {
@ -242,6 +290,8 @@ namespace psemek::util
std::size_t const new_size = std::max<std::size_t>(16, old_size * 2); std::size_t const new_size = std::max<std::size_t>(16, old_size * 2);
list_.resize(new_size); list_.resize(new_size);
created_at_.resize(new_size, 0);
destroyed_at_.resize(new_size, 0);
for (std::size_t i = old_size; i + 1 < new_size; ++i) for (std::size_t i = old_size; i + 1 < new_size; ++i)
list_[i] = i + 1; list_[i] = i + 1;
list_[new_size - 1] = null; list_[new_size - 1] = null;
@ -250,14 +300,15 @@ namespace psemek::util
((std::get<I>(this->entity_components_).resize(new_size)), ...); ((std::get<I>(this->entity_components_).resize(new_size)), ...);
} }
auto result = list_head_; auto id = list_head_;
((std::get<I>(this->entity_components_)[result] = {}), ...); ((std::get<I>(this->entity_components_)[id] = {}), ...);
list_head_ = list_[list_head_]; list_head_ = list_[list_head_];
list_[result] = result; list_[id] = id;
entity_count_++; entity_count_++;
return result; created_at_[id] = version_;
return pack(this->id_, id, version_);
} }
}; };
@ -267,47 +318,58 @@ namespace psemek::util
{ {
using species_impl_base<Components...>::species_impl_base; using species_impl_base<Components...>::species_impl_base;
handle entity_count() const override entity_id entity_count() const override
{ {
return std::get<0>(this->entity_components_).size(); return std::get<0>(this->entity_components_).size();
} }
handle add_entity() override entity_handle add_entity() override
{ {
return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{}); return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{});
} }
void remove_entity(handle h) override void remove_entity(entity_handle h) override
{ {
remove_entity_impl(h, std::make_index_sequence<sizeof...(Components)>{}); remove_entity_impl(h, std::make_index_sequence<sizeof...(Components)>{});
} }
handle const * get_free_list() override bool entity_active(entity_handle h) const override
{
return unpack(h).entity < entity_count();
}
entity_id const * get_free_list() const override
{ {
return nullptr; return nullptr;
} }
std::size_t list_size() const override entity_id list_size() const override
{ {
return 0; return 0;
} }
entity_version const * get_version_list() const override
{
return nullptr;
}
private: private:
template <std::size_t ... I> template <std::size_t ... I>
handle add_entity_impl(std::index_sequence<I...>) entity_handle add_entity_impl(std::index_sequence<I...>)
{ {
handle result = entity_count(); entity_id id = entity_count();
((std::get<I>(this->entity_components_).emplace_back()), ...); ((std::get<I>(this->entity_components_).emplace_back()), ...);
return result; return pack(this->id_, id, 0);
} }
template <std::size_t ... I> template <std::size_t ... I>
void remove_entity_impl(handle h, std::index_sequence<I...>) void remove_entity_impl(entity_handle h, std::index_sequence<I...>)
{ {
if (h + 1 != entity_count()) auto u = unpack(h);
if (u.entity + 1 != entity_count())
{ {
(std::swap(std::get<I>(this->entity_components_)[h], std::get<I>(this->entity_components_).back()), ...); (std::swap(std::get<I>(this->entity_components_)[u.entity], std::get<I>(this->entity_components_).back()), ...);
} }
((std::get<I>(this->entity_components_).pop_back()), ...); ((std::get<I>(this->entity_components_).pop_back()), ...);
@ -318,6 +380,20 @@ namespace psemek::util
struct ecs struct ecs
{ {
struct species_handle
{
ecs_detail::species_handle value;
friend auto operator <=> (species_handle const &, species_handle const &) = default;
};
struct entity_handle
{
ecs_detail::entity_handle value;
friend auto operator <=> (entity_handle const &, entity_handle const &) = default;
};
template <typename ... Components> template <typename ... Components>
struct behavior struct behavior
{ {
@ -326,9 +402,7 @@ namespace psemek::util
struct context struct context
{ {
ecs_detail::handle species; entity_handle entity;
std::string_view species_name;
ecs_detail::handle entity;
component_ptrs components; component_ptrs components;
@ -342,8 +416,6 @@ namespace psemek::util
}; };
}; };
using handle = ecs_detail::handle;
enum class policy enum class policy
{ {
sparse, sparse,
@ -351,139 +423,141 @@ namespace psemek::util
}; };
template <typename ... Components> template <typename ... Components>
handle register_species(std::string name, policy p, Components && ... components); species_handle register_species(std::string name, policy p, Components && ... components);
handle species_count() const { return species_.size(); } ecs_detail::species_handle species_count() const { return species_.size(); }
std::string_view species_name(handle species) const { return species_[species]->name(); } std::string_view species_name(species_handle species) const { return species_[species.value]->name(); }
handle add_entity(handle species); species_handle entity_species(entity_handle entity) const { return {ecs_detail::unpack(entity.value).species}; }
handle entity_count(handle species) const;
void remove_entity(handle species, handle entity);
bool entity_active(handle species, handle entity);
handle capacity(handle species) const { return species_[species]->list_size(); } entity_handle add_entity(species_handle species);
ecs_detail::entity_id entity_count(species_handle species) const;
void remove_entity(entity_handle entity);
bool entity_active(entity_handle entity);
template <typename Component> template <typename Component>
Component & get(handle species); Component & get(species_handle species);
template <typename Component> template <typename Component>
Component const & get(handle species) const; Component const & get(species_handle species) const;
template <typename Component> template <typename Component>
typename Component::data & get(handle species, handle entity); typename Component::data & get(entity_handle entity);
template <typename Component> template <typename Component>
typename Component::data const & get(handle species, handle entity) const; typename Component::data const & get(entity_handle entity) const;
template <typename Component> template <typename Component>
Component * get_if(handle species); Component * get_if(species_handle species);
template <typename Component> template <typename Component>
Component const * get_if(handle species) const; Component const * get_if(species_handle species) const;
template <typename Component> template <typename Component>
typename Component::data * get_if(handle species, handle entity); typename Component::data * get_if(entity_handle entity);
template <typename Component> template <typename Component>
typename Component::data const * get_if(handle species, handle entity) const; typename Component::data const * get_if(entity_handle entity) const;
template <typename Behavior> template <typename Behavior>
void apply(Behavior && behavior); void apply(Behavior && behavior);
template <typename Behavior> template <typename Behavior>
void apply(Behavior && behavior, handle species); void apply(Behavior && behavior, species_handle species);
private: private:
std::vector<std::unique_ptr<ecs_detail::species_base>> species_; std::vector<std::unique_ptr<ecs_detail::species_base>> species_;
}; };
template <typename ... Components> template <typename ... Components>
ecs::handle ecs::register_species(std::string name, policy p, Components && ... components) ecs::species_handle ecs::register_species(std::string name, policy p, Components && ... components)
{ {
handle result = species_.size(); auto result = species_count();
if (p == policy::sparse) if (p == policy::sparse)
species_.push_back(std::make_unique<ecs_detail::sparse_species_impl<Components...>>(std::move(name), result, std::move(components)...)); species_.push_back(std::make_unique<ecs_detail::sparse_species_impl<Components...>>(std::move(name), result, std::move(components)...));
else else
species_.push_back(std::make_unique<ecs_detail::packed_species_impl<Components...>>(std::move(name), result, std::move(components)...)); species_.push_back(std::make_unique<ecs_detail::packed_species_impl<Components...>>(std::move(name), result, std::move(components)...));
return result; return {result};
} }
inline ecs::handle ecs::add_entity(handle species) inline ecs::entity_handle ecs::add_entity(species_handle species)
{ {
return species_[species]->add_entity(); return {species_[species.value]->add_entity()};
} }
inline ecs::handle ecs::entity_count(handle species) const inline ecs_detail::entity_id ecs::entity_count(species_handle species) const
{ {
return species_[species]->entity_count(); return species_[species.value]->entity_count();
} }
inline void ecs::remove_entity(handle species, handle entity) inline void ecs::remove_entity(entity_handle entity)
{ {
species_[species]->remove_entity(entity); species_[ecs_detail::unpack(entity.value).species]->remove_entity(entity.value);
} }
inline bool ecs::entity_active(handle species, handle entity) inline bool ecs::entity_active(entity_handle entity)
{ {
return species_[species]->entity_active(entity); return species_[ecs_detail::unpack(entity.value).species]->entity_active(entity.value);
} }
template <typename Component> template <typename Component>
Component & ecs::get(handle species) Component & ecs::get(species_handle species)
{ {
auto p = species_[species]->get_species_component<Component>(); auto p = species_[species.value]->get_species_component<Component>();
if (!p) if (!p)
throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[species]->name())); throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[species.value]->name()));
return *p; return *p;
} }
template <typename Component> template <typename Component>
Component const & ecs::get(handle species) const Component const & ecs::get(species_handle species) const
{ {
return const_cast<Component const &>(const_cast<ecs *>(this)->get<Component>(species)); return const_cast<Component const &>(const_cast<ecs *>(this)->get<Component>(species));
} }
template <typename Component> template <typename Component>
typename Component::data & ecs::get(handle species, handle entity) typename Component::data & ecs::get(entity_handle entity)
{ {
auto p = species_[species]->get_entity_component<Component>(); auto u = ecs_detail::unpack(entity.value);
auto p = species_[u.species]->get_entity_component<Component>();
if (!p) if (!p)
throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[species]->name())); throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[u.species]->name()));
return p[entity]; return p[u.entity];
} }
template <typename Component> template <typename Component>
typename Component::data const & ecs::get(handle species, handle entity) const typename Component::data const & ecs::get(entity_handle entity) const
{ {
return const_cast<typename Component::data const &>(const_cast<ecs *>(this)->get<Component>(species, entity)); return const_cast<typename Component::data const &>(const_cast<ecs *>(this)->get<Component>(entity));
} }
template <typename Component> template <typename Component>
Component * ecs::get_if(handle species) Component * ecs::get_if(species_handle species)
{ {
return species_[species]->get_species_component<Component>(); return species_[species.value]->get_species_component<Component>();
} }
template <typename Component> template <typename Component>
Component const * ecs::get_if(handle species) const Component const * ecs::get_if(species_handle species) const
{ {
return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get<Component>(species)); return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get<Component>(species));
} }
template <typename Component> template <typename Component>
typename Component::data * ecs::get_if(handle species, handle entity) typename Component::data * ecs::get_if(entity_handle entity)
{ {
auto p = species_[species]->get_entity_component<Component>(); auto u = ecs_detail::unpack(entity.value);
auto p = species_[u.species]->get_entity_component<Component>();
if (p) if (p)
return p + entity; return p + u.entity;
return p; return p;
} }
template <typename Component> template <typename Component>
typename Component::data const * ecs::get_if(handle species, handle entity) const typename Component::data const * ecs::get_if(entity_handle entity) const
{ {
return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get_if<Component>(species, entity)); return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get_if<Component>(entity));
} }
template <typename Behavior> template <typename Behavior>
@ -494,9 +568,32 @@ namespace psemek::util
} }
template <typename Behavior> template <typename Behavior>
void ecs::apply(Behavior && behavior, handle species) void ecs::apply(Behavior && behavior, species_handle species)
{ {
species_[species]->apply(behavior); species_[species.value]->apply(behavior);
} }
} }
namespace std
{
template <>
struct hash<::psemek::util::ecs::species_handle>
{
std::size_t operator()(::psemek::util::ecs::species_handle h) const
{
return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value);
}
};
template <>
struct hash<::psemek::util::ecs::entity_handle>
{
std::size_t operator()(::psemek::util::ecs::entity_handle h) const
{
return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value);
}
};
}