Explorar o código

rollback on signals, no need for these changes

Michele Caini %!s(int64=7) %!d(string=hai) anos
pai
achega
d02636e370
Modificáronse 5 ficheiros con 56 adicións e 101 borrados
  1. 7 5
      TODO
  2. 10 16
      docs/md/entity.md
  3. 25 60
      src/entt/entity/registry.hpp
  4. 4 4
      test/entt/entity/helper.cpp
  5. 10 16
      test/entt/entity/registry.cpp

+ 7 - 5
TODO

@@ -22,8 +22,10 @@
 * review sparse set to allow customization (mix pack in the spec, base is position only)
 * review sparse set to allow customization (mix pack in the spec, base is position only)
   - non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array
   - non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array
 * review 64 bit id: user defined area + dedicated member on the registry to set it
 * review 64 bit id: user defined area + dedicated member on the registry to set it
-* events on replace, so that one can track updated components
-  - introduce ENTT_REPLACE_ONLY mode
-  - define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
-  - define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
-  - from Tommaso on discord view<Health, Transform>().where<Health>([](h) {h > 5}).where<Transform>([](t) {t.inside(aabb)});
+* add NIO to the EnTT in Action list (link to my CV/LinkedIN)
+
+* reactive systems
+  - add exclusion lists to views (part of signature in this case)
+  - add entt::opt for optional components both to views and groups
+  - add try_get both to views and groups (for optional components)
+  - setup support for reactive systems, something like: registry.reactive(entt:get<A...>, entt::opt<B...>, entt::exclude<C...>)

+ 10 - 16
docs/md/entity.md

@@ -313,34 +313,31 @@ the component owned by an entity if any, a null pointer otherwise.
 
 
 Because of how the registry works internally, it stores a couple of signal
 Because of how the registry works internally, it stores a couple of signal
 handlers for each pool in order to notify some of its data structures on the
 handlers for each pool in order to notify some of its data structures on the
-construction and destruction of components. Moreover, it offers also a signal
-handler to listen for changes on components.<br/>
+construction and destruction of components.<br/>
 These signal handlers are also exposed and made available to users. These are
 These signal handlers are also exposed and made available to users. These are
 the basic bricks to build fancy things like dependencies and reactive systems.
 the basic bricks to build fancy things like dependencies and reactive systems.
 
 
 To get a sink to be used to connect and disconnect listeners so as to be
 To get a sink to be used to connect and disconnect listeners so as to be
-notified on the creation of a component, use the `on_assign` member function:
+notified on the creation of a component, use the `construction` member function:
 
 
 ```cpp
 ```cpp
 // connects a free function
 // connects a free function
-registry.on_assign<position>().connect<&my_free_function>();
+registry.construction<position>().connect<&my_free_function>();
 
 
 // connects a member function
 // connects a member function
-registry.on_assign<position>().connect<&my_class::member>(&instance);
+registry.construction<position>().connect<&my_class::member>(&instance);
 
 
 // disconnects a free function
 // disconnects a free function
-registry.on_assign<position>().disconnect<&my_free_function>();
+registry.construction<position>().disconnect<&my_free_function>();
 
 
 // disconnects a member function
 // disconnects a member function
-registry.on_assign<position>().disconnect<&my_class::member>(&instance);
+registry.construction<position>().disconnect<&my_class::member>(&instance);
 ```
 ```
 
 
-The `on_replace` member function returns instead a sink object to which to
-connect listeners that are triggered when components are explicitly replaced.
-To be notified when components are destroyed, use the `on_remove` member
+To be notified when components are destroyed, use the `destruction` member
 function instead.
 function instead.
 
 
-The function type of a listener is the same in all cases and should be
+The function type of a listener is the same in both the cases and should be
 equivalent to:
 equivalent to:
 
 
 ```cpp
 ```cpp
@@ -355,7 +352,6 @@ In other terms, a listener is provided with the registry that triggered the
 notification and the entity affected by the change. Note also that:
 notification and the entity affected by the change. Note also that:
 
 
 * Listeners are invoked **after** components have been assigned to entities.
 * Listeners are invoked **after** components have been assigned to entities.
-* Listeners are invoked **after** components have been replaced for entities.
 * Listeners are invoked **before** components have been removed from entities.
 * Listeners are invoked **before** components have been removed from entities.
 * The order of invocation of the listeners isn't guaranteed in any case.
 * The order of invocation of the listeners isn't guaranteed in any case.
 
 
