#include #include #include #include #include #include #include #include #include #include struct noncopyable_component { noncopyable_component() : value{} {} explicit noncopyable_component(int v) : value{v} {} noncopyable_component(const noncopyable_component &) = delete; noncopyable_component(noncopyable_component &&) = default; noncopyable_component &operator=(const noncopyable_component &) = delete; noncopyable_component &operator=(noncopyable_component &&) = default; int value; }; template struct output_archive { output_archive(Storage &instance) : storage{instance} {} template void operator()(const Value &...value) { (std::get>(storage).push(value), ...); } void operator()(const entt::entity &entity, const noncopyable_component &instance) { (*this)(entity, instance.value); } 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()(entt::entity &entity, noncopyable_component &instance) { (*this)(entity, instance.value); } 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 storage_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue, std::queue, std::queue>; storage_type storage; output_archive output{storage}; input_archive input{storage}; entt::snapshot{registry}.entities(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).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 storage_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue>; storage_type storage; output_archive output{storage}; input_archive input{storage}; entt::snapshot{registry}.entities(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); 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, i, i); registry.emplace(entity, i); if(i % 2) { registry.emplace(entity); } } using storage_type = std::tuple< std::queue, std::queue, std::queue, std::queue>; storage_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()); registry.clear(); entt::snapshot_loader{registry}.component(input); ASSERT_EQ(registry.view().size(), size); 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 storage_type = std::tuple< std::queue, std::queue, std::queue, std::queue, std::queue, std::queue, std::queue>; storage_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, 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, -1); entt::snapshot{src}.entities(output).component(output); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &map_component::keys, &map_component::values, &map_component::both) .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()) noncopyable_component_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, &noncopyable_component_cnt](auto, const auto &component) { ++noncopyable_component_cnt; ASSERT_EQ(component.value, static_cast(dst.storage().size() - noncopyable_component_cnt - 1u)); }); src.view().each([](auto, auto &component) { component.value = 2 * component.key; }); auto size = dst.size(); entt::snapshot{src}.entities(output).component(output); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &map_component::keys, &map_component::values, &map_component::both) .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(), noncopyable_component_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); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &map_component::keys, &map_component::values, &map_component::both) .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); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &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); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &map_component::keys, &map_component::values, &map_component::both) .orphans(); ASSERT_EQ(dst.storage().size(), a_component_cnt); src.clear(); a_component_cnt = {}; entt::snapshot{src}.entities(output).component(output); loader.entities(input) .component( input, &what_a_component::bar, &what_a_component::quux, &map_component::keys, &map_component::values, &map_component::both) .orphans(); ASSERT_EQ(dst.storage().size(), a_component_cnt); } TEST(Snapshot, MoreOnShrink) { using traits_type = entt::entt_traits; entt::registry src; entt::registry dst; entt::continuous_loader loader{dst}; using storage_type = std::tuple< std::queue, std::queue>; storage_type storage; output_archive output{storage}; input_archive input{storage}; auto entity = src.create(); entt::snapshot{src}.entities(output); loader.entities(input).shrink(); ASSERT_TRUE(dst.valid(entity)); loader.shrink(); ASSERT_FALSE(dst.valid(entity)); } TEST(Snapshot, SyncDataMembers) { using traits_type = entt::entt_traits; entt::registry src; entt::registry dst; entt::continuous_loader loader{dst}; using storage_type = std::tuple< std::queue, std::queue, std::queue, std::queue>; storage_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); loader.entities(input).component( input, &what_a_component::bar, &what_a_component::quux, &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 &foobar = dst.get(loader.map(child)); ASSERT_EQ(foobar.keys.at(loader.map(child)), 10); ASSERT_EQ(foobar.values.at(10), loader.map(child)); ASSERT_EQ(foobar.both.at(loader.map(child)), loader.map(child)); }