#include #include #include #include #include #include #include #include #include #include #include #include #include #include struct empty {}; struct shadow { entt::entity target{entt::null}; static void listener(entt::entity &elem, entt::registry ®istry, const entt::entity entt) { elem = registry.get(entt).target; } }; TEST(BasicSnapshot, Constructors) { static_assert(!std::is_default_constructible_v>); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_move_constructible_v>); static_assert(std::is_move_assignable_v>); entt::registry registry; entt::basic_snapshot snapshot{registry}; entt::basic_snapshot other{std::move(snapshot)}; ASSERT_NO_FATAL_FAILURE(snapshot = std::move(other)); } TEST(BasicSnapshot, GetEntityType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot snapshot{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data](auto &&value) { data.emplace_back(std::forward(value)); }; snapshot.get(archive); ASSERT_EQ(data.size(), 2u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), storage.size()); ASSERT_NE(entt::any_cast(&data[1u]), nullptr); ASSERT_EQ(entt::any_cast(data[1u]), storage.in_use()); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.destroy(entities[1u]); data.clear(); snapshot.get(archive, "ignored"_hs); ASSERT_EQ(data.size(), 5u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), storage.size()); ASSERT_NE(entt::any_cast(&data[1u]), nullptr); ASSERT_EQ(entt::any_cast(data[1u]), storage.in_use()); ASSERT_NE(entt::any_cast(&data[2u]), nullptr); ASSERT_EQ(entt::any_cast(data[2u]), storage.data()[0u]); ASSERT_NE(entt::any_cast(&data[3u]), nullptr); ASSERT_EQ(entt::any_cast(data[3u]), storage.data()[1u]); ASSERT_NE(entt::any_cast(&data[4u]), nullptr); ASSERT_EQ(entt::any_cast(data[4u]), storage.data()[2u]); } TEST(BasicSnapshot, GetType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot snapshot{registry}; const auto &storage = registry.storage(); entt::entity entities[3u]; const int values[3u]{1, 2, 3}; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), std::begin(values)); registry.destroy(entities[1u]); std::vector data{}; auto archive = [&data](auto &&value) { data.emplace_back(std::forward(value)); }; snapshot.get(archive, "other"_hs); ASSERT_EQ(data.size(), 1u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), 0u); data.clear(); snapshot.get(archive); ASSERT_EQ(data.size(), 5u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), storage.size()); ASSERT_NE(entt::any_cast(&data[1u]), nullptr); ASSERT_EQ(entt::any_cast(data[1u]), entities[0u]); ASSERT_NE(entt::any_cast(&data[2u]), nullptr); ASSERT_EQ(entt::any_cast(data[2u]), values[0u]); ASSERT_NE(entt::any_cast(&data[3u]), nullptr); ASSERT_EQ(entt::any_cast(data[3u]), entities[2u]); ASSERT_NE(entt::any_cast(&data[4u]), nullptr); ASSERT_EQ(entt::any_cast(data[4u]), values[2u]); } TEST(BasicSnapshot, GetEmptyType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot snapshot{registry}; const auto &storage = registry.storage(); entt::entity entities[3u]; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities)); registry.destroy(entities[1u]); std::vector data{}; auto archive = [&data](auto &&value) { data.emplace_back(std::forward(value)); }; snapshot.get(archive, "other"_hs); ASSERT_EQ(data.size(), 1u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), 0u); data.clear(); snapshot.get(archive); ASSERT_EQ(data.size(), 3u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), storage.size()); ASSERT_NE(entt::any_cast(&data[1u]), nullptr); ASSERT_EQ(entt::any_cast(data[1u]), entities[0u]); ASSERT_NE(entt::any_cast(&data[2u]), nullptr); ASSERT_EQ(entt::any_cast(data[2u]), entities[2u]); } TEST(BasicSnapshot, GetTypeSparse) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot snapshot{registry}; entt::entity entities[3u]; const int values[3u]{1, 2, 3}; registry.create(std::begin(entities), std::end(entities)); registry.insert(std::begin(entities), std::end(entities), std::begin(values)); registry.destroy(entities[1u]); std::vector data{}; auto archive = [&data](auto &&value) { data.emplace_back(std::forward(value)); }; snapshot.get(archive, std::begin(entities), std::end(entities), "other"_hs); ASSERT_EQ(data.size(), 1u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), 0u); data.clear(); snapshot.get(archive, std::begin(entities), std::end(entities)); ASSERT_EQ(data.size(), 6u); ASSERT_NE(entt::any_cast(&data[0u]), nullptr); ASSERT_EQ(entt::any_cast(data[0u]), static_cast(std::distance(std::begin(entities), std::end(entities)))); ASSERT_NE(entt::any_cast(&data[1u]), nullptr); ASSERT_EQ(entt::any_cast(data[1u]), entities[0u]); ASSERT_NE(entt::any_cast(&data[2u]), nullptr); ASSERT_EQ(entt::any_cast(data[2u]), values[0u]); ASSERT_NE(entt::any_cast(&data[3u]), nullptr); ASSERT_EQ(entt::any_cast(data[3u]), static_cast(entt::null)); ASSERT_NE(entt::any_cast(&data[4u]), nullptr); ASSERT_EQ(entt::any_cast(data[4u]), entities[2u]); ASSERT_NE(entt::any_cast(&data[5u]), nullptr); ASSERT_EQ(entt::any_cast(data[5u]), values[2u]); } TEST(BasicSnapshotLoader, Constructors) { static_assert(!std::is_default_constructible_v>); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_move_constructible_v>); static_assert(std::is_move_assignable_v>); entt::registry registry; entt::basic_snapshot_loader loader{registry}; entt::basic_snapshot_loader other{std::move(loader)}; ASSERT_NO_FATAL_FAILURE(loader = std::move(other)); } TEST(BasicSnapshotLoader, GetEntityType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[3u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u), traits_type::construct(1u, 1u)}; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); data.emplace_back(static_cast(0u)); data.emplace_back(static_cast(0u)); loader.get(archive); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(storage.in_use(), 0u); data.emplace_back(static_cast(3u)); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(entities[2u]); loader.get(archive, "ignored"_hs); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_EQ(storage.size(), 3u); ASSERT_EQ(storage.in_use(), 2u); ASSERT_EQ(storage[0u], entities[0u]); ASSERT_EQ(storage[1u], entities[1u]); ASSERT_EQ(storage[2u], entities[2u]); ASSERT_EQ(registry.create(), entities[2u]); } TEST(BasicSnapshotLoader, GetType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int values[2u]{1, 3}; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); data.emplace_back(entities[1u]); data.emplace_back(values[1u]); loader.get(archive); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(entities[0u])); ASSERT_TRUE(storage.contains(entities[1u])); ASSERT_EQ(storage.get(entities[0u]), values[0u]); ASSERT_EQ(storage.get(entities[1u]), values[1u]); } TEST(BasicSnapshotLoader, GetEmptyType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); loader.get(archive); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(entities[0u])); ASSERT_TRUE(storage.contains(entities[1u])); } TEST(BasicSnapshotLoader, GetTypeSparse) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int values[2u]{1, 3}; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); data.emplace_back(static_cast(2u)); data.emplace_back(static_cast(entt::null)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(3u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); data.emplace_back(static_cast(entt::null)); data.emplace_back(entities[1u]); data.emplace_back(values[1u]); loader.get(archive); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(entities[0u])); ASSERT_TRUE(storage.contains(entities[1u])); ASSERT_EQ(storage.get(entities[0u]), values[0u]); ASSERT_EQ(storage.get(entities[1u]), values[1u]); } TEST(BasicSnapshotLoader, GetTypeWithListener) { using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; entt::entity check{entt::null}; std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const auto entity{traits_type::construct(1u, 1u)}; const shadow value{entity}; ASSERT_FALSE(registry.valid(entity)); ASSERT_EQ(check, static_cast(entt::null)); registry.on_construct().connect<&shadow::listener>(check); data.emplace_back(static_cast(1u)); data.emplace_back(entity); data.emplace_back(value); loader.get(archive); ASSERT_TRUE(registry.valid(entity)); ASSERT_EQ(check, entity); } TEST(BasicSnapshotLoader, Orphans) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_snapshot_loader loader{registry}; std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int value = 42; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); data.emplace_back(static_cast(2u)); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); data.emplace_back(value); loader.get(archive); loader.get(archive); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_TRUE(registry.valid(entities[1u])); loader.orphans(); ASSERT_TRUE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); } TEST(BasicContinuousLoader, Constructors) { static_assert(!std::is_default_constructible_v>); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_move_constructible_v>); static_assert(std::is_move_assignable_v>); entt::registry registry; entt::basic_continuous_loader loader{registry}; entt::basic_continuous_loader other{std::move(loader)}; ASSERT_NO_FATAL_FAILURE(loader = std::move(other)); } TEST(BasicContinuousLoader, GetEntityType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[3u]{traits_type::construct(1u, 0u), traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); data.emplace_back(static_cast(0u)); data.emplace_back(static_cast(0u)); loader.get(archive); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_FALSE(registry.valid(entities[2u])); ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_FALSE(loader.contains(entities[2u])); ASSERT_EQ(loader.map(entities[0u]), static_cast(entt::null)); ASSERT_EQ(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_EQ(loader.map(entities[2u]), static_cast(entt::null)); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(storage.in_use(), 0u); data.emplace_back(static_cast(3u)); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(entities[2u]); loader.get(archive, "ignored"_hs); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_FALSE(loader.contains(entities[2u])); ASSERT_NE(loader.map(entities[0u]), static_cast(entt::null)); ASSERT_NE(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_EQ(loader.map(entities[2u]), static_cast(entt::null)); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 2u); ASSERT_EQ(storage.in_use(), 2u); ASSERT_EQ(storage[0u], loader.map(entities[0u])); ASSERT_EQ(storage[1u], loader.map(entities[1u])); ASSERT_EQ(registry.create(), entities[2u]); data.emplace_back(static_cast(3u)); data.emplace_back(static_cast(3u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(entities[2u]); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(loader.contains(entities[2u])); ASSERT_NE(loader.map(entities[0u]), static_cast(entt::null)); ASSERT_NE(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_NE(loader.map(entities[2u]), static_cast(entt::null)); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_TRUE(registry.valid(loader.map(entities[2u]))); ASSERT_EQ(storage.size(), 4u); ASSERT_EQ(storage.in_use(), 4u); ASSERT_EQ(storage[0u], loader.map(entities[0u])); ASSERT_EQ(storage[1u], loader.map(entities[1u])); ASSERT_EQ(storage[3u], loader.map(entities[2u])); registry.destroy(loader.map(entities[1u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_NE(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); data.emplace_back(static_cast(1u)); data.emplace_back(static_cast(1u)); data.emplace_back(entities[1u]); loader.get(archive); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_NE(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage[3u], loader.map(entities[1u])); data.emplace_back(static_cast(3u)); data.emplace_back(static_cast(1u)); data.emplace_back(entities[1u]); data.emplace_back(entities[2u]); data.emplace_back(entities[0u]); loader.get(archive, "ignored"_hs); ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_FALSE(loader.contains(entities[2u])); ASSERT_EQ(loader.map(entities[0u]), static_cast(entt::null)); ASSERT_NE(loader.map(entities[1u]), static_cast(entt::null)); ASSERT_EQ(loader.map(entities[2u]), static_cast(entt::null)); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 4u); ASSERT_EQ(storage.in_use(), 2u); ASSERT_EQ(storage[1u], loader.map(entities[1u])); } TEST(BasicContinuousLoader, GetType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int values[2u]{1, 3}; ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_FALSE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); data.emplace_back(entities[1u]); data.emplace_back(values[1u]); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(loader.map(entities[0u]))); ASSERT_TRUE(storage.contains(loader.map(entities[1u]))); ASSERT_EQ(storage.get(loader.map(entities[0u])), values[0u]); ASSERT_EQ(storage.get(loader.map(entities[1u])), values[1u]); } TEST(BasicContinuousLoader, GetTypeExtended) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; const entt::entity entities[2u]{traits_type::construct(0u, 1u), traits_type::construct(1u, 1u)}; const shadow value{entities[0u]}; auto archive = [&loader, &data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); if constexpr(std::is_same_v, shadow>) { value.target = loader.map(value.target); } }; ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_FALSE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); data.emplace_back(static_cast(2u)); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(static_cast(1u)); data.emplace_back(entities[1u]); data.emplace_back(value); loader.get(archive); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); ASSERT_EQ(storage.size(), 1u); ASSERT_TRUE(storage.contains(loader.map(entities[1u]))); ASSERT_EQ(storage.get(loader.map(entities[1u])).target, loader.map(entities[0u])); } TEST(BasicContinuousLoader, GetEmptyType) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_FALSE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(loader.map(entities[0u]))); ASSERT_TRUE(storage.contains(loader.map(entities[1u]))); } TEST(BasicContinuousLoader, GetTypeSparse) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; const auto &storage = registry.storage(); std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int values[2u]{1, 3}; ASSERT_FALSE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_FALSE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); data.emplace_back(static_cast(2u)); data.emplace_back(static_cast(entt::null)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); loader.get(archive, "other"_hs); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_FALSE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 0u); ASSERT_EQ(registry.storage("other"_hs).size(), 1u); data.emplace_back(static_cast(3u)); data.emplace_back(entities[0u]); data.emplace_back(values[0u]); data.emplace_back(static_cast(entt::null)); data.emplace_back(entities[1u]); data.emplace_back(values[1u]); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); ASSERT_EQ(storage.size(), 2u); ASSERT_TRUE(storage.contains(loader.map(entities[0u]))); ASSERT_TRUE(storage.contains(loader.map(entities[1u]))); ASSERT_EQ(storage.get(loader.map(entities[0u])), values[0u]); ASSERT_EQ(storage.get(loader.map(entities[1u])), values[1u]); } TEST(BasicContinuousLoader, GetTypeWithListener) { using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; entt::entity check{entt::null}; std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const auto entity{traits_type::construct(1u, 1u)}; const shadow value{entity}; ASSERT_FALSE(registry.valid(loader.map(entity))); ASSERT_EQ(check, static_cast(entt::null)); registry.on_construct().connect<&shadow::listener>(check); data.emplace_back(static_cast(1u)); data.emplace_back(entity); data.emplace_back(value); loader.get(archive); ASSERT_TRUE(registry.valid(loader.map(entity))); ASSERT_EQ(check, entity); } TEST(BasicContinuousLoader, Shrink) { entt::registry registry; entt::basic_continuous_loader loader{registry}; ASSERT_NO_FATAL_FAILURE(loader.shrink()); } TEST(BasicContinuousLoader, Orphans) { using namespace entt::literals; using traits_type = entt::entt_traits; entt::registry registry; entt::basic_continuous_loader loader{registry}; std::vector data{}; auto archive = [&data, pos = 0u](auto &value) mutable { value = entt::any_cast>(data[pos++]); }; const entt::entity entities[2u]{traits_type::construct(0u, 0u), traits_type::construct(2u, 0u)}; const int value = 42; ASSERT_FALSE(registry.valid(entities[0u])); ASSERT_FALSE(registry.valid(entities[1u])); data.emplace_back(static_cast(2u)); data.emplace_back(static_cast(2u)); data.emplace_back(entities[0u]); data.emplace_back(entities[1u]); data.emplace_back(static_cast(1u)); data.emplace_back(entities[0u]); data.emplace_back(value); loader.get(archive); loader.get(archive); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_TRUE(registry.valid(loader.map(entities[1u]))); loader.orphans(); ASSERT_TRUE(loader.contains(entities[0u])); ASSERT_TRUE(loader.contains(entities[1u])); ASSERT_TRUE(registry.valid(loader.map(entities[0u]))); ASSERT_FALSE(registry.valid(loader.map(entities[1u]))); } template struct output_archive { output_archive(Storage &instance) : storage{instance} {} template void operator()(const Value &value) { std::get>(storage).push(value); } void operator()(const std::unique_ptr &instance) { (*this)(*instance); } private: Storage &storage; }; template struct input_archive { input_archive(Storage &instance) : storage{instance} {} template void operator()(Value &value) { auto assign = [this](auto &val) { auto &queue = std::get>>(storage); val = queue.front(); queue.pop(); }; assign(value); } void operator()(std::unique_ptr &instance) { instance = std::make_unique(); (*this)(*instance); } private: Storage &storage; }; struct a_component {}; struct another_component { int key{}; int value{}; }; struct what_a_component { entt::entity bar{}; std::vector quux{}; }; struct map_component { std::map keys{}; std::map values{}; std::map both{}; }; TEST(Snapshot, Dump) { using traits_type = entt::entt_traits; entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0, 42); registry.emplace(e0, 'c'); registry.emplace(e0, .1); const auto e1 = registry.create(); const auto e2 = registry.create(); registry.emplace(e2, 3); const auto e3 = registry.create(); registry.emplace(e3); registry.emplace(e3, '0'); registry.destroy(e1); auto v1 = registry.current(e1); using archive_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue, std::queue, std::queue>; archive_type storage; output_archive output{storage}; input_archive input{storage}; entt::snapshot{registry} .entities(output) .component(output) .component(output) .component(output) .component(output) .component(output); registry.clear(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_FALSE(registry.valid(e3)); entt::snapshot_loader{registry} .entities(input) .component(input) .component(input) .component(input) .component(input) .component(input) .orphans(); ASSERT_TRUE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_TRUE(registry.valid(e2)); ASSERT_TRUE(registry.valid(e3)); ASSERT_FALSE(registry.orphan(e0)); ASSERT_FALSE(registry.orphan(e2)); ASSERT_FALSE(registry.orphan(e3)); ASSERT_EQ(registry.get(e0), 42); ASSERT_EQ(registry.get(e0), 'c'); ASSERT_EQ(registry.get(e0), .1); ASSERT_EQ(registry.current(e1), v1); ASSERT_EQ(registry.get(e2), 3); ASSERT_EQ(registry.get(e3), '0'); ASSERT_TRUE(registry.all_of(e3)); ASSERT_TRUE(registry.storage().empty()); } TEST(Snapshot, Partial) { using traits_type = entt::entt_traits; entt::registry registry; const auto e0 = registry.create(); registry.emplace(e0, 42); registry.emplace(e0, 'c'); registry.emplace(e0, .1); const auto e1 = registry.create(); const auto e2 = registry.create(); registry.emplace(e2, 3); const auto e3 = registry.create(); registry.emplace(e3, '0'); registry.destroy(e1); auto v1 = registry.current(e1); using archive_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue>; archive_type storage; output_archive output{storage}; input_archive input{storage}; entt::snapshot{registry} .entities(output) .component(output) .component(output); registry.clear(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_FALSE(registry.valid(e3)); entt::snapshot_loader{registry} .entities(input) .component(input) .component(input); ASSERT_TRUE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_TRUE(registry.valid(e2)); ASSERT_TRUE(registry.valid(e3)); ASSERT_EQ(registry.get(e0), 42); ASSERT_EQ(registry.get(e0), 'c'); ASSERT_FALSE(registry.all_of(e0)); ASSERT_EQ(registry.current(e1), v1); ASSERT_EQ(registry.get(e2), 3); ASSERT_EQ(registry.get(e3), '0'); entt::snapshot{registry} .entities(output); registry.clear(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_FALSE(registry.valid(e3)); entt::snapshot_loader{registry} .entities(input) .orphans(); ASSERT_FALSE(registry.valid(e0)); ASSERT_FALSE(registry.valid(e1)); ASSERT_FALSE(registry.valid(e2)); ASSERT_FALSE(registry.valid(e3)); } TEST(Snapshot, Iterator) { using traits_type = entt::entt_traits; entt::registry registry; for(auto i = 0; i < 50; ++i) { const auto entity = registry.create(); registry.emplace(entity); if(i % 2) { registry.emplace(entity, i, i); registry.emplace>(entity, std::make_unique(i)); } } using archive_type = std::tuple< std::queue, std::queue, std::queue, std::queue>; archive_type storage; output_archive output{storage}; input_archive input{storage}; const auto view = registry.view(); const auto size = view.size(); entt::snapshot{registry} .component(output, view.begin(), view.end()) .component>(output, view.begin(), view.end()); registry.clear(); entt::snapshot_loader{registry} .component(input) .component>(input); ASSERT_EQ(registry.view().size(), size / 2u); registry.view().each([](const auto entity, const auto &) { ASSERT_NE(entt::to_integral(entity) % 2u, 0u); }); } TEST(Snapshot, Continuous) { using traits_type = entt::entt_traits; entt::registry src; entt::registry dst; entt::continuous_loader loader{dst}; std::vector entities; entt::entity entity; using archive_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue, std::queue, std::queue>; archive_type storage; output_archive output{storage}; input_archive input{storage}; for(int i = 0; i < 10; ++i) { static_cast(src.create()); } src.clear(); for(int i = 0; i < 5; ++i) { entity = src.create(); entities.push_back(entity); src.emplace(entity); src.emplace(entity, i, i); src.emplace>(entity, std::make_unique(i)); if(i % 2) { src.emplace(entity, entity); } else { src.emplace(entity); } } src.view().each([&entities](auto, auto &what_a_component) { what_a_component.quux.insert(what_a_component.quux.begin(), entities.begin(), entities.end()); }); src.view().each([&entities](auto, auto &map_component) { for(std::size_t i = 0; i < entities.size(); ++i) { map_component.keys.insert({entities[i], int(i)}); map_component.values.insert({int(i), entities[i]}); map_component.both.insert({entities[entities.size() - i - 1], entities[i]}); } }); entity = dst.create(); dst.emplace(entity); dst.emplace(entity, -1, -1); dst.emplace>(entity, std::make_unique(-1)); entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output) .component>(output); loader .entities(input) .component(input) .component(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .component>(input) .orphans(); decltype(dst.size()) a_component_cnt{}; decltype(dst.size()) another_component_cnt{}; decltype(dst.size()) what_a_component_cnt{}; decltype(dst.size()) map_component_cnt{}; decltype(dst.size()) unique_ptr_cnt{}; dst.each([&dst, &a_component_cnt](auto entt) { ASSERT_TRUE(dst.all_of(entt)); ++a_component_cnt; }); dst.view().each([&another_component_cnt](auto, const auto &component) { ASSERT_EQ(component.value, component.key < 0 ? -1 : component.key); ++another_component_cnt; }); dst.view().each([&dst, &what_a_component_cnt](auto entt, const auto &component) { ASSERT_EQ(entt, component.bar); for(auto child: component.quux) { ASSERT_TRUE(dst.valid(child)); } ++what_a_component_cnt; }); dst.view().each([&dst, &map_component_cnt](const auto &component) { for(auto child: component.keys) { ASSERT_TRUE(dst.valid(child.first)); } for(auto child: component.values) { ASSERT_TRUE(dst.valid(child.second)); } for(auto child: component.both) { ASSERT_TRUE(dst.valid(child.first)); ASSERT_TRUE(dst.valid(child.second)); } ++map_component_cnt; }); dst.view>().each([&dst, &unique_ptr_cnt](auto, const auto &component) { ++unique_ptr_cnt; ASSERT_EQ(*component, static_cast(dst.storage>().size() - unique_ptr_cnt - 1u)); }); src.view().each([](auto, auto &component) { component.value = 2 * component.key; }); auto size = dst.size(); entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output); loader .entities(input) .component(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .component(input) .orphans(); ASSERT_EQ(size, dst.size()); ASSERT_EQ(dst.storage().size(), a_component_cnt); ASSERT_EQ(dst.storage().size(), another_component_cnt); ASSERT_EQ(dst.storage().size(), what_a_component_cnt); ASSERT_EQ(dst.storage().size(), map_component_cnt); ASSERT_EQ(dst.storage>().size(), unique_ptr_cnt); dst.view().each([](auto, auto &component) { ASSERT_EQ(component.value, component.key < 0 ? -1 : (2 * component.key)); }); entity = src.create(); src.view().each([entity](auto, auto &component) { component.bar = entity; }); entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output); loader .entities(input) .component(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .component(input) .orphans(); dst.view().each([&loader, entity](auto, auto &component) { ASSERT_EQ(component.bar, loader.map(entity)); }); entities.clear(); for(auto entt: src.view()) { entities.push_back(entt); } src.destroy(entity); loader.shrink(); entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output); loader .entities(input) .component(input) .component(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .orphans() .shrink(); dst.view().each([&dst](auto, auto &component) { ASSERT_FALSE(dst.valid(component.bar)); }); ASSERT_FALSE(loader.contains(entity)); entity = src.create(); src.view().each([entity](auto, auto &component) { component.bar = entity; }); dst.clear(); a_component_cnt = src.storage().size(); entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output); loader .entities(input) .component(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .component(input) .orphans(); ASSERT_EQ(dst.storage().size(), a_component_cnt); src.clear(); a_component_cnt = {}; entt::snapshot{src} .entities(output) .component(output) .component(output) .component(output) .component(output); loader .entities(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both) .component(input) .component(input) .orphans(); ASSERT_EQ(dst.storage().size(), a_component_cnt); } TEST(Snapshot, SyncDataMembers) { using traits_type = entt::entt_traits; entt::registry src; entt::registry dst; entt::continuous_loader loader{dst}; using archive_type = std::tuple< std::queue, std::queue, std::queue, std::queue>; archive_type storage; output_archive output{storage}; input_archive input{storage}; static_cast(src.create()); static_cast(src.create()); src.clear(); auto parent = src.create(); auto child = src.create(); src.emplace(parent, entt::null); src.emplace(child, parent).quux.push_back(child); src.emplace( child, decltype(map_component::keys){{{child, 10}}}, decltype(map_component::values){{{10, child}}}, decltype(map_component::both){{{child, child}}}); entt::snapshot{src} .entities(output) .component(output) .component(output); loader .entities(input) .component(input, &what_a_component::bar, &what_a_component::quux) .component(input, &map_component::keys, &map_component::values, &map_component::both); ASSERT_FALSE(dst.valid(parent)); ASSERT_FALSE(dst.valid(child)); ASSERT_TRUE(dst.all_of(loader.map(parent))); ASSERT_TRUE(dst.all_of(loader.map(child))); ASSERT_EQ(dst.get(loader.map(parent)).bar, static_cast(entt::null)); const auto &component = dst.get(loader.map(child)); ASSERT_EQ(component.bar, loader.map(parent)); ASSERT_EQ(component.quux[0], loader.map(child)); const auto &elem = dst.get(loader.map(child)); ASSERT_EQ(elem.keys.at(loader.map(child)), 10); ASSERT_EQ(elem.values.at(10), loader.map(child)); ASSERT_EQ(elem.both.at(loader.map(child)), loader.map(child)); }