#ifndef ENTT_ENTITY_OBSERVER_HPP #define ENTT_ENTITY_OBSERVER_HPP #include #include #include #include #include #include "../config/config.h" #include "../core/type_traits.hpp" #include "../signal/delegate.hpp" #include "entity.hpp" #include "fwd.hpp" #include "registry.hpp" #include "storage.hpp" #include "utility.hpp" namespace entt { /*! @brief Grouping matcher. */ template struct matcher {}; /** * @brief Collector. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template struct basic_collector; /** * @brief Collector. * * A collector contains a set of rules (literally, matchers) to use to track * entities.
* Its main purpose is to generate a descriptor that allows an observer to know * how to connect to a registry. */ template<> struct basic_collector<> { /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { return basic_collector, type_list<>, type_list, AllOf...>>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() ENTT_NOEXCEPT { return basic_collector, type_list<>, AnyOf>>{}; } }; /** * @brief Collector. * @copydetails basic_collector<> * @tparam Reject Untracked types used to filter out entities. * @tparam Require Untracked types required by the matcher. * @tparam Rule Specific details of the current matcher. * @tparam Other Other matchers. */ template struct basic_collector, type_list, Rule...>, Other...> { /*! @brief Current matcher. */ using current_type = matcher, type_list, Rule...>; /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() ENTT_NOEXCEPT { return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; } /** * @brief Updates the filter of the last added matcher. * @tparam AllOf Types of components required by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { using extended_type = matcher, type_list, Rule...>; return basic_collector{}; } }; /*! @brief Variable template used to ease the definition of collectors. */ inline constexpr basic_collector<> collector{}; /** * @brief Observer. * * An observer returns all the entities and only the entities that fit the * requirements of at least one matcher. Moreover, it's guaranteed that the * entity list is tightly packed in memory for fast iterations.
* In general, observers don't stay true to the order of any set of components. * * Observers work mainly with two types of matchers, provided through a * collector: * * * Observing matcher: an observer will return at least all the living entities * for which one or more of the given components have been updated and not yet * destroyed. * * Grouping matcher: an observer will return at least all the living entities * that would have entered the given group if it existed and that would have * not yet left it. * * If an entity respects the requirements of multiple matchers, it will be * returned once and only once by the observer in any case. * * Matchers support also filtering by means of a _where_ clause that accepts * both a list of types and an exclusion list.
* Whenever a matcher finds that an entity matches its requirements, the * condition of the filter is verified before to register the entity itself. * Moreover, a registered entity isn't returned by the observer if the condition * set by the filter is broken in the meantime. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @warning * Lifetime of an observer doesn't necessarily have to overcome that of the * registry to which it is connected. However, the observer must be disconnected * from the registry before being destroyed to avoid crashes due to dangling * pointers. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_observer { using payload_type = std::uint32_t; template struct matcher_handler; template struct matcher_handler, type_list, AnyOf>> { template static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { if(reg.template all_of(entt) && !reg.template any_of(entt)) { if(!obs.storage.contains(entt)) { obs.storage.emplace(entt); } obs.storage.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { obs.storage.erase(entt); } } template static void connect(basic_observer &obs, basic_registry ®) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); reg.template on_update().template connect<&maybe_valid_if>(obs); reg.template on_destroy().template connect<&discard_if>(obs); } static void disconnect(basic_observer &obs, basic_registry ®) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); reg.template on_update().disconnect(obs); reg.template on_destroy().disconnect(obs); } }; template struct matcher_handler, type_list, type_list, AllOf...>> { template static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { auto condition = [®, entt]() { if constexpr(sizeof...(Ignore) == 0) { return reg.template all_of(entt) && !reg.template any_of(entt); } else { return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); } }; if(condition()) { if(!obs.storage.contains(entt)) { obs.storage.emplace(entt); } obs.storage.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { obs.storage.erase(entt); } } template static void connect(basic_observer &obs, basic_registry ®) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); } static void disconnect(basic_observer &obs, basic_registry ®) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); } }; template static void disconnect(basic_registry ®, basic_observer &obs) { (matcher_handler::disconnect(obs, reg), ...); } template void connect(basic_registry ®, std::index_sequence) { static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); (matcher_handler::template connect(*this, reg), ...); release.template connect<&basic_observer::disconnect>(reg); } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_sparse_set::iterator; /*! @brief Default constructor. */ basic_observer() : release{}, storage{} {} /*! @brief Default copy constructor, deleted on purpose. */ basic_observer(const basic_observer &) = delete; /*! @brief Default move constructor, deleted on purpose. */ basic_observer(basic_observer &&) = delete; /** * @brief Creates an observer and connects it to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template basic_observer(basic_registry ®, basic_collector) : basic_observer{} { connect(reg, std::index_sequence_for{}); } /*! @brief Default destructor. */ ~basic_observer() = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This observer. */ basic_observer &operator=(const basic_observer &) = delete; /** * @brief Default move assignment operator, deleted on purpose. * @return This observer. */ basic_observer &operator=(basic_observer &&) = delete; /** * @brief Connects an observer to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template void connect(basic_registry ®, basic_collector) { disconnect(); connect(reg, std::index_sequence_for{}); storage.clear(); } /*! @brief Disconnects an observer from the registry it keeps track of. */ void disconnect() { if(release) { release(*this); release.reset(); } } /** * @brief Returns the number of elements in an observer. * @return Number of elements. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return storage.size(); } /** * @brief Checks whether an observer is empty. * @return True if the observer is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return storage.empty(); } /** * @brief Direct access to the list of entities of the observer. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @note * Entities are in the reverse order as returned by the `begin`/`end` * iterators. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { return storage.data(); } /** * @brief Returns an iterator to the first entity of the observer. * * The returned iterator points to the first entity of the observer. If the * container is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the observer. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return storage.basic_sparse_set::begin(); } /** * @brief Returns an iterator that is past the last entity of the observer. * * The returned iterator points to the entity following the last entity of * the observer. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * observer. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return storage.basic_sparse_set::end(); } /*! @brief Clears the underlying container. */ void clear() ENTT_NOEXCEPT { storage.clear(); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity.
* The signature of the function must be equivalent to the following form: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(const auto entity: *this) { func(entity); } } /** * @brief Iterates entities and applies the given function object to them, * then clears the observer. * * @sa each * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) { std::as_const(*this).each(std::move(func)); clear(); } private: delegate release; basic_storage storage; }; } // namespace entt #endif