Explorar o código

Snapshot loader can auto-update entities stored in maps now (#335) - close #334

* Detect if member variable is of type map and update the contained entities if needed
* issue 334: tests for identifier update on maps in snapshots
Stephan Z %!s(int64=6) %!d(string=hai) anos
pai
achega
135132e5f0
Modificáronse 3 ficheiros con 171 adicións e 44 borrados
  1. 1 0
      AUTHORS
  2. 35 3
      src/entt/entity/snapshot.hpp
  3. 135 41
      test/entt/entity/snapshot.cpp

+ 1 - 0
AUTHORS

@@ -31,6 +31,7 @@ Paolo-Oliverio
 pgruenbacher
 prowolf
 suVrik
+szunhammer
 The5-1
 vblanco20-1
 willtunnels

+ 35 - 3
src/entt/entity/snapshot.hpp

@@ -353,6 +353,40 @@ class basic_continuous_loader {
         }
     }
 
+    template<typename Container>
+    auto update(int, Container &container)
+    -> decltype(typename Container::mapped_type{}, void()) {
+        // map like container
+        Container other;
+
+        for(auto &&pair: container) {
+            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>) {
+                other.emplace(map(pair.first), map(pair.second));
+            } else if constexpr(std::is_same_v<first_type, Entity>) {
+                other.emplace(map(pair.first), std::move(pair.second));
+            } else {
+                static_assert(std::is_same_v<second_type, Entity>);
+                other.emplace(std::move(pair.first), map(pair.second));
+            }
+        }
+
+        std::swap(container, other);
+    }
+
+    template<typename Container>
+    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>);
+
+        for(auto &&entt: container) {
+            entt = map(entt);
+        }
+    }
+
     template<typename Other, typename Type, typename Member>
     void update(Other &instance, Member Type:: *member) {
         if constexpr(!std::is_same_v<Other, Type>) {
@@ -361,9 +395,7 @@ class basic_continuous_loader {
             instance.*member = map(instance.*member);
         } else {
             // maybe a container? let's try...
-            for(auto &entt: instance.*member) {
-                entt = map(entt);
-            }
+            update(0, instance.*member);
         }
     }
 

+ 135 - 41
test/entt/entity/snapshot.cpp

@@ -1,3 +1,4 @@
+#include <map>
 #include <tuple>
 #include <queue>
 #include <vector>
@@ -54,6 +55,12 @@ struct what_a_component {
     std::vector<entt::entity> quux;
 };
 
+struct map_component {
+    std::map<entt::entity, int> keys;
+    std::map<int, entt::entity> values;
+    std::map<entt::entity, entt::entity> both;
+};
+
 TEST(Snapshot, Dump) {
     using traits_type = entt::entt_traits<std::underlying_type_t<entt::entity>>;
 
@@ -266,6 +273,7 @@ TEST(Snapshot, Continuous) {
         std::queue<entt::entity>,
         std::queue<another_component>,
         std::queue<what_a_component>,
+        std::queue<map_component>,
         std::queue<double>
     >;
 
@@ -288,6 +296,8 @@ TEST(Snapshot, Continuous) {
 
         if(i % 2) {
             src.assign<what_a_component>(entity, entity);
+        } else {
+            src.assign<map_component>(entity);
         }
     }
 
@@ -295,23 +305,38 @@ TEST(Snapshot, Continuous) {
         what_a_component.quux.insert(what_a_component.quux.begin(), entities.begin(), entities.end());
     });
 
+    src.view<map_component>().each([&entities](auto, auto &map_component) {
+        for(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.assign<a_component>(entity);
     dst.assign<another_component>(entity, -1, -1);
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<a_component, another_component, what_a_component>(output);
+       .entities(output)
+       .destroyed(output)
+       .component<a_component, another_component, what_a_component, map_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<a_component, another_component, what_a_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans();
+        .destroyed(input)
+        .component<a_component, another_component, what_a_component, map_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{};
 
     dst.each([&dst, &a_component_cnt](auto entt) {
         ASSERT_TRUE(dst.has<a_component>(entt));
@@ -333,6 +358,23 @@ TEST(Snapshot, Continuous) {
         ++what_a_component_cnt;
     });
 
+    dst.view<map_component>().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;
+    });
+
     src.view<another_component>().each([](auto, auto &component) {
         component.value = 2 * component.key;
     });
@@ -340,20 +382,27 @@ TEST(Snapshot, Continuous) {
     auto size = dst.size();
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<a_component, what_a_component, another_component>(output);
+        .entities(output)
+        .destroyed(output)
+        .component<a_component, what_a_component, map_component, another_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<a_component, what_a_component, another_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans();
+        .destroyed(input)
+        .component<a_component, what_a_component, map_component, another_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.size<a_component>(), a_component_cnt);
     ASSERT_EQ(dst.size<another_component>(), another_component_cnt);
     ASSERT_EQ(dst.size<what_a_component>(), what_a_component_cnt);
+    ASSERT_EQ(dst.size<map_component>(), map_component_cnt);
 
     dst.view<another_component>().each([](auto, auto &component) {
         ASSERT_EQ(component.value, component.key < 0 ? -1 : (2 * component.key));
@@ -366,14 +415,20 @@ TEST(Snapshot, Continuous) {
     });
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<what_a_component, a_component, another_component>(output);
+        .entities(output)
+        .destroyed(output)
+        .component<what_a_component, map_component, a_component, another_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<what_a_component, a_component, another_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans();
+        .destroyed(input)
+        .component<what_a_component, map_component, a_component, another_component>(
+            input,
+            &what_a_component::bar,
+            &what_a_component::quux,
+            &map_component::keys,
+            &map_component::values,
+            &map_component::both
+        ).orphans();
 
     dst.view<what_a_component>().each([&loader, entity](auto, auto &component) {
         ASSERT_EQ(component.bar, loader.map(entity));
@@ -388,15 +443,20 @@ TEST(Snapshot, Continuous) {
     loader.shrink();
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<a_component, another_component, what_a_component>(output);
+        .entities(output)
+        .destroyed(output)
+        .component<a_component, another_component, what_a_component, map_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<a_component, another_component, what_a_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans()
-            .shrink();
+        .destroyed(input)
+        .component<a_component, another_component, what_a_component, map_component>(
+            input,
+            &what_a_component::bar,
+            &what_a_component::quux,
+            &map_component::keys,
+            &map_component::values,
+            &map_component::both
+        ).orphans().shrink();
 
     dst.view<what_a_component>().each([&dst](auto, auto &component) {
         ASSERT_FALSE(dst.valid(component.bar));
@@ -414,14 +474,20 @@ TEST(Snapshot, Continuous) {
     a_component_cnt = src.size<a_component>();
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<a_component, what_a_component, another_component>(output);
+        .entities(output)
+        .destroyed(output)
+        .component<a_component, what_a_component, map_component, another_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<a_component, what_a_component, another_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans();
+        .destroyed(input)
+        .component<a_component, what_a_component, map_component, another_component>(
+            input,
+            &what_a_component::bar,
+            &what_a_component::quux,
+            &map_component::keys,
+            &map_component::values,
+            &map_component::both
+        ).orphans();
 
     ASSERT_EQ(dst.size<a_component>(), a_component_cnt);
 
@@ -429,14 +495,20 @@ TEST(Snapshot, Continuous) {
     a_component_cnt = {};
 
     src.snapshot()
-            .entities(output)
-            .destroyed(output)
-            .component<what_a_component, a_component, another_component>(output);
+        .entities(output)
+        .destroyed(output)
+        .component<what_a_component, map_component, a_component, another_component>(output);
 
     loader.entities(input)
-            .destroyed(input)
-            .component<what_a_component, a_component, another_component>(input, &what_a_component::bar, &what_a_component::quux)
-            .orphans();
+        .destroyed(input)
+        .component<what_a_component, map_component, a_component, another_component>(
+            input,
+            &what_a_component::bar,
+            &what_a_component::quux,
+            &map_component::keys,
+            &map_component::values,
+            &map_component::both
+        ).orphans();
 
     ASSERT_EQ(dst.size<a_component>(), a_component_cnt);
 }
@@ -452,7 +524,7 @@ TEST(Snapshot, MoreOnShrink) {
     using storage_type = std::tuple<
         std::queue<typename traits_type::entity_type>,
         std::queue<entt::entity>
-    >;
+        >;
 
     storage_type storage;
     output_archive<storage_type> output{storage};
@@ -480,7 +552,8 @@ TEST(Snapshot, SyncDataMembers) {
     using storage_type = std::tuple<
         std::queue<typename traits_type::entity_type>,
         std::queue<entt::entity>,
-        std::queue<what_a_component>
+        std::queue<what_a_component>,
+        std::queue<map_component>
     >;
 
     storage_type storage;
@@ -495,11 +568,27 @@ TEST(Snapshot, SyncDataMembers) {
     auto parent = src.create();
     auto child = src.create();
 
+
     src.assign<what_a_component>(parent, entt::null);
     src.assign<what_a_component>(child, parent).quux.push_back(child);
 
-    src.snapshot().entities(output).component<what_a_component>(output);
-    loader.entities(input).component<what_a_component>(input, &what_a_component::bar, &what_a_component::quux);
+    src.assign<map_component>(
+        child,
+        decltype(map_component::keys){{{ child, 10 }}},
+        decltype(map_component::values){{{ 10, child }}},
+        decltype(map_component::both){{{ child, child }}}
+    );
+
+    src.snapshot().entities(output).component<what_a_component, map_component>(output);
+
+    loader.entities(input).component<what_a_component, map_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));
@@ -513,4 +602,9 @@ TEST(Snapshot, SyncDataMembers) {
 
     ASSERT_EQ(component.bar, loader.map(parent));
     ASSERT_EQ(component.quux[0], loader.map(child));
+
+    const auto &foobar = dst.get<map_component>(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));
 }