diff --git a/headers/ecs.hpp/ecs.hpp b/headers/ecs.hpp/ecs.hpp index 3d0c728..d8b6555 100644 --- a/headers/ecs.hpp/ecs.hpp +++ b/headers/ecs.hpp/ecs.hpp @@ -105,6 +105,7 @@ namespace ecs_hpp std::tuple&& t, std::index_sequence iseq) { + (void)t; (void)iseq; return std::make_tuple(std::move(std::get(t))...); } @@ -114,6 +115,7 @@ namespace ecs_hpp const std::tuple& t, std::index_sequence iseq) { + (void)t; (void)iseq; return std::make_tuple(std::get(t)...); } @@ -289,6 +291,40 @@ namespace ecs_hpp } } +namespace ecs_hpp +{ + namespace detail + { + template < typename Tag > + class incremental_locker final { + public: + incremental_locker() noexcept { + ++lock_count_; + } + + ~incremental_locker() noexcept { + assert(lock_count_); + --lock_count_; + } + + incremental_locker(incremental_locker&&) = delete; + incremental_locker& operator=(incremental_locker&&) = delete; + + incremental_locker(const incremental_locker&) = delete; + incremental_locker& operator=(const incremental_locker&) = delete; + + static bool is_locked() noexcept { + return !!lock_count_; + } + private: + static std::size_t lock_count_; + }; + + template < typename Tag > + std::size_t incremental_locker::lock_count_ = 0u; + } +} + // ----------------------------------------------------------------------------- // // detail::sparse_set @@ -368,11 +404,6 @@ namespace ecs_hpp return true; } - template < typename... Args > - bool emplace(Args&&... args) { - return insert(T(std::forward(args)...)); - } - bool unordered_erase(const T& v) noexcept { if ( !has(v) ) { return false; @@ -509,27 +540,14 @@ namespace ecs_hpp } template < typename UK, typename UT > - bool insert(UK&& k, UT&& v) { - if ( keys_.has(k) ) { - return false; + std::pair insert(UK&& k, UT&& v) { + if ( T* value = find(k) ) { + return std::make_pair(value, false); } values_.push_back(std::forward(v)); try { - return keys_.insert(std::forward(k)); - } catch (...) { - values_.pop_back(); - throw; - } - } - - template < typename UK, typename... Args > - bool emplace(UK&& k, Args&&... args) { - if ( keys_.has(k) ) { - return false; - } - values_.emplace_back(std::forward(args)...); - try { - return keys_.insert(std::forward(k)); + keys_.insert(std::forward(k)); + return std::make_pair(&values_.back(), true); } catch (...) { values_.pop_back(); throw; @@ -537,13 +555,18 @@ namespace ecs_hpp } template < typename UK, typename UT > - bool insert_or_assign(UK&& k, UT&& v) { - if ( keys_.has(k) ) { - get(k) = std::forward(v); - return false; - } else { - insert(std::forward(k), std::forward(v)); - return true; + std::pair insert_or_assign(UK&& k, UT&& v) { + if ( T* value = find(k) ) { + *value = std::forward(v); + return std::make_pair(value, false); + } + values_.push_back(std::forward(v)); + try { + keys_.insert(std::forward(k)); + return std::make_pair(&values_.back(), true); + } catch (...) { + values_.pop_back(); + throw; } } @@ -665,8 +688,21 @@ namespace ecs_hpp template < typename... Args > T& assign(entity_id id, Args&&... args) { - components_.insert_or_assign(id, T(std::forward(args)...)); - return components_.get(id); + if ( T* value = components_.find(id) ) { + *value = T(std::forward(args)...); + return *value; + } + assert(!components_locker::is_locked()); + return *components_.insert(id, T(std::forward(args)...)).first; + } + + template < typename... Args > + T& ensure(entity_id id, Args&&... args) { + if ( T* value = components_.find(id) ) { + return *value; + } + assert(!components_locker::is_locked()); + return *components_.insert(id, T(std::forward(args)...)).first; } bool exists(entity_id id) const noexcept { @@ -674,10 +710,12 @@ namespace ecs_hpp } bool remove(entity_id id) noexcept override { + assert(!components_locker::is_locked()); return components_.unordered_erase(id); } std::size_t remove_all() noexcept { + assert(!components_locker::is_locked()); const std::size_t count = components_.size(); components_.clear(); return count; @@ -700,14 +738,15 @@ namespace ecs_hpp } void clone(entity_id from, entity_id to) override { - const T* c = components_.find(from); + const T* c = find(from); if ( c ) { - components_.insert_or_assign(to, *c); + assign(to, *c); } } template < typename F > void for_each_component(F&& f) { + components_locker lock; for ( const entity_id id : components_ ) { f(id, components_.get(id)); } @@ -715,6 +754,7 @@ namespace ecs_hpp template < typename F > void for_each_component(F&& f) const { + components_locker lock; for ( const entity_id id : components_ ) { f(id, components_.get(id)); } @@ -726,6 +766,7 @@ namespace ecs_hpp private: registry& owner_; detail::sparse_map components_; + using components_locker = detail::incremental_locker; }; template < typename T > @@ -736,6 +777,20 @@ namespace ecs_hpp template < typename... Args > T& assign(entity_id id, Args&&...) { + if ( components_.has(id) ) { + return empty_value_; + } + assert(!components_locker::is_locked()); + components_.insert(id); + return empty_value_; + } + + template < typename... Args > + T& ensure(entity_id id, Args&&...) { + if ( components_.has(id) ) { + return empty_value_; + } + assert(!components_locker::is_locked()); components_.insert(id); return empty_value_; } @@ -745,10 +800,12 @@ namespace ecs_hpp } bool remove(entity_id id) noexcept override { + assert(!components_locker::is_locked()); return components_.unordered_erase(id); } std::size_t remove_all() noexcept { + assert(!components_locker::is_locked()); const std::size_t count = components_.size(); components_.clear(); return count; @@ -775,13 +832,15 @@ namespace ecs_hpp } void clone(entity_id from, entity_id to) override { - if ( components_.has(from) ) { - components_.insert(to); + const T* c = find(from); + if ( c ) { + assign(to, *c); } } template < typename F > void for_each_component(F&& f) { + components_locker lock; for ( const entity_id id : components_ ) { f(id, empty_value_); } @@ -789,6 +848,7 @@ namespace ecs_hpp template < typename F > void for_each_component(F&& f) const { + components_locker lock; for ( const entity_id id : components_ ) { f(id, empty_value_); } @@ -801,6 +861,7 @@ namespace ecs_hpp registry& owner_; static T empty_value_; detail::sparse_set components_; + using components_locker = detail::incremental_locker; }; template < typename T > @@ -839,6 +900,9 @@ namespace ecs_hpp template < typename T, typename... Args > T& assign_component(Args&&... args); + template < typename T, typename... Args > + T& ensure_component(Args&&... args); + template < typename T > bool remove_component(); @@ -992,6 +1056,9 @@ namespace ecs_hpp template < typename... Args > T& assign(Args&&... args); + template < typename... Args > + T& ensure(Args&&... args); + T& get(); const T& get() const; @@ -1262,6 +1329,9 @@ namespace ecs_hpp template < typename T, typename... Args > T& assign_component(const uentity& ent, Args&&... args); + template < typename T, typename... Args > + T& ensure_component(const uentity& ent, Args&&... args); + template < typename T > bool remove_component(const uentity& ent); @@ -1396,6 +1466,7 @@ namespace ecs_hpp entity_id last_entity_id_{0u}; std::vector free_entity_ids_; detail::sparse_set entity_ids_; + using entity_ids_locker = detail::incremental_locker; using storage_uptr = std::unique_ptr; detail::sparse_map storages_; @@ -1490,6 +1561,13 @@ namespace ecs_hpp std::forward(args)...); } + template < typename T, typename... Args > + T& entity::ensure_component(Args&&... args) { + return (*owner_).ensure_component( + id_, + std::forward(args)...); + } + template < typename T > bool entity::remove_component() { return (*owner_).remove_component(id_); @@ -1695,6 +1773,13 @@ namespace ecs_hpp std::forward(args)...); } + template < typename T > + template < typename... Args > + T& component::ensure(Args&&... args) { + return owner_.ensure_component( + std::forward(args)...); + } + template < typename T > T& component::get() { return owner_.get_component(); @@ -2105,6 +2190,7 @@ namespace ecs_hpp } inline entity registry::create_entity() { + assert(!entity_ids_locker::is_locked()); if ( !free_entity_ids_.empty() ) { const auto free_ent_id = free_entity_ids_.back(); const auto new_ent_id = detail::upgrade_entity_id(free_ent_id); @@ -2153,6 +2239,7 @@ namespace ecs_hpp } inline void registry::destroy_entity(const uentity& ent) noexcept { + assert(!entity_ids_locker::is_locked()); assert(valid_entity(ent)); remove_all_components(ent); if ( entity_ids_.unordered_erase(ent) ) { @@ -2174,6 +2261,14 @@ namespace ecs_hpp std::forward(args)...); } + template < typename T, typename... Args > + T& registry::ensure_component(const uentity& ent, Args&&... args) { + assert(valid_entity(ent)); + return get_or_create_storage_().ensure( + ent, + std::forward(args)...); + } + template < typename T > bool registry::remove_component(const uentity& ent) { assert(valid_entity(ent)); @@ -2251,24 +2346,28 @@ namespace ecs_hpp template < typename... Ts > std::tuple registry::get_components(const uentity& ent) { + (void)ent; assert(valid_entity(ent)); return std::make_tuple(std::ref(get_component(ent))...); } template < typename... Ts > std::tuple registry::get_components(const const_uentity& ent) const { + (void)ent; assert(valid_entity(ent)); return std::make_tuple(std::cref(get_component(ent))...); } template < typename... Ts > std::tuple registry::find_components(const uentity& ent) noexcept { + (void)ent; assert(valid_entity(ent)); return std::make_tuple(find_component(ent)...); } template < typename... Ts > std::tuple registry::find_components(const const_uentity& ent) const noexcept { + (void)ent; assert(valid_entity(ent)); return std::make_tuple(find_component(ent)...); } @@ -2298,6 +2397,7 @@ namespace ecs_hpp template < typename F > void registry::for_each_entity(F&& f) { + entity_ids_locker lock; for ( const auto id : entity_ids_ ) { f({*this, id}); } @@ -2305,6 +2405,7 @@ namespace ecs_hpp template < typename F > void registry::for_each_entity(F&& f) const { + entity_ids_locker lock; for ( const auto id : entity_ids_ ) { f({*this, id}); } @@ -2351,10 +2452,9 @@ namespace ecs_hpp [](priority_t pr, const auto& r){ return pr < r.first; }); - systems_.emplace( + systems_.insert( iter, - priority, - std::make_unique(std::forward(args)...)); + std::make_pair(priority, std::make_unique(std::forward(args)...))); } inline void registry::process_all_systems() { @@ -2431,7 +2531,7 @@ namespace ecs_hpp return *storage; } const auto family = detail::type_family::id(); - storages_.emplace( + storages_.insert( family, std::make_unique>(*this)); return *static_cast*>( diff --git a/untests/ecs_tests.cpp b/untests/ecs_tests.cpp index 4f9e68b..321fa83 100644 --- a/untests/ecs_tests.cpp +++ b/untests/ecs_tests.cpp @@ -177,7 +177,7 @@ TEST_CASE("detail") { 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.insert(position_c(3,4))); REQUIRE(s.has(position_c(3,4))); REQUIRE(s.get_dense_index(position_c(1,2)) == 0); REQUIRE(s.get_dense_index(position_c(3,4)) == 1); @@ -209,16 +209,16 @@ TEST_CASE("detail") { { obj_t o{21u}; - REQUIRE(m.insert(21u, o)); - REQUIRE(m.insert(42u, obj_t{42u})); - REQUIRE(m.emplace(84u, 84u)); + REQUIRE(m.insert(21u, o).second); + REQUIRE(m.insert(42u, obj_t{42u}).second); + REQUIRE(m.insert(84u, obj_t(84u)).second); } { obj_t o{21u}; - REQUIRE_FALSE(m.insert(21u, o)); - REQUIRE_FALSE(m.insert(42u, obj_t{42u})); - REQUIRE_FALSE(m.emplace(84u, 84u)); + REQUIRE_FALSE(m.insert(21u, o).second); + REQUIRE_FALSE(m.insert(42u, obj_t{42u}).second); + REQUIRE_FALSE(m.insert(84u, obj_t(84u)).second); } REQUIRE_FALSE(m.empty()); @@ -270,10 +270,10 @@ TEST_CASE("detail") { }; sparse_map 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.insert(position_c(1,2), obj_t{1}).second); + REQUIRE_FALSE(s.insert(position_c(1,2), obj_t{1}).second); REQUIRE(s.has(position_c(1,2))); - REQUIRE(s.emplace(position_c(3,4), obj_t{3})); + REQUIRE(s.insert(position_c(3,4), obj_t{3}).second); REQUIRE(s.has(position_c(3,4))); REQUIRE(s.get(position_c(1,2)).x == 1); REQUIRE(s.get(position_c(3,4)).x == 3); @@ -291,14 +291,14 @@ TEST_CASE("detail") { }; sparse_map m; - REQUIRE(m.insert_or_assign(42, obj_t(42))); + REQUIRE(m.insert_or_assign(42, obj_t(42)).second); REQUIRE(m.has(42)); REQUIRE(m.get(42).x == 42); - REQUIRE_FALSE(m.insert_or_assign(42, obj_t(21))); + REQUIRE_FALSE(m.insert_or_assign(42, obj_t(21)).second); 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.insert_or_assign(84, obj_t(84)).second); REQUIRE(m.has(84)); REQUIRE(m.get(84).x == 84); REQUIRE(m.size() == 2); @@ -813,6 +813,31 @@ TEST_CASE("registry") { e1.destroy(); } } + SECTION("component_ensuring") { + { + ecs::registry w; + ecs::entity e1 = w.create_entity(); + + e1.ensure_component(1,2); + e1.ensure_component(); + + REQUIRE(e1.get_component() == position_c(1,2)); + REQUIRE(e1.exists_component()); + + e1.ensure_component(10,20).x = 15; + e1.ensure_component(); + + REQUIRE(e1.get_component() == position_c(15,2)); + REQUIRE(e1.exists_component()); + + ecs::component c1 = w.wrap_component(e1); + REQUIRE_FALSE(c1.exists()); + c1.ensure(2, 1).y = 10; + REQUIRE(c1.get() == velocity_c(2, 10)); + c1.ensure(10, 20).x = 20; + REQUIRE(c1.get() == velocity_c(20, 10)); + } + } SECTION("component_accessing") { { ecs::registry w;