ECS handles refactor: handle includes species & version
This commit is contained in:
parent
b05e209c6d
commit
f22b45c5e8
1 changed files with 199 additions and 102 deletions
|
|
@ -10,6 +10,7 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
|
||||
#include <experimental/type_traits>
|
||||
|
||||
|
|
@ -19,11 +20,43 @@ namespace psemek::util
|
|||
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
|
||||
{
|
||||
species_base(std::string name, handle id)
|
||||
species_base(std::string name, species_handle id)
|
||||
: name_(std::move(name))
|
||||
, id_(id)
|
||||
{}
|
||||
|
|
@ -45,15 +78,12 @@ namespace psemek::util
|
|||
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 void remove_entity(handle h) = 0;
|
||||
virtual entity_handle add_entity() = 0;
|
||||
virtual void remove_entity(entity_handle h) = 0;
|
||||
|
||||
bool entity_active(handle h)
|
||||
{
|
||||
return get_free_list()[h] == h;
|
||||
}
|
||||
virtual bool entity_active(entity_handle h) const = 0;
|
||||
|
||||
template <typename Behavior>
|
||||
void apply(Behavior & behavior)
|
||||
|
|
@ -63,12 +93,14 @@ namespace psemek::util
|
|||
|
||||
virtual ~species_base() {}
|
||||
|
||||
virtual handle const * get_free_list() = 0;
|
||||
virtual std::size_t list_size() const = 0;
|
||||
virtual entity_id const * get_free_list() const = 0;
|
||||
virtual entity_id list_size() const = 0;
|
||||
|
||||
private:
|
||||
virtual entity_version const * get_version_list() const = 0;
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
handle id_;
|
||||
species_handle id_;
|
||||
|
||||
template <typename Behavior, typename ... Components, std::size_t ... Is>
|
||||
void apply_impl(Behavior & behavior, std::tuple<Components...> *, std::index_sequence<Is...>)
|
||||
|
|
@ -86,8 +118,6 @@ namespace psemek::util
|
|||
return;
|
||||
|
||||
typename Behavior::context ctx;
|
||||
ctx.species = id_;
|
||||
ctx.species_name = name_;
|
||||
|
||||
((std::get<Components *>(ctx.components) = get_species_component<Components>()), ...);
|
||||
|
||||
|
|
@ -113,19 +143,21 @@ namespace psemek::util
|
|||
if (list)
|
||||
{
|
||||
// sparse
|
||||
std::size_t const size = list_size();
|
||||
for (std::size_t i = 0; i < size; ++i)
|
||||
auto const size = list_size();
|
||||
auto version = get_version_list();
|
||||
for (entity_id i = 0; i < size; ++i)
|
||||
{
|
||||
if (*list == i)
|
||||
{
|
||||
ctx.entity = i;
|
||||
ctx.entity.value = pack(id_, i, *version);
|
||||
ctx.remove = false;
|
||||
std::apply(visit, cptrs);
|
||||
if (ctx.remove)
|
||||
remove_entity(i);
|
||||
remove_entity(ctx.entity.value);
|
||||
}
|
||||
std::apply(increment, cptrs);
|
||||
++list;
|
||||
++version;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -133,11 +165,11 @@ namespace psemek::util
|
|||
// packed
|
||||
for (std::size_t i = 0; i < entity_count();)
|
||||
{
|
||||
ctx.entity = i;
|
||||
ctx.entity.value = pack(id_, i, 0);
|
||||
ctx.remove = false;
|
||||
std::apply(visit, cptrs);
|
||||
if (ctx.remove)
|
||||
remove_entity(i);
|
||||
remove_entity(ctx.entity.value);
|
||||
else
|
||||
{
|
||||
std::apply(increment, cptrs);
|
||||
|
|
@ -152,7 +184,7 @@ namespace psemek::util
|
|||
struct species_impl_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_components_{std::move(components)...}
|
||||
{}
|
||||
|
|
@ -199,42 +231,58 @@ namespace psemek::util
|
|||
{
|
||||
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_;
|
||||
}
|
||||
|
||||
handle add_entity() override
|
||||
entity_handle add_entity() override
|
||||
{
|
||||
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_;
|
||||
list_head_ = h;
|
||||
auto id = unpack(h).entity;
|
||||
list_[id] = list_head_;
|
||||
list_head_ = id;
|
||||
destroyed_at_[id] = ++version_;
|
||||
--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();
|
||||
}
|
||||
|
||||
std::size_t list_size() const override
|
||||
entity_id list_size() const override
|
||||
{
|
||||
return list_.size();
|
||||
}
|
||||
|
||||
entity_version const * get_version_list() const override
|
||||
{
|
||||
return created_at_.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<handle> list_;
|
||||
handle list_head_ = null;
|
||||
std::size_t entity_count_ = 0;
|
||||
std::vector<entity_id> list_;
|
||||
entity_id list_head_ = null;
|
||||
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>
|
||||
handle add_entity_impl(std::index_sequence<I...>)
|
||||
entity_handle add_entity_impl(std::index_sequence<I...>)
|
||||
{
|
||||
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);
|
||||
|
||||
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)
|
||||
list_[i] = i + 1;
|
||||
list_[new_size - 1] = null;
|
||||
|
|
@ -250,14 +300,15 @@ namespace psemek::util
|
|||
((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_[result] = result;
|
||||
list_[id] = id;
|
||||
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;
|
||||
|
||||
handle entity_count() const override
|
||||
entity_id entity_count() const override
|
||||
{
|
||||
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)>{});
|
||||
}
|
||||
|
||||
void remove_entity(handle h) override
|
||||
void remove_entity(entity_handle h) override
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
std::size_t list_size() const override
|
||||
entity_id list_size() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
entity_version const * get_version_list() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
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()), ...);
|
||||
return result;
|
||||
return pack(this->id_, id, 0);
|
||||
}
|
||||
|
||||
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()), ...);
|
||||
|
|
@ -318,6 +380,20 @@ namespace psemek::util
|
|||
|
||||
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>
|
||||
struct behavior
|
||||
{
|
||||
|
|
@ -326,9 +402,7 @@ namespace psemek::util
|
|||
|
||||
struct context
|
||||
{
|
||||
ecs_detail::handle species;
|
||||
std::string_view species_name;
|
||||
ecs_detail::handle entity;
|
||||
entity_handle entity;
|
||||
|
||||
component_ptrs components;
|
||||
|
||||
|
|
@ -342,8 +416,6 @@ namespace psemek::util
|
|||
};
|
||||
};
|
||||
|
||||
using handle = ecs_detail::handle;
|
||||
|
||||
enum class policy
|
||||
{
|
||||
sparse,
|
||||
|
|
@ -351,139 +423,141 @@ namespace psemek::util
|
|||
};
|
||||
|
||||
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);
|
||||
handle entity_count(handle species) const;
|
||||
void remove_entity(handle species, handle entity);
|
||||
bool entity_active(handle species, handle entity);
|
||||
species_handle entity_species(entity_handle entity) const { return {ecs_detail::unpack(entity.value).species}; }
|
||||
|
||||
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>
|
||||
Component & get(handle species);
|
||||
Component & get(species_handle species);
|
||||
|
||||
template <typename Component>
|
||||
Component const & get(handle species) const;
|
||||
Component const & get(species_handle species) const;
|
||||
|
||||
template <typename Component>
|
||||
typename Component::data & get(handle species, handle entity);
|
||||
typename Component::data & get(entity_handle entity);
|
||||
|
||||
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>
|
||||
Component * get_if(handle species);
|
||||
Component * get_if(species_handle species);
|
||||
|
||||
template <typename Component>
|
||||
Component const * get_if(handle species) const;
|
||||
Component const * get_if(species_handle species) const;
|
||||
|
||||
template <typename Component>
|
||||
typename Component::data * get_if(handle species, handle entity);
|
||||
typename Component::data * get_if(entity_handle entity);
|
||||
|
||||
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>
|
||||
void apply(Behavior && behavior);
|
||||
|
||||
template <typename Behavior>
|
||||
void apply(Behavior && behavior, handle species);
|
||||
void apply(Behavior && behavior, species_handle species);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ecs_detail::species_base>> species_;
|
||||
};
|
||||
|
||||
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)
|
||||
species_.push_back(std::make_unique<ecs_detail::sparse_species_impl<Components...>>(std::move(name), result, std::move(components)...));
|
||||
else
|
||||
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>
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[species]->name()));
|
||||
return p[entity];
|
||||
throw std::runtime_error(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[u.species]->name()));
|
||||
return p[u.entity];
|
||||
}
|
||||
|
||||
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>
|
||||
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>
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
return p + entity;
|
||||
return p + u.entity;
|
||||
return p;
|
||||
}
|
||||
|
||||
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>
|
||||
|
|
@ -494,9 +568,32 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue