Merge pull request #6 from BlackMATov/dev

Dev
This commit is contained in:
BlackMat MATov
2018-12-28 08:58:10 +07:00
committed by GitHub
2 changed files with 302 additions and 94 deletions

271
ecs.hpp
View File

@@ -37,6 +37,12 @@ namespace ecs_hpp
using family_id = std::uint16_t; using family_id = std::uint16_t;
using entity_id = std::uint32_t; using entity_id = std::uint32_t;
const std::size_t entity_id_index_bits = 22;
const std::size_t entity_id_index_mask = (1 << entity_id_index_bits) - 1;
const std::size_t entity_id_version_bits = 10;
const std::size_t entity_id_version_mask = (1 << entity_id_version_bits) - 1;
static_assert( static_assert(
std::is_unsigned<family_id>::value, std::is_unsigned<family_id>::value,
"ecs_hpp::family_id must be an unsigned integer"); "ecs_hpp::family_id must be an unsigned integer");
@@ -44,6 +50,22 @@ namespace ecs_hpp
static_assert( static_assert(
std::is_unsigned<entity_id>::value, std::is_unsigned<entity_id>::value,
"ecs_hpp::entity_id must be an unsigned integer"); "ecs_hpp::entity_id must be an unsigned integer");
static_assert(
sizeof(entity_id) == (entity_id_index_bits + entity_id_version_bits) / 8u,
"ecs_hpp::entity_id mismatch index and version bits");
constexpr inline entity_id entity_id_index(entity_id id) noexcept {
return id & entity_id_index_mask;
}
constexpr inline entity_id entity_id_version(entity_id id) noexcept {
return (id >> entity_id_index_bits) & entity_id_version_mask;
}
constexpr inline entity_id entity_id_join(entity_id index, entity_id version) noexcept {
return index | (version << entity_id_index_bits);
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -72,13 +94,19 @@ namespace ecs_hpp
namespace impl namespace impl
{ {
template < typename T, typename... Ts, std::size_t... Is > template < typename T, typename... Ts, std::size_t... Is >
std::tuple<Ts...> tuple_tail_impl(std::tuple<T, Ts...>&& t, std::index_sequence<Is...> iseq) { std::tuple<Ts...> tuple_tail_impl(
std::tuple<T, Ts...>&& t,
std::index_sequence<Is...> iseq)
{
(void)iseq; (void)iseq;
return std::make_tuple(std::move(std::get<Is + 1u>(t))...); return std::make_tuple(std::move(std::get<Is + 1u>(t))...);
} }
template < typename T, typename... Ts, std::size_t... Is > template < typename T, typename... Ts, std::size_t... Is >
std::tuple<Ts...> tuple_tail_impl(const std::tuple<T, Ts...>& t, std::index_sequence<Is...> iseq) { std::tuple<Ts...> tuple_tail_impl(
const std::tuple<T, Ts...>& t,
std::index_sequence<Is...> iseq)
{
(void)iseq; (void)iseq;
return std::make_tuple(std::get<Is + 1u>(t)...); return std::make_tuple(std::get<Is + 1u>(t)...);
} }
@@ -86,12 +114,16 @@ namespace ecs_hpp
template < typename T, typename... Ts > template < typename T, typename... Ts >
std::tuple<Ts...> tuple_tail(std::tuple<T, Ts...>&& t) { std::tuple<Ts...> tuple_tail(std::tuple<T, Ts...>&& t) {
return impl::tuple_tail_impl(std::move(t), std::make_index_sequence<sizeof...(Ts)>()); return impl::tuple_tail_impl(
std::move(t),
std::make_index_sequence<sizeof...(Ts)>());
} }
template < typename T, typename... Ts > template < typename T, typename... Ts >
std::tuple<Ts...> tuple_tail(const std::tuple<T, Ts...>& t) { std::tuple<Ts...> tuple_tail(const std::tuple<T, Ts...>& t) {
return impl::tuple_tail_impl(t, std::make_index_sequence<sizeof...(Ts)>()); return impl::tuple_tail_impl(
t,
std::make_index_sequence<sizeof...(Ts)>());
} }
// //
@@ -149,6 +181,33 @@ namespace ecs_hpp
} }
} }
// -----------------------------------------------------------------------------
//
// detail::sparse_indexer
//
// -----------------------------------------------------------------------------
namespace ecs_hpp
{
namespace detail
{
template < typename T
, bool = std::is_unsigned<T>::value && sizeof(T) <= sizeof(std::size_t) >
struct sparse_unsigned_indexer {
std::size_t operator()(const T v) const noexcept {
return static_cast<std::size_t>(v);
}
};
template < typename T >
struct sparse_unsigned_indexer<T, false> {};
template < typename T >
struct sparse_indexer
: public sparse_unsigned_indexer<T> {};
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// detail::sparse_set // detail::sparse_set
@@ -159,12 +218,10 @@ namespace ecs_hpp
{ {
namespace detail namespace detail
{ {
template < typename T > template < typename T
, typename Indexer = sparse_indexer<T> >
class sparse_set final { class sparse_set final {
public: public:
static_assert(
std::is_unsigned<T>::value,
"sparse_set<T> can contain an unsigned integers only");
using iterator = typename std::vector<T>::iterator; using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator; using const_iterator = typename std::vector<T>::const_iterator;
public: public:
@@ -195,27 +252,52 @@ namespace ecs_hpp
return cbegin() + static_cast<dt>(size_); return cbegin() + static_cast<dt>(size_);
} }
public: public:
bool insert(const T v) { sparse_set(const Indexer& indexer = Indexer())
: indexer_(indexer) {}
bool insert(T&& v) {
if ( has(v) ) { if ( has(v) ) {
return false; return false;
} }
if ( v >= capacity_ ) { const std::size_t vi = indexer_(v);
reserve(new_capacity_for_(v + 1u)); if ( vi >= capacity_ ) {
reserve(new_capacity_for_(vi + 1u));
} }
dense_[size_] = v; dense_[size_] = std::move(v);
sparse_[v] = size_; sparse_[vi] = size_;
++size_; ++size_;
return true; return true;
} }
bool unordered_erase(const T v) noexcept { bool insert(const T& v) {
if ( has(v) ) {
return false;
}
const std::size_t vi = indexer_(v);
if ( vi >= capacity_ ) {
reserve(new_capacity_for_(vi + 1u));
}
dense_[size_] = v;
sparse_[vi] = size_;
++size_;
return true;
}
template < typename... Args >
bool emplace(Args&&... args) {
return insert(T(std::forward<Args>(args)...));
}
bool unordered_erase(const T& v)
noexcept(std::is_nothrow_move_assignable<T>::value)
{
if ( !has(v) ) { if ( !has(v) ) {
return false; return false;
} }
const std::size_t index = sparse_[v]; const std::size_t vi = indexer_(v);
const T last = dense_[size_ - 1u]; const std::size_t index = sparse_[vi];
dense_[index] = last; dense_[index] = std::move(dense_[size_ - 1u]);
sparse_[last] = index; sparse_[indexer_(dense_[index])] = index;
--size_; --size_;
return true; return true;
} }
@@ -224,29 +306,31 @@ namespace ecs_hpp
size_ = 0u; size_ = 0u;
} }
bool has(const T v) const noexcept { bool has(const T& v) const noexcept {
return v < capacity_ const std::size_t vi = indexer_(v);
&& sparse_[v] < size_ return vi < capacity_
&& dense_[sparse_[v]] == v; && sparse_[vi] < size_
&& dense_[sparse_[vi]] == v;
} }
const_iterator find(const T v) const noexcept { const_iterator find(const T& v) const noexcept {
const std::size_t vi = indexer_(v);
return has(v) return has(v)
? begin() + sparse_[v] ? begin() + sparse_[vi]
: end(); : end();
} }
std::size_t get_index(const T v) const { std::size_t get_index(const T& v) const {
const auto p = find_index(v); const auto p = find_index(v);
if ( p.second ) { if ( p.second ) {
return p.first; return p.first;
} }
throw std::out_of_range("sparse_set<T>"); throw std::logic_error("ecs_hpp::sparse_set(value not found)");
} }
std::pair<std::size_t,bool> find_index(const T v) const noexcept { std::pair<std::size_t,bool> find_index(const T& v) const noexcept {
return has(v) return has(v)
? std::make_pair(sparse_[v], true) ? std::make_pair(sparse_[indexer_(v)], true)
: std::make_pair(std::size_t(-1), false); : std::make_pair(std::size_t(-1), false);
} }
@@ -281,7 +365,7 @@ namespace ecs_hpp
std::size_t new_capacity_for_(std::size_t nsize) const { std::size_t new_capacity_for_(std::size_t nsize) const {
const std::size_t ms = max_size(); const std::size_t ms = max_size();
if ( nsize > ms ) { if ( nsize > ms ) {
throw std::length_error("sparse_set<T>"); throw std::length_error("ecs_hpp::sparse_set");
} }
if ( capacity_ >= ms / 2u ) { if ( capacity_ >= ms / 2u ) {
return ms; return ms;
@@ -289,6 +373,7 @@ namespace ecs_hpp
return std::max(capacity_ * 2u, nsize); return std::max(capacity_ * 2u, nsize);
} }
private: private:
Indexer indexer_;
std::vector<T> dense_; std::vector<T> dense_;
std::vector<std::size_t> sparse_; std::vector<std::size_t> sparse_;
std::size_t size_{0u}; std::size_t size_{0u};
@@ -307,12 +392,11 @@ namespace ecs_hpp
{ {
namespace detail namespace detail
{ {
template < typename K, typename T > template < typename K
, typename T
, typename Indexer = sparse_indexer<K> >
class sparse_map final { class sparse_map final {
public: public:
static_assert(
std::is_unsigned<K>::value,
"sparse_map<K,T> can contain unsigned integers keys only");
using iterator = typename std::vector<K>::iterator; using iterator = typename std::vector<K>::iterator;
using const_iterator = typename std::vector<K>::const_iterator; using const_iterator = typename std::vector<K>::const_iterator;
public: public:
@@ -340,7 +424,10 @@ namespace ecs_hpp
return keys_.cend(); return keys_.cend();
} }
public: public:
bool insert(const K k, const T& v) { sparse_map(const Indexer& indexer = Indexer())
: keys_(indexer) {}
bool insert(const K& k, const T& v) {
if ( keys_.has(k) ) { if ( keys_.has(k) ) {
return false; return false;
} }
@@ -353,7 +440,7 @@ namespace ecs_hpp
} }
} }
bool insert(const K k, T&& v) { bool insert(const K& k, T&& v) {
if ( keys_.has(k) ) { if ( keys_.has(k) ) {
return false; return false;
} }
@@ -367,7 +454,7 @@ namespace ecs_hpp
} }
template < typename... Args > template < typename... Args >
bool emplace(const K k, Args&&... args) { bool emplace(const K& k, Args&&... args) {
if ( keys_.has(k) ) { if ( keys_.has(k) ) {
return false; return false;
} }
@@ -380,7 +467,10 @@ namespace ecs_hpp
} }
} }
bool unordered_erase(const K k) { std::enable_if_t<
std::is_nothrow_move_assignable<K>::value,
bool>
unordered_erase(const K& k) {
if ( !keys_.has(k) ) { if ( !keys_.has(k) ) {
return false; return false;
} }
@@ -396,26 +486,26 @@ namespace ecs_hpp
values_.clear(); values_.clear();
} }
bool has(const K k) const noexcept { bool has(const K& k) const noexcept {
return keys_.has(k); return keys_.has(k);
} }
T& get_value(const K k) { T& get_value(const K& k) {
return values_[keys_.get_index(k)]; return values_[keys_.get_index(k)];
} }
const T& get_value(const K k) const { const T& get_value(const K& k) const {
return values_[keys_.get_index(k)]; return values_[keys_.get_index(k)];
} }
T* find_value(const K k) noexcept { T* find_value(const K& k) noexcept {
const auto ip = keys_.find_index(k); const auto ip = keys_.find_index(k);
return ip.second return ip.second
? &values_[ip.first] ? &values_[ip.first]
: nullptr; : nullptr;
} }
const T* find_value(const K k) const noexcept { const T* find_value(const K& k) const noexcept {
const auto ip = keys_.find_index(k); const auto ip = keys_.find_index(k);
return ip.second return ip.second
? &values_[ip.first] ? &values_[ip.first]
@@ -443,12 +533,30 @@ namespace ecs_hpp
return values_.capacity(); return values_.capacity();
} }
private: private:
sparse_set<K> keys_; sparse_set<K, Indexer> keys_;
std::vector<T> values_; std::vector<T> values_;
}; };
} }
} }
// -----------------------------------------------------------------------------
//
// detail::entity_id_indexer
//
// -----------------------------------------------------------------------------
namespace ecs_hpp
{
namespace detail
{
struct entity_id_indexer {
std::size_t operator()(entity_id id) const noexcept {
return entity_id_index(id);
}
};
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// detail::component_storage // detail::component_storage
@@ -484,7 +592,7 @@ namespace ecs_hpp
void for_each_component(F&& f) const noexcept; void for_each_component(F&& f) const noexcept;
private: private:
registry& owner_; registry& owner_;
detail::sparse_map<entity_id, T> components_; detail::sparse_map<entity_id, T, entity_id_indexer> components_;
}; };
template < typename T > template < typename T >
@@ -537,21 +645,6 @@ namespace ecs_hpp
} }
} }
// -----------------------------------------------------------------------------
//
// exceptions
//
// -----------------------------------------------------------------------------
namespace ecs_hpp
{
class basic_exception : public std::logic_error {
public:
basic_exception(const char* msg)
: std::logic_error(msg) {}
};
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// entity // entity
@@ -604,7 +697,7 @@ namespace ecs_hpp
template < typename... Ts > template < typename... Ts >
std::tuple<const Ts*...> find_components() const noexcept; std::tuple<const Ts*...> find_components() const noexcept;
private: private:
registry& owner_; registry* owner_;
entity_id id_{0u}; entity_id id_{0u};
}; };
@@ -761,7 +854,7 @@ namespace ecs_hpp
private: private:
entity_id last_entity_id_{0u}; entity_id last_entity_id_{0u};
std::vector<entity_id> free_entity_ids_; std::vector<entity_id> free_entity_ids_;
detail::sparse_set<entity_id> entity_ids_; detail::sparse_set<entity_id, detail::entity_id_indexer> entity_ids_;
using storage_uptr = std::unique_ptr<detail::component_storage_base>; using storage_uptr = std::unique_ptr<detail::component_storage_base>;
detail::sparse_map<family_id, storage_uptr> storages_; detail::sparse_map<family_id, storage_uptr> storages_;
@@ -780,14 +873,14 @@ namespace ecs_hpp
namespace ecs_hpp namespace ecs_hpp
{ {
inline entity::entity(registry& owner) inline entity::entity(registry& owner)
: owner_(owner) {} : owner_(&owner) {}
inline entity::entity(registry& owner, entity_id id) inline entity::entity(registry& owner, entity_id id)
: owner_(owner) : owner_(&owner)
, id_(id) {} , id_(id) {}
inline const registry& entity::owner() const noexcept { inline const registry& entity::owner() const noexcept {
return owner_; return *owner_;
} }
inline entity_id entity::id() const noexcept { inline entity_id entity::id() const noexcept {
@@ -795,72 +888,72 @@ namespace ecs_hpp
} }
inline bool entity::destroy() { inline bool entity::destroy() {
return owner_.destroy_entity(*this); return (*owner_).destroy_entity(*this);
} }
inline bool entity::is_alive() const noexcept { inline bool entity::is_alive() const noexcept {
return detail::as_const(owner_).is_entity_alive(*this); return detail::as_const(*owner_).is_entity_alive(*this);
} }
template < typename T, typename... Args > template < typename T, typename... Args >
bool entity::assign_component(Args&&... args) { bool entity::assign_component(Args&&... args) {
return owner_.assign_component<T>( return (*owner_).assign_component<T>(
*this, *this,
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
template < typename T > template < typename T >
bool entity::remove_component() { bool entity::remove_component() {
return owner_.remove_component<T>(*this); return (*owner_).remove_component<T>(*this);
} }
template < typename T > template < typename T >
bool entity::exists_component() const noexcept { bool entity::exists_component() const noexcept {
return detail::as_const(owner_).exists_component<T>(*this); return detail::as_const(*owner_).exists_component<T>(*this);
} }
inline std::size_t entity::remove_all_components() noexcept { inline std::size_t entity::remove_all_components() noexcept {
return owner_.remove_all_components(*this); return (*owner_).remove_all_components(*this);
} }
template < typename T > template < typename T >
T& entity::get_component() { T& entity::get_component() {
return owner_.get_component<T>(*this); return (*owner_).get_component<T>(*this);
} }
template < typename T > template < typename T >
const T& entity::get_component() const { const T& entity::get_component() const {
return detail::as_const(owner_).get_component<T>(*this); return detail::as_const(*owner_).get_component<T>(*this);
} }
template < typename T > template < typename T >
T* entity::find_component() noexcept { T* entity::find_component() noexcept {
return owner_.find_component<T>(*this); return (*owner_).find_component<T>(*this);
} }
template < typename T > template < typename T >
const T* entity::find_component() const noexcept { const T* entity::find_component() const noexcept {
return detail::as_const(owner_).find_component<T>(*this); return detail::as_const(*owner_).find_component<T>(*this);
} }
template < typename... Ts > template < typename... Ts >
std::tuple<Ts&...> entity::get_components() { std::tuple<Ts&...> entity::get_components() {
return owner_.get_components<Ts...>(*this); return (*owner_).get_components<Ts...>(*this);
} }
template < typename... Ts > template < typename... Ts >
std::tuple<const Ts&...> entity::get_components() const { std::tuple<const Ts&...> entity::get_components() const {
return detail::as_const(owner_).get_components<Ts...>(*this); return detail::as_const(*owner_).get_components<Ts...>(*this);
} }
template < typename... Ts > template < typename... Ts >
std::tuple<Ts*...> entity::find_components() noexcept { std::tuple<Ts*...> entity::find_components() noexcept {
return owner_.find_components<Ts...>(*this); return (*owner_).find_components<Ts...>(*this);
} }
template < typename... Ts > template < typename... Ts >
std::tuple<const Ts*...> entity::find_components() const noexcept { std::tuple<const Ts*...> entity::find_components() const noexcept {
return detail::as_const(owner_).find_components<Ts...>(*this); return detail::as_const(*owner_).find_components<Ts...>(*this);
} }
inline bool operator==(const entity& l, const entity& r) noexcept { inline bool operator==(const entity& l, const entity& r) noexcept {
@@ -883,16 +976,22 @@ namespace ecs_hpp
{ {
inline entity registry::create_entity() { inline entity registry::create_entity() {
if ( !free_entity_ids_.empty() ) { if ( !free_entity_ids_.empty() ) {
auto ent = entity(*this, free_entity_ids_.back()); const auto free_ent_id = free_entity_ids_.back();
entity_ids_.insert(ent.id()); const auto new_ent_id = entity_id_join(
entity_id_index(free_ent_id),
entity_id_version(free_ent_id) + 1u);
auto ent = entity(*this, new_ent_id);
entity_ids_.insert(new_ent_id);
free_entity_ids_.pop_back(); free_entity_ids_.pop_back();
return ent; return ent;
} }
assert(last_entity_id_ < std::numeric_limits<entity_id>::max()); if ( last_entity_id_ < entity_id_index_mask ) {
auto ent = entity(*this, ++last_entity_id_); auto ent = entity(*this, ++last_entity_id_);
entity_ids_.insert(ent.id()); entity_ids_.insert(ent.id());
return ent; return ent;
}
throw std::logic_error("ecs_hpp::registry(entity index overlow)");
} }
inline bool registry::destroy_entity(const entity& ent) { inline bool registry::destroy_entity(const entity& ent) {
@@ -951,7 +1050,7 @@ namespace ecs_hpp
if ( component ) { if ( component ) {
return *component; return *component;
} }
throw basic_exception("component not found"); throw std::logic_error("ecs_hpp::registry(component not found)");
} }
template < typename T > template < typename T >
@@ -960,7 +1059,7 @@ namespace ecs_hpp
if ( component ) { if ( component ) {
return *component; return *component;
} }
throw basic_exception("component not found"); throw std::logic_error("ecs_hpp::registry(component not found)");
} }
template < typename T > template < typename T >

View File

@@ -37,6 +37,19 @@ namespace
return l.x == r.x return l.x == r.x
&& l.y == r.y; && l.y == r.y;
} }
struct mult_indexer {
template < typename T >
std::size_t operator()(const T& v) const noexcept {
return static_cast<std::size_t>(v * 2);
}
};
struct position_c_indexer {
std::size_t operator()(const position_c& v) const noexcept {
return static_cast<std::size_t>(v.x);
}
};
} }
TEST_CASE("detail") { TEST_CASE("detail") {
@@ -70,7 +83,7 @@ TEST_CASE("detail") {
SECTION("sparse_set") { SECTION("sparse_set") {
using namespace ecs::detail; using namespace ecs::detail;
{ {
sparse_set<unsigned> s; sparse_set<unsigned, mult_indexer> s{mult_indexer{}};
REQUIRE(s.empty()); REQUIRE(s.empty());
REQUIRE_FALSE(s.size()); REQUIRE_FALSE(s.size());
@@ -84,7 +97,7 @@ TEST_CASE("detail") {
REQUIRE_FALSE(s.empty()); REQUIRE_FALSE(s.empty());
REQUIRE(s.size() == 1u); REQUIRE(s.size() == 1u);
REQUIRE(s.capacity() == 43u); REQUIRE(s.capacity() == 85u);
REQUIRE(s.has(42u)); REQUIRE(s.has(42u));
REQUIRE_FALSE(s.has(84u)); REQUIRE_FALSE(s.has(84u));
@@ -108,7 +121,7 @@ TEST_CASE("detail") {
REQUIRE_FALSE(s.has(84u)); REQUIRE_FALSE(s.has(84u));
REQUIRE(s.empty()); REQUIRE(s.empty());
REQUIRE_FALSE(s.size()); REQUIRE_FALSE(s.size());
REQUIRE(s.capacity() == 43u * 2); REQUIRE(s.capacity() == 85u * 2);
s.insert(42u); s.insert(42u);
s.insert(84u); s.insert(84u);
@@ -133,6 +146,22 @@ TEST_CASE("detail") {
REQUIRE_THROWS(s.get_index(42u)); REQUIRE_THROWS(s.get_index(42u));
REQUIRE(s.get_index(84u) == 0u); REQUIRE(s.get_index(84u) == 0u);
} }
{
sparse_set<position_c, position_c_indexer> s{position_c_indexer()};
REQUIRE(s.insert(position_c(1,2)));
REQUIRE_FALSE(s.insert(position_c(1,2)));
REQUIRE(s.has(position_c(1,2)));
REQUIRE(s.emplace(3,4));
REQUIRE(s.has(position_c(3,4)));
REQUIRE(s.get_index(position_c(1,2)) == 0);
REQUIRE(s.get_index(position_c(3,4)) == 1);
REQUIRE(s.find_index(position_c(1,2)).first == 0);
REQUIRE(s.find_index(position_c(3,4)).first == 1);
REQUIRE(s.find_index(position_c(1,2)).second);
REQUIRE(s.find_index(position_c(3,4)).second);
REQUIRE(s.unordered_erase(position_c(1,2)));
REQUIRE(s.get_index(position_c(3,4)) == 0);
}
} }
SECTION("sparse_map") { SECTION("sparse_map") {
using namespace ecs::detail; using namespace ecs::detail;
@@ -210,6 +239,27 @@ TEST_CASE("detail") {
REQUIRE_FALSE(m.has(42u)); REQUIRE_FALSE(m.has(42u));
REQUIRE_FALSE(m.has(84u)); REQUIRE_FALSE(m.has(84u));
} }
{
struct obj_t {
int x;
obj_t(int nx) : x(nx) {}
};
sparse_map<position_c, obj_t, position_c_indexer> s{position_c_indexer()};
REQUIRE(s.insert(position_c(1,2), obj_t{1}));
REQUIRE_FALSE(s.insert(position_c(1,2), obj_t{1}));
REQUIRE(s.has(position_c(1,2)));
REQUIRE(s.emplace(position_c(3,4), obj_t{3}));
REQUIRE(s.has(position_c(3,4)));
REQUIRE(s.get_value(position_c(1,2)).x == 1);
REQUIRE(s.get_value(position_c(3,4)).x == 3);
REQUIRE(s.find_value(position_c(1,2))->x == 1);
REQUIRE(s.find_value(position_c(3,4))->x == 3);
REQUIRE(s.find_value(position_c(1,2)));
REQUIRE(s.find_value(position_c(3,4)));
REQUIRE(s.unordered_erase(position_c(1,2)));
REQUIRE(s.get_value(position_c(3,4)).x == 3);
}
} }
} }
@@ -253,9 +303,40 @@ TEST_CASE("registry") {
ecs::registry w; ecs::registry w;
const auto e1 = w.create_entity(); const auto e1 = w.create_entity();
w.destroy_entity(e1); w.destroy_entity(e1);
const auto e2 = w.create_entity(); const auto e2 = w.create_entity();
REQUIRE(e1 == e2); REQUIRE(e1 != e2);
REQUIRE(ecs::entity_id_index(e1.id()) == ecs::entity_id_index(e2.id()));
REQUIRE(ecs::entity_id_version(e1.id()) + 1 == ecs::entity_id_version(e2.id()));
w.destroy_entity(e2);
const auto e3 = w.create_entity();
REQUIRE(e3 != e2);
REQUIRE(ecs::entity_id_index(e2.id()) == ecs::entity_id_index(e3.id()));
REQUIRE(ecs::entity_id_version(e2.id()) + 1 == ecs::entity_id_version(e3.id()));
}
{
ecs::registry w;
auto e = w.create_entity();
const auto e_id = e.id();
for ( std::size_t i = 0; i < ecs::entity_id_version_mask; ++i ) {
e.destroy();
e = w.create_entity();
REQUIRE(ecs::entity_id_version(e_id) != ecs::entity_id_version(e.id()));
}
// entity version wraps around
e.destroy();
e = w.create_entity();
REQUIRE(ecs::entity_id_version(e_id) == ecs::entity_id_version(e.id()));
}
{
ecs::registry w;
for ( std::size_t i = 0; i < ecs::entity_id_index_mask; ++i ) {
w.create_entity();
}
// entity index overflow
REQUIRE_THROWS_AS(w.create_entity(), std::logic_error);
} }
} }
SECTION("component_assigning") { SECTION("component_assigning") {
@@ -345,8 +426,8 @@ TEST_CASE("registry") {
REQUIRE(e2.get_component<velocity_c>().x == 3); REQUIRE(e2.get_component<velocity_c>().x == 3);
REQUIRE(e2.get_component<velocity_c>().y == 4); REQUIRE(e2.get_component<velocity_c>().y == 4);
REQUIRE_THROWS_AS(e1.get_component<velocity_c>(), ecs::basic_exception); REQUIRE_THROWS_AS(e1.get_component<velocity_c>(), std::logic_error);
REQUIRE_THROWS_AS(e2.get_component<position_c>(), ecs::basic_exception); REQUIRE_THROWS_AS(e2.get_component<position_c>(), std::logic_error);
} }
{ {
ecs::registry w; ecs::registry w;
@@ -371,8 +452,8 @@ TEST_CASE("registry") {
REQUIRE(ww.get_component<velocity_c>(e2).x == 3); REQUIRE(ww.get_component<velocity_c>(e2).x == 3);
REQUIRE(ww.get_component<velocity_c>(e2).y == 4); REQUIRE(ww.get_component<velocity_c>(e2).y == 4);
REQUIRE_THROWS_AS(ww.get_component<velocity_c>(e1), ecs::basic_exception); REQUIRE_THROWS_AS(ww.get_component<velocity_c>(e1), std::logic_error);
REQUIRE_THROWS_AS(ww.get_component<position_c>(e2), ecs::basic_exception); REQUIRE_THROWS_AS(ww.get_component<position_c>(e2), std::logic_error);
ww.remove_all_components(e1); ww.remove_all_components(e1);
ww.remove_all_components(e2); ww.remove_all_components(e2);
@@ -494,6 +575,34 @@ TEST_CASE("registry") {
REQUIRE(acc2 == 6); REQUIRE(acc2 == 6);
} }
} }
{
ecs::registry w;
{
auto e1 = w.create_entity();
auto e2 = w.create_entity();
e1.destroy();
e2.destroy();
}
auto e3 = w.create_entity();
auto e4 = w.create_entity();
e3.assign_component<position_c>(1, 2);
e4.assign_component<position_c>(3, 4);
{
ecs::entity_id acc1 = 0;
int acc2 = 0;
w.for_each_component<position_c>([&acc1, &acc2](ecs::entity e, position_c& p){
acc1 += e.id();
acc2 += p.x;
});
REQUIRE(acc1 == e3.id() + e4.id());
REQUIRE(acc2 == 4);
}
}
} }
SECTION("for_joined_components") { SECTION("for_joined_components") {
{ {