@@ -364,8 +360,6 @@ particular:
 
 
 * Connecting and disconnecting other functions from within the body of a
 * Connecting and disconnecting other functions from within the body of a
   listener should be avoided. It can lead to undefined behavior in some cases.
   listener should be avoided. It can lead to undefined behavior in some cases.
-* Replacing components from within the body of a listener that observes changes
-  on entities should be avoided. Intuitively, it could trigger an infinite loop.
 * Assigning and removing components from within the body of a listener that
 * Assigning and removing components from within the body of a listener that
   observes the destruction of instances of a given type should be avoided. It
   observes the destruction of instances of a given type should be avoided. It
   can lead to undefined behavior in some cases. This type of listeners is
   can lead to undefined behavior in some cases. This type of listeners is
@@ -784,7 +778,7 @@ The following adds components `a_type` and `another_type` whenever `my_type` is
 assigned to an entity:
 assigned to an entity:
 
 
 ```cpp
 ```cpp
-entt::connnect<a_type, another_type>(registry.on_assign<my_type>());
+entt::connnect<a_type, another_type>(registry.construction<my_type>());
 ```
 ```
 
 
 A component is assigned to an entity and thus default initialized only in case
 A component is assigned to an entity and thus default initialized only in case
@@ -793,7 +787,7 @@ be overriden.<br/>
 A dependency can easily be broken by means of the following function template:
 A dependency can easily be broken by means of the following function template:
 
 
 ```cpp
 ```cpp
-entt::disconnect<a_type, another_type>(registry.on_assign<my_type>());
+entt::disconnect<a_type, another_type>(registry.construction<my_type>());
 ```
 ```
 
 
 ### Tags
 ### Tags

+ 25 - 60
src/entt/entity/registry.hpp

