Merge pull request #20 from BlackMATov/dev

Dev
This commit is contained in:
BlackMat MATov
2019-04-12 05:10:36 +07:00
committed by GitHub
2 changed files with 620 additions and 183 deletions

552
ecs.hpp
View File

@@ -37,9 +37,14 @@ namespace ecs_hpp
template < typename T >
class const_component;
class prototype;
class system;
class registry;
class entity_filler;
class registry_filler;
using family_id = std::uint16_t;
using entity_id = std::uint32_t;
using priority_t = std::int32_t;
@@ -157,6 +162,44 @@ namespace ecs_hpp
return impl::tuple_contains_impl<0>(t, v);
}
//
// tiny_tuple_apply
//
namespace impl
{
template < typename F, typename Tuple, std::size_t... I >
void tiny_tuple_apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) {
std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
}
template < typename F, typename Tuple >
void tiny_tuple_apply(F&& f, Tuple&& args) {
impl::tiny_tuple_apply_impl(
std::forward<F>(f),
std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
//
// next_capacity_size
//
inline std::size_t next_capacity_size(
std::size_t cur_size,
std::size_t min_size,
std::size_t max_size)
{
if ( min_size > max_size ) {
throw std::length_error("ecs_hpp::next_capacity_size");
}
if ( cur_size >= max_size / 2u ) {
return max_size;
}
return std::max(cur_size * 2u, min_size);
}
//
// entity_id index/version
//
@@ -262,6 +305,15 @@ namespace ecs_hpp
public:
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
public:
static_assert(
noexcept(std::declval<Indexer>()(std::declval<T>())),
"unsupported sparse_set indexer");
static_assert(
std::is_nothrow_destructible<T>::value &&
std::is_nothrow_move_assignable<T>::value &&
noexcept(std::declval<T>() == std::declval<T>()),
"unsupported sparse_set value type");
public:
iterator begin() noexcept {
return dense_.begin();
@@ -290,28 +342,29 @@ namespace ecs_hpp
sparse_set(const Indexer& indexer = Indexer())
: indexer_(indexer) {}
bool insert(T&& v) {
if ( has(v) ) {
return false;
}
const std::size_t vi = indexer_(v);
if ( vi >= sparse_.size() ) {
sparse_.resize(new_sparse_size_for_(vi + 1u));
}
dense_.push_back(std::move(v));
sparse_[vi] = dense_.size() - 1u;
return true;
sparse_set(const sparse_set& other) = default;
sparse_set& operator=(const sparse_set& other) = default;
sparse_set(sparse_set&& other) noexcept = default;
sparse_set& operator=(sparse_set&& other) noexcept = default;
void swap(sparse_set& other) noexcept {
using std::swap;
swap(dense_, other.dense_);
swap(sparse_, other.sparse_);
}
bool insert(const T& v) {
template < typename UT >
bool insert(UT&& v) {
if ( has(v) ) {
return false;
}
const std::size_t vi = indexer_(v);
if ( vi >= sparse_.size() ) {
sparse_.resize(new_sparse_size_for_(vi + 1u));
sparse_.resize(next_capacity_size(
sparse_.size(), vi + 1u, sparse_.max_size()));
}
dense_.push_back(v);
dense_.push_back(std::forward<UT>(v));
sparse_[vi] = dense_.size() - 1u;
return true;
}
@@ -321,16 +374,16 @@ namespace ecs_hpp
return insert(T(std::forward<Args>(args)...));
}
bool unordered_erase(const T& v)
noexcept(std::is_nothrow_move_assignable<T>::value)
{
bool unordered_erase(const T& v) noexcept {
if ( !has(v) ) {
return false;
}
const std::size_t vi = indexer_(v);
const std::size_t dense_index = sparse_[vi];
dense_[dense_index] = std::move(dense_.back());
sparse_[indexer_(dense_[dense_index])] = dense_index;
if ( dense_index != dense_.size() - 1 ) {
dense_[dense_index] = std::move(dense_.back());
sparse_[indexer_(dense_[dense_index])] = dense_index;
}
dense_.pop_back();
return true;
}
@@ -347,9 +400,8 @@ namespace ecs_hpp
}
const_iterator find(const T& v) const noexcept {
const std::size_t vi = indexer_(v);
return has(v)
? begin() + sparse_[vi]
? begin() + sparse_[indexer_(v)]
: end();
}
@@ -374,22 +426,20 @@ namespace ecs_hpp
std::size_t size() const noexcept {
return dense_.size();
}
private:
std::size_t new_sparse_size_for_(std::size_t nsize) const {
const std::size_t ms = sparse_.max_size();
if ( nsize > ms ) {
throw std::length_error("ecs_hpp::sparse_set");
}
if ( sparse_.size() >= ms / 2u ) {
return ms;
}
return std::max(sparse_.size() * 2u, nsize);
}
private:
Indexer indexer_;
std::vector<T> dense_;
std::vector<std::size_t> sparse_;
};
template < typename T
, typename Indexer >
void swap(
sparse_set<T, Indexer>& l,
sparse_set<T, Indexer>& r) noexcept
{
l.swap(r);
}
}
}
@@ -410,6 +460,11 @@ namespace ecs_hpp
public:
using iterator = typename std::vector<K>::iterator;
using const_iterator = typename std::vector<K>::const_iterator;
public:
static_assert(
std::is_nothrow_destructible<T>::value &&
std::is_nothrow_move_assignable<T>::value,
"unsupported sparse_map value type");
public:
iterator begin() noexcept {
return keys_.begin();
@@ -438,57 +493,65 @@ namespace ecs_hpp
sparse_map(const Indexer& indexer = Indexer())
: keys_(indexer) {}
bool insert(const K& k, const T& v) {
sparse_map(const sparse_map& other) = default;
sparse_map& operator=(const sparse_map& other) = default;
sparse_map(sparse_map&& other) noexcept = default;
sparse_map& operator=(sparse_map&& other) noexcept = default;
void swap(sparse_map& other) noexcept {
using std::swap;
swap(keys_, other.keys_);
swap(values_, other.values_);
}
template < typename UK, typename UT >
bool insert(UK&& k, UT&& v) {
if ( keys_.has(k) ) {
return false;
}
values_.push_back(v);
values_.push_back(std::forward<UT>(v));
try {
return keys_.insert(k);
return keys_.insert(std::forward<UK>(k));
} catch (...) {
values_.pop_back();
throw;
}
}
bool insert(const K& k, T&& v) {
if ( keys_.has(k) ) {
return false;
}
values_.push_back(std::move(v));
try {
return keys_.insert(k);
} catch (...) {
values_.pop_back();
throw;
}
}
template < typename... Args >
bool emplace(const K& k, Args&&... args) {
template < typename UK, typename... Args >
bool emplace(UK&& k, Args&&... args) {
if ( keys_.has(k) ) {
return false;
}
values_.emplace_back(std::forward<Args>(args)...);
try {
return keys_.insert(k);
return keys_.insert(std::forward<UK>(k));
} catch (...) {
values_.pop_back();
throw;
}
}
std::enable_if_t<
std::is_nothrow_move_assignable<K>::value,
bool>
unordered_erase(const K& k)
noexcept(std::is_nothrow_move_assignable<T>::value)
{
if ( !keys_.has(k) ) {
template < typename UK, typename UT >
bool insert_or_assign(UK&& k, UT&& v) {
if ( keys_.has(k) ) {
get(k) = std::forward<UT>(v);
return false;
} else {
insert(std::forward<UK>(k), std::forward<UT>(v));
return true;
}
}
bool unordered_erase(const K& k) noexcept {
const auto value_index_p = keys_.find_dense_index(k);
if ( !value_index_p.second ) {
return false;
}
const std::size_t value_index = keys_.get_dense_index(k);
values_[value_index] = std::move(values_.back());
if ( value_index_p.first != values_.size() - 1 ) {
values_[value_index_p.first] = std::move(values_.back());
}
values_.pop_back();
keys_.unordered_erase(k);
return true;
@@ -536,6 +599,16 @@ namespace ecs_hpp
sparse_set<K, Indexer> keys_;
std::vector<T> values_;
};
template < typename K
, typename T
, typename Indexer >
void swap(
sparse_map<K, T, Indexer>& l,
sparse_map<K, T, Indexer>& r) noexcept
{
l.swap(r);
}
}
}
@@ -572,6 +645,7 @@ namespace ecs_hpp
virtual ~component_storage_base() = default;
virtual bool remove(entity_id id) noexcept = 0;
virtual bool has(entity_id id) const noexcept = 0;
virtual void clone(entity_id from, entity_id to) = 0;
};
template < typename T >
@@ -580,7 +654,7 @@ namespace ecs_hpp
component_storage(registry& owner);
template < typename... Args >
void assign(entity_id id, Args&&... args);
T& assign(entity_id id, Args&&... args);
bool exists(entity_id id) const noexcept;
bool remove(entity_id id) noexcept override;
@@ -589,6 +663,7 @@ namespace ecs_hpp
std::size_t count() const noexcept;
bool has(entity_id id) const noexcept override;
void clone(entity_id from, entity_id to) override;
template < typename F >
void for_each_component(F&& f);
@@ -605,10 +680,9 @@ namespace ecs_hpp
template < typename T >
template < typename... Args >
void component_storage<T>::assign(entity_id id, Args&&... args) {
if ( !components_.emplace(id, std::forward<Args>(args)...) ) {
components_.get(id) = T(std::forward<Args>(args)...);
}
T& component_storage<T>::assign(entity_id id, Args&&... args) {
components_.insert_or_assign(id, T(std::forward<Args>(args)...));
return components_.get(id);
}
template < typename T >
@@ -641,10 +715,18 @@ namespace ecs_hpp
return components_.has(id);
}
template < typename T >
void component_storage<T>::clone(entity_id from, entity_id to) {
const T* c = components_.find(from);
if ( c ) {
components_.insert_or_assign(to, *c);
}
}
template < typename T >
template < typename F >
void component_storage<T>::for_each_component(F&& f) {
for ( const auto id : components_ ) {
for ( const entity_id id : components_ ) {
f(id, components_.get(id));
}
}
@@ -652,7 +734,7 @@ namespace ecs_hpp
template < typename T >
template < typename F >
void component_storage<T>::for_each_component(F&& f) const {
for ( const auto id : components_ ) {
for ( const entity_id id : components_ ) {
f(id, components_.get(id));
}
}
@@ -683,11 +765,12 @@ namespace ecs_hpp
entity_id id() const noexcept;
bool destroy();
bool alive() const noexcept;
entity clone() const;
void destroy() noexcept;
bool valid() const noexcept;
template < typename T, typename... Args >
bool assign_component(Args&&... args);
T& assign_component(Args&&... args);
template < typename T >
bool remove_component();
@@ -718,6 +801,8 @@ namespace ecs_hpp
std::tuple<Ts*...> find_components() noexcept;
template < typename... Ts >
std::tuple<const Ts*...> find_components() const noexcept;
std::size_t component_count() const noexcept;
private:
registry* owner_{nullptr};
entity_id id_{0u};
@@ -770,7 +855,7 @@ namespace ecs_hpp
const registry& owner() const noexcept;
entity_id id() const noexcept;
bool alive() const noexcept;
bool valid() const noexcept;
template < typename T >
bool exists_component() const noexcept;
@@ -786,6 +871,8 @@ namespace ecs_hpp
template < typename... Ts >
std::tuple<const Ts*...> find_components() const noexcept;
std::size_t component_count() const noexcept;
private:
const registry* owner_{nullptr};
entity_id id_{0u};
@@ -840,7 +927,7 @@ namespace ecs_hpp
bool exists() const noexcept;
template < typename... Args >
bool assign(Args&&... args);
T& assign(Args&&... args);
T& get();
const T& get() const;
@@ -933,6 +1020,71 @@ namespace std
};
}
// -----------------------------------------------------------------------------
//
// prototype
//
// -----------------------------------------------------------------------------
namespace ecs_hpp
{
namespace detail
{
class component_applier_base;
using component_applier_uptr = std::unique_ptr<component_applier_base>;
class component_applier_base {
public:
virtual ~component_applier_base() = default;
virtual component_applier_uptr clone() const = 0;
virtual void apply(entity& ent, bool override) const = 0;
};
template < typename T, typename... Args >
class component_applier final : public component_applier_base {
public:
component_applier(std::tuple<Args...>&& args);
component_applier(const std::tuple<Args...>& args);
component_applier_uptr clone() const override;
void apply(entity& ent, bool override) const override;
private:
std::tuple<Args...> args_;
};
}
class prototype final {
public:
prototype() = default;
~prototype() noexcept = default;
prototype(const prototype& other);
prototype& operator=(const prototype& other);
prototype(prototype&& other) noexcept;
prototype& operator=(prototype&& other) noexcept;
void clear() noexcept;
bool empty() const noexcept;
void swap(prototype& other) noexcept;
template < typename T, typename... Args >
prototype& assign_component(Args&&... args) &;
template < typename T, typename... Args >
prototype&& assign_component(Args&&... args) &&;
prototype& merge(const prototype& other, bool override) &;
prototype&& merge(const prototype& other, bool override) &&;
void apply(entity& ent, bool override) const;
private:
detail::sparse_map<
family_id,
detail::component_applier_uptr> appliers_;
};
void swap(prototype& l, prototype& r) noexcept;
}
// -----------------------------------------------------------------------------
//
// system
@@ -1018,12 +1170,14 @@ namespace ecs_hpp
const_component<T> wrap_component(const const_uentity& ent) const noexcept;
entity create_entity();
entity create_entity(const prototype& proto);
entity create_entity(const const_uentity& proto);
bool destroy_entity(const uentity& ent);
bool alive_entity(const const_uentity& ent) const noexcept;
void destroy_entity(const uentity& ent) noexcept;
bool valid_entity(const const_uentity& ent) const noexcept;
template < typename T, typename... Args >
bool assign_component(const uentity& ent, Args&&... args);
T& assign_component(const uentity& ent, Args&&... args);
template < typename T >
bool remove_component(const uentity& ent);
@@ -1055,7 +1209,7 @@ namespace ecs_hpp
template < typename T >
std::size_t component_count() const noexcept;
template < typename T >
std::size_t entity_count() const noexcept;
std::size_t entity_component_count(const const_uentity& ent) const noexcept;
template < typename F >
@@ -1222,16 +1376,20 @@ namespace ecs_hpp
return id_;
}
inline bool entity::destroy() {
return (*owner_).destroy_entity(id_);
inline entity entity::clone() const {
return (*owner_).create_entity(id_);
}
inline bool entity::alive() const noexcept {
return detail::as_const(*owner_).alive_entity(id_);
inline void entity::destroy() noexcept {
(*owner_).destroy_entity(id_);
}
inline bool entity::valid() const noexcept {
return detail::as_const(*owner_).valid_entity(id_);
}
template < typename T, typename... Args >
bool entity::assign_component(Args&&... args) {
T& entity::assign_component(Args&&... args) {
return (*owner_).assign_component<T>(
id_,
std::forward<Args>(args)...);
@@ -1291,6 +1449,10 @@ namespace ecs_hpp
return detail::as_const(*owner_).find_components<Ts...>(id_);
}
inline std::size_t entity::component_count() const noexcept {
return detail::as_const(*owner_).entity_component_count(id_);
}
inline bool operator<(const entity& l, const entity& r) noexcept {
return (&l.owner() < &r.owner())
|| (&l.owner() == &r.owner() && l.id() < r.id());
@@ -1342,8 +1504,8 @@ namespace ecs_hpp
return id_;
}
inline bool const_entity::alive() const noexcept {
return (*owner_).alive_entity(id_);
inline bool const_entity::valid() const noexcept {
return (*owner_).valid_entity(id_);
}
template < typename T >
@@ -1371,6 +1533,10 @@ namespace ecs_hpp
return (*owner_).find_components<Ts...>(id_);
}
inline std::size_t const_entity::component_count() const noexcept {
return (*owner_).entity_component_count(id_);
}
inline bool operator<(const const_entity& l, const const_entity& r) noexcept {
return (&l.owner() < &r.owner())
|| (&l.owner() == &r.owner() && l.id() < r.id());
@@ -1429,7 +1595,7 @@ namespace ecs_hpp
template < typename T >
template < typename... Args >
bool component<T>::assign(Args&&... args) {
T& component<T>::assign(Args&&... args) {
return owner_.assign_component<T>(
std::forward<Args>(args)...);
}
@@ -1542,6 +1708,122 @@ namespace ecs_hpp
}
}
// -----------------------------------------------------------------------------
//
// prototype impl
//
// -----------------------------------------------------------------------------
namespace ecs_hpp
{
namespace detail
{
template < typename T, typename... Args >
component_applier<T,Args...>::component_applier(std::tuple<Args...>&& args)
: args_(std::move(args)) {}
template < typename T, typename... Args >
component_applier<T,Args...>::component_applier(const std::tuple<Args...>& args)
: args_(args) {}
template < typename T, typename... Args >
component_applier_uptr component_applier<T,Args...>::clone() const {
return std::make_unique<component_applier>(args_);
}
template < typename T, typename... Args >
void component_applier<T,Args...>::apply(entity& ent, bool override) const {
detail::tiny_tuple_apply([&ent, override](const Args&... args){
if ( override || !ent.exists_component<T>() ) {
ent.assign_component<T>(args...);
}
}, args_);
}
}
inline prototype::prototype(const prototype& other) {
for ( const family_id id : other.appliers_ ) {
appliers_.insert(id, other.appliers_.get(id)->clone());
}
}
inline prototype& prototype::operator=(const prototype& other) {
if ( this != &other ) {
prototype p(other);
swap(p);
}
return *this;
}
inline prototype::prototype(prototype&& other) noexcept
: appliers_(std::move(other.appliers_)) {}
inline prototype& prototype::operator=(prototype&& other) noexcept {
if ( this != &other ) {
swap(other);
other.clear();
}
return *this;
}
inline void prototype::clear() noexcept {
appliers_.clear();
}
inline bool prototype::empty() const noexcept {
return appliers_.empty();
}
inline void prototype::swap(prototype& other) noexcept {
using std::swap;
swap(appliers_, other.appliers_);
}
template < typename T, typename... Args >
prototype& prototype::assign_component(Args&&... args) & {
using applier_t = detail::component_applier<
T,
std::decay_t<Args>...>;
auto applier = std::make_unique<applier_t>(
std::make_tuple(std::forward<Args>(args)...));
const auto family = detail::type_family<T>::id();
appliers_.emplace(family, std::move(applier));
return *this;
}
template < typename T, typename... Args >
prototype&& prototype::assign_component(Args&&... args) && {
assign_component<T>(std::forward<Args>(args)...);
return std::move(*this);
}
prototype& prototype::merge(const prototype& other, bool override) & {
for ( const auto family_id : other.appliers_ ) {
if ( override || !appliers_.has(family_id) ) {
appliers_.insert_or_assign(
family_id,
other.appliers_.get(family_id)->clone());
}
}
return *this;
}
prototype&& prototype::merge(const prototype& other, bool override) && {
merge(other, override);
return std::move(*this);
}
inline void prototype::apply(entity& ent, bool override) const {
for ( const auto family_id : appliers_ ) {
appliers_.get(family_id)->apply(ent, override);
}
}
inline void swap(prototype& l, prototype& r) noexcept {
l.swap(r);
}
}
// -----------------------------------------------------------------------------
//
// registry impl
@@ -1670,46 +1952,70 @@ namespace ecs_hpp
return wrap_entity(new_ent_id);
}
if ( last_entity_id_ < detail::entity_id_index_mask ) {
entity_ids_.insert(last_entity_id_ + 1);
return wrap_entity(++last_entity_id_);
if ( last_entity_id_ >= detail::entity_id_index_mask ) {
throw std::logic_error("ecs_hpp::registry (entity index overlow)");
}
throw std::logic_error("ecs_hpp::registry (entity index overlow)");
if ( free_entity_ids_.capacity() <= entity_ids_.size() ) {
// ensure free entity ids capacity for safe (noexcept) entity destroying
free_entity_ids_.reserve(detail::next_capacity_size(
free_entity_ids_.capacity(),
entity_ids_.size() + 1,
free_entity_ids_.max_size()));
}
entity_ids_.insert(last_entity_id_ + 1);
return wrap_entity(++last_entity_id_);
}
inline bool registry::destroy_entity(const uentity& ent) {
assert(ent.check_owner(this));
inline entity registry::create_entity(const prototype& proto) {
auto ent = create_entity();
try {
proto.apply(ent, true);
} catch (...) {
destroy_entity(ent);
throw;
}
return ent;
}
inline entity registry::create_entity(const const_uentity& proto) {
assert(valid_entity(proto));
entity ent = create_entity();
try {
for ( const auto family_id : storages_ ) {
storages_.get(family_id)->clone(proto, ent.id());
}
} catch (...) {
destroy_entity(ent);
throw;
}
return ent;
}
inline void registry::destroy_entity(const uentity& ent) noexcept {
assert(valid_entity(ent));
remove_all_components(ent);
if ( entity_ids_.unordered_erase(ent) ) {
assert(free_entity_ids_.size() < free_entity_ids_.capacity());
free_entity_ids_.push_back(ent);
return true;
}
return false;
}
inline bool registry::alive_entity(const const_uentity& ent) const noexcept {
inline bool registry::valid_entity(const const_uentity& ent) const noexcept {
assert(ent.check_owner(this));
return entity_ids_.has(ent);
}
template < typename T, typename... Args >
bool registry::assign_component(const uentity& ent, Args&&... args) {
assert(ent.check_owner(this));
if ( !alive_entity(ent) ) {
return false;
}
get_or_create_storage_<T>().assign(
T& registry::assign_component(const uentity& ent, Args&&... args) {
assert(valid_entity(ent));
return get_or_create_storage_<T>().assign(
ent,
std::forward<Args>(args)...);
return true;
}
template < typename T >
bool registry::remove_component(const uentity& ent) {
assert(ent.check_owner(this));
if ( !alive_entity(ent) ) {
return false;
}
assert(valid_entity(ent));
detail::component_storage<T>* storage = find_storage_<T>();
return storage
? storage->remove(ent)
@@ -1718,10 +2024,7 @@ namespace ecs_hpp
template < typename T >
bool registry::exists_component(const const_uentity& ent) const noexcept {
assert(ent.check_owner(this));
if ( !alive_entity(ent) ) {
return false;
}
assert(valid_entity(ent));
const detail::component_storage<T>* storage = find_storage_<T>();
return storage
? storage->exists(ent)
@@ -1729,10 +2032,7 @@ namespace ecs_hpp
}
inline std::size_t registry::remove_all_components(const uentity& ent) noexcept {
assert(ent.check_owner(this));
if ( !alive_entity(ent) ) {
return 0u;
}
assert(valid_entity(ent));
std::size_t removed_count = 0u;
for ( const auto family_id : storages_ ) {
if ( storages_.get(family_id)->remove(ent) ) {
@@ -1744,7 +2044,7 @@ namespace ecs_hpp
template < typename T >
T& registry::get_component(const uentity& ent) {
assert(ent.check_owner(this));
assert(valid_entity(ent));
T* component = find_component<T>(ent);
if ( component ) {
return *component;
@@ -1754,7 +2054,7 @@ namespace ecs_hpp
template < typename T >
const T& registry::get_component(const const_uentity& ent) const {
assert(ent.check_owner(this));
assert(valid_entity(ent));
const T* component = find_component<T>(ent);
if ( component ) {
return *component;
@@ -1764,7 +2064,7 @@ namespace ecs_hpp
template < typename T >
T* registry::find_component(const uentity& ent) noexcept {
assert(ent.check_owner(this));
assert(valid_entity(ent));
detail::component_storage<T>* storage = find_storage_<T>();
return storage
? storage->find(ent)
@@ -1773,7 +2073,7 @@ namespace ecs_hpp
template < typename T >
const T* registry::find_component(const const_uentity& ent) const noexcept {
assert(ent.check_owner(this));
assert(valid_entity(ent));
const detail::component_storage<T>* storage = find_storage_<T>();
return storage
? storage->find(ent)
@@ -1782,25 +2082,25 @@ namespace ecs_hpp
template < typename... Ts >
std::tuple<Ts&...> registry::get_components(const uentity& ent) {
assert(ent.check_owner(this));
assert(valid_entity(ent));
return std::make_tuple(std::ref(get_component<Ts>(ent))...);
}
template < typename... Ts >
std::tuple<const Ts&...> registry::get_components(const const_uentity& ent) const {
assert(ent.check_owner(this));
assert(valid_entity(ent));
return std::make_tuple(std::cref(get_component<Ts>(ent))...);
}
template < typename... Ts >
std::tuple<Ts*...> registry::find_components(const uentity& ent) noexcept {
assert(ent.check_owner(this));
assert(valid_entity(ent));
return std::make_tuple(find_component<Ts>(ent)...);
}
template < typename... Ts >
std::tuple<const Ts*...> registry::find_components(const const_uentity& ent) const noexcept {
assert(ent.check_owner(this));
assert(valid_entity(ent));
return std::make_tuple(find_component<Ts>(ent)...);
}
@@ -1812,12 +2112,12 @@ namespace ecs_hpp
: 0u;
}
template < typename T >
std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept {
assert(ent.check_owner(this));
if ( !alive_entity(ent) ) {
return 0u;
}
inline std::size_t registry::entity_count() const noexcept {
return entity_ids_.size();
}
inline std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept {
assert(valid_entity(ent));
std::size_t component_count = 0u;
for ( const auto family_id : storages_ ) {
if ( storages_.get(family_id)->has(ent) ) {

View File

@@ -118,6 +118,7 @@ TEST_CASE("detail") {
REQUIRE_THROWS(s.get_dense_index(42u));
REQUIRE(s.insert(42u));
REQUIRE_FALSE(s.insert(42u));
REQUIRE_FALSE(s.empty());
REQUIRE(s.size() == 1u);
@@ -280,6 +281,25 @@ TEST_CASE("detail") {
REQUIRE(s.unordered_erase(position_c(1,2)));
REQUIRE(s.get(position_c(3,4)).x == 3);
}
{
struct obj_t {
int x;
obj_t(int nx) : x(nx) {}
};
sparse_map<unsigned, obj_t> m;
REQUIRE(m.insert_or_assign(42, obj_t(42)));
REQUIRE(m.has(42));
REQUIRE(m.get(42).x == 42);
REQUIRE_FALSE(m.insert_or_assign(42, obj_t(21)));
REQUIRE(m.has(42));
REQUIRE(m.get(42).x == 21);
REQUIRE(m.size() == 1);
REQUIRE(m.insert_or_assign(84, obj_t(84)));
REQUIRE(m.has(84));
REQUIRE(m.get(84).x == 84);
REQUIRE(m.size() == 2);
}
}
}
@@ -311,12 +331,9 @@ TEST_CASE("registry") {
REQUIRE_FALSE(e1 != e2);
REQUIRE_FALSE(e2 != e3);
REQUIRE_FALSE(w.alive_entity(e1));
REQUIRE_FALSE(w.alive_entity(e2));
REQUIRE_FALSE(w.alive_entity(e3));
REQUIRE_FALSE(w.destroy_entity(e1));
REQUIRE_FALSE(w.destroy_entity(e2));
REQUIRE_FALSE(w.valid_entity(e1));
REQUIRE_FALSE(w.valid_entity(e2));
REQUIRE_FALSE(w.valid_entity(e3));
}
{
ecs::registry w;
@@ -334,26 +351,22 @@ TEST_CASE("registry") {
REQUIRE_FALSE(e2 == e3);
REQUIRE(e3 == ee3);
REQUIRE(w.alive_entity(e1));
REQUIRE(w.alive_entity(e2));
REQUIRE(w.alive_entity(e3));
REQUIRE(w.alive_entity(ee3));
REQUIRE(w.valid_entity(e1));
REQUIRE(w.valid_entity(e2));
REQUIRE(w.valid_entity(e3));
REQUIRE(w.valid_entity(ee3));
REQUIRE(w.destroy_entity(e1));
REQUIRE_FALSE(w.alive_entity(e1));
REQUIRE(w.alive_entity(e2));
w.destroy_entity(e1);
REQUIRE_FALSE(w.valid_entity(e1));
REQUIRE(w.valid_entity(e2));
REQUIRE(w.destroy_entity(e2));
REQUIRE_FALSE(w.alive_entity(e1));
REQUIRE_FALSE(w.alive_entity(e2));
w.destroy_entity(e2);
REQUIRE_FALSE(w.valid_entity(e1));
REQUIRE_FALSE(w.valid_entity(e2));
REQUIRE(w.destroy_entity(ee3));
REQUIRE_FALSE(w.alive_entity(e3));
REQUIRE_FALSE(w.alive_entity(ee3));
REQUIRE_FALSE(w.destroy_entity(e1));
REQUIRE_FALSE(w.destroy_entity(e2));
REQUIRE_FALSE(w.destroy_entity(ee3));
w.destroy_entity(ee3);
REQUIRE_FALSE(w.valid_entity(e3));
REQUIRE_FALSE(w.valid_entity(ee3));
}
{
ecs::registry w;
@@ -393,11 +406,27 @@ TEST_CASE("registry") {
ecs::registry w;
using namespace ecs::detail;
std::vector<ecs::entity> entities;
for ( std::size_t i = 0; i < entity_id_index_mask; ++i ) {
w.create_entity();
entities.push_back(w.create_entity());
}
// entity index overflow
REQUIRE_THROWS_AS(w.create_entity(), std::logic_error);
for ( auto& ent : entities ) {
ent.destroy();
}
}
{
ecs::registry w;
REQUIRE_FALSE(w.entity_count());
auto e1 = w.create_entity();
REQUIRE(w.entity_count() == 1u);
auto e2 = w.create_entity();
REQUIRE(w.entity_count() == 2u);
e1.destroy();
REQUIRE(w.entity_count() == 1u);
e2.destroy();
REQUIRE_FALSE(w.entity_count());
}
}
SECTION("components") {
@@ -450,7 +479,7 @@ TEST_CASE("registry") {
REQUIRE_THROWS_AS(c1.get(), std::logic_error);
REQUIRE_THROWS_AS(c2.get(), std::logic_error);
REQUIRE(c1.assign(4,2));
REQUIRE(c1.assign(4,2) == position_c(4,2));
REQUIRE(c1.exists());
REQUIRE(c2.exists());
@@ -463,7 +492,7 @@ TEST_CASE("registry") {
REQUIRE(c2.get().x == 4);
REQUIRE(c2.get().y == 2);
REQUIRE(c1.assign(2,4));
REQUIRE(&c1.assign(2,4) == &c1.get());
REQUIRE(c1.find()->x == 2);
REQUIRE(c1.find()->y == 4);
@@ -486,6 +515,92 @@ TEST_CASE("registry") {
REQUIRE_FALSE(c1.remove());
}
}
SECTION("prototypes") {
{
ecs::prototype p;
p.assign_component<position_c>(1, 2);
ecs::registry w;
const auto e1 = w.create_entity(p);
const auto e2 = w.create_entity(p);
REQUIRE(w.entity_count() == 2u);
REQUIRE(w.component_count<position_c>() == 2u);
REQUIRE(w.component_count<velocity_c>() == 0u);
REQUIRE(e1.component_count() == 1u);
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
REQUIRE(e2.component_count() == 1u);
REQUIRE(e2.get_component<position_c>() == position_c(1,2));
}
{
const auto p = ecs::prototype()
.assign_component<position_c>(1,2)
.assign_component<velocity_c>(3,4);
ecs::registry w;
const auto e1 = w.create_entity(p);
const auto e2 = w.create_entity(p);
REQUIRE(w.entity_count() == 2u);
REQUIRE(w.component_count<position_c>() == 2u);
REQUIRE(w.component_count<velocity_c>() == 2u);
REQUIRE(e1.component_count() == 2u);
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
REQUIRE(e1.get_component<velocity_c>() == velocity_c(3,4));
REQUIRE(e2.component_count() == 2u);
REQUIRE(e2.get_component<position_c>() == position_c(1,2));
REQUIRE(e2.get_component<velocity_c>() == velocity_c(3,4));
}
{
const auto p1 = ecs::prototype()
.assign_component<position_c>(1,2)
.assign_component<velocity_c>(3,4);
ecs::prototype p2 = p1;
ecs::prototype p3;
p3 = p2;
ecs::registry w;
const auto e3 = w.create_entity(p3);
REQUIRE(e3.get_component<position_c>() == position_c(1,2));
REQUIRE(e3.get_component<velocity_c>() == velocity_c(3,4));
}
{
const auto p1 = ecs::prototype()
.assign_component<position_c>(1,2)
.merge(ecs::prototype().assign_component<position_c>(3,4), false);
const auto p2 = ecs::prototype()
.assign_component<position_c>(1,2)
.merge(ecs::prototype().assign_component<position_c>(3,4), true);
const auto p3 = ecs::prototype()
.assign_component<position_c>(1,2)
.merge(ecs::prototype().assign_component<velocity_c>(3,4), false);
const auto p4 = ecs::prototype()
.assign_component<position_c>(1,2)
.merge(ecs::prototype().assign_component<velocity_c>(3,4), true);
ecs::registry w;
const auto e1 = w.create_entity(p1);
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
const auto e2 = w.create_entity(p2);
REQUIRE(e2.get_component<position_c>() == position_c(3,4));
const auto e3 = w.create_entity(p3);
REQUIRE(e3.get_component<position_c>() == position_c(1,2));
REQUIRE(e3.get_component<velocity_c>() == velocity_c(3,4));
const auto e4 = w.create_entity(p4);
REQUIRE(e4.get_component<position_c>() == position_c(1,2));
REQUIRE(e4.get_component<velocity_c>() == velocity_c(3,4));
}
}
SECTION("component_assigning") {
{
ecs::registry w;
@@ -495,20 +610,23 @@ TEST_CASE("registry") {
REQUIRE_FALSE(w.exists_component<position_c>(e1));
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
REQUIRE_FALSE(w.component_count<position_c>());
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
REQUIRE_FALSE(w.entity_component_count(e1));
REQUIRE_FALSE(e1.component_count());
REQUIRE(w.assign_component<position_c>(e1));
REQUIRE(w.assign_component<position_c>(e1) == position_c());
REQUIRE(w.exists_component<position_c>(e1));
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
REQUIRE(w.component_count<position_c>() == 1u);
REQUIRE(w.component_count<velocity_c>() == 0u);
REQUIRE(w.entity_component_count<position_c>(e1) == 1u);
REQUIRE(w.entity_component_count(e1) == 1u);
REQUIRE(e1.component_count() == 1u);
REQUIRE(w.assign_component<velocity_c>(e1));
REQUIRE(w.assign_component<velocity_c>(e1) == velocity_c());
REQUIRE(w.component_count<position_c>() == 1u);
REQUIRE(w.component_count<velocity_c>() == 1u);
REQUIRE(w.entity_component_count<position_c>(e1) == 2u);
REQUIRE(w.entity_component_count(e1) == 2u);
REQUIRE(e1.component_count() == 2u);
REQUIRE(w.exists_component<position_c>(e1));
REQUIRE(w.exists_component<velocity_c>(e1));
@@ -519,30 +637,28 @@ TEST_CASE("registry") {
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
REQUIRE_FALSE(w.component_count<position_c>());
REQUIRE_FALSE(w.component_count<velocity_c>());
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
REQUIRE_FALSE(w.entity_component_count(e1));
REQUIRE_FALSE(e1.component_count());
}
{
REQUIRE_FALSE(e1.exists_component<position_c>());
REQUIRE_FALSE(e1.exists_component<velocity_c>());
REQUIRE(e1.assign_component<position_c>());
REQUIRE(e1.assign_component<position_c>() == position_c());
REQUIRE(e1.exists_component<position_c>());
REQUIRE_FALSE(e1.exists_component<velocity_c>());
REQUIRE(e1.assign_component<velocity_c>());
REQUIRE(e1.assign_component<velocity_c>() == velocity_c());
REQUIRE(e1.exists_component<position_c>());
REQUIRE(e1.exists_component<velocity_c>());
REQUIRE(e1.destroy());
e1.destroy();
REQUIRE_FALSE(e1.exists_component<position_c>());
REQUIRE_FALSE(e1.exists_component<velocity_c>());
REQUIRE_FALSE(w.component_count<position_c>());
REQUIRE_FALSE(w.component_count<velocity_c>());
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
}
}
{
@@ -551,39 +667,32 @@ TEST_CASE("registry") {
auto e1 = w.create_entity();
auto e2 = w.create_entity();
REQUIRE(w.assign_component<position_c>(e1));
REQUIRE(w.assign_component<velocity_c>(e1));
w.assign_component<position_c>(e1);
w.assign_component<velocity_c>(e1);
REQUIRE(w.assign_component<position_c>(e2));
REQUIRE(w.assign_component<velocity_c>(e2));
w.assign_component<position_c>(e2);
w.assign_component<velocity_c>(e2);
REQUIRE(w.destroy_entity(e1));
REQUIRE_FALSE(w.exists_component<position_c>(e1));
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
w.destroy_entity(e1);
REQUIRE(w.exists_component<position_c>(e2));
REQUIRE(w.exists_component<velocity_c>(e2));
}
{
ecs::registry w;
auto e1 = w.create_entity();
REQUIRE(e1.destroy());
REQUIRE_FALSE(e1.assign_component<position_c>());
REQUIRE_FALSE(w.exists_component<position_c>(e1));
}
{
ecs::registry w;
auto e1 = w.create_entity();
auto e2 = w.create_entity();
REQUIRE(w.assign_component<position_c>(e1));
const position_c& e1_pos = w.assign_component<position_c>(e1);
REQUIRE(&e1_pos == &w.get_component<position_c>(e1));
REQUIRE(w.assign_component<position_c>(e2));
REQUIRE(w.assign_component<velocity_c>(e2));
const position_c& e2_pos = w.assign_component<position_c>(e2);
const velocity_c& e2_vel = w.assign_component<velocity_c>(e2);
REQUIRE(&e2_pos == &w.get_component<position_c>(e2));
REQUIRE(&e2_vel == &w.get_component<velocity_c>(e2));
REQUIRE(e1.destroy());
e1.destroy();
}
}
SECTION("component_accessing") {
@@ -713,6 +822,34 @@ TEST_CASE("registry") {
REQUIRE(e1.get_component<velocity_c>().y == 40);
}
}
SECTION("cloning") {
{
ecs::registry w;
auto e1 = w.create_entity();
ecs::entity_filler(e1)
.component<position_c>(1, 2)
.component<velocity_c>(3, 4);
auto e2 = w.create_entity(e1);
REQUIRE(w.component_count<position_c>() == 2);
REQUIRE(w.component_count<velocity_c>() == 2);
REQUIRE(e2.exists_component<position_c>());
REQUIRE(e2.exists_component<velocity_c>());
REQUIRE(e2.get_component<position_c>() == position_c(1, 2));
REQUIRE(e2.get_component<velocity_c>() == velocity_c(3, 4));
e2.remove_component<velocity_c>();
auto e3 = e2.clone();
REQUIRE(w.component_count<position_c>() == 3);
REQUIRE(w.component_count<velocity_c>() == 1);
REQUIRE(e3.exists_component<position_c>());
REQUIRE_FALSE(e3.exists_component<velocity_c>());
REQUIRE(e3.get_component<position_c>() == position_c(1, 2));
}
}
SECTION("for_each_entity") {
{
ecs::registry w;