snapshot: registry friendship is no longer required

This commit is contained in:
Michele Caini
2020-05-07 00:55:48 +02:00
parent 4d73adb540
commit 4ac46bf19d
3 changed files with 93 additions and 149 deletions

9
TODO
View File

@@ -16,12 +16,11 @@ Next:
* make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
* review multi component views to reduce instantiations once empty types are gone...
* add observer functions aside observer class
* registry clear can use the range-destroy and run much, much faster!
* WIP:
- introduce the component iterators for non-contiguous collections of entities (multi component views, observers, user defined collections)
- deprecate snapshot, loader, ...
- provide documentation to describe alternatives
* WIP: snapshot rework/deprecation
- remove snapshot/loader from registry, make them external (faster) tools
- deprecate snapshot classes, update documentation to describe alternatives
* WIP: snapshot rework
- snapshot: use entity injection to load all entities at once
- update documentation to describe alternatives

View File

@@ -1580,20 +1580,9 @@ public:
*
* @return A temporary object to use to take snasphosts.
*/
[[deprecated("basic_snapshot has now a constructor that accepts a reference to a registry")]]
entt::basic_snapshot<Entity> snapshot() const {
using follow_fn_type = entity_type(const basic_registry &, const entity_type);
const auto head = to_integral(destroyed);
const entity_type seed = (destroyed == null) ? destroyed : entity_type{head | (to_integral(entities[head]) & (traits_type::version_mask << traits_type::entity_shift))};
follow_fn_type *follow = [](const basic_registry &reg, const entity_type entity) -> entity_type {
const auto &others = reg.entities;
const auto entt = to_integral(entity) & traits_type::entity_mask;
const auto curr = to_integral(others[entt]) & traits_type::entity_mask;
return entity_type{curr | (to_integral(others[curr]) & (traits_type::version_mask << traits_type::entity_shift))};
};
return { this, seed, follow };
return { *this };
}
/**
@@ -1611,33 +1600,9 @@ public:
*
* @return A temporary object to use to load snasphosts.
*/
[[deprecated("basic_snapshot_loader has now a constructor that accepts a reference to a registry")]]
basic_snapshot_loader<Entity> loader() {
using force_fn_type = void(basic_registry &, const entity_type, const bool);
force_fn_type *force = [](basic_registry &reg, const entity_type entity, const bool drop) {
const auto entt = to_integral(entity) & traits_type::entity_mask;
auto &others = reg.entities;
if(!(entt < others.size())) {
auto curr = others.size();
others.resize(entt + 1);
std::generate(others.data() + curr, others.data() + entt, [&curr]() { return entity_type(curr++); });
}
others[entt] = entity;
if(drop) {
reg.destroy(entity);
const auto version = to_integral(entity) & (traits_type::version_mask << traits_type::entity_shift);
others[entt] = entity_type{(to_integral(others[entt]) & traits_type::entity_mask) | version};
}
};
clear();
entities.clear();
destroyed = null;
return { this, force };
return { *this };
}
/**

View File

@@ -3,6 +3,7 @@
#include <array>
#include <vector>
#include <cstddef>
#include <utility>
#include <iterator>
@@ -31,15 +32,8 @@ class basic_snapshot {
/*! @brief A registry is allowed to create snapshots. */
friend class basic_registry<Entity>;
using follow_fn_type = Entity(const basic_registry<Entity> &, const Entity);
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
basic_snapshot(const basic_registry<Entity> *source, Entity init, follow_fn_type *fn) ENTT_NOEXCEPT
: reg{source},
seed{init},
follow{fn}
{}
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
archive(typename traits_type::entity_type(sz));
@@ -71,6 +65,17 @@ class basic_snapshot {
}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) = default;
@@ -78,10 +83,10 @@ public:
basic_snapshot & operator=(basic_snapshot &&) = default;
/**
* @brief Puts aside all the entities that are still in use.
* @brief Puts aside all the entities from the underlying registry.
*
* Entities are serialized along with their versions. Destroyed entities are
* not taken in consideration by this function.
* taken in consideration as well by this function.
*
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
@@ -89,39 +94,28 @@ public:
*/
template<typename Archive>
const basic_snapshot & entities(Archive &archive) const {
archive(typename traits_type::entity_type(reg->alive()));
reg->each([&archive](const auto entt) { archive(entt); });
return *this;
}
const auto sz = reg->size();
auto first = reg->data();
const auto last = first + sz;
/**
* @brief Puts aside destroyed entities.
*
* Entities are serialized along with their versions. Entities that are
* still in use are not taken in consideration by this function.
*
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot & destroyed(Archive &archive) const {
auto size = reg->size() - reg->alive();
archive(typename traits_type::entity_type(size));
archive(typename traits_type::entity_type(sz));
if(size) {
auto curr = seed;
archive(curr);
for(--size; size; --size) {
curr = follow(*reg, curr);
archive(curr);
}
while(first != last) {
archive(*(first++));
}
return *this;
}
/**
* @brief Deprecated function. Currently, it does nothing.
* @tparam Archive Type of output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
[[deprecated("use ::entities instead, it exports now also destroyed entities")]]
const basic_snapshot & destroyed(Archive &) const { return *this; }
/**
* @brief Puts aside the given components.
*
@@ -160,9 +154,7 @@ public:
}
private:
const basic_registry<Entity> *reg;
const Entity seed;
follow_fn_type *follow;
const basic_registry<entity_type> *reg;
};
@@ -181,36 +173,15 @@ class basic_snapshot_loader {
/*! @brief A registry is allowed to create snapshot loaders. */
friend class basic_registry<Entity>;
using force_fn_type = void(basic_registry<Entity> &, const Entity, const bool);
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
basic_snapshot_loader(basic_registry<Entity> *source, force_fn_type *fn) ENTT_NOEXCEPT
: reg{source},
force{fn}
{
// to restore a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty());
}
template<typename Archive>
void assure(Archive &archive, bool discard) const {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
archive(entt);
force(*reg, entt, discard);
}
}
template<typename Type, typename Archive, typename... Args>
void assign(Archive &archive, Args... args) const {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
entity_type entt{};
if constexpr(std::is_empty_v<Type>) {
archive(entt);
@@ -228,6 +199,20 @@ class basic_snapshot_loader {
}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty());
}
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) = default;
@@ -246,27 +231,28 @@ public:
*/
template<typename Archive>
const basic_snapshot_loader & entities(Archive &archive) const {
static constexpr auto discard = false;
assure(archive, discard);
typename traits_type::entity_type length{};
archive(length);
std::vector<entity_type> entities(length);
for(decltype(length) pos{}; pos < length; ++pos) {
archive(entities[pos]);
}
reg->assign(entities.cbegin(), entities.cend());
return *this;
}
/**
* @brief Restores entities that were destroyed during serialization.
*
* This function restores the entities that were destroyed during
* serialization and gives them the versions they originally had.
*
* @brief Deprecated function. Currently, it does nothing.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader & destroyed(Archive &archive) const {
static constexpr auto discard = true;
assure(archive, discard);
return *this;
}
[[deprecated("use ::entities instead, it imports now also destroyed entities")]]
const basic_snapshot_loader & destroyed(Archive &) const { return *this; }
/**
* @brief Restores components and assigns them to the right entities.
@@ -306,8 +292,7 @@ public:
}
private:
basic_registry<Entity> *reg;
force_fn_type *force;
basic_registry<entity_type> *reg;
};
@@ -364,12 +349,12 @@ class basic_continuous_loader {
using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
using second_type = typename std::decay_t<decltype(pair)>::second_type;
if constexpr(std::is_same_v<first_type, Entity> && std::is_same_v<second_type, Entity>) {
if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
other.emplace(map(pair.first), map(pair.second));
} else if constexpr(std::is_same_v<first_type, Entity>) {
} else if constexpr(std::is_same_v<first_type, entity_type>) {
other.emplace(map(pair.first), std::move(pair.second));
} else {
static_assert(std::is_same_v<second_type, Entity>);
static_assert(std::is_same_v<second_type, entity_type>);
other.emplace(std::move(pair.first), map(pair.second));
}
}
@@ -381,7 +366,7 @@ class basic_continuous_loader {
auto update(char, Container &container)
-> decltype(typename Container::value_type{}, void()) {
// vector like container
static_assert(std::is_same_v<typename Container::value_type, Entity>);
static_assert(std::is_same_v<typename Container::value_type, entity_type>);
for(auto &&entt: container) {
entt = map(entt);
@@ -392,7 +377,7 @@ class basic_continuous_loader {
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) {
if constexpr(!std::is_same_v<Other, Type>) {
return;
} else if constexpr(std::is_same_v<Member, Entity>) {
} else if constexpr(std::is_same_v<Member, entity_type>) {
instance.*member = map(instance.*member);
} else {
// maybe a container? let's try...
@@ -400,18 +385,6 @@ class basic_continuous_loader {
}
}
template<typename Archive>
void assure(Archive &archive, void(basic_continuous_loader:: *member)(Entity)) {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
archive(entt);
(this->*member)(entt);
}
}
template<typename Component>
void remove_if_exists() {
for(auto &&ref: remloc) {
@@ -429,7 +402,7 @@ class basic_continuous_loader {
archive(length);
while(length--) {
Entity entt{};
entity_type entt{};
if constexpr(std::is_empty_v<Other>) {
archive(entt);
@@ -450,7 +423,7 @@ public:
using entity_type = Entity;
/**
* @brief Constructs a loader that is bound to a given registry.
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
@@ -475,25 +448,32 @@ public:
*/
template<typename Archive>
basic_continuous_loader & entities(Archive &archive) {
assure(archive, &basic_continuous_loader::restore);
typename traits_type::entity_type length{};
entity_type entt{};
archive(length);
for(decltype(length) pos{}; pos < length; ++pos) {
archive(entt);
if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) {
restore(entt);
} else {
destroy(entt);
}
}
return *this;
}
/**
* @brief Restores entities that were destroyed during serialization.
*
* This function restores the entities that were destroyed during
* serialization and creates local counterparts for them if required.
*
* @brief Deprecated function. Currently, it does nothing.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A non-const reference to this loader.
*/
template<typename Archive>
basic_continuous_loader & destroyed(Archive &archive) {
assure(archive, &basic_continuous_loader::destroy);
return *this;
}
[[deprecated("use ::entities instead, it imports now also destroyed entities")]]
basic_continuous_loader & destroyed(Archive &) { return *this; }
/**
* @brief Restores components and assigns them to the right entities.