@@ -68,7 +68,7 @@ class basic_registry {
         template<typename... Args>
         template<typename... Args>
         Component & construct(Entity entt, Args &&... args) {
         Component & construct(Entity entt, Args &&... args) {
             auto &component = sparse_set<Entity, Component>::construct(entt, std::forward<Args>(args)...);
             auto &component = sparse_set<Entity, Component>::construct(entt, std::forward<Args>(args)...);
-            on_assign.publish(*owner, entt);
+            construction.publish(*owner, entt);
             return component;
             return component;
         }
         }
 
 
@@ -76,9 +76,9 @@ class basic_registry {
         Component * batch(It first, It last) {
         Component * batch(It first, It last) {
             auto *component = sparse_set<Entity, Component>::batch(first, last);
             auto *component = sparse_set<Entity, Component>::batch(first, last);
 
 
-            if(!on_assign.empty()) {
+            if(!construction.empty()) {
                 std::for_each(first, last, [this](const auto entt) {
                 std::for_each(first, last, [this](const auto entt) {
-                    on_assign.publish(*owner, entt);
+                    construction.publish(*owner, entt);
                 });
                 });
             }
             }
 
 
@@ -86,21 +86,12 @@ class basic_registry {
         }
         }
 
 
         void destroy(Entity entt) override {
         void destroy(Entity entt) override {
-            on_destroy.publish(*owner, entt);
+            destruction.publish(*owner, entt);
             sparse_set<Entity, Component>::destroy(entt);
             sparse_set<Entity, Component>::destroy(entt);
         }
         }
 
 
-        template<typename... Args>
-        Component & replace(const Entity entt, Args &&... args) {
-            auto &component = sparse_set<Entity, Component>::get(entt);
-            component = std::decay_t<Component>{std::forward<Args>(args)...};
-            on_replace.publish(*owner, entt);
-            return component;
-        }
-
-        signal_type on_assign;
-        signal_type on_destroy;
-        signal_type on_replace;
+        signal_type construction;
+        signal_type destruction;
         basic_registry *owner;
         basic_registry *owner;
     };
     };
 
 
@@ -864,7 +855,7 @@ public:
      */
      */
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & replace(const entity_type entity, Args &&... args) {
     Component & replace(const entity_type entity, Args &&... args) {
-        return assure<Component>()->replace(entity, std::forward<Args>(args)...);
+        return pool<Component>()->get(entity) = Component{std::forward<Args>(args)...};
     }
     }
 
 
     /**
     /**
@@ -896,9 +887,10 @@ public:
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & assign_or_replace(const entity_type entity, Args &&... args) {
     Component & assign_or_replace(const entity_type entity, Args &&... args) {
         auto *cpool = assure<Component>();
         auto *cpool = assure<Component>();
+        auto *component = cpool->try_get(entity);
 
 
-        return cpool->has(entity)
-                ? cpool->replace(entity, std::forward<Args>(args)...)
+        return component
+                ? (*component = Component{std::forward<Args>(args)...})
                 : cpool->construct(entity, std::forward<Args>(args)...);
                 : cpool->construct(entity, std::forward<Args>(args)...);
     }
     }
 
 
@@ -926,8 +918,8 @@ public:
      * @return A temporary sink object.
      * @return A temporary sink object.
      */
      */
     template<typename Component>
     template<typename Component>
-    sink_type on_assign() ENTT_NOEXCEPT {
-        return assure<Component>()->on_assign.sink();
+    sink_type construction() ENTT_NOEXCEPT {
+        return assure<Component>()->construction.sink();
     }
     }
 
 
     /**
     /**
@@ -954,35 +946,8 @@ public:
      * @return A temporary sink object.
      * @return A temporary sink object.
      */
      */
     template<typename Component>
     template<typename Component>
-    sink_type on_remove() ENTT_NOEXCEPT {
-        return assure<Component>()->on_destroy.sink();
-    }
-
-    /**
-     * @brief Returns a sink object for the given component.
-     *
-     * A sink is an opaque object used to connect listeners to components.<br/>
-     * The sink returned by this function can be used to receive notifications
-     * whenever an instance of the given component is explicitly replaced.
-     *
-     * The function type for a listener is equivalent to:
-     * @code{.cpp}
-     * void(registry<Entity> &, Entity);
-     * @endcode
-     *
-     * Listeners are invoked **after** the component has been replaced. The
-     * order of invocation of the listeners isn't guaranteed.<br/>
-     * Note also that the greater the number of listeners, the greater the
-     * performance hit when a component is replaced.
-     *
-     * @sa sink
-     *
-     * @tparam Component Type of component of which to get the sink.
-     * @return A temporary sink object.
-     */
-    template<typename Component>
-    sink_type on_replace() ENTT_NOEXCEPT {
-        return assure<Component>()->on_replace.sink();
+    sink_type destruction() ENTT_NOEXCEPT {
+        return assure<Component>()->destruction.sink();
     }
     }
 
 
     /**
     /**
@@ -1115,7 +1080,7 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     void reset() {
     void reset() {
-        if(auto *cpool = assure<Component>(); cpool->on_destroy.empty()) {
+        if(auto *cpool = assure<Component>(); cpool->destruction.empty()) {
             // no group set, otherwise the signal wouldn't be empty
             // no group set, otherwise the signal wouldn't be empty
             cpool->reset();
             cpool->reset();
         } else {
         } else {
@@ -1336,11 +1301,11 @@ public:
                 auto *curr = static_cast<group_type *>(gdata.data.get());
                 auto *curr = static_cast<group_type *>(gdata.data.get());
                 const auto cpools = std::make_tuple(assure<Get>()..., assure<Exclude>()...);
                 const auto cpools = std::make_tuple(assure<Get>()..., assure<Exclude>()...);
 
 
-                (std::get<pool_type<Get> *>(cpools)->on_destroy.sink().template connect<&group_type::destroy_if>(curr), ...);
-                (std::get<pool_type<Get> *>(cpools)->on_assign.sink().template connect<&group_type::template construct_if<0>>(curr), ...);
+                (std::get<pool_type<Get> *>(cpools)->destruction.sink().template connect<&group_type::destroy_if>(curr), ...);
+                (std::get<pool_type<Get> *>(cpools)->construction.sink().template connect<&group_type::template construct_if<0>>(curr), ...);
 
 
-                (std::get<pool_type<Exclude> *>(cpools)->on_destroy.sink().template connect<&group_type::template construct_if<1>>(curr), ...);
-                (std::get<pool_type<Exclude> *>(cpools)->on_assign.sink().template connect<&group_type::destroy_if>(curr), ...);
+                (std::get<pool_type<Exclude> *>(cpools)->destruction.sink().template connect<&group_type::template construct_if<1>>(curr), ...);
+                (std::get<pool_type<Exclude> *>(cpools)->construction.sink().template connect<&group_type::destroy_if>(curr), ...);
 
 
                 for(const auto entity: view<Get...>()) {
                 for(const auto entity: view<Get...>()) {
                     if(!(has<Exclude>(entity) || ...)) {
                     if(!(has<Exclude>(entity) || ...)) {
@@ -1374,14 +1339,14 @@ public:
                 auto *curr = static_cast<group_type *>(gdata.data.get());
                 auto *curr = static_cast<group_type *>(gdata.data.get());
                 const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
                 const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
 
 
-                (std::get<pool_type<Owned> *>(cpools)->on_assign.sink().template connect<&group_type::template induce_if<0>>(curr), ...);
-                (std::get<pool_type<Owned> *>(cpools)->on_destroy.sink().template connect<&group_type::discard_if>(curr), ...);
+                (std::get<pool_type<Owned> *>(cpools)->construction.sink().template connect<&group_type::template induce_if<0>>(curr), ...);
+                (std::get<pool_type<Owned> *>(cpools)->destruction.sink().template connect<&group_type::discard_if>(curr), ...);
 
 
-                (std::get<pool_type<Get> *>(cpools)->on_assign.sink().template connect<&group_type::template induce_if<0>>(curr), ...);
-                (std::get<pool_type<Get> *>(cpools)->on_destroy.sink().template connect<&group_type::discard_if>(curr), ...);
+                (std::get<pool_type<Get> *>(cpools)->construction.sink().template connect<&group_type::template induce_if<0>>(curr), ...);
+                (std::get<pool_type<Get> *>(cpools)->destruction.sink().template connect<&group_type::discard_if>(curr), ...);
 
 
-                (std::get<pool_type<Exclude> *>(cpools)->on_destroy.sink().template connect<&group_type::template induce_if<1>>(curr), ...);
-                (std::get<pool_type<Exclude> *>(cpools)->on_assign.sink().template connect<&group_type::discard_if>(curr), ...);
+                (std::get<pool_type<Exclude> *>(cpools)->destruction.sink().template connect<&group_type::template induce_if<1>>(curr), ...);
+                (std::get<pool_type<Exclude> *>(cpools)->construction.sink().template connect<&group_type::discard_if>(curr), ...);
 
 
                 const auto *cpool = std::min({ static_cast<sparse_set<entity_type> *>(std::get<pool_type<Owned> *>(cpools))... }, [](const auto *lhs, const auto *rhs) {
                 const auto *cpool = std::min({ static_cast<sparse_set<entity_type> *>(std::get<pool_type<Owned> *>(cpools))... }, [](const auto *lhs, const auto *rhs) {
                     return lhs->size() < rhs->size();
                     return lhs->size() < rhs->size();

+ 4 - 4
test/entt/entity/helper.cpp

@@ -25,7 +25,7 @@ TEST(Helper, AsGroup) {
 TEST(Helper, Dependency) {
 TEST(Helper, Dependency) {
     entt::registry registry;
     entt::registry registry;
     const auto entity = registry.create();
     const auto entity = registry.create();
-    entt::connect<double, float>(registry.on_assign<int>());
+    entt::connect<double, float>(registry.construction<int>());
 
 
     ASSERT_FALSE(registry.has<double>(entity));
     ASSERT_FALSE(registry.has<double>(entity));
     ASSERT_FALSE(registry.has<float>(entity));
     ASSERT_FALSE(registry.has<float>(entity));
@@ -61,7 +61,7 @@ TEST(Helper, Dependency) {
     registry.remove<int>(entity);
     registry.remove<int>(entity);
     registry.remove<double>(entity);
     registry.remove<double>(entity);
     registry.remove<float>(entity);
     registry.remove<float>(entity);
-    entt::disconnect<double, float>(registry.on_assign<int>());
+    entt::disconnect<double, float>(registry.construction<int>());
     registry.assign<int>(entity);
     registry.assign<int>(entity);
 
 
     ASSERT_FALSE(registry.has<double>(entity));
     ASSERT_FALSE(registry.has<double>(entity));
@@ -70,8 +70,8 @@ TEST(Helper, Dependency) {
 
 
 TEST(Dependency, MultipleListenersOnTheSameType) {
 TEST(Dependency, MultipleListenersOnTheSameType) {
     entt::registry registry;
     entt::registry registry;
-    entt::connect<double>(registry.on_assign<int>());
-    entt::connect<char>(registry.on_assign<int>());
+    entt::connect<double>(registry.construction<int>());
+    entt::connect<char>(registry.construction<int>());
 
 
     const auto entity = registry.create();
     const auto entity = registry.create();
     registry.assign<int>(entity);
     registry.assign<int>(entity);

+ 10 - 16
test/entt/entity/registry.cpp

@@ -854,9 +854,8 @@ TEST(Registry, Signals) {
     entt::registry registry;
     entt::registry registry;
     listener listener;
     listener listener;
 
 
-    registry.on_assign<int>().connect<&listener::incr<int>>(&listener);
-    registry.on_remove<int>().connect<&listener::decr<int>>(&listener);
-    registry.on_replace<int>().connect<&listener::incr<int>>(&listener);
+    registry.construction<int>().connect<&listener::incr<int>>(&listener);
+    registry.destruction<int>().connect<&listener::decr<int>>(&listener);
 
 
     auto e0 = registry.create();
     auto e0 = registry.create();
     auto e1 = registry.create();
     auto e1 = registry.create();
@@ -872,20 +871,20 @@ TEST(Registry, Signals) {
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 
 
-    registry.on_remove<int>().disconnect<&listener::decr<int>>(&listener);
+    registry.destruction<int>().disconnect<&listener::decr<int>>(&listener);
     registry.remove<int>(e1);
     registry.remove<int>(e1);
 
 
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 
 
-    registry.on_assign<int>().disconnect<&listener::incr<int>>(&listener);
+    registry.construction<int>().disconnect<&listener::incr<int>>(&listener);
     registry.assign<int>(e1);
     registry.assign<int>(e1);
 
 
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 
 
-    registry.on_assign<int>().connect<&listener::incr<int>>(&listener);
-    registry.on_remove<int>().connect<&listener::decr<int>>(&listener);
+    registry.construction<int>().connect<&listener::incr<int>>(&listener);
+    registry.destruction<int>().connect<&listener::decr<int>>(&listener);
     registry.assign<int>(e0);
     registry.assign<int>(e0);
     registry.reset<int>(e1);
     registry.reset<int>(e1);
 
 
@@ -912,12 +911,7 @@ TEST(Registry, Signals) {
 
 
     registry.assign_or_replace<int>(e0);
     registry.assign_or_replace<int>(e0);
 
 
-    ASSERT_EQ(listener.counter, 2);
-    ASSERT_EQ(listener.last, e0);
-
-    registry.replace<int>(e0);
-
-    ASSERT_EQ(listener.counter, 3);
+    ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 }
 }
 
 
@@ -1063,7 +1057,7 @@ TEST(Registry, CreateManyEntitiesWithComponentsAtOnceWithListener) {
     entt::entity entities[3];
     entt::entity entities[3];
     listener listener;
     listener listener;
 
 
-    registry.on_assign<int>().connect<&listener::incr<int>>(&listener);
+    registry.construction<int>().connect<&listener::incr<int>>(&listener);
     registry.create<int, char>(std::begin(entities), std::end(entities));
     registry.create<int, char>(std::begin(entities), std::end(entities));
 
 
     ASSERT_EQ(listener.counter, 3);
     ASSERT_EQ(listener.counter, 3);
@@ -1240,8 +1234,8 @@ TEST(Registry, Clone) {
     ASSERT_NE(e1, entity);
     ASSERT_NE(e1, entity);
     ASSERT_EQ(registry.entity(e1), registry.entity(entity));
     ASSERT_EQ(registry.entity(e1), registry.entity(entity));
 
 
-    registry.on_assign<char>().connect<&listener::incr<char>>(&listener);
-    registry.on_remove<char>().connect<&listener::decr<char>>(&listener);
+    registry.construction<char>().connect<&listener::incr<char>>(&listener);
+    registry.destruction<char>().connect<&listener::decr<char>>(&listener);
     registry.assign<char>(entity, 'e');
     registry.assign<char>(entity, 'e');
     registry.assign<char>(e0, '0');
     registry.assign<char>(e0, '0');
     registry.remove<char>(e0);
     registry.remove<char>(e0);