From 3bf42c6ff3805a0d42bbc661794a95ff31bedc26 Mon Sep 17 00:00:00 2001 From: untodesu Date: Sat, 15 Mar 2025 16:22:09 +0500 Subject: Add whatever I was working on for the last month --- deps/include/entt/entity/observer.hpp | 438 ++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 deps/include/entt/entity/observer.hpp (limited to 'deps/include/entt/entity/observer.hpp') diff --git a/deps/include/entt/entity/observer.hpp b/deps/include/entt/entity/observer.hpp new file mode 100644 index 0000000..5698723 --- /dev/null +++ b/deps/include/entt/entity/observer.hpp @@ -0,0 +1,438 @@ +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include +#include +#include +#include +#include +#include "../core/type_traits.hpp" +#include "fwd.hpp" +#include "storage.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 elements tracked by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = exclude_t{}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of element for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() 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 elements tracked by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = exclude_t{}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of element for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() noexcept { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of elements required by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = exclude_t{}) 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 elements. + * + * 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 elements 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 elements are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given elements 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 elements in any way + * invalidates all the iterators. + * + * @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 Registry Basic registry type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_observer { + using mask_type = std::uint64_t; + using storage_type = basic_storage::template rebind_alloc>; + + template + static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) { + if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) { + storage.erase(entt); + } + } + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { + if(parent.template all_of(entt) && !parent.template any_of(entt)) { + if(!storage.contains(entt)) { + storage.emplace(entt); + } + + storage.get(entt) |= (1 << Index); + } + } + + template + static void connect(storage_type &storage, Registry &parent) { + (parent.template on_destroy().template connect<&discard_if>(storage), ...); + (parent.template on_construct().template connect<&discard_if>(storage), ...); + parent.template on_update().template connect<&maybe_valid_if>(storage); + parent.template on_destroy().template connect<&discard_if>(storage); + } + + static void disconnect(storage_type &storage, Registry &parent) { + (parent.template on_destroy().disconnect(&storage), ...); + (parent.template on_construct().disconnect(&storage), ...); + parent.template on_update().disconnect(&storage); + parent.template on_destroy().disconnect(&storage); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { + bool guard{}; + + if constexpr(sizeof...(Ignore) == 0) { + guard = parent.template all_of(entt) && !parent.template any_of(entt); + } else { + guard = parent.template all_of(entt) && ((std::is_same_v || !parent.template any_of(entt)) && ...) && !parent.template any_of(entt); + } + + if(guard) { + if(!storage.contains(entt)) { + storage.emplace(entt); + } + + storage.get(entt) |= (1 << Index); + } + } + + template + static void connect(storage_type &storage, Registry &parent) { + (parent.template on_destroy().template connect<&discard_if>(storage), ...); + (parent.template on_construct().template connect<&discard_if>(storage), ...); + (parent.template on_construct().template connect<&maybe_valid_if>(storage), ...); + (parent.template on_destroy().template connect<&maybe_valid_if>(storage), ...); + (parent.template on_destroy().template connect<&discard_if>(storage), ...); + (parent.template on_construct().template connect<&discard_if>(storage), ...); + } + + static void disconnect(storage_type &storage, Registry &parent) { + (parent.template on_destroy().disconnect(&storage), ...); + (parent.template on_construct().disconnect(&storage), ...); + (parent.template on_construct().disconnect(&storage), ...); + (parent.template on_destroy().disconnect(&storage), ...); + (parent.template on_destroy().disconnect(&storage), ...); + (parent.template on_construct().disconnect(&storage), ...); + } + }; + + template + static void disconnect(Registry &parent, storage_type &storage) { + (matcher_handler::disconnect(storage, parent), ...); + } + + template + static void connect(Registry &parent, storage_type &storage, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(storage, parent), ...); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename registry_type::common_type::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : basic_observer{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_observer(const allocator_type &allocator) + : release{}, + parent{}, + storage{allocator} {} + + /*! @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. + * @param allocator The allocator to use. + */ + template + basic_observer(registry_type ®, basic_collector, const allocator_type &allocator = allocator_type{}) + : release{&basic_observer::disconnect}, + parent{®}, + storage{allocator} { + connect(reg, storage, std::index_sequence_for{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() noexcept = 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(registry_type ®, basic_collector) { + disconnect(); + storage.clear(); + + parent = ® + release = &basic_observer::disconnect; + connect(reg, storage, std::index_sequence_for{}); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*parent, storage); + release = nullptr; + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return storage.size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const 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 noexcept { + return storage.data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * If the observer is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const noexcept { + return storage.storage_type::base_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const noexcept { + return storage.storage_type::base_type::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() 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: + void (*release)(registry_type &, storage_type &); + registry_type *parent; + storage_type storage; +}; + +} // namespace entt + +#endif -- cgit