diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp
index 760b1b2af..88680b100 100644
--- a/src/entt/entity/registry.hpp
+++ b/src/entt/entity/registry.hpp
@@ -134,17 +134,17 @@ class basic_registry {
}
Entity recycle_identifier() {
- ENTT_ASSERT(destroyed != null);
- const auto curr = to_integral(destroyed);
+ ENTT_ASSERT(available != null);
+ const auto curr = to_integral(available);
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};
+ available = entity_type{to_integral(entities[curr]) & traits_type::entity_mask};
return entities[curr] = entity_type{curr | version};
}
void release_entity(const Entity entity, const typename traits_type::version_type version) {
const auto entt = to_integral(entity) & traits_type::entity_mask;
- entities[entt] = entity_type{to_integral(destroyed) | (typename traits_type::entity_type{version} << traits_type::entity_shift)};
- destroyed = entity_type{entt};
+ entities[entt] = entity_type{to_integral(available) | (typename traits_type::entity_type{version} << traits_type::entity_shift)};
+ available = entity_type{entt};
}
public:
@@ -198,7 +198,7 @@ public:
*/
[[nodiscard]] size_type alive() const {
auto sz = entities.size();
- auto curr = destroyed;
+ auto curr = available;
for(; curr != null; --sz) {
curr = entities[to_integral(curr) & traits_type::entity_mask];
@@ -340,6 +340,18 @@ public:
return entities.data();
}
+ /**
+ * @brief Returns the head of the list of destroyed entities.
+ *
+ * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases.
+ *
+ * @return The head of the list of destroyed entities.
+ */
+ [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT {
+ return available;
+ }
+
/**
* @brief Checks if an entity identifier refers to a valid entity.
* @param entity An entity identifier, either valid or not.
@@ -398,7 +410,7 @@ public:
* @return A valid entity identifier.
*/
entity_type create() {
- return destroyed == null ? generate_identifier() : recycle_identifier();
+ return available == null ? generate_identifier() : recycle_identifier();
}
/**
@@ -427,7 +439,7 @@ public:
} else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) {
entt = create();
} else {
- auto *it = &destroyed;
+ auto *it = &available;
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;
@@ -447,7 +459,7 @@ public:
*/
template
void create(It first, It last) {
- for(; destroyed != null && first != last; ++first) {
+ for(; available != null && first != last; ++first) {
*first = recycle_identifier();
}
@@ -459,9 +471,11 @@ public:
/**
* @brief Assigns entities to an empty registry.
*
- * This function is intended for use in conjunction with `raw`.
- * Don't try to inject ranges of randomly generated entities. There is no
- * guarantee that a registry will continue to work properly in this case.
+ * This function is intended for use in conjunction with `raw` and
+ * `assign`.
+ * Don't try to inject ranges of randomly generated entities nor the _wrong_
+ * head for the list of destroyed entities. There is no guarantee that a
+ * registry will continue to work properly in this case.
*
* @warning
* An assertion will abort the execution at runtime in debug mode if all
@@ -470,18 +484,13 @@ public:
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
+ * @param destroyed The head of the list of destroyed entities.
*/
template
- void assign(It first, It last) {
+ void assign(It first, It last, const entity_type destroyed) {
ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); }));
entities.assign(first, last);
- destroyed = null;
-
- for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) {
- if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) {
- release_entity(entity_type{static_cast(pos)}, version(entities[pos]));
- }
- }
+ available = destroyed;
}
/**
@@ -963,7 +972,7 @@ public:
*/
template
void each(Func func) const {
- if(destroyed == null) {
+ if(available == null) {
for(auto pos = entities.size(); pos; --pos) {
func(entities[pos-1]);
}
@@ -1633,7 +1642,7 @@ private:
mutable std::vector pools{};
std::vector entities{};
std::vector vars{};
- entity_type destroyed{null};
+ entity_type available{null};
};
diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp
index d34e87c6f..b2c243846 100644
--- a/src/entt/entity/snapshot.hpp
+++ b/src/entt/entity/snapshot.hpp
@@ -95,15 +95,15 @@ public:
template
const basic_snapshot & entities(Archive &archive) const {
const auto sz = reg->size();
- auto first = reg->data();
- const auto last = first + sz;
archive(typename traits_type::entity_type(sz));
- while(first != last) {
- archive(*(first++));
+ for(auto first = reg->data(), last = first + sz; first != last; ++first) {
+ archive(*first);
}
+ archive(reg->destroyed());
+
return *this;
}
@@ -234,7 +234,10 @@ public:
archive(all[pos]);
}
- reg->assign(all.cbegin(), all.cend());
+ entity_type destroyed;
+ archive(destroyed);
+
+ reg->assign(all.cbegin(), all.cend(), destroyed);
return *this;
}
@@ -302,12 +305,10 @@ class basic_continuous_loader {
using traits_type = entt_traits;
void destroy(Entity entt) {
- const auto it = remloc.find(entt);
-
- if(it == remloc.cend()) {
+ if(const auto it = remloc.find(entt); it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
- reg->destroy(local);
+ reg->destroy(local, 0u);
}
}
@@ -318,7 +319,10 @@ class basic_continuous_loader {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
} else {
- remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create();
+ if(!reg->valid(remloc[entt].first)) {
+ remloc[entt].first = reg->create();
+ }
+
// set the dirty flag
remloc[entt].second = true;
}
@@ -451,6 +455,9 @@ public:
}
}
+ // discards the head of the list of destroyed entities
+ archive(entt);
+
return *this;
}
diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp
index 7020a86b1..cd0ff3d81 100644
--- a/test/entt/entity/registry.cpp
+++ b/test/entt/entity/registry.cpp
@@ -1404,7 +1404,8 @@ TEST(Registry, AssignEntities) {
registry.destroy(entities[2]);
entt::registry other;
- other.assign(registry.data(), registry.data() + registry.size());
+ const auto *data = registry.data();
+ other.assign(data, data + registry.size(), registry.destroyed());
ASSERT_EQ(registry.size(), other.size());
ASSERT_TRUE(other.valid(entities[0]));