diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index a7def6aa8..1e9e7e16e 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -68,7 +68,7 @@ class basic_registry { template decltype(auto) assign(basic_registry &owner, const Entity entt, Args &&... args) { - decltype(auto) component = storage::construct(entt, std::forward(args)...); + decltype(auto) component = storage::construct(entt, std::forward(args)...); construction.publish(entt, owner, component); return component; } @@ -76,7 +76,7 @@ class basic_registry { template std::enable_if_t::value_type, Entity>, typename storage::reverse_iterator_type> assign(basic_registry &owner, It first, It last, Args &&... args) { - auto it = storage::construct(first, last, std::forward(args)...); + auto it = storage::construct(first, last, std::forward(args)...); const auto end = it + (!construction.empty() * std::distance(first, last)); std::for_each(it, end, [this, &owner, &first](decltype(*it) component) { @@ -88,14 +88,14 @@ class basic_registry { void remove(basic_registry &owner, const Entity entt) { destruction.publish(entt, owner); - storage::destroy(entt); + storage::destroy(entt); } template decltype(auto) replace(basic_registry &owner, const Entity entt, Args &&... args) { Component component{std::forward(args)...}; update.publish(entt, owner, component); - return (storage::get(entt) = std::move(component)); + return (storage::get(entt) = std::move(component)); } private: @@ -194,8 +194,7 @@ class basic_registry { const auto curr = to_integral(destroyed); const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; - entt = entity_type{curr | version}; - entities[curr] = entt; + entt = entities[curr] = entity_type{curr | version}; } return entt; @@ -205,9 +204,8 @@ class basic_registry { // lengthens the implicit list of destroyed entities const auto entt = to_integral(entity) & traits_type::entity_mask; const auto version = ((to_integral(entity) >> traits_type::entity_shift) + 1) << traits_type::entity_shift; - const auto node = to_integral(destroyed) | version; - entities[entt] = Entity{node}; - destroyed = Entity{entt}; + entities[entt] = entity_type{to_integral(destroyed) | version}; + destroyed = entity_type{entt}; } template @@ -225,16 +223,16 @@ class basic_registry { pdata.type_id = type_info::id(); pdata.pool = std::make_unique>(std::forward(args)...); - pdata.remove = [](sparse_set &cpool, basic_registry &owner, const Entity entt) { + pdata.remove = [](sparse_set &cpool, basic_registry &owner, const entity_type entt) { static_cast &>(cpool).remove(owner, entt); }; if constexpr(std::is_copy_constructible_v>) { - pdata.assure = [](basic_registry &other, const sparse_set &cpool) { + pdata.assure = [](basic_registry &other, const sparse_set &cpool) { other.assure(static_cast &>(cpool)); }; - pdata.stamp = [](basic_registry &other, const Entity dst, const sparse_set &cpool, const Entity src) { + pdata.stamp = [](basic_registry &other, const entity_type dst, const sparse_set &cpool, const entity_type src) { other.assign_or_replace(dst, static_cast &>(cpool).get(src)); }; } @@ -532,6 +530,50 @@ public: } } + /** + * @brief Creates a new entity and returns it. + * + * @sa create + * + * If the requested entity isn't in use, the suggested identifier is created + * and returned. Otherwise, a new one will be generated for this purpose. + * + * @tparam Component Types of components to assign to the entity. + * @param hint A desired entity identifier. + * @return A valid entity identifier if the component list is empty, a tuple + * containing the entity identifier and the references to the components + * just created otherwise. + */ + template + auto create(const entity_type hint) { + ENTT_ASSERT(hint != null); + entity_type entt; + + if(const auto req = (to_integral(hint) & traits_type::entity_mask); !(req < entities.size())) { + entities.reserve(req + 1); + + for(auto pos = entities.size(); pos < req; ++pos) { + entities.emplace_back(destroyed); + destroyed = entity_type(pos); + } + + entt = entities.emplace_back(hint); + } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { + entt = create(); + } else { + auto *it = &destroyed; + for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); + *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; + entt = entities[req] = hint; + } + + if constexpr(sizeof...(Component) == 0) { + return entt; + } else { + return std::tuple_cat(std::make_tuple(entt), std::forward_as_tuple(assign(entt)...)); + } + } + /** * @brief Destroys an entity and lets the registry recycle the identifier. * diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 0013a6d6c..2466e6927 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -1231,6 +1231,39 @@ TEST(Registry, CreateManyEntitiesWithComponentsAtOnceWithListener) { ASSERT_EQ(listener.counter, 6); } +TEST(Registry, CreateWithHint) { + entt::registry registry; + auto e3 = registry.create(entt::entity{3}); + auto e2 = registry.create(entt::entity{3}); + + ASSERT_EQ(e2, entt::entity{2}); + ASSERT_FALSE(registry.valid(entt::entity{1})); + ASSERT_EQ(e3, entt::entity{3}); + + registry.destroy(e2); + + ASSERT_EQ(registry.version(e2), 0); + ASSERT_EQ(registry.current(e2), 1); + + e2 = registry.create(); + auto e1 = registry.create(entt::entity{2}); + + ASSERT_EQ(registry.entity(e2), entt::entity{2}); + ASSERT_EQ(registry.version(e2), 1); + + ASSERT_EQ(registry.entity(e1), entt::entity{1}); + ASSERT_EQ(registry.version(e1), 0); + + registry.destroy(e1); + registry.destroy(e2); + auto e0 = std::get<0>(registry.create(entt::entity{0})); + + ASSERT_EQ(e0, entt::entity{0}); + ASSERT_EQ(registry.version(e0), 0); + ASSERT_TRUE(registry.has(e0)); + ASSERT_FALSE(registry.has(e0)); +} + TEST(Registry, NonOwningGroupInterleaved) { entt::registry registry; typename entt::entity entity = entt::null;