From 5815549f7d30634dc71a58e621b90064cbc03670 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 4 Apr 2020 20:26:02 +0200 Subject: [PATCH] [Scene] Added. [external/entt] Added. --- external/entt/config/config.h | 16 + external/entt/core/algorithm.hpp | 111 ++ external/entt/core/family.hpp | 53 + external/entt/core/hashed_string.hpp | 121 ++ external/entt/core/ident.hpp | 104 ++ external/entt/core/monostate.hpp | 61 + external/entt/entity/actor.hpp | 243 ++++ external/entt/entity/attachee.hpp | 230 ++++ external/entt/entity/entity.hpp | 84 ++ external/entt/entity/entt_traits.hpp | 102 ++ external/entt/entity/helper.hpp | 105 ++ external/entt/entity/prototype.hpp | 497 +++++++ external/entt/entity/registry.hpp | 1662 ++++++++++++++++++++++ external/entt/entity/snapshot.hpp | 724 ++++++++++ external/entt/entity/sparse_set.hpp | 1120 +++++++++++++++ external/entt/entity/utility.hpp | 23 + external/entt/entity/view.hpp | 1889 ++++++++++++++++++++++++++ external/entt/entt.hpp | 26 + external/entt/locator/locator.hpp | 116 ++ external/entt/process/process.hpp | 339 +++++ external/entt/process/scheduler.hpp | 311 +++++ external/entt/resource/cache.hpp | 201 +++ external/entt/resource/handle.hpp | 116 ++ external/entt/resource/loader.hpp | 62 + external/entt/signal/delegate.hpp | 166 +++ external/entt/signal/dispatcher.hpp | 188 +++ external/entt/signal/emitter.hpp | 336 +++++ external/entt/signal/sigh.hpp | 426 ++++++ resources/shaders/color.f.glsl | 14 - resources/shaders/game.f.glsl | 13 +- source/client/hud/BlockCursor.cpp | 1 + source/client/scene/Scene.cpp | 64 + source/client/scene/Scene.hpp | 54 + source/client/states/GameState.cpp | 1 - source/client/world/ClientWorld.cpp | 5 + source/client/world/ClientWorld.hpp | 5 +- 36 files changed, 9568 insertions(+), 21 deletions(-) create mode 100644 external/entt/config/config.h create mode 100644 external/entt/core/algorithm.hpp create mode 100644 external/entt/core/family.hpp create mode 100644 external/entt/core/hashed_string.hpp create mode 100644 external/entt/core/ident.hpp create mode 100644 external/entt/core/monostate.hpp create mode 100644 external/entt/entity/actor.hpp create mode 100644 external/entt/entity/attachee.hpp create mode 100644 external/entt/entity/entity.hpp create mode 100644 external/entt/entity/entt_traits.hpp create mode 100644 external/entt/entity/helper.hpp create mode 100644 external/entt/entity/prototype.hpp create mode 100644 external/entt/entity/registry.hpp create mode 100644 external/entt/entity/snapshot.hpp create mode 100644 external/entt/entity/sparse_set.hpp create mode 100644 external/entt/entity/utility.hpp create mode 100644 external/entt/entity/view.hpp create mode 100644 external/entt/entt.hpp create mode 100644 external/entt/locator/locator.hpp create mode 100644 external/entt/process/process.hpp create mode 100644 external/entt/process/scheduler.hpp create mode 100644 external/entt/resource/cache.hpp create mode 100644 external/entt/resource/handle.hpp create mode 100644 external/entt/resource/loader.hpp create mode 100644 external/entt/signal/delegate.hpp create mode 100644 external/entt/signal/dispatcher.hpp create mode 100644 external/entt/signal/emitter.hpp create mode 100644 external/entt/signal/sigh.hpp delete mode 100644 resources/shaders/color.f.glsl create mode 100644 source/client/scene/Scene.cpp create mode 100644 source/client/scene/Scene.hpp diff --git a/external/entt/config/config.h b/external/entt/config/config.h new file mode 100644 index 00000000..367798c0 --- /dev/null +++ b/external/entt/config/config.h @@ -0,0 +1,16 @@ +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +#define ENTT_NOEXCEPT noexcept +#endif // ENTT_NOEXCEPT + + +#ifndef ENTT_HS_SUFFIX +#define ENTT_HS_SUFFIX _hs +#endif // ENTT_HS_SUFFIX + + + +#endif // ENTT_CONFIG_CONFIG_H diff --git a/external/entt/core/algorithm.hpp b/external/entt/core/algorithm.hpp new file mode 100644 index 00000000..9ac0a906 --- /dev/null +++ b/external/entt/core/algorithm.hpp @@ -0,0 +1,111 @@ +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include + + +namespace entt { + + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct StdSort final { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + + +/*! @brief Function object for performing insertion sort. */ +struct InsertionSort final { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + auto it = first + 1; + + while(it != last) { + auto value = *it; + auto pre = it; + + while(pre != first && compare(value, *(pre-1))) { + *pre = *(pre-1); + --pre; + } + + *pre = value; + ++it; + } + } +}; + + +/*! @brief Function object for performing bubble sort (single iteration). */ +struct OneShotBubbleSort final { + /** + * @brief Tries to sort the elements in a range. + * + * Performs a single iteration to sort the elements in a range using the + * given binary comparison function. The range may not be completely sorted + * after running this function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first != last) { + auto it = first++; + + while(first != last) { + if(compare(*first, *it)) { + using std::swap; + std::swap(*first, *it); + } + + it = first++; + } + } + } +}; + + +} + + +#endif // ENTT_CORE_ALGORITHM_HPP diff --git a/external/entt/core/family.hpp b/external/entt/core/family.hpp new file mode 100644 index 00000000..11cf82f9 --- /dev/null +++ b/external/entt/core/family.hpp @@ -0,0 +1,53 @@ +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + + +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template +class Family { + static std::atomic identifier; + + template + static std::size_t family() ENTT_NOEXCEPT { + static const std::size_t value = identifier.fetch_add(1); + return value; + } + +public: + /*! @brief Unsigned integer type. */ + using family_type = std::size_t; + + /** + * @brief Returns an unique identifier for the given type. + * @return Statically generated unique identifier for the given type. + */ + template + inline static family_type type() ENTT_NOEXCEPT { + return family...>(); + } +}; + + +template +std::atomic Family::identifier{}; + + +} + + +#endif // ENTT_CORE_FAMILY_HPP diff --git a/external/entt/core/hashed_string.hpp b/external/entt/core/hashed_string.hpp new file mode 100644 index 00000000..bea5db0a --- /dev/null +++ b/external/entt/core/hashed_string.hpp @@ -0,0 +1,121 @@ +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @brief Zero overhead resource identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + */ +class HashedString final { + struct ConstCharWrapper final { + // non-explicit constructor on purpose + constexpr ConstCharWrapper(const char *str) ENTT_NOEXCEPT: str{str} {} + const char *str; + }; + + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr std::uint64_t helper(std::uint64_t partial, const char *str) ENTT_NOEXCEPT { + return str[0] == 0 ? partial : helper((partial^str[0])*prime, str+1); + } + +public: + /*! @brief Unsigned integer type. */ + using hash_type = std::uint64_t; + + /** + * @brief Constructs a hashed string from an array of const chars. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * HashedString sh{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ + template + constexpr HashedString(const char (&str)[N]) ENTT_NOEXCEPT + : hash{helper(offset, str)}, str{str} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const char *`. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr HashedString(ConstCharWrapper wrapper) ENTT_NOEXCEPT + : hash{helper(offset, wrapper.str)}, str{wrapper.str} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr operator const char *() const ENTT_NOEXCEPT { return str; } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return hash; } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const HashedString &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const hash_type hash; + const char *str; +}; + + +/** + * @brief Compares two hashed strings. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +constexpr bool operator!=(const HashedString &lhs, const HashedString &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::HashedString operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::HashedString{str}; +} + + +#endif // ENTT_CORE_HASHED_STRING_HPP diff --git a/external/entt/core/ident.hpp b/external/entt/core/ident.hpp new file mode 100644 index 00000000..42503d87 --- /dev/null +++ b/external/entt/core/ident.hpp @@ -0,0 +1,104 @@ +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + + +#include +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct IsPartOf; + +template +struct IsPartOf: std::conditional_t::value, std::true_type, IsPartOf> {}; + +template +struct IsPartOf: std::false_type {}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Types identifiers. + * + * Variable template used to generate identifiers at compile-time for the given + * types. Use the `get` member function to know what's the identifier associated + * to the specific type. + * + * @note + * Identifiers are constant expression and can be used in any context where such + * an expression is required. As an example: + * @code{.cpp} + * using ID = entt::Identifier; + * + * switch(aTypeIdentifier) { + * case ID::get(): + * // ... + * break; + * case ID::get(): + * // ... + * break; + * default: + * // ... + * } + * @endcode + * + * @tparam Types List of types for which to generate identifiers. + */ +template +class Identifier final { + using tuple_type = std::tuple...>; + + template + static constexpr std::size_t get(std::index_sequence) ENTT_NOEXCEPT { + static_assert(internal::IsPartOf::value, "!"); + + std::size_t max{}; + using accumulator_type = std::size_t[]; + accumulator_type accumulator = { (max = std::is_same>::value ? Indexes : max)... }; + (void)accumulator; + return max; + } + +public: + /*! @brief Unsigned integer type. */ + using identifier_type = std::size_t; + + /** + * @brief Returns the identifier associated with a given type. + * @tparam Type of which to return the identifier. + * @return The identifier associated with the given type. + */ + template + static constexpr identifier_type get() ENTT_NOEXCEPT { + return get>(std::make_index_sequence{}); + } +}; + + +} + + +#endif // ENTT_CORE_IDENT_HPP diff --git a/external/entt/core/monostate.hpp b/external/entt/core/monostate.hpp new file mode 100644 index 00000000..f0fa47fc --- /dev/null +++ b/external/entt/core/monostate.hpp @@ -0,0 +1,61 @@ +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + + +#include +#include +#include "family.hpp" +#include "hashed_string.hpp" + + +namespace entt { + + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.
+ * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template +struct Monostate { + /** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + */ + template + void operator=(Type val) const ENTT_NOEXCEPT { + Monostate::value = val; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const ENTT_NOEXCEPT { + return Monostate::value; + } + +private: + template + static std::atomic value; +}; + + +template +template +std::atomic Monostate::value{}; + + +} + + +#endif // ENTT_CORE_MONOSTATE_HPP diff --git a/external/entt/entity/actor.hpp b/external/entt/entity/actor.hpp new file mode 100644 index 00000000..435aa87b --- /dev/null +++ b/external/entt/entity/actor.hpp @@ -0,0 +1,243 @@ +#ifndef ENTT_ENTITY_ACTOR_HPP +#define ENTT_ENTITY_ACTOR_HPP + + +#include +#include +#include "../config/config.h" +#include "registry.hpp" +#include "entity.hpp" + + +namespace entt { + + +/** + * @brief Dedicated to those who aren't confident with entity-component systems. + * + * Tiny wrapper around a registry, for all those users that aren't confident + * with entity-component systems and prefer to iterate objects directly. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct Actor { + /*! @brief Type of registry used internally. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an actor by using the given registry. + * @param reg An entity-component system properly initialized. + */ + Actor(Registry ®) + : reg{®}, entt{reg.create()} + {} + + /*! @brief Default destructor. */ + virtual ~Actor() { + reg->destroy(entt); + } + + /*! @brief Copying an actor isn't allowed. */ + Actor(const Actor &) = delete; + + /** + * @brief Move constructor. + * + * After actor move construction, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + */ + Actor(Actor &&other) + : reg{other.reg}, entt{other.entt} + { + other.entt = entt::null; + } + + /*! @brief Default copy assignment operator. @return This actor. */ + Actor & operator=(const Actor &) = delete; + + /** + * @brief Move assignment operator. + * + * After actor move assignment, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + * @return This actor. + */ + Actor & operator=(Actor &&other) { + if(this != &other) { + auto tmp{std::move(other)}; + std::swap(reg, tmp.reg); + std::swap(entt, tmp.entt); + } + + return *this; + } + + /** + * @brief Assigns the given tag to an actor. + * + * A new instance of the given tag is created and initialized with the + * arguments provided (the tag must have a proper constructor or be of + * aggregate type). Then the tag is removed from its previous owner (if any) + * and assigned to the actor. + * + * @tparam Tag Type of the tag to create. + * @tparam Args Types of arguments to use to construct the tag. + * @param args Parameters to use to initialize the tag. + * @return A reference to the newly created tag. + */ + template + Tag & assign(tag_t, Args &&... args) { + return (reg->template remove(), reg->template assign(tag_t{}, entt, std::forward(args)...)); + } + + /** + * @brief Assigns the given component to an actor. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the actor.
+ * In case the actor already has a component of the given type, it's + * replaced with the new one. + * + * @tparam Component Type of the component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & assign(Args &&... args) { + return reg->template accommodate(entt, std::forward(args)...); + } + + /** + * @brief Removes the given tag from an actor. + * @tparam Tag Type of the tag to remove. + */ + template + void remove(tag_t) { + assert(has(tag_t{})); + reg->template remove(); + } + + /** + * @brief Removes the given component from an actor. + * @tparam Component Type of the component to remove. + */ + template + void remove() { + reg->template remove(entt); + } + + /** + * @brief Checks if an actor owns the given tag. + * @tparam Tag Type of the tag for which to perform the check. + * @return True if the actor owns the tag, false otherwise. + */ + template + bool has(tag_t) const ENTT_NOEXCEPT { + return (reg->template has() && (reg->template attachee() == entt)); + } + + /** + * @brief Checks if an actor has the given component. + * @tparam Component Type of the component for which to perform the check. + * @return True if the actor has the component, false otherwise. + */ + template + bool has() const ENTT_NOEXCEPT { + return reg->template has(entt); + } + + /** + * @brief Returns a reference to the given tag for an actor. + * @tparam Tag Type of the tag to get. + * @return A reference to the instance of the tag owned by the actor. + */ + template + const Tag & get(tag_t) const ENTT_NOEXCEPT { + assert(has(tag_t{})); + return reg->template get(); + } + + /** + * @brief Returns a reference to the given tag for an actor. + * @tparam Tag Type of the tag to get. + * @return A reference to the instance of the tag owned by the actor. + */ + template + inline Tag & get(tag_t) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(tag_t{})); + } + + /** + * @brief Returns a reference to the given component for an actor. + * @tparam Component Type of the component to get. + * @return A reference to the instance of the component owned by the actor. + */ + template + const Component & get() const ENTT_NOEXCEPT { + return reg->template get(entt); + } + + /** + * @brief Returns a reference to the given component for an actor. + * @tparam Component Type of the component to get. + * @return A reference to the instance of the component owned by the actor. + */ + template + inline Component & get() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get()); + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + inline const registry_type & registry() const ENTT_NOEXCEPT { + return *reg; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + inline registry_type & registry() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->registry()); + } + + /** + * @brief Returns the entity associated with an actor. + * @return The entity associated with the actor. + */ + inline entity_type entity() const ENTT_NOEXCEPT { + return entt; + } + +private: + registry_type * reg; + Entity entt; +}; + + +/** + * @brief Default actor class. + * + * The default actor is the best choice for almost all the applications.
+ * Users should have a really good reason to choose something different. + */ +using DefaultActor = Actor; + + +} + + +#endif // ENTT_ENTITY_ACTOR_HPP diff --git a/external/entt/entity/attachee.hpp b/external/entt/entity/attachee.hpp new file mode 100644 index 00000000..b972c7d3 --- /dev/null +++ b/external/entt/entity/attachee.hpp @@ -0,0 +1,230 @@ +#ifndef ENTT_ENTITY_ATTACHEE_HPP +#define ENTT_ENTITY_ATTACHEE_HPP + + +#include +#include +#include +#include "../config/config.h" +#include "entity.hpp" + + +namespace entt { + + +/** + * @brief Attachee. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class Attachee; + + +/** + * @brief Basic attachee implementation. + * + * Convenience data structure used to store single instance components. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class Attachee { +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /*! @brief Default constructor. */ + Attachee() ENTT_NOEXCEPT + : owner{null} + {} + + /*! @brief Default copy constructor. */ + Attachee(const Attachee &) = default; + /*! @brief Default move constructor. */ + Attachee(Attachee &&) = default; + + /*! @brief Default copy assignment operator. @return This attachee. */ + Attachee & operator=(const Attachee &) = default; + /*! @brief Default move assignment operator. @return This attachee. */ + Attachee & operator=(Attachee &&) = default; + + /*! @brief Default destructor. */ + virtual ~Attachee() ENTT_NOEXCEPT = default; + + /** + * @brief Returns the owner of an attachee. + * @return A valid entity identifier if an owner exists, the null entity + * identifier otherwise. + */ + inline entity_type get() const ENTT_NOEXCEPT { + return owner; + } + + /** + * @brief Assigns an entity to an attachee. + * + * @warning + * Attempting to assigns an entity to an attachee that already has an owner + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case + * the attachee already has an owner. + * + * @param entity A valid entity identifier. + */ + inline void construct(const entity_type entity) ENTT_NOEXCEPT { + assert(owner == null); + owner = entity; + } + + /** + * @brief Removes an entity from an attachee. + * + * @warning + * Attempting to free an empty attachee results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * attachee is already empty. + */ + virtual void destroy() ENTT_NOEXCEPT { + assert(owner != null); + owner = null; + } + +private: + entity_type owner; +}; + + +/** + * @brief Extended attachee implementation. + * + * This specialization of an attachee associates an object to an entity. The + * main purpose of this class is to use attachees to store tags in a Registry. + * It guarantees fast access both to the element and to the entity. + * + * @sa Attachee + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of object assigned to the entity. + */ +template +class Attachee: public Attachee { + using underlying_type = Attachee; + +public: + /*! @brief Type of the object associated to the attachee. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + + /*! @brief Default constructor. */ + Attachee() ENTT_NOEXCEPT = default; + + /*! @brief Copying an attachee isn't allowed. */ + Attachee(const Attachee &) = delete; + /*! @brief Moving an attachee isn't allowed. */ + Attachee(Attachee &&) = delete; + + /*! @brief Copying an attachee isn't allowed. @return This attachee. */ + Attachee & operator=(const Attachee &) = delete; + /*! @brief Moving an attachee isn't allowed. @return This attachee. */ + Attachee & operator=(Attachee &&) = delete; + + /*! @brief Default destructor. */ + ~Attachee() { + if(underlying_type::get() != null) { + reinterpret_cast(&storage)->~Type(); + } + } + + /** + * @brief Returns the object associated to an attachee. + * + * @warning + * Attempting to query an empty attachee results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * attachee is empty. + * + * @return The object associated to the attachee. + */ + const Type & get() const ENTT_NOEXCEPT { + assert(underlying_type::get() != null); + return *reinterpret_cast(&storage); + } + + /** + * @brief Returns the object associated to an attachee. + * + * @warning + * Attempting to query an empty attachee results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * attachee is empty. + * + * @return The object associated to the attachee. + */ + Type & get() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get()); + } + + /** + * @brief Assigns an entity to an attachee and constructs its object. + * + * @warning + * Attempting to assigns an entity to an attachee that already has an owner + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case + * the attachee already has an owner. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + * @return The object associated to the attachee. + */ + template + Type & construct(entity_type entity, Args &&... args) ENTT_NOEXCEPT { + underlying_type::construct(entity); + new (&storage) Type{std::forward(args)...}; + return *reinterpret_cast(&storage); + } + + /** + * @brief Removes an entity from an attachee and destroies its object. + * + * @warning + * Attempting to free an empty attachee results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * attachee is already empty. + */ + void destroy() ENTT_NOEXCEPT override { + reinterpret_cast(&storage)->~Type(); + underlying_type::destroy(); + } + + /** + * @brief Changes the owner of an attachee. + * + * The ownership of the attachee is transferred from one entity to another. + * + * @warning + * Attempting to transfer the ownership of an attachee that hasn't an owner + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case + * the attachee hasn't an owner yet. + * + * @param entity A valid entity identifier. + */ + void move(const entity_type entity) ENTT_NOEXCEPT { + underlying_type::destroy(); + underlying_type::construct(entity); + } + +private: + std::aligned_storage_t storage; +}; + + +} + + +#endif // ENTT_ENTITY_ATTACHEE_HPP diff --git a/external/entt/entity/entity.hpp b/external/entt/entity/entity.hpp new file mode 100644 index 00000000..4ef2265b --- /dev/null +++ b/external/entt/entity/entity.hpp @@ -0,0 +1,84 @@ +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + + +#include "../config/config.h" +#include "entt_traits.hpp" + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct Null { + explicit constexpr Null() = default; + + template + constexpr operator Entity() const ENTT_NOEXCEPT { + using traits_type = entt::entt_traits; + return traits_type::entity_mask | (traits_type::version_mask << traits_type::entity_shift); + } + + constexpr bool operator==(Null) const ENTT_NOEXCEPT { + return true; + } + + constexpr bool operator!=(Null) const ENTT_NOEXCEPT { + return false; + } + + template + constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + return entity == static_cast(*this); + } + + template + constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + return entity != static_cast(*this); + } +}; + + +template +constexpr bool operator==(const Entity entity, Null null) ENTT_NOEXCEPT { + return null == entity; +} + + +template +constexpr bool operator!=(const Entity entity, Null null) ENTT_NOEXCEPT { + return null != entity; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Null entity. + * + * There exist implicit conversions from this variable to entity identifiers of + * any allowed type. Similarly, there exist comparision operators between the + * null entity and any other entity identifier. + */ +constexpr auto null = internal::Null{}; + + +} + + +#endif // ENTT_ENTITY_ENTITY_HPP diff --git a/external/entt/entity/entt_traits.hpp b/external/entt/entity/entt_traits.hpp new file mode 100644 index 00000000..df2cb60d --- /dev/null +++ b/external/entt/entity/entt_traits.hpp @@ -0,0 +1,102 @@ +#ifndef ENTT_ENTITY_ENTT_TRAITS_HPP +#define ENTT_ENTITY_ENTT_TRAITS_HPP + + +#include + + +namespace entt { + + +/** + * @brief Entity traits. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is an accepted entity type. + */ +template +struct entt_traits; + + +/** + * @brief Entity traits for a 16 bits entity identifier. + * + * A 16 bits entity identifier guarantees: + * + * * 12 bits for the entity number (up to 4k entities). + * * 4 bit for the version (resets in [0-15]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint16_t; + /*! @brief Underlying version type. */ + using version_type = std::uint8_t; + /*! @brief Difference type. */ + using difference_type = std::int32_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint16_t entity_mask = 0xFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint16_t version_mask = 0xF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 12; +}; + + +/** + * @brief Entity traits for a 32 bits entity identifier. + * + * A 32 bits entity identifier guarantees: + * + * * 20 bits for the entity number (suitable for almost all the games). + * * 12 bit for the version (resets in [0-4095]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint32_t; + /*! @brief Underlying version type. */ + using version_type = std::uint16_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint32_t entity_mask = 0xFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint32_t version_mask = 0xFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 20; +}; + + +/** + * @brief Entity traits for a 64 bits entity identifier. + * + * A 64 bits entity identifier guarantees: + * + * * 32 bits for the entity number (an indecently large number). + * * 32 bit for the version (an indecently large number). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint64_t; + /*! @brief Underlying version type. */ + using version_type = std::uint32_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint64_t entity_mask = 0xFFFFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint64_t version_mask = 0xFFFFFFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 32; +}; + + +} + + +#endif // ENTT_ENTITY_ENTT_TRAITS_HPP diff --git a/external/entt/entity/helper.hpp b/external/entt/entity/helper.hpp new file mode 100644 index 00000000..90eab876 --- /dev/null +++ b/external/entt/entity/helper.hpp @@ -0,0 +1,105 @@ +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + + +#include +#include "../core/hashed_string.hpp" +#include "../signal/sigh.hpp" +#include "registry.hpp" +#include "utility.hpp" + + +namespace entt { + + +/** + * @brief Dependency function prototype. + * + * A _dependency function_ is a built-in listener to use to automatically assign + * components to an entity when a type has a dependency on some other types. + * + * This is a prototype function to use to create dependencies.
+ * It isn't intended for direct use, although nothing forbids using it freely. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components to assign to an entity if triggered. + * @param registry A valid reference to a registry. + * @param entity A valid entity identifier. + */ +template +void dependency(Registry ®istry, const Entity entity) { + using accumulator_type = int[]; + accumulator_type accumulator = { ((registry.template has(entity) ? void() : (registry.template assign(entity), void())), 0)... }; + (void)accumulator; +} + + +/** + * @brief Connects a dependency function to the given sink. + * + * A _dependency function_ is a built-in listener to use to automatically assign + * components to an entity when a type has a dependency on some other types. + * + * The following adds components `AType` and `AnotherType` whenever `MyType` is + * assigned to an entity: + * @code{.cpp} + * entt::DefaultRegistry registry; + * entt::connect(registry.construction()); + * @endcode + * + * @tparam Dependency Types of components to assign to an entity if triggered. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param sink A sink object properly initialized. + */ +template +inline void connect(Sink &, const Entity)> sink) { + sink.template connect>(); +} + + +/** + * @brief Disconnects a dependency function from the given sink. + * + * A _dependency function_ is a built-in listener to use to automatically assign + * components to an entity when a type has a dependency on some other types. + * + * The following breaks the dependency between the component `MyType` and the + * components `AType` and `AnotherType`: + * @code{.cpp} + * entt::DefaultRegistry registry; + * entt::disconnect(registry.construction()); + * @endcode + * + * @tparam Dependency Types of components used to create the dependency. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param sink A sink object properly initialized. + */ +template +inline void disconnect(Sink &, const Entity)> sink) { + sink.template disconnect>(); +} + + +/** + * @brief Alias template to ease the assignment of labels to entities. + * + * If used in combination with hashed strings, it simplifies the assignment of + * labels to entities and the use of labels in general where a type would be + * required otherwise.
+ * As an example and where the user defined literal for hashed strings hasn't + * been changed: + * @code{.cpp} + * entt::DefaultRegistry registry; + * registry.assign>(entity); + * @endcode + * + * @tparam Value The numeric representation of an instance of hashed string. + */ +template +using label = std::integral_constant; + + +} + + +#endif // ENTT_ENTITY_HELPER_HPP diff --git a/external/entt/entity/prototype.hpp b/external/entt/entity/prototype.hpp new file mode 100644 index 00000000..82c6bcb6 --- /dev/null +++ b/external/entt/entity/prototype.hpp @@ -0,0 +1,497 @@ +#ifndef ENTT_ENTITY_PROTOTYPE_HPP +#define ENTT_ENTITY_PROTOTYPE_HPP + + +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "registry.hpp" +#include "entity.hpp" + + +namespace entt { + + +/** + * @brief Prototype container for _concepts_. + * + * A prototype is used to define a _concept_ in terms of components.
+ * Prototypes act as templates for those specific types of an application which + * users would otherwise define through a series of component assignments to + * entities. In other words, prototypes can be used to assign components to + * entities of a registry at once. + * + * @note + * Components used along with prototypes must be copy constructible. Prototypes + * wrap component types with custom types, so they do not interfere with other + * users of the registry they were built with. + * + * @warning + * Prototypes directly use their underlying registries to store entities and + * components for their purposes. Users must ensure that the lifetime of a + * registry and its contents exceed that of the prototypes that use it. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class Prototype final { + using basic_fn_type = void(const Prototype &, Registry &, const Entity); + using component_type = typename Registry::component_type; + + template + struct Wrapper { Component component; }; + + struct Handler { + basic_fn_type *accommodate; + basic_fn_type *assign; + }; + + void release() { + if(registry->valid(entity)) { + registry->destroy(entity); + } + } + +public: + /*! @brief Registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /** + * @brief Constructs a prototype that is bound to a given registry. + * @param registry A valid reference to a registry. + */ + Prototype(Registry ®istry) + : registry{®istry}, + entity{registry.create()} + {} + + /** + * @brief Releases all its resources. + */ + ~Prototype() { + release(); + } + + /*! @brief Copying a prototype isn't allowed. */ + Prototype(const Prototype &) = delete; + + /** + * @brief Move constructor. + * + * After prototype move construction, instances that have been moved from + * are placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + */ + Prototype(Prototype &&other) + : handlers{std::move(other.handlers)}, + registry{other.registry}, + entity{other.entity} + { + other.entity = entt::null; + } + + /*! @brief Copying a prototype isn't allowed. @return This Prototype. */ + Prototype & operator=(const Prototype &) = delete; + + /** + * @brief Move assignment operator. + * + * After prototype move assignment, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + * @return This Prototype. + */ + Prototype & operator=(Prototype &&other) { + if(this != &other) { + auto tmp{std::move(other)}; + handlers.swap(tmp.handlers); + std::swap(registry, tmp.registry); + std::swap(entity, tmp.entity); + } + + return *this; + } + + /** + * @brief Assigns to or replaces the given component of a prototype. + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & set(Args &&... args) { + basic_fn_type *accommodate = [](const Prototype &prototype, Registry &other, const Entity dst) { + const auto &wrapper = prototype.registry->template get>(prototype.entity); + other.template accommodate(dst, wrapper.component); + }; + + basic_fn_type *assign = [](const Prototype &prototype, Registry &other, const Entity dst) { + if(!other.template has(dst)) { + const auto &wrapper = prototype.registry->template get>(prototype.entity); + other.template assign(dst, wrapper.component); + } + }; + + handlers[registry->template type()] = Handler{accommodate, assign}; + auto &wrapper = registry->template accommodate>(entity, Component{std::forward(args)...}); + return wrapper.component; + } + + /** + * @brief Removes the given component from a prototype. + * @tparam Component Type of component to remove. + */ + template + void unset() ENTT_NOEXCEPT { + registry->template reset>(entity); + handlers.erase(registry->template type()); + } + + /** + * @brief Checks if a prototype owns all the given components. + * @tparam Component Components for which to perform the check. + * @return True if the prototype owns all the components, false otherwise. + */ + template + bool has() const ENTT_NOEXCEPT { + return registry->template has...>(entity); + } + + /** + * @brief Returns a reference to the given component. + * + * @warning + * Attempting to get a component from a prototype that doesn't own it + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * prototype doesn't own an instance of the given component. + * + * @tparam Component Type of component to get. + * @return A reference to the component owned by the prototype. + */ + template + const Component & get() const ENTT_NOEXCEPT { + return registry->template get>(entity).component; + } + + /** + * @brief Returns a reference to the given component. + * + * @warning + * Attempting to get a component from a prototype that doesn't own it + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * prototype doesn't own an instance of the given component. + * + * @tparam Component Type of component to get. + * @return A reference to the component owned by the prototype. + */ + template + inline Component & get() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get()); + } + + /** + * @brief Returns a reference to the given components. + * + * @warning + * Attempting to get components from a prototype that doesn't own them + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * prototype doesn't own instances of the given components. + * + * @tparam Component Type of components to get. + * @return References to the components owned by the prototype. + */ + template + inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple> + get() const ENTT_NOEXCEPT { + return std::tuple{get()...}; + } + + /** + * @brief Returns a reference to the given components. + * + * @warning + * Attempting to get components from a prototype that doesn't own them + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * prototype doesn't own instances of the given components. + * + * @tparam Component Type of components to get. + * @return References to the components owned by the prototype. + */ + template + inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple> + get() ENTT_NOEXCEPT { + return std::tuple{get()...}; + } + + /** + * @brief Creates a new entity using a given prototype. + * + * Utility shortcut, equivalent to the following snippet: + * + * @code{.cpp} + * const auto entity = registry.create(); + * prototype(registry, entity); + * @endcode + * + * @note + * The registry may or may not be different from the one already used by + * the prototype. There is also an overload that directly uses the + * underlying registry. + * + * @param other A valid reference to a registry. + * @return A valid entity identifier. + */ + entity_type create(registry_type &other) const { + const auto entity = other.create(); + assign(other, entity); + return entity; + } + + /** + * @brief Creates a new entity using a given prototype. + * + * Utility shortcut, equivalent to the following snippet: + * + * @code{.cpp} + * const auto entity = registry.create(); + * prototype(entity); + * @endcode + * + * @note + * This overload directly uses the underlying registry as a working space. + * Therefore, the components of the prototype and of the entity will share + * the same registry. + * + * @return A valid entity identifier. + */ + inline entity_type create() const { + return create(*registry); + } + + /** + * @brief Assigns the components of a prototype to a given entity. + * + * Assigning a prototype to an entity won't overwrite existing components + * under any circumstances.
+ * In other words, only those components that the entity doesn't own yet are + * copied over. All the other components remain unchanged. + * + * @note + * The registry may or may not be different from the one already used by + * the prototype. There is also an overload that directly uses the + * underlying registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param other A valid reference to a registry. + * @param dst A valid entity identifier. + */ + void assign(registry_type &other, const entity_type dst) const { + for(auto &handler: handlers) { + handler.second.assign(*this, other, dst); + } + } + + /** + * @brief Assigns the components of a prototype to a given entity. + * + * Assigning a prototype to an entity won't overwrite existing components + * under any circumstances.
+ * In other words, only those components that the entity doesn't own yet are + * copied over. All the other components remain unchanged. + * + * @note + * This overload directly uses the underlying registry as a working space. + * Therefore, the components of the prototype and of the entity will share + * the same registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param dst A valid entity identifier. + */ + inline void assign(const entity_type dst) const { + assign(*registry, dst); + } + + /** + * @brief Assigns or replaces the components of a prototype for an entity. + * + * Existing components are overwritten, if any. All the other components + * will be copied over to the target entity. + * + * @note + * The registry may or may not be different from the one already used by + * the prototype. There is also an overload that directly uses the + * underlying registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param other A valid reference to a registry. + * @param dst A valid entity identifier. + */ + void accommodate(registry_type &other, const entity_type dst) const { + for(auto &handler: handlers) { + handler.second.accommodate(*this, other, dst); + } + } + + /** + * @brief Assigns or replaces the components of a prototype for an entity. + * + * Existing components are overwritten, if any. All the other components + * will be copied over to the target entity. + * + * @note + * This overload directly uses the underlying registry as a working space. + * Therefore, the components of the prototype and of the entity will share + * the same registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param dst A valid entity identifier. + */ + inline void accommodate(const entity_type dst) const { + accommodate(*registry, dst); + } + + /** + * @brief Assigns the components of a prototype to an entity. + * + * Assigning a prototype to an entity won't overwrite existing components + * under any circumstances.
+ * In other words, only the components that the entity doesn't own yet are + * copied over. All the other components remain unchanged. + * + * @note + * The registry may or may not be different from the one already used by + * the prototype. There is also an overload that directly uses the + * underlying registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param other A valid reference to a registry. + * @param dst A valid entity identifier. + */ + inline void operator()(registry_type &other, const entity_type dst) const ENTT_NOEXCEPT { + assign(other, dst); + } + + /** + * @brief Assigns the components of a prototype to an entity. + * + * Assigning a prototype to an entity won't overwrite existing components + * under any circumstances.
+ * In other words, only the components that the entity doesn't own yet are + * copied over. All the other components remain unchanged. + * + * @note + * This overload directly uses the underlying registry as a working space. + * Therefore, the components of the prototype and of the entity will share + * the same registry. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param dst A valid entity identifier. + */ + inline void operator()(const entity_type dst) const ENTT_NOEXCEPT { + assign(*registry, dst); + } + + /** + * @brief Creates a new entity using a given prototype. + * + * Utility shortcut, equivalent to the following snippet: + * + * @code{.cpp} + * const auto entity = registry.create(); + * prototype(registry, entity); + * @endcode + * + * @note + * The registry may or may not be different from the one already used by + * the prototype. There is also an overload that directly uses the + * underlying registry. + * + * @param other A valid reference to a registry. + * @return A valid entity identifier. + */ + inline entity_type operator()(registry_type &other) const ENTT_NOEXCEPT { + return create(other); + } + + /** + * @brief Creates a new entity using a given prototype. + * + * Utility shortcut, equivalent to the following snippet: + * + * @code{.cpp} + * const auto entity = registry.create(); + * prototype(entity); + * @endcode + * + * @note + * This overload directly uses the underlying registry as a working space. + * Therefore, the components of the prototype and of the entity will share + * the same registry. + * + * @return A valid entity identifier. + */ + inline entity_type operator()() const ENTT_NOEXCEPT { + return create(*registry); + } + +private: + std::unordered_map handlers; + Registry *registry; + entity_type entity; +}; + + +/** + * @brief Default prototype + * + * The default prototype is the best choice for almost all the + * applications.
+ * Users should have a really good reason to choose something different. + */ +using DefaultPrototype = Prototype; + + +} + + +#endif // ENTT_ENTITY_PROTOTYPE_HPP diff --git a/external/entt/entity/registry.hpp b/external/entt/entity/registry.hpp new file mode 100644 index 00000000..4916ce30 --- /dev/null +++ b/external/entt/entity/registry.hpp @@ -0,0 +1,1662 @@ +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/algorithm.hpp" +#include "../core/family.hpp" +#include "../signal/sigh.hpp" +#include "attachee.hpp" +#include "entity.hpp" +#include "entt_traits.hpp" +#include "snapshot.hpp" +#include "sparse_set.hpp" +#include "utility.hpp" +#include "view.hpp" + + +namespace entt { + + +/** + * @brief Fast and reliable entity-component system. + * + * The registry is the core class of the entity-component framework.
+ * It stores entities and arranges pools of components on a per request basis. + * By means of a registry, users can manage entities and components and thus + * create views to iterate them. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class Registry { + using tag_family = Family; + using component_family = Family; + using handler_family = Family; + using signal_type = SigH; + using traits_type = entt_traits; + + template + struct Pool: SparseSet { + Pool(Registry *registry) ENTT_NOEXCEPT + : registry{registry} + {} + + template + Component & construct(const Entity entity, Args &&... args) { + auto &component = SparseSet::construct(entity, std::forward(args)...); + ctor.publish(*registry, entity); + return component; + } + + void destroy(const Entity entity) override { + dtor.publish(*registry, entity); + SparseSet::destroy(entity); + } + + typename signal_type::sink_type construction() ENTT_NOEXCEPT { + return ctor.sink(); + } + + typename signal_type::sink_type destruction() ENTT_NOEXCEPT { + return dtor.sink(); + } + + private: + Registry *registry; + signal_type ctor; + signal_type dtor; + }; + + template + struct Attaching: Attachee { + Attaching(Registry *registry) + : registry{registry} + {} + + template + Tag & construct(const Entity entity, Args &&... args) ENTT_NOEXCEPT { + auto &tag = Attachee::construct(entity, std::forward(args)...); + ctor.publish(*registry, entity); + return tag; + } + + void destroy() ENTT_NOEXCEPT override { + dtor.publish(*registry, Attachee::get()); + Attachee::destroy(); + } + + Entity move(const Entity entity) ENTT_NOEXCEPT { + const auto owner = Attachee::get(); + dtor.publish(*registry, owner); + Attachee::move(entity); + ctor.publish(*registry, entity); + return owner; + } + + typename signal_type::sink_type construction() ENTT_NOEXCEPT { + return ctor.sink(); + } + + typename signal_type::sink_type destruction() ENTT_NOEXCEPT { + return dtor.sink(); + } + + private: + Registry *registry; + signal_type ctor; + signal_type dtor; + }; + + template + static void creating(Registry ®istry, const Entity entity) { + if(registry.has(entity)) { + registry.handlers[Type()]->construct(entity); + } + } + + template + static void destroying(Registry ®istry, const Entity entity) { + auto &handler = *registry.handlers[handler_family::type()]; + return handler.has(entity) ? handler.destroy(entity) : void(); + } + + template + inline bool managed(tag_t) const ENTT_NOEXCEPT { + const auto ttype = tag_family::type(); + return ttype < tags.size() && tags[ttype]; + } + + template + inline bool managed() const ENTT_NOEXCEPT { + const auto ctype = component_family::type(); + return ctype < pools.size() && pools[ctype]; + } + + template + inline const Attaching & pool(tag_t) const ENTT_NOEXCEPT { + assert(managed(tag_t{})); + return static_cast &>(*tags[tag_family::type()]); + } + + template + inline Attaching & pool(tag_t) ENTT_NOEXCEPT { + return const_cast &>(const_cast(this)->pool(tag_t{})); + } + + template + inline const Pool & pool() const ENTT_NOEXCEPT { + assert(managed()); + return static_cast &>(*pools[component_family::type()]); + } + + template + inline Pool & pool() ENTT_NOEXCEPT { + return const_cast &>(const_cast(this)->pool()); + } + + template + void connect(std::index_sequence) { + pool().construction().template connect<&Registry::creating<&handler_family::type, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple>...>>(); + pool().destruction().template connect<&Registry::destroying>(); + } + + template + void connect(std::index_sequence) { + using accumulator_type = int[]; + accumulator_type accumulator = { (assure(), connect(std::make_index_sequence{}), 0)... }; + (void)accumulator; + } + + template + void disconnect(std::index_sequence) { + pool().construction().template disconnect<&Registry::creating<&handler_family::type, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple>...>>(); + pool().destruction().template disconnect<&Registry::destroying>(); + } + + template + void disconnect(std::index_sequence) { + using accumulator_type = int[]; + // if a set exists, pools have already been created for it + accumulator_type accumulator = { (disconnect(std::make_index_sequence{}), 0)... }; + (void)accumulator; + } + + template + void assure() { + const auto ctype = component_family::type(); + + if(!(ctype < pools.size())) { + pools.resize(ctype + 1); + } + + if(!pools[ctype]) { + pools[ctype] = std::make_unique>(this); + } + } + + template + void assure(tag_t) { + const auto ttype = tag_family::type(); + + if(!(ttype < tags.size())) { + tags.resize(ttype + 1); + } + + if(!tags[ttype]) { + tags[ttype] = std::make_unique>(this); + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Unsigned integer type. */ + using tag_type = typename tag_family::family_type; + /*! @brief Unsigned integer type. */ + using component_type = typename component_family::family_type; + /*! @brief Type of sink for the given component. */ + using sink_type = typename signal_type::sink_type; + + /*! @brief Default constructor. */ + Registry() = default; + + /*! @brief Copying a registry isn't allowed. */ + Registry(const Registry &) = delete; + /*! @brief Default move constructor. */ + Registry(Registry &&) = default; + + /*! @brief Copying a registry isn't allowed. @return This registry. */ + Registry & operator=(const Registry &) = delete; + /*! @brief Default move assignment operator. @return This registry. */ + Registry & operator=(Registry &&) = default; + + /** + * @brief Returns the numeric identifier of a type of tag at runtime. + * + * The given tag doesn't need to be necessarily in use. However, the + * registry could decide to prepare internal data structures for it for + * later uses.
+ * Do not use this functionality to provide numeric identifiers to types at + * runtime. + * + * @tparam Tag Type of tag to query. + * @return Runtime numeric identifier of the given type of tag. + */ + template + static tag_type type(tag_t) ENTT_NOEXCEPT { + return tag_family::type(); + } + + /** + * @brief Returns the numeric identifier of a type of component at runtime. + * + * The given component doesn't need to be necessarily in use. However, the + * registry could decide to prepare internal data structures for it for + * later uses.
+ * Do not use this functionality to provide numeric identifiers to types at + * runtime. + * + * @tparam Component Type of component to query. + * @return Runtime numeric identifier of the given type of component. + */ + template + static component_type type() ENTT_NOEXCEPT { + return component_family::type(); + } + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + size_type size() const ENTT_NOEXCEPT { + return managed() ? pool().size() : size_type{}; + } + + /** + * @brief Returns the number of entities created so far. + * @return Number of entities created so far. + */ + size_type size() const ENTT_NOEXCEPT { + return entities.size(); + } + + /** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ + size_type alive() const ENTT_NOEXCEPT { + return entities.size() - available; + } + + /** + * @brief Increases the capacity of the pool for the given component. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @tparam Component Type of component for which to reserve storage. + * @param cap Desired capacity. + */ + template + void reserve(const size_type cap) { + assure(); + pool().reserve(cap); + } + + /** + * @brief Increases the capacity of a registry in terms of entities. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + entities.reserve(cap); + } + + /** + * @brief Returns the capacity of the pool for the given component. + * @tparam Component Type of component in which one is interested. + * @return Capacity of the pool of the given component. + */ + template + size_type capacity() const ENTT_NOEXCEPT { + return managed() ? pool().capacity() : size_type{}; + } + + /** + * @brief Returns the number of entities that a registry has currently + * allocated space for. + * @return Capacity of the registry. + */ + size_type capacity() const ENTT_NOEXCEPT { + return entities.capacity(); + } + + /** + * @brief Checks whether the pool of the given component is empty. + * @tparam Component Type of component in which one is interested. + * @return True if the pool of the given component is empty, false + * otherwise. + */ + template + bool empty() const ENTT_NOEXCEPT { + return !managed() || pool().empty(); + } + + /** + * @brief Checks if there exists at least an entity still in use. + * @return True if at least an entity is still in use, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return entities.size() == available; + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use a view if you + * want to iterate entities and components in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components of the given type. + */ + template + const Component * raw() const ENTT_NOEXCEPT { + return managed() ? pool().raw() : nullptr; + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use a view if you + * want to iterate entities and components in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components of the given type. + */ + template + inline Component * raw() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->raw()); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use a view if you + * want to iterate entities and components in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + const entity_type * data() const ENTT_NOEXCEPT { + return managed() ? pool().data() : nullptr; + } + + /** + * @brief Checks if an entity identifier refers to a valid entity. + * @param entity An entity identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + bool valid(const entity_type entity) const ENTT_NOEXCEPT { + const auto pos = size_type(entity & traits_type::entity_mask); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Checks if an entity identifier refers to a valid entity. + * + * Alternative version of `valid`. It accesses the internal data structures + * without bounds checking and thus it's both unsafe and risky to use.
+ * You should not invoke directly this function unless you know exactly what + * you are doing. Prefer the `valid` member function instead. + * + * @warning + * Attempting to use an entity that doesn't belong to the registry can + * result in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * bounds violation. + * + * @param entity A valid entity identifier. + * @return True if the identifier is valid, false otherwise. + */ + bool fast(const entity_type entity) const ENTT_NOEXCEPT { + const auto pos = size_type(entity & traits_type::entity_mask); + assert(pos < entities.size()); + return (entities[pos] == entity); + } + + /** + * @brief Returns the entity identifier without the version. + * @param entity An entity identifier, either valid or not. + * @return The entity identifier without the version. + */ + entity_type entity(const entity_type entity) const ENTT_NOEXCEPT { + return entity & traits_type::entity_mask; + } + + /** + * @brief Returns the version stored along with an entity identifier. + * @param entity An entity identifier, either valid or not. + * @return The version stored along with the given entity identifier. + */ + version_type version(const entity_type entity) const ENTT_NOEXCEPT { + return version_type(entity >> traits_type::entity_shift); + } + + /** + * @brief Returns the actual version for an entity identifier. + * + * In case entity identifers are stored around, this function can be used to + * know if they are still valid or the entity has been destroyed and + * potentially recycled. + * + * @warning + * Attempting to use an entity that doesn't belong to the registry results + * in undefined behavior. An entity belongs to the registry even if it has + * been previously destroyed and/or recycled.
+ * An assertion will abort the execution at runtime in debug mode if the + * registry doesn't own the given entity. + * + * @param entity A valid entity identifier. + * @return Actual version for the given entity identifier. + */ + version_type current(const entity_type entity) const ENTT_NOEXCEPT { + const auto pos = size_type(entity & traits_type::entity_mask); + assert(pos < entities.size()); + return version_type(entities[pos] >> traits_type::entity_shift); + } + + /** + * @brief Creates a new entity and returns it. + * + * There are two kinds of entity identifiers: + * + * * Newly created ones in case no entities have been previously destroyed. + * * Recycled ones with updated versions. + * + * Users should not care about the type of the returned entity identifier. + * In case entity identifers are stored around, the `valid` member + * function can be used to know if they are still valid or the entity has + * been destroyed and potentially recycled. + * + * The returned entity has no components nor tags assigned. + * + * @return A valid entity identifier. + */ + entity_type create() { + entity_type entity; + + if(available) { + const auto entt = next; + const auto version = entities[entt] & (traits_type::version_mask << traits_type::entity_shift); + next = entities[entt] & traits_type::entity_mask; + entity = entt | version; + entities[entt] = entity; + --available; + } else { + entity = entity_type(entities.size()); + entities.push_back(entity); + // traits_type::entity_mask is reserved to allow for null identifiers + assert(entity < traits_type::entity_mask); + } + + return entity; + } + + /** + * @brief Destroys the entity that owns the given tag, if any. + * + * Convenient shortcut to destroy an entity by means of a tag type.
+ * Syntactic sugar for the following snippet: + * + * @code{.cpp} + * if(registry.has()) { + * registry.destroy(registry.attachee()); + * } + * @endcode + * + * @tparam Tag Type of tag to use to search for the entity. + */ + template + void destroy(tag_t) { + return has() ? destroy(attachee()) : void(); + } + + /** + * @brief Destroys an entity and lets the registry recycle the identifier. + * + * When an entity is destroyed, its version is updated and the identifier + * can be recycled at any time. In case entity identifers are stored around, + * the `valid` member function can be used to know if they are still valid + * or the entity has been destroyed and potentially recycled. + * + * @warning + * In case there are listeners that observe the destruction of components + * and assign other components to the entity in their bodies, the result of + * invoking this function may not be as expected. In the worst case, it + * could lead to undefined behavior. An assertion will abort the execution + * at runtime in debug mode if a violation is detected. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param entity A valid entity identifier. + */ + void destroy(const entity_type entity) { + assert(valid(entity)); + + for(auto pos = pools.size(); pos; --pos) { + auto &cpool = pools[pos-1]; + + if(cpool && cpool->has(entity)) { + cpool->destroy(entity); + } + }; + + for(auto pos = tags.size(); pos; --pos) { + auto &tag = tags[pos-1]; + + if(tag && tag->get() == entity) { + tag->destroy(); + } + }; + + // just a way to protect users from listeners that attach components + assert(orphan(entity)); + + // lengthens the implicit list of destroyed entities + const auto entt = entity & traits_type::entity_mask; + const auto version = ((entity >> traits_type::entity_shift) + 1) << traits_type::entity_shift; + const auto node = (available ? next : ((entt + 1) & traits_type::entity_mask)) | version; + entities[entt] = node; + next = entt; + ++available; + } + + /** + * @brief Destroys the entities that own the given components, if any. + * + * Convenient shortcut to destroy a set of entities at once.
+ * Syntactic sugar for the following snippet: + * + * @code{.cpp} + * for(const auto entity: registry.view(Type{}...)) { + * registry.destroy(entity); + * } + * @endcode + * + * @tparam Component Types of components to use to search for the entities. + * @tparam Type Type of view to use or empty to use a standard view. + */ + template + void destroy(Type...) { + for(const auto entity: view(Type{}...)) { + destroy(entity); + } + } + + /** + * @brief Attaches the given tag to an entity. + * + * Usually, pools of components allocate enough memory to store a bunch of + * elements even if only one of them is used. On the other hand, there are + * cases where all what is needed is a single instance component to attach + * to an entity.
+ * Tags are the right tool to achieve the purpose. + * + * @warning + * Attempting to use an invalid entity or to attach to an entity a tag that + * already has an owner results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the tag has been already attached to another entity. + * + * @tparam Tag Type of tag to create. + * @tparam Args Types of arguments to use to construct the tag. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the tag. + * @return A reference to the newly created tag. + */ + template + Tag & assign(tag_t, const entity_type entity, Args &&... args) { + assert(valid(entity)); + assert(!has()); + assure(tag_t{}); + return pool(tag_t{}).construct(entity, std::forward(args)...); + } + + /** + * @brief Assigns the given component to an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity already owns an instance of the given + * component. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & assign(const entity_type entity, Args &&... args) { + assert(valid(entity)); + assure(); + return pool().construct(entity, std::forward(args)...); + } + + /** + * @brief Removes the given tag from its owner, if any. + * @tparam Tag Type of tag to remove. + */ + template + void remove() { + return has() ? pool(tag_t{}).destroy() : void(); + } + + /** + * @brief Removes the given component from an entity. + * + * @warning + * Attempting to use an invalid entity or to remove a component from an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to remove. + * @param entity A valid entity identifier. + */ + template + void remove(const entity_type entity) { + assert(valid(entity)); + assert(managed()); + pool().destroy(entity); + } + + /** + * @brief Checks if the given tag has an owner. + * @tparam Tag Type of tag for which to perform the check. + * @return True if the tag already has an owner, false otherwise. + */ + template + bool has() const ENTT_NOEXCEPT { + return managed(tag_t{}) && tags[tag_family::type()]->get() != null; + } + + /** + * @brief Checks if an entity owns the given tag. + * + * Syntactic sugar for the following snippet: + * + * @code{.cpp} + * registry.has() && registry.attachee() == entity + * @endcode + * + * @tparam Tag Type of tag for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity owns the tag, false otherwise. + */ + template + bool has(tag_t, const entity_type entity) const ENTT_NOEXCEPT { + return has() && attachee() == entity; + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + bool has(const entity_type entity) const ENTT_NOEXCEPT { + assert(valid(entity)); + bool all = true; + using accumulator_type = bool[]; + accumulator_type accumulator = { all, (all = all && managed() && pool().has(entity))... }; + (void)accumulator; + return all; + } + + /** + * @brief Returns a reference to the given tag. + * + * @warning + * Attempting to get a tag that hasn't an owner results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * tag hasn't been previously attached to an entity. + * + * @tparam Tag Type of tag to get. + * @return A reference to the tag. + */ + template + const Tag & get() const ENTT_NOEXCEPT { + assert(has()); + return pool(tag_t{}).get(); + } + + /** + * @brief Returns a reference to the given tag. + * + * @warning + * Attempting to get a tag that hasn't an owner results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * tag hasn't been previously attached to an entity. + * + * @tparam Tag Type of tag to get. + * @return A reference to the tag. + */ + template + inline Tag & get() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get()); + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to get. + * @param entity A valid entity identifier. + * @return A reference to the component owned by the entity. + */ + template + const Component & get(const entity_type entity) const ENTT_NOEXCEPT { + assert(valid(entity)); + assert(managed()); + return pool().get(entity); + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to get. + * @param entity A valid entity identifier. + * @return A reference to the component owned by the entity. + */ + template + inline Component & get(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(entity)); + } + + /** + * @brief Returns a reference to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get components from an entity + * that doesn't own them results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own instances of the given + * components. + * + * @tparam Component Type of components to get. + * @param entity A valid entity identifier. + * @return References to the components owned by the entity. + */ + template + inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple> + get(const entity_type entity) const ENTT_NOEXCEPT { + return std::tuple{get(entity)...}; + } + + /** + * @brief Returns a reference to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get components from an entity + * that doesn't own them results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own instances of the given + * components. + * + * @tparam Component Type of components to get. + * @param entity A valid entity identifier. + * @return References to the components owned by the entity. + */ + template + inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple> + get(const entity_type entity) ENTT_NOEXCEPT { + return std::tuple{get(entity)...}; + } + + /** + * @brief Replaces the given tag. + * + * A new instance of the given tag is created and initialized with the + * arguments provided (the tag must have a proper constructor or be of + * aggregate type). + * + * @warning + * Attempting to replace a tag that hasn't an owner results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * tag hasn't been previously attached to an entity. + * + * @tparam Tag Type of tag to replace. + * @tparam Args Types of arguments to use to construct the tag. + * @param args Parameters to use to initialize the tag. + * @return A reference to the tag. + */ + template + Tag & replace(tag_t, Args &&... args) { + return (get() = Tag{std::forward(args)...}); + } + + /** + * @brief Replaces the given component for an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & replace(const entity_type entity, Args &&... args) { + return (get(entity) = Component{std::forward(args)...}); + } + + /** + * @brief Changes the owner of the given tag. + * + * The ownership of the tag is transferred from one entity to another. + * + * @warning + * Attempting to use an invalid entity or to transfer the ownership of a tag + * that hasn't an owner results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the tag hasn't been previously attached to an + * entity. + * + * @tparam Tag Type of tag of which to transfer the ownership. + * @param entity A valid entity identifier. + * @return A valid entity identifier. + */ + template + entity_type move(const entity_type entity) ENTT_NOEXCEPT { + assert(valid(entity)); + assert(has()); + return pool(tag_t{}).move(entity); + } + + /** + * @brief Gets the owner of the given tag, if any. + * @tparam Tag Type of tag of which to get the owner. + * @return A valid entity identifier if an owner exists, the null entity + * identifier otherwise. + */ + template + entity_type attachee() const ENTT_NOEXCEPT { + return managed(tag_t{}) ? tags[tag_family::type()]->get() : null; + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * if(registry.has(entity)) { + * registry.replace(entity, args...); + * } else { + * registry.assign(entity, args...); + * } + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & accommodate(const entity_type entity, Args &&... args) { + assure(); + auto &cpool = pool(); + + return cpool.has(entity) + ? cpool.get(entity) = Component{std::forward(args)...} + : cpool.construct(entity, std::forward(args)...); + } + + /** + * @brief Returns a sink object for the given tag. + * + * A sink is an opaque object used to connect listeners to tags.
+ * The sink returned by this function can be used to receive notifications + * whenever a new instance of the given tag is created and assigned to an + * entity. + * + * The function type for a listener is: + * @code{.cpp} + * void(Registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the tag has been assigned to the entity. + * The order of invocation of the listeners isn't guaranteed.
+ * Note also that the greater the number of listeners, the greater the + * performance hit when a new tag is created. + * + * @sa SigH::Sink + * + * @tparam Tag Type of tag of which to get the sink. + * @return A temporary sink object. + */ + template + sink_type construction(tag_t) ENTT_NOEXCEPT { + assure(tag_t{}); + return pool(tag_t{}).construction(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever a new instance of the given component is created and assigned to + * an entity. + * + * The function type for a listener is: + * @code{.cpp} + * void(Registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been assigned to the + * entity. The order of invocation of the listeners isn't guaranteed.
+ * Note also that the greater the number of listeners, the greater the + * performance hit when a new component is created. + * + * @sa SigH::Sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + sink_type construction() ENTT_NOEXCEPT { + assure(); + return pool().construction(); + } + + /** + * @brief Returns a sink object for the given tag. + * + * A sink is an opaque object used to connect listeners to tag.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given tag is removed from an entity and thus + * destroyed. + * + * The function type for a listener is: + * @code{.cpp} + * void(Registry &, Entity); + * @endcode + * + * Listeners are invoked **before** the tag has been removed from the + * entity. The order of invocation of the listeners isn't guaranteed.
+ * Note also that the greater the number of listeners, the greater the + * performance hit when a tag is destroyed. + * + * @sa SigH::Sink + * + * @tparam Tag Type of tag of which to get the sink. + * @return A temporary sink object. + */ + template + sink_type destruction(tag_t) ENTT_NOEXCEPT { + assure(tag_t{}); + return pool(tag_t{}).destruction(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is removed from an entity and + * thus destroyed. + * + * The function type for a listener is: + * @code{.cpp} + * void(Registry &, Entity); + * @endcode + * + * Listeners are invoked **before** the component has been removed from the + * entity. The order of invocation of the listeners isn't guaranteed.
+ * Note also that the greater the number of listeners, the greater the + * performance hit when a component is destroyed. + * + * @sa SigH::Sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + sink_type destruction() ENTT_NOEXCEPT { + assure(); + return pool().destruction(); + } + + /** + * @brief Sorts the pool of entities for the given component. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order.
+ * This function can be used to impose an order to the elements in the pool + * of the given component. The order is kept valid until a component of the + * given type is assigned or removed from an entity. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Component &, const Component &) + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * The comparison funtion object received by the sort function object hasn't + * necessarily the type of the one passed along with the other parameters to + * this member function. + * + * @tparam Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param sort A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort sort = Sort{}, Args &&... args) { + assure(); + pool().sort(std::move(compare), std::move(sort), std::forward(args)...); + } + + /** + * @brief Sorts two pools of components in the same way. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order. + * + * It happens that different pools of components must be sorted the same way + * because of runtime and/or performance constraints. This function can be + * used to order a pool of components according to the order between the + * entities in another pool of components. + * + * @b How @b it @b works + * + * Being `A` and `B` the two sets where `B` is the master (the one the order + * of which rules) and `A` is the slave (the one to sort), after a call to + * this function an iterator for `A` will return the entities according to + * the following rules: + * + * * All the entities in `A` that are also in `B` are returned first + * according to the order they have in `B`. + * * All the entities in `A` that are not in `B` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `B` won't affect the order in `A`. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ + template + void sort() { + assure(); + assure(); + pool().respect(pool()); + } + + /** + * @brief Resets the given component for an entity. + * + * If the entity has an instance of the component, this function removes the + * component from the entity. Otherwise it does nothing. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to reset. + * @param entity A valid entity identifier. + */ + template + void reset(const entity_type entity) { + assert(valid(entity)); + assure(); + auto &cpool = pool(); + + if(cpool.has(entity)) { + cpool.destroy(entity); + } + } + + /** + * @brief Resets the pool of the given component. + * + * For each entity that has an instance of the given component, the + * component itself is removed and thus destroyed. + * + * @tparam Component Type of component whose pool must be reset. + */ + template + void reset() { + assure(); + auto &cpool = pool(); + + for(const auto entity: static_cast &>(cpool)) { + cpool.destroy(entity); + } + } + + /** + * @brief Resets a whole registry. + * + * Destroys all the entities. After a call to `reset`, all the entities + * still in use are recycled with a new version number. In case entity + * identifers are stored around, the `valid` member function can be used + * to know if they are still valid. + */ + void reset() { + each([this](const auto entity) { + // useless this-> used to suppress a warning with clang + this->destroy(entity); + }); + } + + /** + * @brief Iterates all the entities that are still in use. + * + * The function object is invoked for each entity that is still in use.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * This function is fairly slow and should not be used frequently.
+ * Consider using a view if the goal is to iterate entities that have a + * determinate set of components. A view is usually faster than combining + * this function with a bunch of custom tests.
+ * On the other side, this function can be used to iterate all the entities + * that are in use, regardless of their components. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(available) { + for(auto pos = entities.size(); pos; --pos) { + const auto curr = entity_type(pos - 1); + const auto entity = entities[curr]; + const auto entt = entity & traits_type::entity_mask; + + if(curr == entt) { + func(entity); + } + } + } else { + for(auto pos = entities.size(); pos; --pos) { + func(entities[pos-1]); + } + } + } + + /** + * @brief Checks if an entity is an orphan. + * + * An orphan is an entity that has neither assigned components nor + * tags. + * + * @param entity A valid entity identifier. + * @return True if the entity is an orphan, false otherwise. + */ + bool orphan(const entity_type entity) const { + assert(valid(entity)); + bool orphan = true; + + for(std::size_t i = 0; i < pools.size() && orphan; ++i) { + const auto &cpool = pools[i]; + orphan = !(cpool && cpool->has(entity)); + } + + for(std::size_t i = 0; i < tags.size() && orphan; ++i) { + const auto &tag = tags[i]; + orphan = !(tag && (tag->get() == entity)); + } + + return orphan; + } + + /** + * @brief Iterates orphans and applies them the given function object. + * + * The function object is invoked for each entity that is still in use and + * has neither assigned components nor tags.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * This function can be very slow and should not be used frequently. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void orphans(Func func) const { + each([func = std::move(func), this](const auto entity) { + if(orphan(entity)) { + func(entity); + } + }); + } + + /** + * @brief Returns a standard view for the given components. + * + * This kind of views are created on the fly and share with the registry its + * internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Standard views do their best to iterate the smallest set of candidate + * entities. In particular: + * + * * Single component views are incredibly fast and iterate a packed array + * of entities, all of which has the given component. + * * Multi component views look at the number of entities available for each + * component and pick up a reference to the smallest set of candidates to + * test for the given components. + * + * @note + * Multi component views are pretty fast. However their performance tend to + * degenerate when the number of components to iterate grows up and the most + * of the entities have all the given components.
+ * To get a performance boost, consider using a PersistentView instead. + * + * @see View + * @see View + * @see PersistentView + * @see RawView + * @see RuntimeView + * + * @tparam Component Type of components used to construct the view. + * @return A newly created standard view. + */ + template + View view() { + return View{(assure(), pool())...}; + } + + /** + * @brief Prepares the internal data structures used by persistent views. + * + * Persistent views are an incredibly fast tool used to iterate a packed + * array of entities all of which have specific components.
+ * The initialization of a persistent view is also a pretty cheap operation, + * but for the first time they are created. That's mainly because of the + * internal data structures of the registry that are dedicated to this kind + * of views and that don't exist yet the very first time they are + * requested.
+ * To avoid costly operations, internal data structures for persistent views + * can be prepared with this function. Just use the same set of components + * that would have been used otherwise to construct the view. + * + * @tparam Component Types of components used to prepare the view. + */ + template + void prepare() { + static_assert(sizeof...(Component) > 1, "!"); + const auto htype = handler_family::type(); + + if(!(htype < handlers.size())) { + handlers.resize(htype + 1); + } + + if(!handlers[htype]) { + connect(std::make_index_sequence{}); + handlers[htype] = std::make_unique>(); + auto &handler = *handlers[htype]; + + for(auto entity: view()) { + handler.construct(entity); + } + } + } + + /** + * @brief Discards all the data structures used for a given persitent view. + * + * Persistent views occupy memory, no matter if they are in use or not.
+ * This function can be used to discard all the internal data structures + * dedicated to a specific persistent view, with the goal of reducing the + * memory pressure. + * + * @warning + * Attempting to use a persistent view created before calling this function + * results in undefined behavior. No assertion available in this case, + * neither in debug mode nor in release mode. + * + * @tparam Component Types of components of the persistent view. + */ + template + void discard() { + if(contains()) { + disconnect(std::make_index_sequence{}); + handlers[handler_family::type()].reset(); + } + } + + /** + * @brief Checks if a persistent view has already been prepared. + * @tparam Component Types of components of the persistent view. + * @return True if the view has already been prepared, false otherwise. + */ + template + bool contains() const ENTT_NOEXCEPT { + static_assert(sizeof...(Component) > 1, "!"); + const auto htype = handler_family::type(); + return (htype < handlers.size() && handlers[htype]); + } + + /** + * @brief Returns a persistent view for the given components. + * + * This kind of views are created on the fly and share with the registry its + * internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Persistent views are the right choice to iterate entities when the number + * of components grows up and the most of the entities have all the given + * components.
+ * However they have also drawbacks: + * + * * Each kind of persistent view requires a dedicated data structure that + * is allocated within the registry and it increases memory pressure. + * * Internal data structures used to construct persistent views must be + * kept updated and it affects slightly construction and destruction of + * entities and components. + * + * That being said, persistent views are an incredibly powerful tool if used + * with care and offer a boost of performance undoubtedly. + * + * @note + * Consider to use the `prepare` member function to initialize the internal + * data structures used by persistent views when the registry is still + * empty. Initialization could be a costly operation otherwise and it will + * be performed the very first time each view is created. + * + * @see View + * @see View + * @see PersistentView + * @see RawView + * @see RuntimeView + * + * @tparam Component Types of components used to construct the view. + * @return A newly created persistent view. + */ + template + PersistentView view(persistent_t) { + prepare(); + const auto htype = handler_family::type(); + return PersistentView{*handlers[htype], (assure(), pool())...}; + } + + /** + * @brief Returns a raw view for the given component. + * + * This kind of views are created on the fly and share with the registry its + * internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Raw views are incredibly fast and must be considered the best tool to + * iterate components whenever knowing the entities to which they belong + * isn't required. + * + * @see View + * @see View + * @see PersistentView + * @see RawView + * @see RuntimeView + * + * @tparam Component Type of component used to construct the view. + * @return A newly created raw view. + */ + template + RawView view(raw_t) { + assure(); + return RawView{pool()}; + } + + /** + * @brief Returns a runtime view for the given components. + * + * This kind of views are created on the fly and share with the registry its + * internal data structures.
+ * Users should throw away the view after use. Fortunately, creating and + * destroying a view is an incredibly cheap operation because they do not + * require any type of initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Runtime views are well suited when users want to construct a view from + * some external inputs and don't know at compile-time what are the required + * components.
+ * This is particularly well suited to plugin systems and mods in general. + * + * @see View + * @see View + * @see PersistentView + * @see RawView + * @see RuntimeView + * + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range of components. + * @param last An iterator past the last element of the range of components. + * @return A newly created runtime view. + */ + template + RuntimeView view(It first, It last) { + static_assert(std::is_convertible::value_type, component_type>::value, "!"); + std::vector *> set(last - first); + + std::transform(first, last, set.begin(), [this](const component_type ctype) { + return ctype < pools.size() ? pools[ctype].get() : nullptr; + }); + + return RuntimeView{std::move(set)}; + } + + /** + * @brief Returns a temporary object to use to create snapshots. + * + * A snapshot is either a full or a partial dump of a registry.
+ * It can be used to save and restore its internal state or to keep two or + * more instances of this class in sync, as an example in a client-server + * architecture. + * + * @return A temporary object to use to take snasphosts. + */ + Snapshot snapshot() const ENTT_NOEXCEPT { + using follow_fn_type = entity_type(const Registry &, const entity_type); + const entity_type seed = available ? (next | (entities[next] & (traits_type::version_mask << traits_type::entity_shift))) : next; + + follow_fn_type *follow = [](const Registry ®istry, const entity_type entity) -> entity_type { + const auto &entities = registry.entities; + const auto entt = entity & traits_type::entity_mask; + const auto next = entities[entt] & traits_type::entity_mask; + return (next | (entities[next] & (traits_type::version_mask << traits_type::entity_shift))); + }; + + return { *this, seed, follow }; + } + + /** + * @brief Returns a temporary object to use to load snapshots. + * + * A snapshot is either a full or a partial dump of a registry.
+ * It can be used to save and restore its internal state or to keep two or + * more instances of this class in sync, as an example in a client-server + * architecture. + * + * @warning + * The loader returned by this function requires that the registry be empty. + * In case it isn't, all the data will be automatically deleted before to + * return. + * + * @return A temporary object to use to load snasphosts. + */ + SnapshotLoader restore() ENTT_NOEXCEPT { + using assure_fn_type = void(Registry &, const entity_type, const bool); + + assure_fn_type *assure = [](Registry ®istry, const entity_type entity, const bool destroyed) { + using promotion_type = std::conditional_t= sizeof(entity_type), size_type, entity_type>; + // explicit promotion to avoid warnings with std::uint16_t + const auto entt = promotion_type{entity} & traits_type::entity_mask; + auto &entities = registry.entities; + + if(!(entt < entities.size())) { + auto curr = entities.size(); + entities.resize(entt + 1); + std::iota(entities.data() + curr, entities.data() + entt, entity_type(curr)); + } + + entities[entt] = entity; + + if(destroyed) { + registry.destroy(entity); + const auto version = entity & (traits_type::version_mask << traits_type::entity_shift); + entities[entt] = ((entities[entt] & traits_type::entity_mask) | version); + } + }; + + return { (*this = {}), assure }; + } + +private: + std::vector>> handlers; + std::vector>> pools; + std::vector>> tags; + std::vector entities; + size_type available{}; + entity_type next{}; +}; + + +/** + * @brief Default registry class. + * + * The default registry is the best choice for almost all the applications.
+ * Users should have a really good reason to choose something different. + */ +using DefaultRegistry = Registry; + + +} + + +#endif // ENTT_ENTITY_REGISTRY_HPP diff --git a/external/entt/entity/snapshot.hpp b/external/entt/entity/snapshot.hpp new file mode 100644 index 00000000..e9d1128f --- /dev/null +++ b/external/entt/entity/snapshot.hpp @@ -0,0 +1,724 @@ +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "entt_traits.hpp" +#include "utility.hpp" + + +namespace entt { + + +/** + * @brief Forward declaration of the registry class. + */ +template +class Registry; + + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components and tags of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class Snapshot final { + /*! @brief A registry is allowed to create snapshots. */ + friend class Registry; + + using follow_fn_type = Entity(const Registry &, const Entity); + + Snapshot(const Registry ®istry, Entity seed, follow_fn_type *follow) ENTT_NOEXCEPT + : registry{registry}, + seed{seed}, + follow{follow} + {} + + template + void get(Archive &archive, std::size_t sz, It first, It last) const { + archive(static_cast(sz)); + + while(first != last) { + const auto entity = *(first++); + + if(registry.template has(entity)) { + archive(entity, registry.template get(entity)); + } + } + } + + template + void component(Archive &archive, It first, It last, std::index_sequence) const { + std::array size{}; + auto begin = first; + + while(begin != last) { + const auto entity = *(begin++); + using accumulator_type = std::size_t[]; + accumulator_type accumulator = { (registry.template has(entity) ? ++size[Indexes] : size[Indexes])... }; + (void)accumulator; + } + + using accumulator_type = int[]; + accumulator_type accumulator = { (get(archive, size[Indexes], first, last), 0)... }; + (void)accumulator; + } + +public: + /*! @brief Copying a snapshot isn't allowed. */ + Snapshot(const Snapshot &) = delete; + /*! @brief Default move constructor. */ + Snapshot(Snapshot &&) = default; + + /*! @brief Copying a snapshot isn't allowed. @return This snapshot. */ + Snapshot & operator=(const Snapshot &) = delete; + /*! @brief Default move assignment operator. @return This snapshot. */ + Snapshot & operator=(Snapshot &&) = default; + + /** + * @brief Puts aside all the entities that are still in use. + * + * Entities are serialized along with their versions. Destroyed entities are + * not taken in consideration by this function. + * + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const Snapshot & entities(Archive &archive) const { + archive(static_cast(registry.alive())); + registry.each([&archive](const auto entity) { archive(entity); }); + return *this; + } + + /** + * @brief Puts aside destroyed entities. + * + * Entities are serialized along with their versions. Entities that are + * still in use are not taken in consideration by this function. + * + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const Snapshot & destroyed(Archive &archive) const { + auto size = registry.size() - registry.alive(); + archive(static_cast(size)); + + if(size) { + auto curr = seed; + archive(curr); + + for(--size; size; --size) { + curr = follow(registry, curr); + archive(curr); + } + } + + return *this; + } + + /** + * @brief Puts aside the given component. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Type of component to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const Snapshot & component(Archive &archive) const { + const auto sz = registry.template size(); + const auto *entities = registry.template data(); + + archive(static_cast(sz)); + + for(std::remove_const_t i{}; i < sz; ++i) { + const auto entity = entities[i]; + archive(entity, registry.template get(entity)); + }; + + return *this; + } + + /** + * @brief Puts aside the given components. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + std::enable_if_t<(sizeof...(Component) > 1), const Snapshot &> + component(Archive &archive) const { + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (component(archive), 0)... }; + (void)accumulator; + return *this; + } + + /** + * @brief Puts aside the given components for the entities in a range. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @return An object of this type to continue creating the snapshot. + */ + template + const Snapshot & component(Archive &archive, It first, It last) const { + component(archive, first, last, std::make_index_sequence{}); + return *this; + } + + /** + * @brief Puts aside the given tag. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Tag Type of tag to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const Snapshot & tag(Archive &archive) const { + const bool has = registry.template has(); + + // numerical length is forced for tags to facilitate loading + archive(has ? Entity(1): Entity{}); + + if(has) { + archive(registry.template attachee(), registry.template get()); + } + + return *this; + } + + /** + * @brief Puts aside the given tags. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Tag Types of tags to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + std::enable_if_t<(sizeof...(Tag) > 1), const Snapshot &> + tag(Archive &archive) const { + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (tag(archive), 0)... }; + (void)accumulator; + return *this; + } + +private: + const Registry ®istry; + const Entity seed; + follow_fn_type *follow; +}; + + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class SnapshotLoader final { + /*! @brief A registry is allowed to create snapshot loaders. */ + friend class Registry; + + using assure_fn_type = void(Registry &, const Entity, const bool); + + SnapshotLoader(Registry ®istry, assure_fn_type *assure_fn) ENTT_NOEXCEPT + : registry{registry}, + assure_fn{assure_fn} + { + // restore a snapshot as a whole requires a clean registry + assert(!registry.capacity()); + } + + template + void assure(Archive &archive, bool destroyed) const { + Entity length{}; + archive(length); + + while(length--) { + Entity entity{}; + archive(entity); + assure_fn(registry, entity, destroyed); + } + } + + template + void assign(Archive &archive, Args... args) const { + Entity length{}; + archive(length); + + while(length--) { + Entity entity{}; + Type instance{}; + archive(entity, instance); + static constexpr auto destroyed = false; + assure_fn(registry, entity, destroyed); + registry.template assign(args..., entity, static_cast(instance)); + } + } + +public: + /*! @brief Copying a snapshot loader isn't allowed. */ + SnapshotLoader(const SnapshotLoader &) = delete; + /*! @brief Default move constructor. */ + SnapshotLoader(SnapshotLoader &&) = default; + + /*! @brief Copying a snapshot loader isn't allowed. @return This loader. */ + SnapshotLoader & operator=(const SnapshotLoader &) = delete; + /*! @brief Default move assignment operator. @return This loader. */ + SnapshotLoader & operator=(SnapshotLoader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and gives them the versions they originally had. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const SnapshotLoader & entities(Archive &archive) const { + static constexpr auto destroyed = false; + assure(archive, destroyed); + return *this; + } + + /** + * @brief Restores entities that were destroyed during serialization. + * + * This function restores the entities that were destroyed during + * serialization and gives them the versions they originally had. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const SnapshotLoader & destroyed(Archive &archive) const { + static constexpr auto destroyed = true; + assure(archive, destroyed); + return *this; + } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create it with + * the version it originally had. + * + * @tparam Component Types of components to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const SnapshotLoader & component(Archive &archive) const { + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (assign(archive), 0)... }; + (void)accumulator; + return *this; + } + + /** + * @brief Restores tags and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the tag is assigned + * doesn't exist yet, the loader will take care to create it with the + * version it originally had. + * + * @tparam Tag Types of tags to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const SnapshotLoader & tag(Archive &archive) const { + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (assign(archive, tag_t{}), 0)... }; + (void)accumulator; + return *this; + } + + /** + * @brief Destroys those entities that have neither components nor tags. + * + * In case all the entities were serialized but only part of the components + * and tags was saved, it could happen that some of the entities have + * neither components nor tags once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + const SnapshotLoader & orphans() const { + registry.orphans([this](const auto entity) { + registry.destroy(entity); + }); + + return *this; + } + +private: + Registry ®istry; + assure_fn_type *assure_fn; +}; + + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accomodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server applications with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class ContinuousLoader final { + using traits_type = entt_traits; + + void destroy(Entity entity) { + const auto it = remloc.find(entity); + + if(it == remloc.cend()) { + const auto local = registry.create(); + remloc.emplace(entity, std::make_pair(local, true)); + registry.destroy(local); + } + } + + void restore(Entity entity) { + const auto it = remloc.find(entity); + + if(it == remloc.cend()) { + const auto local = registry.create(); + remloc.emplace(entity, std::make_pair(local, true)); + } else { + remloc[entity].first = + registry.valid(remloc[entity].first) + ? remloc[entity].first + : registry.create(); + + // set the dirty flag + remloc[entity].second = true; + } + } + + template + std::enable_if_t::value> + update(Type &instance, Member Type:: *member) { + instance.*member = map(instance.*member); + } + + template + std::enable_if_t::value_type, Entity>::value> + update(Type &instance, Member Type:: *member) { + for(auto &entity: instance.*member) { + entity = map(entity); + } + } + + template + std::enable_if_t::value> + update(Other &, Member Type:: *) {} + + template + void assure(Archive &archive, void(ContinuousLoader:: *member)(Entity)) { + Entity length{}; + archive(length); + + while(length--) { + Entity entity{}; + archive(entity); + (this->*member)(entity); + } + } + + template + void reset() { + for(auto &&ref: remloc) { + const auto local = ref.second.first; + + if(registry.valid(local)) { + registry.template reset(local); + } + } + } + + template + void assign(Archive &archive, Func func, Member Type:: *... member) { + Entity length{}; + archive(length); + + while(length--) { + Entity entity{}; + Other instance{}; + + archive(entity, instance); + restore(entity); + + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (update(instance, member), 0)... }; + (void)accumulator; + + func(map(entity), instance); + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs a loader that is bound to a given registry. + * @param registry A valid reference to a registry. + */ + ContinuousLoader(Registry ®istry) ENTT_NOEXCEPT + : registry{registry} + {} + + /*! @brief Copying a snapshot loader isn't allowed. */ + ContinuousLoader(const ContinuousLoader &) = delete; + /*! @brief Default move constructor. */ + ContinuousLoader(ContinuousLoader &&) = default; + + /*! @brief Copying a snapshot loader isn't allowed. @return This loader. */ + ContinuousLoader & operator=(const ContinuousLoader &) = delete; + /*! @brief Default move assignment operator. @return This loader. */ + ContinuousLoader & operator=(ContinuousLoader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + ContinuousLoader & entities(Archive &archive) { + assure(archive, &ContinuousLoader::restore); + return *this; + } + + /** + * @brief Restores entities that were destroyed during serialization. + * + * This function restores the entities that were destroyed during + * serialization and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + ContinuousLoader & destroyed(Archive &archive) { + assure(archive, &ContinuousLoader::destroy); + return *this; + } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create a local + * counterpart for it.
+ * Members can be either data members of type entity_type or containers of + * entities. In both cases, the loader will visit them and update the + * entities by replacing each one with its local counterpart. + * + * @tparam Component Type of component to restore. + * @tparam Archive Type of input archive. + * @tparam Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ + template + ContinuousLoader & component(Archive &archive, Member Type:: *... member) { + auto apply = [this](const auto entity, const auto &component) { + registry.template accommodate>(entity, component); + }; + + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (reset(), assign(archive, apply, member...), 0)... }; + (void)accumulator; + return *this; + } + + /** + * @brief Restores tags and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the tag is assigned + * doesn't exist yet, the loader will take care to create a local + * counterpart for it.
+ * Members can be either data members of type entity_type or containers of + * entities. In both cases, the loader will visit them and update the + * entities by replacing each one with its local counterpart. + * + * @tparam Tag Type of tag to restore. + * @tparam Archive Type of input archive. + * @tparam Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ + template + ContinuousLoader & tag(Archive &archive, Member Type:: *... member) { + auto apply = [this](const auto entity, const auto &tag) { + registry.template assign>(tag_t{}, entity, tag); + }; + + using accumulator_type = int[]; + accumulator_type accumulator = { 0, (registry.template remove(), assign(archive, apply, member...), 0)... }; + (void)accumulator; + return *this; + } + + /** + * @brief Helps to purge entities that no longer have a conterpart. + * + * Users should invoke this member function after restoring each snapshot, + * unless they know exactly what they are doing. + * + * @return A non-const reference to this loader. + */ + ContinuousLoader & shrink() { + auto it = remloc.begin(); + + while(it != remloc.cend()) { + const auto local = it->second.first; + bool &dirty = it->second.second; + + if(dirty) { + dirty = false; + ++it; + } else { + if(registry.valid(local)) { + registry.destroy(local); + } + + it = remloc.erase(it); + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have neither components nor tags. + * + * In case all the entities were serialized but only part of the components + * and tags was saved, it could happen that some of the entities have + * neither components nor tags once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + ContinuousLoader & orphans() { + registry.orphans([this](const auto entity) { + registry.destroy(entity); + }); + + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entity An entity identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + bool has(entity_type entity) const ENTT_NOEXCEPT { + return (remloc.find(entity) != remloc.cend()); + } + + /** + * @brief Returns the identifier to which an entity refers. + * + * @warning + * Attempting to use an entity that isn't managed by the loader results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * loader doesn't knows about the entity. + * + * @param entity An entity identifier. + * @return The identifier to which `entity` refers in the target registry. + */ + entity_type map(entity_type entity) const ENTT_NOEXCEPT { + assert(has(entity)); + return remloc.find(entity)->second.first; + } + +private: + std::unordered_map> remloc; + Registry ®istry; +}; + + +} + + +#endif // ENTT_ENTITY_SNAPSHOT_HPP diff --git a/external/entt/entity/sparse_set.hpp b/external/entt/entity/sparse_set.hpp new file mode 100644 index 00000000..fc0e32ce --- /dev/null +++ b/external/entt/entity/sparse_set.hpp @@ -0,0 +1,1120 @@ +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/algorithm.hpp" +#include "entt_traits.hpp" +#include "entity.hpp" + + +namespace entt { + + +/** + * @brief Sparse set. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class SparseSet; + + +/** + * @brief Basic sparse set implementation. + * + * Sparse set or packed array or whatever is the name users give it.
+ * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a + * _packed_ one; one used for direct access through contiguous memory, the other + * one used to get the data through an extra level of indirection.
+ * This is largely used by the Registry to offer users the fastest access ever + * to the components. View and PersistentView are entirely designed around + * sparse sets. + * + * This type of data structure is widely documented in the literature and on the + * web. This is nothing more than a customized implementation suitable for the + * purpose of the framework. + * + * @note + * There are no guarantees that entities are returned in the insertion order + * when iterate a sparse set. Do not make assumption on the order in any case. + * + * @note + * Internal data structures arrange elements to maximize performance. Because of + * that, there are no guarantees that elements have the expected order when + * iterate directly the internal packed array (see `data` and `size` member + * functions for that). Use `begin` and `end` instead. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class SparseSet { + using traits_type = entt_traits; + + class Iterator final { + friend class SparseSet; + + using direct_type = const std::vector; + using index_type = typename traits_type::difference_type; + + Iterator(direct_type *direct, index_type index) ENTT_NOEXCEPT + : direct{direct}, index{index} + {} + + public: + using difference_type = index_type; + using value_type = const Entity; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::random_access_iterator_tag; + + Iterator() ENTT_NOEXCEPT = default; + + Iterator(const Iterator &) ENTT_NOEXCEPT = default; + Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default; + + Iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + Iterator operator++(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return ++(*this), orig; + } + + Iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + Iterator operator--(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return --(*this), orig; + } + + Iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + Iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + return Iterator{direct, index-value}; + } + + inline Iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + inline Iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const Iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*direct)[pos]; + } + + bool operator==(const Iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const Iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const Iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + inline bool operator<=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + inline bool operator>=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1); + return &(*direct)[pos]; + } + + inline reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + direct_type *direct; + index_type index; + }; + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator_type = Iterator; + /*! @brief Constant input iterator type. */ + using const_iterator_type = Iterator; + + /*! @brief Default constructor. */ + SparseSet() ENTT_NOEXCEPT = default; + + /*! @brief Default destructor. */ + virtual ~SparseSet() ENTT_NOEXCEPT = default; + + /*! @brief Copying a sparse set isn't allowed. */ + SparseSet(const SparseSet &) = delete; + /*! @brief Default move constructor. */ + SparseSet(SparseSet &&) = default; + + /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */ + SparseSet & operator=(const SparseSet &) = delete; + /*! @brief Default move assignment operator. @return This sparse set. */ + SparseSet & operator=(SparseSet &&) = default; + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + direct.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + size_type capacity() const ENTT_NOEXCEPT { + return direct.capacity(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + size_type extent() const ENTT_NOEXCEPT { + return reverse.size(); + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + size_type size() const ENTT_NOEXCEPT { + return direct.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return direct.empty(); + } + + /** + * @brief Direct access to the internal packed array. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though `respect` has been + * previously invoked. Internal data structures arrange elements to maximize + * performance. Accessing them directly gives a performance boost but less + * guarantees. Use `begin` and `end` if you want to iterate the sparse set + * in the expected order. + * + * @return A pointer to the internal packed array. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return direct.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first entity of the internal packed + * array. If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the first entity of the internal packed array. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = direct.size(); + return const_iterator_type{&direct, pos}; + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first entity of the internal packed + * array. If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the first entity of the internal packed array. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first entity of the internal packed + * array. If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the first entity of the internal packed array. + */ + inline iterator_type begin() ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return const_iterator_type{&direct, {}}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to `respect`. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + inline iterator_type end() ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + inline const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT { + return cbegin()[pos]; + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entity A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + bool has(const entity_type entity) const ENTT_NOEXCEPT { + const auto pos = size_type(entity & traits_type::entity_mask); + // testing against null permits to avoid accessing the direct vector + return (pos < reverse.size()) && (reverse[pos] != null); + } + + /** + * @brief Checks if a sparse set contains an entity (unsafe). + * + * Alternative version of `has`. It accesses the underlying data structures + * without bounds checking and thus it's both unsafe and risky to use.
+ * You should not invoke directly this function unless you know exactly what + * you are doing. Prefer the `has` member function instead. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set can + * result in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * bounds violation. + * + * @param entity A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + bool fast(const entity_type entity) const ENTT_NOEXCEPT { + const auto pos = size_type(entity & traits_type::entity_mask); + assert(pos < reverse.size()); + // testing against null permits to avoid accessing the direct vector + return (reverse[pos] != null); + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entity A valid entity identifier. + * @return The position of the entity in the sparse set. + */ + size_type get(const entity_type entity) const ENTT_NOEXCEPT { + assert(has(entity)); + const auto pos = size_type(entity & traits_type::entity_mask); + return size_type(reverse[pos]); + } + + /** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @param entity A valid entity identifier. + */ + void construct(const entity_type entity) { + assert(!has(entity)); + const auto pos = size_type(entity & traits_type::entity_mask); + + if(!(pos < reverse.size())) { + // null is safe in all cases for our purposes + reverse.resize(pos+1, null); + } + + reverse[pos] = entity_type(direct.size()); + direct.push_back(entity); + } + + /** + * @brief Removes an entity from a sparse set. + * + * @warning + * Attempting to remove an entity that doesn't belong to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entity A valid entity identifier. + */ + virtual void destroy(const entity_type entity) { + assert(has(entity)); + const auto back = direct.back(); + auto &candidate = reverse[size_type(entity & traits_type::entity_mask)]; + // swapping isn't required here, we are getting rid of the last element + reverse[back & traits_type::entity_mask] = candidate; + direct[size_type(candidate)] = back; + candidate = null; + direct.pop_back(); + } + + /** + * @brief Swaps the position of two entities in the internal packed array. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entities. + * + * @param lhs A valid position within the sparse set. + * @param rhs A valid position within the sparse set. + */ + void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT { + assert(lhs < direct.size()); + assert(rhs < direct.size()); + auto &src = direct[lhs]; + auto &dst = direct[rhs]; + std::swap(reverse[src & traits_type::entity_mask], reverse[dst & traits_type::entity_mask]); + std::swap(src, dst); + } + + /** + * @brief Sort entities according to their order in another sparse set. + * + * Entities that are part of both the sparse sets are ordered internally + * according to the order they have in `other`. All the other entities goes + * to the end of the list and there are no guarantess on their order.
+ * In other terms, this function can be used to impose the same order on two + * sets by using one of them as a master and the other one as a slave. + * + * Iterating the sparse set with a couple of iterators returns elements in + * the expected order after a call to `respect`. See `begin` and `end` for + * more details. + * + * @note + * Attempting to iterate elements using the raw pointer returned by `data` + * gives no guarantees on the order, even though `respect` has been invoked. + * + * @param other The sparse sets that imposes the order of the entities. + */ + void respect(const SparseSet &other) ENTT_NOEXCEPT { + auto from = other.cbegin(); + auto to = other.cend(); + + size_type pos = direct.size() - 1; + + while(pos && from != to) { + if(has(*from)) { + if(*from != direct[pos]) { + swap(pos, get(*from)); + } + + --pos; + } + + ++from; + } + } + + /** + * @brief Resets a sparse set. + */ + virtual void reset() { + reverse.clear(); + direct.clear(); + } + +private: + std::vector reverse; + std::vector direct; +}; + + +/** + * @brief Extended sparse set implementation. + * + * This specialization of a sparse set associates an object to an entity. The + * main purpose of this class is to use sparse sets to store components in a + * Registry. It guarantees fast access both to the elements and to the entities. + * + * @note + * Entities and objects have the same order. It's guaranteed both in case of raw + * access (either to entities or objects) and when using input iterators. + * + * @note + * Internal data structures arrange elements to maximize performance. Because of + * that, there are no guarantees that elements have the expected order when + * iterate directly the internal packed array (see `raw` and `size` member + * functions for that). Use `begin` and `end` instead. + * + * @sa SparseSet + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +class SparseSet: public SparseSet { + using underlying_type = SparseSet; + using traits_type = entt_traits; + + template + class Iterator final { + friend class SparseSet; + + using instance_type = std::conditional_t, std::vector>; + using index_type = typename traits_type::difference_type; + + Iterator(instance_type *instances, index_type index) ENTT_NOEXCEPT + : instances{instances}, index{index} + {} + + public: + using difference_type = index_type; + using value_type = std::conditional_t; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::random_access_iterator_tag; + + Iterator() ENTT_NOEXCEPT = default; + + Iterator(const Iterator &) ENTT_NOEXCEPT = default; + Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default; + + Iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + Iterator operator++(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return ++(*this), orig; + } + + Iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + Iterator operator--(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return --(*this), orig; + } + + Iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + Iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + return Iterator{instances, index-value}; + } + + inline Iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + inline Iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const Iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*instances)[pos]; + } + + bool operator==(const Iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const Iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const Iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + inline bool operator<=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + inline bool operator>=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1); + return &(*instances)[pos]; + } + + inline reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + instance_type *instances; + index_type index; + }; + +public: + /*! @brief Type of the objects associated to the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename underlying_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = Iterator; + /*! @brief Constant input iterator type. */ + using const_iterator_type = Iterator; + + /*! @brief Default constructor. */ + SparseSet() ENTT_NOEXCEPT = default; + + /*! @brief Copying a sparse set isn't allowed. */ + SparseSet(const SparseSet &) = delete; + /*! @brief Default move constructor. */ + SparseSet(SparseSet &&) = default; + + /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */ + SparseSet & operator=(const SparseSet &) = delete; + /*! @brief Default move assignment operator. @return This sparse set. */ + SparseSet & operator=(SparseSet &&) = default; + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + underlying_type::reserve(cap); + instances.reserve(cap); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though either `sort` or + * `respect` has been previously invoked. Internal data structures arrange + * elements to maximize performance. Accessing them directly gives a + * performance boost but less guarantees. Use `begin` and `end` if you want + * to iterate the sparse set in the expected order. + * + * @return A pointer to the array of objects. + */ + const object_type * raw() const ENTT_NOEXCEPT { + return instances.data(); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though either `sort` or + * `respect` has been previously invoked. Internal data structures arrange + * elements to maximize performance. Accessing them directly gives a + * performance boost but less guarantees. Use `begin` and `end` if you want + * to iterate the sparse set in the expected order. + * + * @return A pointer to the array of objects. + */ + object_type * raw() ENTT_NOEXCEPT { + return instances.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the sparse set is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = instances.size(); + return const_iterator_type{&instances, pos}; + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the sparse set is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the sparse set is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + iterator_type begin() ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = instances.size(); + return iterator_type{&instances, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return const_iterator_type{&instances, {}}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + iterator_type end() ENTT_NOEXCEPT { + return iterator_type{&instances, {}}; + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + inline const object_type & operator[](const size_type pos) const ENTT_NOEXCEPT { + return cbegin()[pos]; + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + inline object_type & operator[](const size_type pos) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->operator[](pos)); + } + + /** + * @brief Returns the object associated to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entity A valid entity identifier. + * @return The object associated to the entity. + */ + const object_type & get(const entity_type entity) const ENTT_NOEXCEPT { + return instances[underlying_type::get(entity)]; + } + + /** + * @brief Returns the object associated to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entity A valid entity identifier. + * @return The object associated to the entity. + */ + inline object_type & get(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(entity)); + } + + /** + * @brief Assigns an entity to a sparse set and constructs its object. + * + * @note + * _Sfinae'd_ function.
+ * This version is used for types that can be constructed in place directly. + * It doesn't work well with aggregates because of the placement new usually + * performed under the hood during an _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + * @return The object associated to the entity. + */ + template + std::enable_if_t::value, object_type &> + construct(const entity_type entity, Args &&... args) { + underlying_type::construct(entity); + instances.emplace_back(std::forward(args)...); + return instances.back(); + } + + /** + * @brief Assigns an entity to a sparse set and constructs its object. + * + * @note + * _Sfinae'd_ function.
+ * Fallback for aggregates and types in general that do not work well with a + * placement new as performed usually under the hood during an + * _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + * @return The object associated to the entity. + */ + template + std::enable_if_t::value, object_type &> + construct(const entity_type entity, Args &&... args) { + underlying_type::construct(entity); + instances.emplace_back(Type{std::forward(args)...}); + return instances.back(); + } + + /** + * @brief Removes an entity from a sparse set and destroies its object. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entity A valid entity identifier. + */ + void destroy(const entity_type entity) override { + // swapping isn't required here, we are getting rid of the last element + // however, we must protect ourselves from self assignments (see #37) + auto tmp = std::move(instances.back()); + instances[underlying_type::get(entity)] = std::move(tmp); + instances.pop_back(); + underlying_type::destroy(entity); + } + + /** + * @brief Sort components according to the given comparison function. + * + * Sort the elements so that iterating the sparse set with a couple of + * iterators returns them in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Type &, const Type &) + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * The comparison funtion object received by the sort function object hasn't + * necessarily the type of the one passed along with the other parameters to + * this member function. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `sort` has been invoked. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param sort A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort sort = Sort{}, Args &&... args) { + std::vector copy(instances.size()); + std::iota(copy.begin(), copy.end(), 0); + + sort(copy.begin(), copy.end(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(const_cast(instances[rhs]), const_cast(instances[lhs])); + }, std::forward(args)...); + + for(size_type pos = 0, last = copy.size(); pos < last; ++pos) { + auto curr = pos; + auto next = copy[curr]; + + while(curr != next) { + const auto lhs = copy[curr]; + const auto rhs = copy[next]; + std::swap(instances[lhs], instances[rhs]); + underlying_type::swap(lhs, rhs); + copy[curr] = curr; + curr = next; + next = copy[curr]; + } + } + } + + /** + * @brief Sort components according to the order of the entities in another + * sparse set. + * + * Entities that are part of both the sparse sets are ordered internally + * according to the order they have in `other`. All the other entities goes + * to the end of the list and there are no guarantess on their order. + * Components are sorted according to the entities to which they + * belong.
+ * In other terms, this function can be used to impose the same order on two + * sets by using one of them as a master and the other one as a slave. + * + * Iterating the sparse set with a couple of iterators returns elements in + * the expected order after a call to `respect`. See `begin` and `end` for + * more details. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `respect` has been invoked. + * + * @param other The sparse sets that imposes the order of the entities. + */ + void respect(const SparseSet &other) ENTT_NOEXCEPT { + auto from = other.cbegin(); + auto to = other.cend(); + + size_type pos = underlying_type::size() - 1; + const auto *local = underlying_type::data(); + + while(pos && from != to) { + const auto curr = *from; + + if(underlying_type::has(curr)) { + if(curr != *(local + pos)) { + auto candidate = underlying_type::get(curr); + std::swap(instances[pos], instances[candidate]); + underlying_type::swap(pos, candidate); + } + + --pos; + } + + ++from; + } + } + + /** + * @brief Resets a sparse set. + */ + void reset() override { + underlying_type::reset(); + instances.clear(); + } + +private: + std::vector instances; +}; + + +} + + +#endif // ENTT_ENTITY_SPARSE_SET_HPP diff --git a/external/entt/entity/utility.hpp b/external/entt/entity/utility.hpp new file mode 100644 index 00000000..31f138e3 --- /dev/null +++ b/external/entt/entity/utility.hpp @@ -0,0 +1,23 @@ +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + + +namespace entt { + + +/*! @brief Tag class type used to disambiguate overloads. */ +struct tag_t final {}; + + +/*! @brief Persistent view type used to disambiguate overloads. */ +struct persistent_t final {}; + + +/*! @brief Raw view type used to disambiguate overloads. */ +struct raw_t final {}; + + +} + + +#endif // ENTT_ENTITY_UTILITY_HPP diff --git a/external/entt/entity/view.hpp b/external/entt/entity/view.hpp new file mode 100644 index 00000000..214f862a --- /dev/null +++ b/external/entt/entity/view.hpp @@ -0,0 +1,1889 @@ +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "entt_traits.hpp" +#include "sparse_set.hpp" + + +namespace entt { + + +/** + * @brief Forward declaration of the registry class. + */ +template +class Registry; + + +/** + * @brief Persistent view. + * + * A persistent view returns all the entities and only the entities that have + * at least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations.
+ * In general, persistent views don't stay true to the order of any set of + * components unless users explicitly sort them. + * + * @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). + * + * 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. + * + * @note + * Views share references to the underlying data structures with the Registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by + * views.
+ * Moreover, sorting a persistent view affects all the other views of the same + * type (it means that users don't have to call `sort` on each view to sort all + * of them because they share the set of entities). + * + * @warning + * Lifetime of a view must overcome the one of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @sa View + * @sa View + * @sa RawView + * @sa RuntimeView + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + */ +template +class PersistentView final { + static_assert(sizeof...(Component) > 1, "!"); + + /*! @brief A registry is allowed to create views. */ + friend class Registry; + + template + using pool_type = SparseSet; + + using view_type = SparseSet; + using pattern_type = std::tuple &...>; + + PersistentView(view_type &view, pool_type &... pools) ENTT_NOEXCEPT + : view{view}, pools{pools...} + {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = typename view_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename view_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = typename view_type::iterator_type; + /*! @brief Constant input iterator type. */ + using const_iterator_type = typename view_type::const_iterator_type; + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + size_type size() const ENTT_NOEXCEPT { + return view.size(); + } + + /** + * @brief Checks whether the view is empty. + * @return True if the view is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return view.empty(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return view.data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + return view.cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + iterator_type begin() ENTT_NOEXCEPT { + return view.begin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return view.cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + iterator_type end() ENTT_NOEXCEPT { + return view.end(); + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT { + return view[pos]; + } + + /** + * @brief Checks if a view contains an entity. + * @param entity A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entity) const ENTT_NOEXCEPT { + return view.has(entity) && (view.data()[view.get(entity)] == entity); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Type of component to get. + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + template + const Comp & get(const entity_type entity) const ENTT_NOEXCEPT { + assert(contains(entity)); + return std::get &>(pools).get(entity); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Type of component to get. + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + template + inline Comp & get(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(entity)); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use invalid component types results in a compilation error. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of the components to get. + * @param entity A valid entity identifier. + * @return The components assigned to the entity. + */ + template + inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple> + get(const entity_type entity) const ENTT_NOEXCEPT { + assert(contains(entity)); + return std::tuple{get(entity)...}; + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use invalid component types results in a compilation error. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of the components to get. + * @param entity A valid entity identifier. + * @return The components assigned to the entity. + */ + template + inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple> + get(const entity_type entity) ENTT_NOEXCEPT { + assert(contains(entity)); + return std::tuple{get(entity)...}; + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of const references to all the components of the + * view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, const Component &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + std::for_each(view.cbegin(), view.cend(), [&func, this](const auto entity) { + func(entity, std::get &>(pools).get(entity)...); + }); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to all the components of the + * view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, Component &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void each(Func func) { + const_cast(this)->each([&func](const entity_type entity, const Component &... component) { + func(entity, const_cast(component)...); + }); + } + + /** + * @brief Sort the shared pool of entities according to the given component. + * + * Persistent views of the same type share with the Registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the persistent views. + * + * @tparam Comp Type of component to use to impose the order. + */ + template + void sort() { + view.respect(std::get &>(pools)); + } + +private: + view_type &view; + const pattern_type pools; +}; + + +/** + * @brief Multi component view. + * + * Multi component views iterate over those entities that have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and picks up a + * reference to the smallest set of candidate entities in order to get a + * performance boost when iterate.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structures. See SparseSet and its specializations for more + * details. + * + * @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). + * + * 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. + * + * @note + * Views share references to the underlying data structures with the Registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must overcome the one of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @sa View + * @sa PersistentView + * @sa RawView + * @sa RuntimeView + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + */ +template +class View final { + static_assert(sizeof...(Component) > 1, "!"); + + /*! @brief A registry is allowed to create views. */ + friend class Registry; + + template + using pool_type = SparseSet; + + template + using component_iterator_type = typename pool_type::const_iterator_type; + + using view_type = SparseSet; + using underlying_iterator_type = typename view_type::const_iterator_type; + using unchecked_type = std::array; + using pattern_type = std::tuple &...>; + using traits_type = entt_traits; + + class Iterator { + friend class View; + + using extent_type = typename view_type::size_type; + + Iterator(unchecked_type unchecked, underlying_iterator_type begin, underlying_iterator_type end) ENTT_NOEXCEPT + : unchecked{unchecked}, + begin{begin}, + end{end}, + extent{min(std::make_index_sequence{})} + { + if(begin != end && !valid()) { + ++(*this); + } + } + + template + extent_type min(std::index_sequence) const ENTT_NOEXCEPT { + return std::min({ std::get(unchecked)->extent()... }); + } + + bool valid() const ENTT_NOEXCEPT { + const auto entity = *begin; + const auto sz = size_type(entity & traits_type::entity_mask); + + return sz < extent && std::all_of(unchecked.cbegin(), unchecked.cend(), [entity](const view_type *view) { + return view->fast(entity); + }); + } + + public: + using difference_type = typename underlying_iterator_type::difference_type; + using value_type = typename underlying_iterator_type::value_type; + using pointer = typename underlying_iterator_type::pointer; + using reference = typename underlying_iterator_type::reference; + using iterator_category = std::forward_iterator_tag; + + Iterator() ENTT_NOEXCEPT = default; + + Iterator(const Iterator &) ENTT_NOEXCEPT = default; + Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default; + + Iterator & operator++() ENTT_NOEXCEPT { + return (++begin != end && !valid()) ? ++(*this) : *this; + } + + Iterator operator++(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return ++(*this), orig; + } + + bool operator==(const Iterator &other) const ENTT_NOEXCEPT { + return other.begin == begin; + } + + inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + pointer operator->() const ENTT_NOEXCEPT { + return begin.operator->(); + } + + inline reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + unchecked_type unchecked; + underlying_iterator_type begin; + underlying_iterator_type end; + extent_type extent; + }; + + View(pool_type &... pools) ENTT_NOEXCEPT + : pools{pools...} + {} + + template + const pool_type & pool() const ENTT_NOEXCEPT { + return std::get &>(pools); + } + + const view_type * candidate() const ENTT_NOEXCEPT { + return std::min({ static_cast(&pool())... }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); + } + + unchecked_type unchecked(const view_type *view) const ENTT_NOEXCEPT { + unchecked_type other{}; + std::size_t pos{}; + using accumulator_type = const view_type *[]; + accumulator_type accumulator = { (&pool() == view ? view : other[pos++] = &pool())... }; + (void)accumulator; + return other; + } + + template + inline std::enable_if_t::value, const Other &> + get(const component_iterator_type &it, const Entity) const ENTT_NOEXCEPT { return *it; } + + template + inline std::enable_if_t::value, const Other &> + get(const component_iterator_type &, const Entity entity) const ENTT_NOEXCEPT { return pool().get(entity); } + + template + void each(const pool_type &cpool, Func func, std::index_sequence) const { + const auto other = unchecked(&cpool); + std::array data{{std::get(other)->cbegin()...}}; + const auto extent = std::min({ pool().extent()... }); + auto raw = std::make_tuple(pool().cbegin()...); + const auto end = cpool.view_type::cend(); + auto begin = cpool.view_type::cbegin(); + + // we can directly use the raw iterators if pools are ordered + while(begin != end && std::min({ (*(std::get(data)++) == *begin)... })) { + func(*(begin++), *(std::get>(raw)++)...); + } + + // fallback to visit what remains using indirections + while(begin != end) { + const auto entity = *(begin++); + const auto it = std::get>(raw)++; + const auto sz = size_type(entity & traits_type::entity_mask); + + if(sz < extent && std::all_of(other.cbegin(), other.cend(), [entity](const view_type *view) { return view->fast(entity); })) { + // avoided at least the indirection due to the sparse set for the pivot type (see get for more details) + func(entity, get(it, entity)...); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = typename view_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename view_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = Iterator; + /*! @brief Constant input iterator type. */ + using const_iterator_type = Iterator; + + /** + * @brief Estimates the number of entities that have the given components. + * @return Estimated number of entities that have the given components. + */ + size_type size() const ENTT_NOEXCEPT { + return std::min({ pool().size()... }); + } + + /** + * @brief Checks if the view is definitely empty. + * @return True if the view is definitely empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return std::max({ pool().empty()... }); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + const auto *view = candidate(); + return const_iterator_type{unchecked(view), view->cbegin(), view->cend()}; + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + inline iterator_type begin() ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + const auto *view = candidate(); + return const_iterator_type{unchecked(view), view->cend(), view->cend()}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + inline iterator_type end() ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Checks if a view contains an entity. + * @param entity A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entity) const ENTT_NOEXCEPT { + const auto sz = size_type(entity & traits_type::entity_mask); + const auto extent = std::min({ pool().extent()... }); + return sz < extent && std::min({ (pool().has(entity) && (pool().data()[pool().view_type::get(entity)] == entity))... }); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Type of component to get. + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + template + const Comp & get(const entity_type entity) const ENTT_NOEXCEPT { + assert(contains(entity)); + return pool().get(entity); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Type of component to get. + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + template + inline Comp & get(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(entity)); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use invalid component types results in a compilation error. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of the components to get. + * @param entity A valid entity identifier. + * @return The components assigned to the entity. + */ + template + inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple> + get(const entity_type entity) const ENTT_NOEXCEPT { + assert(contains(entity)); + return std::tuple{get(entity)...}; + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use invalid component types results in a compilation error. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of the components to get. + * @param entity A valid entity identifier. + * @return The components assigned to the entity. + */ + template + inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple> + get(const entity_type entity) ENTT_NOEXCEPT { + assert(contains(entity)); + return std::tuple{get(entity)...}; + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of const references to all the components of the + * view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, const Component &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + const auto *view = candidate(); + using accumulator_type = int[]; + accumulator_type accumulator = { (&pool() == view ? (each(pool(), std::move(func), std::make_index_sequence{}), 0) : 0)... }; + (void)accumulator; + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to all the components of the + * view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, Component &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void each(Func func) { + const_cast(this)->each([&func](const entity_type entity, const Component &... component) { + func(entity, const_cast(component)...); + }); + } + +private: + const pattern_type pools; +}; + + +/** + * @brief Single component view specialization. + * + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structure. See SparseSet and its specializations for more + * details. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * + * In all the other cases, modifying the pool of the given component in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share a reference to the underlying data structure with the Registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must overcome the one of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @sa View + * @sa PersistentView + * @sa RawView + * @sa RuntimeView + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class View final { + /*! @brief A registry is allowed to create views. */ + friend class Registry; + + using view_type = SparseSet; + using pool_type = SparseSet; + + View(pool_type &pool) ENTT_NOEXCEPT + : pool{pool} + {} + +public: + /*! @brief Type of component iterated by the view. */ + using raw_type = typename pool_type::object_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename pool_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename pool_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = typename view_type::iterator_type; + /*! @brief Constant input iterator type. */ + using const_iterator_type = typename view_type::const_iterator_type; + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + size_type size() const ENTT_NOEXCEPT { + return pool.size(); + } + + /** + * @brief Checks whether the view is empty. + * @return True if the view is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return pool.empty(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of components. + */ + const raw_type * raw() const ENTT_NOEXCEPT { + return pool.raw(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of components. + */ + inline raw_type * raw() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->raw()); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return pool.data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * component. + * + * The returned iterator points to the first entity that has the given + * component. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given component. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + return pool.view_type::cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * component. + * + * The returned iterator points to the first entity that has the given + * component. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given component. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * component. + * + * The returned iterator points to the first entity that has the given + * component. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given component. + */ + iterator_type begin() ENTT_NOEXCEPT { + return pool.view_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given component. + * + * The returned iterator points to the entity following the last entity that + * has the given component. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given component. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return pool.view_type::cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given component. + * + * The returned iterator points to the entity following the last entity that + * has the given component. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given component. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given component. + * + * The returned iterator points to the entity following the last entity that + * has the given component. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given component. + */ + iterator_type end() ENTT_NOEXCEPT { + return pool.view_type::end(); + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT { + return pool.view_type::operator[](pos); + } + + /** + * @brief Checks if a view contains an entity. + * @param entity A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entity) const ENTT_NOEXCEPT { + return pool.has(entity) && (pool.data()[pool.view_type::get(entity)] == entity); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + const Component & get(const entity_type entity) const ENTT_NOEXCEPT { + assert(contains(entity)); + return pool.get(entity); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `Registry::get` during iterations. It has + * far better performance than its companion function. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @param entity A valid entity identifier. + * @return The component assigned to the entity. + */ + inline Component & get(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->get(entity)); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a const reference to the component of the view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, const Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [&func, raw = pool.cbegin()](const auto entity) mutable { + func(entity, *(raw++)); + }); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component of the view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type, Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void each(Func func) { + const_cast(this)->each([&func](const entity_type entity, const Component &component) { + func(entity, const_cast(component)); + }); + } + +private: + pool_type &pool; +}; + + +/** + * @brief Raw view. + * + * Raw views are meant to easily iterate components without having to resort to + * using any other member function, so as to further increase the performance. + * Whenever knowing the entity to which a component belongs isn't required, this + * should be the preferred tool.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structure. See SparseSet and its specializations for more + * details. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity to which the component belongs is modified (as an example, the + * given component is destroyed). + * + * In all the other cases, modifying the pool of the given component in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share a reference to the underlying data structure with the Registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must overcome the one of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @sa View + * @sa View + * @sa PersistentView + * @sa RuntimeView + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class RawView final { + /*! @brief A registry is allowed to create views. */ + friend class Registry; + + using pool_type = SparseSet; + + RawView(pool_type &pool) ENTT_NOEXCEPT + : pool{pool} + {} + +public: + /*! @brief Type of component iterated by the view. */ + using raw_type = typename pool_type::object_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename pool_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename pool_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = typename pool_type::iterator_type; + /*! @brief Constant input iterator type. */ + using const_iterator_type = typename pool_type::const_iterator_type; + + /** + * @brief Returns the number of instances of the given type. + * @return Number of instances of the given component. + */ + size_type size() const ENTT_NOEXCEPT { + return pool.size(); + } + + /** + * @brief Checks whether the view is empty. + * @return True if the view is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return pool.empty(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of components. + */ + const raw_type * raw() const ENTT_NOEXCEPT { + return pool.raw(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of components. + */ + inline raw_type * raw() ENTT_NOEXCEPT { + return const_cast(const_cast(this)->raw()); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return pool.data(); + } + + /** + * @brief Returns an iterator to the first instance of the given type. + * + * The returned iterator points to the first instance of the given type. If + * the view is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first instance of the given type. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + return pool.cbegin(); + } + + /** + * @brief Returns an iterator to the first instance of the given type. + * + * The returned iterator points to the first instance of the given type. If + * the view is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first instance of the given type. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the first instance of the given type. + * + * The returned iterator points to the first instance of the given type. If + * the view is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first instance of the given type. + */ + iterator_type begin() ENTT_NOEXCEPT { + return pool.begin(); + } + + /** + * @brief Returns an iterator that is past the last instance of the given + * type. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return pool.cend(); + } + + /** + * @brief Returns an iterator that is past the last instance of the given + * type. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator that is past the last instance of the given + * type. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + iterator_type end() ENTT_NOEXCEPT { + return pool.end(); + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + const raw_type & operator[](const size_type pos) const ENTT_NOEXCEPT { + return pool[pos]; + } + + /** + * @brief Returns a reference to the element at the given position. + * @param pos Position of the element to return. + * @return A reference to the requested element. + */ + inline raw_type & operator[](const size_type pos) ENTT_NOEXCEPT { + return const_cast(const_cast(this)->operator[](pos)); + } + + /** + * @brief Iterates components and applies the given function object to them. + * + * The function object is provided with a const reference to each component + * of the view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + std::for_each(pool.cbegin(), pool.cend(), func); + } + + /** + * @brief Iterates components and applies the given function object to them. + * + * The function object is provided with a reference to each component of the + * view.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) { + std::for_each(pool.begin(), pool.end(), func); + } + +private: + pool_type &pool; +}; + + +/** + * @brief Runtime view. + * + * Runtime views iterate over those entities that have at least all the given + * components in their bags. During initialization, a runtime view looks at the + * number of entities available for each component and picks up a reference to + * the smallest set of candidate entities in order to get a performance boost + * when iterate.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structures. See SparseSet and its specializations for more + * details. + * + * @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). + * + * 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. + * + * @note + * Views share references to the underlying data structures with the Registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by views, + * unless a pool wasn't missing when the view was built (in this case, the view + * won't have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must overcome the one of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @sa View + * @sa View + * @sa PersistentView + * @sa RawView + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class RuntimeView { + /*! @brief A registry is allowed to create views. */ + friend class Registry; + + using view_type = SparseSet; + using underlying_iterator_type = typename view_type::const_iterator_type; + using pattern_type = std::vector; + using extent_type = typename view_type::size_type; + using traits_type = entt_traits; + + class Iterator { + friend class RuntimeView; + + Iterator(underlying_iterator_type begin, underlying_iterator_type end, const view_type * const *first, const view_type * const *last, extent_type extent) ENTT_NOEXCEPT + : begin{begin}, + end{end}, + first{first}, + last{last}, + extent{extent} + { + if(begin != end && !valid()) { + ++(*this); + } + } + + bool valid() const ENTT_NOEXCEPT { + const auto entity = *begin; + const auto sz = size_type(entity & traits_type::entity_mask); + + return sz < extent && std::all_of(first, last, [entity](const auto *view) { + return view->fast(entity); + }); + } + + public: + using difference_type = typename underlying_iterator_type::difference_type; + using value_type = typename underlying_iterator_type::value_type; + using pointer = typename underlying_iterator_type::pointer; + using reference = typename underlying_iterator_type::reference; + using iterator_category = std::forward_iterator_tag; + + Iterator() ENTT_NOEXCEPT = default; + + Iterator(const Iterator &) ENTT_NOEXCEPT = default; + Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default; + + Iterator & operator++() ENTT_NOEXCEPT { + return (++begin != end && !valid()) ? ++(*this) : *this; + } + + Iterator operator++(int) ENTT_NOEXCEPT { + Iterator orig = *this; + return ++(*this), orig; + } + + bool operator==(const Iterator &other) const ENTT_NOEXCEPT { + return other.begin == begin; + } + + inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + pointer operator->() const ENTT_NOEXCEPT { + return begin.operator->(); + } + + inline reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + underlying_iterator_type begin; + underlying_iterator_type end; + const view_type * const *first; + const view_type * const *last; + extent_type extent; + }; + + RuntimeView(pattern_type others) ENTT_NOEXCEPT + : pools{std::move(others)} + { + const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { + return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); + }); + + // brings the best candidate (if any) on front of the vector + std::rotate(pools.begin(), it, pools.end()); + } + + extent_type min() const ENTT_NOEXCEPT { + extent_type extent{}; + + if(valid()) { + const auto it = std::min_element(pools.cbegin(), pools.cend(), [](const auto *lhs, const auto *rhs) { + return lhs->extent() < rhs->extent(); + }); + + extent = (*it)->extent(); + } + + return extent; + } + + inline bool valid() const ENTT_NOEXCEPT { + return !pools.empty() && pools.front(); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = typename view_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename view_type::size_type; + /*! @brief Input iterator type. */ + using iterator_type = Iterator; + /*! @brief Constant input iterator type. */ + using const_iterator_type = Iterator; + + /** + * @brief Estimates the number of entities that have the given components. + * @return Estimated number of entities that have the given components. + */ + size_type size() const ENTT_NOEXCEPT { + return valid() ? pools.front()->size() : size_type{}; + } + + /** + * @brief Checks if the view is definitely empty. + * @return True if the view is definitely empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return !valid() || pools.front()->empty(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + const_iterator_type it{}; + + if(valid()) { + const auto &pool = *pools.front(); + const auto * const *data = pools.data(); + it = { pool.cbegin(), pool.cend(), data + 1, data + pools.size(), min() }; + } + + return it; + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + inline iterator_type begin() ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + const_iterator_type it{}; + + if(valid()) { + const auto &pool = *pools.front(); + it = { pool.cend(), pool.cend(), nullptr, nullptr, min() }; + } + + return it; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + inline iterator_type end() ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Checks if a view contains an entity. + * @param entity A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entity) const ENTT_NOEXCEPT { + return valid() && std::all_of(pools.cbegin(), pools.cend(), [entity](const auto *view) { + return view->has(entity) && view->data()[view->get(entity)] == entity; + }); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.
+ * The signature of the function should be equivalent to the following: + * + * @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 { + std::for_each(cbegin(), cend(), func); + } + +private: + pattern_type pools; +}; + + +} + + +#endif // ENTT_ENTITY_VIEW_HPP diff --git a/external/entt/entt.hpp b/external/entt/entt.hpp new file mode 100644 index 00000000..ceb1fefa --- /dev/null +++ b/external/entt/entt.hpp @@ -0,0 +1,26 @@ +#include "core/algorithm.hpp" +#include "core/family.hpp" +#include "core/hashed_string.hpp" +#include "core/ident.hpp" +#include "core/monostate.hpp" +#include "entity/actor.hpp" +#include "entity/attachee.hpp" +#include "entity/entity.hpp" +#include "entity/entt_traits.hpp" +#include "entity/helper.hpp" +#include "entity/prototype.hpp" +#include "entity/registry.hpp" +#include "entity/snapshot.hpp" +#include "entity/sparse_set.hpp" +#include "entity/utility.hpp" +#include "entity/view.hpp" +#include "locator/locator.hpp" +#include "process/process.hpp" +#include "process/scheduler.hpp" +#include "resource/cache.hpp" +#include "resource/handle.hpp" +#include "resource/loader.hpp" +#include "signal/delegate.hpp" +#include "signal/dispatcher.hpp" +#include "signal/emitter.hpp" +#include "signal/sigh.hpp" diff --git a/external/entt/locator/locator.hpp b/external/entt/locator/locator.hpp new file mode 100644 index 00000000..2c66ca38 --- /dev/null +++ b/external/entt/locator/locator.hpp @@ -0,0 +1,116 @@ +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + + +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @brief Service locator, nothing more. + * + * A service locator can be used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This template + * based implementation tries to fill the gap and to get rid of the burden of + * defining a different specific locator for each application. + * + * @tparam Service Type of service managed by the locator. + */ +template +struct ServiceLocator final { + /*! @brief Type of service offered. */ + using service_type = Service; + + /*! @brief Default constructor, deleted on purpose. */ + ServiceLocator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~ServiceLocator() = delete; + + /** + * @brief Tests if a valid service implementation is set. + * @return True if the service is set, false otherwise. + */ + inline static bool empty() ENTT_NOEXCEPT { + return !static_cast(service); + } + + /** + * @brief Returns a weak pointer to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @return A reference to the service implementation currently set, if any. + */ + inline static std::weak_ptr get() ENTT_NOEXCEPT { + return service; + } + + /** + * @brief Returns a weak reference to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @warning + * In case no service implementation has been set, a call to this function + * results in undefined behavior. + * + * @return A reference to the service implementation currently set, if any. + */ + inline static Service & ref() ENTT_NOEXCEPT { + return *service; + } + + /** + * @brief Sets or replaces a service. + * @tparam Impl Type of the new service to use. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + */ + template + inline static void set(Args &&... args) { + service = std::make_shared(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @param ptr Service to use to replace the current one. + */ + inline static void set(std::shared_ptr ptr) { + assert(static_cast(ptr)); + service = std::move(ptr); + } + + /** + * @brief Resets a service. + * + * The service is no longer valid after a reset. + */ + inline static void reset() { + service.reset(); + } + +private: + static std::shared_ptr service; +}; + + +template +std::shared_ptr ServiceLocator::service{}; + + +} + + +#endif // ENTT_LOCATOR_LOCATOR_HPP diff --git a/external/entt/process/process.hpp b/external/entt/process/process.hpp new file mode 100644 index 00000000..2077b043 --- /dev/null +++ b/external/entt/process/process.hpp @@ -0,0 +1,339 @@ +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + + +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.
+ * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(void *); + * @endcode + * + * It's invoked at the first tick, immediately before an update. The `void *` + * parameter is an opaque pointer to user data (if any) forwarded directly to + * the process during an update. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa Scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template +class Process { + enum class State: unsigned int { + UNINITIALIZED = 0, + RUNNING, + PAUSED, + SUCCEEDED, + FAILED, + ABORTED, + FINISHED + }; + + template + using tag = std::integral_constant; + + template + auto tick(int, tag, void *data) + -> decltype(std::declval().init(data)) { + static_cast(this)->init(data); + } + + template + auto tick(int, tag, Delta delta, void *data) + -> decltype(std::declval().update(delta, data)) { + static_cast(this)->update(delta, data); + } + + template + auto tick(int, tag) + -> decltype(std::declval().succeeded()) { + static_cast(this)->succeeded(); + } + + template + auto tick(int, tag) + -> decltype(std::declval().failed()) { + static_cast(this)->failed(); + } + + template + auto tick(int, tag) + -> decltype(std::declval().aborted()) { + static_cast(this)->aborted(); + } + + template + void tick(char, tag, Args &&...) const ENTT_NOEXCEPT {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() ENTT_NOEXCEPT { + if(alive()) { + current = State::SUCCEEDED; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() ENTT_NOEXCEPT { + if(alive()) { + current = State::FAILED; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() ENTT_NOEXCEPT { + if(current == State::RUNNING) { + current = State::PAUSED; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() ENTT_NOEXCEPT { + if(current == State::PAUSED) { + current = State::RUNNING; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~Process() ENTT_NOEXCEPT { + static_assert(std::is_base_of::value, "!"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) ENTT_NOEXCEPT { + if(alive()) { + current = State::ABORTED; + + if(immediately) { + tick(0); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + bool alive() const ENTT_NOEXCEPT { + return current == State::RUNNING || current == State::PAUSED; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + bool dead() const ENTT_NOEXCEPT { + return current == State::FINISHED; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + bool paused() const ENTT_NOEXCEPT { + return current == State::PAUSED; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + bool rejected() const ENTT_NOEXCEPT { + return stopped; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch (current) { + case State::UNINITIALIZED: + tick(0, tag{}, data); + current = State::RUNNING; + // no break on purpose, tasks are executed immediately + case State::RUNNING: + tick(0, tag{}, delta, data); + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case State::SUCCEEDED: + tick(0, tag{}); + current = State::FINISHED; + break; + case State::FAILED: + tick(0, tag{}); + current = State::FINISHED; + stopped = true; + break; + case State::ABORTED: + tick(0, tag{}); + current = State::FINISHED; + stopped = true; + break; + default: + // suppress warnings + break; + } + } + +private: + State current{State::UNINITIALIZED}; + bool stopped{false}; +}; + + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa Process + * @sa Scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct ProcessAdaptor: Process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + ProcessAdaptor(Args &&... args) + : Func{std::forward(args)...} + {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); + } +}; + + +} + + +#endif // ENTT_PROCESS_PROCESS_HPP diff --git a/external/entt/process/scheduler.hpp b/external/entt/process/scheduler.hpp new file mode 100644 index 00000000..c7729bbe --- /dev/null +++ b/external/entt/process/scheduler.hpp @@ -0,0 +1,311 @@ +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + + +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "process.hpp" + + +namespace entt { + + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.
+ * A process can also have a child. In this case, the process is replaced with + * its child when it terminates if it returns with success. In case of errors, + * both the process and its child are discarded. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }).then(arguments...); + * @endcode + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa Process + * + * @tparam Delta Type to use to provide elapsed time. + */ +template +class Scheduler final { + struct ProcessHandler final { + using instance_type = std::unique_ptr; + using update_fn_type = bool(ProcessHandler &, Delta, void *); + using abort_fn_type = void(ProcessHandler &, bool); + using next_type = std::unique_ptr; + + instance_type instance; + update_fn_type *update; + abort_fn_type *abort; + next_type next; + }; + + struct Then final { + Then(ProcessHandler *handler) + : handler{handler} + {} + + template + decltype(auto) then(Args &&... args) && { + static_assert(std::is_base_of, Proc>::value, "!"); + handler = Scheduler::then(handler, std::forward(args)...); + return std::move(*this); + } + + template + decltype(auto) then(Func &&func) && { + using Proc = ProcessAdaptor, Delta>; + return std::move(*this).template then(std::forward(func)); + } + + private: + ProcessHandler *handler; + }; + + template + static bool update(ProcessHandler &handler, const Delta delta, void *data) { + auto *process = static_cast(handler.instance.get()); + process->tick(delta, data); + + auto dead = process->dead(); + + if(dead) { + if(handler.next && !process->rejected()) { + handler = std::move(*handler.next); + dead = handler.update(handler, delta, data); + } else { + handler.instance.reset(); + } + } + + return dead; + } + + template + static void abort(ProcessHandler &handler, const bool immediately) { + static_cast(handler.instance.get())->abort(immediately); + } + + template + static void deleter(void *proc) { + delete static_cast(proc); + } + + template + static auto then(ProcessHandler *handler, Args &&... args) { + if(handler) { + auto proc = typename ProcessHandler::instance_type{new Proc{std::forward(args)...}, &Scheduler::deleter}; + handler->next.reset(new ProcessHandler{std::move(proc), &Scheduler::update, &Scheduler::abort, nullptr}); + handler = handler->next.get(); + } + + return handler; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename std::vector::size_type; + + /*! @brief Default constructor. */ + Scheduler() ENTT_NOEXCEPT = default; + + /*! @brief Copying a scheduler isn't allowed. */ + Scheduler(const Scheduler &) = delete; + /*! @brief Default move constructor. */ + Scheduler(Scheduler &&) = default; + + /*! @brief Copying a scheduler isn't allowed. @return This scheduler. */ + Scheduler & operator=(const Scheduler &) = delete; + /*! @brief Default move assignment operator. @return This scheduler. */ + Scheduler & operator=(Scheduler &&) = default; + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + size_type size() const ENTT_NOEXCEPT { + return handlers.size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return handlers.empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach(arguments...) + * // appends a child in the form of a lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another process class + * .then(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Args &&... args) { + static_assert(std::is_base_of, Proc>::value, "!"); + + auto proc = typename ProcessHandler::instance_type{new Proc{std::forward(args)...}, &Scheduler::deleter}; + ProcessHandler handler{std::move(proc), &Scheduler::update, &Scheduler::abort, nullptr}; + handlers.push_back(std::move(handler)); + + return Then{&handlers.back()}; + } + + /** + * @brief Schedules a process for the next tick. + * + * A process can be either a lambda or a functor. The scheduler wraps both + * of them in a process adaptor internally.
+ * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a lambda function + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of a process class + * .then(arguments...); + * @endcode + * + * @sa ProcessAdaptor + * + * @tparam Func Type of process to schedule. + * @param func Either a lambda or a functor to use as a process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Func &&func) { + using Proc = ProcessAdaptor, Delta>; + return attach(std::forward(func)); + } + + /** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.
+ * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data = nullptr) { + bool clean = false; + + for(auto pos = handlers.size(); pos; --pos) { + auto &handler = handlers[pos-1]; + const bool dead = handler.update(handler, delta, data); + clean = clean || dead; + } + + if(clean) { + handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](auto &handler) { + return !handler.instance; + }), handlers.end()); + } + } + + /** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.
+ * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + decltype(handlers) exec; + exec.swap(handlers); + + std::for_each(exec.begin(), exec.end(), [immediately](auto &handler) { + handler.abort(handler, immediately); + }); + + std::move(handlers.begin(), handlers.end(), std::back_inserter(exec)); + handlers.swap(exec); + } + +private: + std::vector handlers{}; +}; + + +} + + +#endif // ENTT_PROCESS_SCHEDULER_HPP diff --git a/external/entt/resource/cache.hpp b/external/entt/resource/cache.hpp new file mode 100644 index 00000000..032d2699 --- /dev/null +++ b/external/entt/resource/cache.hpp @@ -0,0 +1,201 @@ +#ifndef ENTT_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_CACHE_HPP + + +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/hashed_string.hpp" +#include "handle.hpp" +#include "loader.hpp" + + +namespace entt { + + +/** + * @brief Simple cache for resources of a given type. + * + * Minimal implementation of a cache for resources of a given type. It doesn't + * offer much functionalities but it's suitable for small or medium sized + * applications and can be freely inherited to add targeted functionalities for + * large sized applications. + * + * @tparam Resource Type of resources managed by a cache. + */ +template +class ResourceCache { + using container_type = std::unordered_map>; + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename container_type::size_type; + /*! @brief Type of resources managed by a cache. */ + using resource_type = HashedString; + + /*! @brief Default constructor. */ + ResourceCache() = default; + + /*! @brief Copying a cache isn't allowed. */ + ResourceCache(const ResourceCache &) ENTT_NOEXCEPT = delete; + /*! @brief Default move constructor. */ + ResourceCache(ResourceCache &&) ENTT_NOEXCEPT = default; + + /*! @brief Copying a cache isn't allowed. @return This cache. */ + ResourceCache & operator=(const ResourceCache &) ENTT_NOEXCEPT = delete; + /*! @brief Default move assignment operator. @return This cache. */ + ResourceCache & operator=(ResourceCache &&) ENTT_NOEXCEPT = default; + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + size_type size() const ENTT_NOEXCEPT { + return resources.size(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return resources.empty(); + } + + /** + * @brief Clears a cache and discards all its resources. + * + * Handles are not invalidated and the memory used by a resource isn't + * freed as long as at least a handle keeps the resource itself alive. + */ + void clear() ENTT_NOEXCEPT { + resources.clear(); + } + + /** + * @brief Loads the resource that corresponds to a given identifier. + * + * In case an identifier isn't already present in the cache, it loads its + * resource and stores it aside for future uses. Arguments are forwarded + * directly to the loader in order to construct properly the requested + * resource. + * + * @note + * If the identifier is already present in the cache, this function does + * nothing and the arguments are simply discarded. + * + * @tparam Loader Type of loader to use to load the resource if required. + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return True if the resource is ready to use, false otherwise. + */ + template + bool load(const resource_type id, Args &&... args) { + static_assert(std::is_base_of, Loader>::value, "!"); + + bool loaded = true; + + if(resources.find(id) == resources.cend()) { + std::shared_ptr resource = Loader{}.get(std::forward(args)...); + loaded = (static_cast(resource) ? (resources[id] = std::move(resource), loaded) : false); + } + + return loaded; + } + + /** + * @brief Reloads a resource or loads it for the first time if not present. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * cache.discard(id); + * cache.load(id, args...); + * @endcode + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource. + * @return True if the resource is ready to use, false otherwise. + */ + template + bool reload(const resource_type id, Args &&... args) { + return (discard(id), load(id, std::forward(args)...)); + } + + /** + * @brief Creates a temporary handle for a resource. + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. The handle isn't stored aside and the + * cache isn't in charge of the lifetime of the resource itself. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param args Arguments to use to load the resource. + * @return A handle for the given resource. + */ + template + ResourceHandle temp(Args &&... args) const { + return { Loader{}.get(std::forward(args)...) }; + } + + /** + * @brief Creates a handle for a given resource identifier. + * + * A resource handle can be in a either valid or invalid state. In other + * terms, a resource handle is properly initialized with a resource if the + * cache contains the resource itself. Otherwise the returned handle is + * uninitialized and accessing it results in undefined behavior. + * + * @sa ResourceHandle + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + ResourceHandle handle(const resource_type id) const { + auto it = resources.find(id); + return { it == resources.end() ? nullptr : it->second }; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + bool contains(const resource_type id) const ENTT_NOEXCEPT { + return (resources.find(id) != resources.cend()); + } + + /** + * @brief Discards the resource that corresponds to a given identifier. + * + * Handles are not invalidated and the memory used by the resource isn't + * freed as long as at least a handle keeps the resource itself alive. + * + * @param id Unique resource identifier. + */ + void discard(const resource_type id) ENTT_NOEXCEPT { + auto it = resources.find(id); + + if(it != resources.end()) { + resources.erase(it); + } + } + +private: + container_type resources; +}; + + +} + + +#endif // ENTT_RESOURCE_CACHE_HPP diff --git a/external/entt/resource/handle.hpp b/external/entt/resource/handle.hpp new file mode 100644 index 00000000..ef54f30a --- /dev/null +++ b/external/entt/resource/handle.hpp @@ -0,0 +1,116 @@ +#ifndef ENTT_RESOURCE_HANDLE_HPP +#define ENTT_RESOURCE_HANDLE_HPP + + +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +template +class ResourceCache; + + +/** + * @brief Shared resource handle. + * + * A shared resource handle is a small class that wraps a resource and keeps it + * alive even if it's deleted from the cache. It can be either copied or + * moved. A handle shares a reference to the same resource with all the other + * handles constructed for the same identifier.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to keep references to them. + * + * @tparam Resource Type of resource managed by a handle. + */ +template +class ResourceHandle final { + /*! @brief Resource handles are friends of their caches. */ + friend class ResourceCache; + + ResourceHandle(std::shared_ptr res) ENTT_NOEXCEPT + : resource{std::move(res)} + {} + +public: + /*! @brief Default copy constructor. */ + ResourceHandle(const ResourceHandle &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + ResourceHandle(ResourceHandle &&) ENTT_NOEXCEPT = default; + + /*! @brief Default copy assignment operator. @return This handle. */ + ResourceHandle & operator=(const ResourceHandle &) ENTT_NOEXCEPT = default; + /*! @brief Default move assignment operator. @return This handle. */ + ResourceHandle & operator=(ResourceHandle &&) ENTT_NOEXCEPT = default; + + /** + * @brief Gets a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A reference to the managed resource. + */ + const Resource & get() const ENTT_NOEXCEPT { + assert(static_cast(resource)); + return *resource; + } + + /** + * @brief Casts a handle and gets a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + */ + inline operator const Resource &() const ENTT_NOEXCEPT { return get(); } + + /** + * @brief Dereferences a handle to obtain the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A reference to the managed resource. + */ + inline const Resource & operator *() const ENTT_NOEXCEPT { return get(); } + + /** + * @brief Gets a pointer to the managed resource from a handle. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A pointer to the managed resource or `nullptr` if the handle + * contains no resource at all. + */ + inline const Resource * operator ->() const ENTT_NOEXCEPT { + assert(static_cast(resource)); + return resource.get(); + } + + /** + * @brief Returns true if the handle contains a resource, false otherwise. + */ + explicit operator bool() const { return static_cast(resource); } + +private: + std::shared_ptr resource; +}; + + +} + + +#endif // ENTT_RESOURCE_HANDLE_HPP diff --git a/external/entt/resource/loader.hpp b/external/entt/resource/loader.hpp new file mode 100644 index 00000000..cd2ed2ef --- /dev/null +++ b/external/entt/resource/loader.hpp @@ -0,0 +1,62 @@ +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + + +#include + + +namespace entt { + + +template +class ResourceCache; + + +/** + * @brief Base class for resource loaders. + * + * Resource loaders must inherit from this class and stay true to the CRTP + * idiom. Moreover, a resource loader must expose a public, const member + * function named `load` that accepts a variable number of arguments and returns + * a shared pointer to the resource just created.
+ * As an example: + * + * @code{.cpp} + * struct MyResource {}; + * + * struct MyLoader: entt::ResourceLoader { + * std::shared_ptr load(int) const { + * // use the integer value somehow + * return std::make_shared(); + * } + * }; + * @endcode + * + * In general, resource loaders should not have a state or retain data of any + * type. They should let the cache manage their resources instead. + * + * @note + * Base class and CRTP idiom aren't strictly required with the current + * implementation. One could argue that a cache can easily work with loaders of + * any type. However, future changes won't be breaking ones by forcing the use + * of a base class today and that's why the model is already in its place. + * + * @tparam Loader Type of the derived class. + * @tparam Resource Type of resource for which to use the loader. + */ +template +class ResourceLoader { + /*! @brief Resource loaders are friends of their caches. */ + friend class ResourceCache; + + template + std::shared_ptr get(Args &&... args) const { + return static_cast(this)->load(std::forward(args)...); + } +}; + + +} + + +#endif // ENTT_RESOURCE_LOADER_HPP diff --git a/external/entt/signal/delegate.hpp b/external/entt/signal/delegate.hpp new file mode 100644 index 00000000..7fdd75c0 --- /dev/null +++ b/external/entt/signal/delegate.hpp @@ -0,0 +1,166 @@ +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class Delegate; + + +/** + * @brief Utility class to send around functions and member functions. + * + * Unmanaged delegate for function pointers and member functions. Users of this + * class are in charge of disconnecting instances before deleting them. + * + * A delegate can be used as general purpose invoker with no memory overhead for + * free functions and member functions provided along with an instance on which + * to invoke them. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class Delegate final { + using proto_fn_type = Ret(void *, Args...); + using stub_type = std::pair; + + template + static Ret proto(void *, Args... args) { + return (Function)(args...); + } + + template + static Ret proto(void *instance, Args... args) { + return (static_cast(instance)->*Member)(args...); + } + + template + static Ret proto(void *instance, Args... args) { + return (static_cast(instance)->*Member)(args...); + } + +public: + /*! @brief Default constructor. */ + Delegate() ENTT_NOEXCEPT + : stub{} + {} + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return True if the delegate is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + // no need to test also stub.first + return !stub.second; + } + + /** + * @brief Binds a free function to a delegate. + * @tparam Function A valid free function pointer. + */ + template + void connect() ENTT_NOEXCEPT { + stub = std::make_pair(nullptr, &proto); + } + + /** + * @brief Connects a member function for a given instance to a delegate. + * + * The delegate isn't responsible for the connected object. Users must + * guarantee that the lifetime of the instance overcomes the one of the + * delegate. + * + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the delegate. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void connect(Class *instance) ENTT_NOEXCEPT { + stub = std::make_pair(instance, &proto); + } + + /** + * @brief Connects a member function for a given instance to a delegate. + * + * The delegate isn't responsible for the connected object. Users must + * guarantee that the lifetime of the instance overcomes the one of the + * delegate. + * + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the delegate. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void connect(Class *instance) ENTT_NOEXCEPT { + stub = std::make_pair(instance, &proto); + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate can be safely invoked with no effect. + */ + void reset() ENTT_NOEXCEPT { + stub.second = nullptr; + } + + /** + * @brief Triggers a delegate. + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + return stub.second(stub.first, args...); + } + + /** + * @brief Checks if the contents of the two delegates are different. + * + * Two delegates are identical if they contain the same listener. + * + * @param other Delegate with which to compare. + * @return True if the two delegates are identical, false otherwise. + */ + bool operator==(const Delegate &other) const ENTT_NOEXCEPT { + return stub.first == other.stub.first && stub.second == other.stub.second; + } + +private: + stub_type stub; +}; + + +/** + * @brief Checks if the contents of the two delegates are different. + * + * Two delegates are identical if they contain the same listener. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two delegates are different, false otherwise. + */ +template +bool operator!=(const Delegate &lhs, const Delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +} + + +#endif // ENTT_SIGNAL_DELEGATE_HPP diff --git a/external/entt/signal/dispatcher.hpp b/external/entt/signal/dispatcher.hpp new file mode 100644 index 00000000..8743ebd7 --- /dev/null +++ b/external/entt/signal/dispatcher.hpp @@ -0,0 +1,188 @@ +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + + +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/family.hpp" +#include "sigh.hpp" + + +namespace entt { + + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.
+ * Listeners are provided in the form of member functions. For each event of + * type `Event`, listeners must have the following function type: + * @code{.cpp} + * void(const Event &) + * @endcode + * + * Member functions named `receive` are automatically detected and registered or + * unregistered by the dispatcher. The type of the instances is `Class *` (a + * naked pointer). It means that users must guarantee that the lifetimes of the + * instances overcome the one of the dispatcher itself to avoid crashes. + */ +class Dispatcher final { + using event_family = Family; + + template + using instance_type = typename SigH::template instance_type; + + struct BaseSignalWrapper { + virtual ~BaseSignalWrapper() = default; + virtual void publish() = 0; + }; + + template + struct SignalWrapper final: BaseSignalWrapper { + using sink_type = typename SigH::sink_type; + + void publish() override { + const auto &curr = current++; + current %= std::extent::value; + std::for_each(events[curr].cbegin(), events[curr].cend(), [this](const auto &event) { signal.publish(event); }); + events[curr].clear(); + } + + inline sink_type sink() ENTT_NOEXCEPT { + return signal.sink(); + } + + template + inline void trigger(Args &&... args) { + signal.publish({ std::forward(args)... }); + } + + template + inline void enqueue(Args &&... args) { + events[current].push_back({ std::forward(args)... }); + } + + private: + SigH signal{}; + std::vector events[2]; + int current{}; + }; + + template + SignalWrapper & wrapper() { + const auto type = event_family::type(); + + if(!(type < wrappers.size())) { + wrappers.resize(type + 1); + } + + if(!wrappers[type]) { + wrappers[type] = std::make_unique>(); + } + + return static_cast &>(*wrappers[type]); + } + +public: + /*! @brief Type of sink for the given event. */ + template + using sink_type = typename SignalWrapper::sink_type; + + /** + * @brief Returns a sink object for the given event. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is: + * @code{.cpp} + * void(const Event &) + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa SigH::Sink + * + * @tparam Event Type of event of which to get the sink. + * @return A temporary sink object. + */ + template + inline sink_type sink() ENTT_NOEXCEPT { + return wrapper().sink(); + } + + /** + * @brief Triggers an immediate event of the given type. + * + * All the listeners registered for the given type are immediately notified. + * The event is discarded after the execution. + * + * @tparam Event Type of event to trigger. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + inline void trigger(Args &&... args) { + wrapper().trigger(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * + * An event of the given type is queued. No listener is invoked. Use the + * `update` member function to notify listeners when ready. + * + * @tparam Event Type of event to trigger. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + inline void enqueue(Args &&... args) { + wrapper().enqueue(std::forward(args)...); + } + + /** + * @brief Delivers all the pending events of the given type. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + * + * @tparam Event Type of events to send. + */ + template + inline void update() { + wrapper().publish(); + } + + /** + * @brief Delivers all the pending events. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + */ + inline void update() const { + for(auto pos = wrappers.size(); pos; --pos) { + auto &wrapper = wrappers[pos-1]; + + if(wrapper) { + wrapper->publish(); + } + } + } + +private: + std::vector> wrappers; +}; + + +} + + +#endif // ENTT_SIGNAL_DISPATCHER_HPP diff --git a/external/entt/signal/emitter.hpp b/external/entt/signal/emitter.hpp new file mode 100644 index 00000000..39ef3d1b --- /dev/null +++ b/external/entt/signal/emitter.hpp @@ -0,0 +1,336 @@ +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../config/config.h" +#include "../core/family.hpp" + + +namespace entt { + + +/** + * @brief General purpose event emitter. + * + * The emitter class template follows the CRTP idiom. To create a custom emitter + * type, derived classes must inherit directly from the base class as: + * + * ```cpp + * struct MyEmitter: Emitter { + * // ... + * } + * ``` + * + * Handlers for the type of events are created internally on the fly. It's not + * required to specify in advance the full list of accepted types.
+ * Moreover, whenever an event is published, an emitter provides the listeners + * with a reference to itself along with a const reference to the event. + * Therefore listeners have an handy way to work with it without incurring in + * the need of capturing a reference to the emitter. + * + * @tparam Derived Actual type of emitter that extends the class template. + */ +template +class Emitter { + using handler_family = Family; + + struct BaseHandler { + virtual ~BaseHandler() = default; + virtual bool empty() const ENTT_NOEXCEPT = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + }; + + template + struct Handler final: BaseHandler { + using listener_type = std::function; + using element_type = std::pair; + using container_type = std::list; + using connection_type = typename container_type::iterator; + + bool empty() const ENTT_NOEXCEPT override { + auto pred = [](auto &&element) { return element.first; }; + + return std::all_of(onceL.cbegin(), onceL.cend(), pred) && + std::all_of(onL.cbegin(), onL.cend(), pred); + } + + void clear() ENTT_NOEXCEPT override { + if(publishing) { + auto func = [](auto &&element) { element.first = true; }; + std::for_each(onceL.begin(), onceL.end(), func); + std::for_each(onL.begin(), onL.end(), func); + } else { + onceL.clear(); + onL.clear(); + } + } + + inline connection_type once(listener_type listener) { + return onceL.emplace(onceL.cend(), false, std::move(listener)); + } + + inline connection_type on(listener_type listener) { + return onL.emplace(onL.cend(), false, std::move(listener)); + } + + void erase(connection_type conn) ENTT_NOEXCEPT { + conn->first = true; + + if(!publishing) { + auto pred = [](auto &&element) { return element.first; }; + onceL.remove_if(pred); + onL.remove_if(pred); + } + } + + void publish(const Event &event, Derived &ref) { + container_type currentL; + onceL.swap(currentL); + + auto func = [&event, &ref](auto &&element) { + return element.first ? void() : element.second(event, ref); + }; + + publishing = true; + + std::for_each(onL.rbegin(), onL.rend(), func); + std::for_each(currentL.rbegin(), currentL.rend(), func); + + publishing = false; + + onL.remove_if([](auto &&element) { return element.first; }); + } + + private: + bool publishing{false}; + container_type onceL{}; + container_type onL{}; + }; + + template + Handler & handler() ENTT_NOEXCEPT { + const std::size_t family = handler_family::type(); + + if(!(family < handlers.size())) { + handlers.resize(family+1); + } + + if(!handlers[family]) { + handlers[family] = std::make_unique>(); + } + + return static_cast &>(*handlers[family]); + } + +public: + /** @brief Type of listeners accepted for the given event. */ + template + using Listener = typename Handler::listener_type; + + /** + * @brief Generic connection type for events. + * + * Type of the connection object returned by the event emitter whenever a + * listener for the given type is registered.
+ * It can be used to break connections still in use. + * + * @tparam Event Type of event for which the connection is created. + */ + template + struct Connection final: private Handler::connection_type { + /** @brief Event emitters are friend classes of connections. */ + friend class Emitter; + + /*! @brief Default constructor. */ + Connection() ENTT_NOEXCEPT = default; + + /** + * @brief Creates a connection that wraps its underlying instance. + * @param conn A connection object to wrap. + */ + Connection(typename Handler::connection_type conn) + : Handler::connection_type{std::move(conn)} + {} + + /*! @brief Default copy constructor. */ + Connection(const Connection &) = default; + /*! @brief Default move constructor. */ + Connection(Connection &&) = default; + + /** + * @brief Default copy assignment operator. + * @return This connection. + */ + Connection & operator=(const Connection &) = default; + + /** + * @brief Default move assignment operator. + * @return This connection. + */ + Connection & operator=(Connection &&) = default; + }; + + /*! @brief Default constructor. */ + Emitter() ENTT_NOEXCEPT = default; + + /*! @brief Default destructor. */ + virtual ~Emitter() ENTT_NOEXCEPT { + static_assert(std::is_base_of, Derived>::value, "!"); + } + + /*! @brief Copying an emitter isn't allowed. */ + Emitter(const Emitter &) = delete; + /*! @brief Default move constructor. */ + Emitter(Emitter &&) = default; + + /*! @brief Copying an emitter isn't allowed. @return This emitter. */ + Emitter & operator=(const Emitter &) = delete; + /*! @brief Default move assignment operator. @return This emitter. */ + Emitter & operator=(Emitter &&) = default; + + /** + * @brief Emits the given event. + * + * All the listeners registered for the specific event type are invoked with + * the given event. The event type must either have a proper constructor for + * the arguments provided or be an aggregate type. + * + * @tparam Event Type of event to publish. + * @tparam Args Types of arguments to use to construct the event. + * @param args Parameters to use to initialize the event. + */ + template + void publish(Args &&... args) { + handler().publish({ std::forward(args)... }, *static_cast(this)); + } + + /** + * @brief Registers a long-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * more than once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is `void(const Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param listener The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + Connection on(Listener listener) { + return handler().on(std::move(listener)); + } + + /** + * @brief Registers a short-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * only once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is `void(const Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param listener The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + Connection once(Listener listener) { + return handler().once(std::move(listener)); + } + + /** + * @brief Disconnects a listener from the event emitter. + * + * Do not use twice the same connection to disconnect a listener, it results + * in undefined behavior. Once used, discard the connection object. + * + * @tparam Event Type of event of the connection. + * @param conn A valid connection. + */ + template + void erase(Connection conn) ENTT_NOEXCEPT { + handler().erase(std::move(conn)); + } + + /** + * @brief Disconnects all the listeners for the given event type. + * + * All the connections previously returned for the given event are + * invalidated. Using them results in undefined behavior. + * + * @tparam Event Type of event to reset. + */ + template + void clear() ENTT_NOEXCEPT { + handler().clear(); + } + + /** + * @brief Disconnects all the listeners. + * + * All the connections previously returned are invalidated. Using them + * results in undefined behavior. + */ + void clear() ENTT_NOEXCEPT { + std::for_each(handlers.begin(), handlers.end(), [](auto &&handler) { + return handler ? handler->clear() : void(); + }); + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Event Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + bool empty() const ENTT_NOEXCEPT { + const std::size_t family = handler_family::type(); + + return (!(family < handlers.size()) || + !handlers[family] || + static_cast &>(*handlers[family]).empty()); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&handler) { + return !handler || handler->empty(); + }); + } + +private: + std::vector> handlers{}; +}; + + +} + + +#endif // ENTT_SIGNAL_EMITTER_HPP diff --git a/external/entt/signal/sigh.hpp b/external/entt/signal/sigh.hpp new file mode 100644 index 00000000..d6f795fd --- /dev/null +++ b/external/entt/signal/sigh.hpp @@ -0,0 +1,426 @@ +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include "../config/config.h" + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct sigh_traits; + + +template +struct sigh_traits { + using proto_fn_type = Ret(void *, Args...); + using call_type = std::pair; +}; + + +template +struct Invoker; + + +template +struct Invoker { + using proto_fn_type = typename sigh_traits::proto_fn_type; + + virtual ~Invoker() = default; + + bool invoke(Collector &collector, proto_fn_type *proto, void *instance, Args... args) const { + return collector(proto(instance, args...)); + } +}; + + +template +struct Invoker { + using proto_fn_type = typename sigh_traits::proto_fn_type; + + virtual ~Invoker() = default; + + bool invoke(Collector &, proto_fn_type *proto, void *instance, Args... args) const { + return (proto(instance, args...), true); + } +}; + + +template +struct NullCollector final { + using result_type = Ret; + bool operator()(result_type) const ENTT_NOEXCEPT { return true; } +}; + + +template<> +struct NullCollector final { + using result_type = void; + bool operator()() const ENTT_NOEXCEPT { return true; } +}; + + +template +struct DefaultCollector; + + +template +struct DefaultCollector final { + using collector_type = NullCollector; +}; + + +template +using DefaultCollectorType = typename DefaultCollector::collector_type; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Sink implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class Sink; + + +/** + * @brief Unmanaged signal handler declaration. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + * @tparam Collector Type of collector to use, if any. + */ +template> +class SigH; + + +/** + * @brief Sink implementation. + * + * A sink is an opaque object used to connect listeners to signals.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the + * former as private data member without exposing the publish functionality + * to the users of a class. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class Sink final { + /*! @brief A signal is allowed to create sinks. */ + template + friend class SigH; + + using call_type = typename internal::sigh_traits::call_type; + + template + static Ret proto(void *, Args... args) { + return (Function)(args...); + } + + template + static Ret proto(void *instance, Args... args) { + return (static_cast(instance)->*Member)(args...); + } + + template + static Ret proto(void *instance, Args... args) { + return (static_cast(instance)->*Member)(args...); + } + + Sink(std::vector *calls) ENTT_NOEXCEPT + : calls{calls} + {} + +public: + /** + * @brief Connects a free function to a signal. + * + * The signal handler performs checks to avoid multiple connections for + * free functions. + * + * @tparam Function A valid free function pointer. + */ + template + void connect() { + disconnect(); + calls->emplace_back(nullptr, &proto); + } + + /** + * @brief Connects a member function for a given instance to a signal. + * + * The signal isn't responsible for the connected object. Users must + * guarantee that the lifetime of the instance overcomes the one of the + * signal. On the other side, the signal handler performs checks to + * avoid multiple connections for the same member function of a given + * instance. + * + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the signal. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void connect(Class *instance) { + disconnect(instance); + calls->emplace_back(instance, &proto); + } + + /** + * @brief Connects a member function for a given instance to a signal. + * + * The signal isn't responsible for the connected object. Users must + * guarantee that the lifetime of the instance overcomes the one of the + * signal. On the other side, the signal handler performs checks to + * avoid multiple connections for the same member function of a given + * instance. + * + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the signal. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void connect(Class *instance) { + disconnect(instance); + calls->emplace_back(instance, &proto); + } + + /** + * @brief Disconnects a free function from a signal. + * @tparam Function A valid free function pointer. + */ + template + void disconnect() { + call_type target{nullptr, &proto}; + calls->erase(std::remove(calls->begin(), calls->end(), std::move(target)), calls->end()); + } + + /** + * @brief Disconnects the given member function from a signal. + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the signal. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void disconnect(Class *instance) { + call_type target{instance, &proto}; + calls->erase(std::remove(calls->begin(), calls->end(), std::move(target)), calls->end()); + } + + /** + * @brief Disconnects the given member function from a signal. + * @tparam Class Type of class to which the member function belongs. + * @tparam Member Member function to connect to the signal. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void disconnect(Class *instance) { + call_type target{instance, &proto}; + calls->erase(std::remove(calls->begin(), calls->end(), std::move(target)), calls->end()); + } + + /** + * @brief Removes all existing connections for the given instance. + * @tparam Class Type of class to which the member function belongs. + * @param instance A valid instance of type pointer to `Class`. + */ + template + void disconnect(Class *instance) { + auto func = [instance](const call_type &call) { return call.first == instance; }; + calls->erase(std::remove_if(calls->begin(), calls->end(), std::move(func)), calls->end()); + } + + /** + * @brief Disconnects all the listeners from a signal. + */ + void disconnect() { + calls->clear(); + } + +private: + std::vector *calls; +}; + + +/** + * @brief Unmanaged signal handler definition. + * + * Unmanaged signal handler. It works directly with naked pointers to classes + * and pointers to member functions as well as pointers to free functions. Users + * of this class are in charge of disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals used later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * The default collector does nothing. To properly collect data, define and use + * a class that has a call operator the signature of which is `bool(Param)` and: + * + * * `Param` is a type to which `Ret` can be converted. + * * The return type is true if the handler must stop collecting data, false + * otherwise. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Collector Type of collector to use, if any. + */ +template +class SigH final: private internal::Invoker { + using call_type = typename internal::sigh_traits::call_type; + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename std::vector::size_type; + /*! @brief Collector type. */ + using collector_type = Collector; + /*! @brief Sink type. */ + using sink_type = Sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Returns a sink object for the given signal. + * + * A sink is an opaque object used to connect listeners to signals.
+ * The function type for a listener is the one of the signal to which it + * belongs. The order of invocation of the listeners isn't guaranteed. + * + * @return A temporary sink object. + */ + sink_type sink() ENTT_NOEXCEPT { + return { &calls }; + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + auto &call = calls[pos-1]; + call.second(call.first, args...); + } + } + + /** + * @brief Collects return values from the listeners. + * @param args Arguments to use to invoke listeners. + * @return An instance of the collector filled with collected data. + */ + collector_type collect(Args... args) const { + collector_type collector; + + for(auto &&call: calls) { + if(!this->invoke(collector, call.second, call.first, args...)) { + break; + } + } + + return collector; + } + + /** + * @brief Swaps listeners between the two signals. + * @param lhs A valid signal object. + * @param rhs A valid signal object. + */ + friend void swap(SigH &lhs, SigH &rhs) { + using std::swap; + swap(lhs.calls, rhs.calls); + } + + /** + * @brief Checks if the contents of the two signals are identical. + * + * Two signals are identical if they have the same size and the same + * listeners registered exactly in the same order. + * + * @param other Signal with which to compare. + * @return True if the two signals are identical, false otherwise. + */ + bool operator==(const SigH &other) const ENTT_NOEXCEPT { + return std::equal(calls.cbegin(), calls.cend(), other.calls.cbegin(), other.calls.cend()); + } + +private: + std::vector calls; +}; + + +/** + * @brief Checks if the contents of the two signals are different. + * + * Two signals are identical if they have the same size and the same + * listeners registered exactly in the same order. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid signal object. + * @param rhs A valid signal object. + * @return True if the two signals are different, false otherwise. + */ +template +bool operator!=(const SigH &lhs, const SigH &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +} + + +#endif // ENTT_SIGNAL_SIGH_HPP diff --git a/resources/shaders/color.f.glsl b/resources/shaders/color.f.glsl deleted file mode 100644 index 348318a7..00000000 --- a/resources/shaders/color.f.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version 120 - -varying vec4 v_color; -varying vec2 v_texCoord; - -uniform sampler2D u_tex; - -vec4 getColor() { - if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) { - return texture2D(u_tex, v_texCoord); - } - - return v_color; -} diff --git a/resources/shaders/game.f.glsl b/resources/shaders/game.f.glsl index 6a2c8a07..18114e83 100644 --- a/resources/shaders/game.f.glsl +++ b/resources/shaders/game.f.glsl @@ -2,6 +2,7 @@ varying vec4 v_coord3d; varying vec4 v_color; +varying vec2 v_texCoord; varying vec2 v_lightValue; varying float v_ambientOcclusion; @@ -10,8 +11,7 @@ varying float v_dist; uniform int u_renderDistance; -// Get current pixel color -vec4 getColor(); +uniform sampler2D u_tex; // Get light color vec4 light(vec4 color, vec3 lightColor, vec4 lightPosition, float ambientIntensity, float diffuseIntensity); @@ -29,9 +29,12 @@ void main() { if(blockFace > -1. && v_dist > u_renderDistance) discard; // Get current pixel color and apply multiplier on grayscale textures - vec4 color = getColor(); - if (blockFace > -1 && color != v_color && color.r == color.g && color.g == color.b) { - color *= v_color; + vec4 color = v_color; + if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) { + color = texture2D(u_tex, v_texCoord); + if (blockFace > -1 && color.r == color.g && color.g == color.b) { + color *= v_color; + } } // Block breaking animation diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index 3f74dd31..a1d91258 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -260,6 +260,7 @@ void BlockCursor::draw(gk::RenderTarget &target, gk::RenderStates states) const glCheck(glDisable(GL_POLYGON_OFFSET_FILL)); glCheck(glDisable(GL_CULL_FACE)); + glCheck(glEnable(GL_DEPTH_TEST)); // Subtract the camera position - see comment in ClientWorld::draw() gk::Vector3d cameraPosition = m_player.camera().getDPosition(); diff --git a/source/client/scene/Scene.cpp b/source/client/scene/Scene.cpp new file mode 100644 index 00000000..fd0bdbd6 --- /dev/null +++ b/source/client/scene/Scene.cpp @@ -0,0 +1,64 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "Scene.hpp" + +struct RotationAnimation { + float axisX; + float axisY; + float axisZ; + + float angle; +}; + +Scene::Scene() { + auto testEntity = m_registry.create(); + + gk::BoxShape &shape = m_registry.assign(testEntity, 0.25f, 0.25f, 0.25f); + shape.setOrigin(shape.getSize().x / 2.f, shape.getSize().y / 2.f, shape.getSize().z / 2.f); + shape.setPosition(13 + shape.getOrigin().x, 13 + shape.getOrigin().y, 16 + shape.getOrigin().z); + + m_registry.assign(testEntity, 0.f, 0.f, 1.f, 1.f); +} + +void Scene::update() { + m_registry.view().each([](auto, auto &boxShape, auto &rotation) { + boxShape.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); + }); +} + +void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { + if (!m_camera) return; + + // Subtract the camera position - see comment in ClientWorld::draw() + gk::Vector3d cameraPosition = m_camera->getDPosition(); + states.transform.translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z); + + m_registry.view().each([&](auto, auto &boxShape) { + target.draw(boxShape, states); + }); +} + diff --git a/source/client/scene/Scene.hpp b/source/client/scene/Scene.hpp new file mode 100644 index 00000000..60ebddca --- /dev/null +++ b/source/client/scene/Scene.hpp @@ -0,0 +1,54 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef SCENE_HPP_ +#define SCENE_HPP_ + +#include +#include +#include + +#include + +class Scene : public gk::Drawable { + public: + Scene(); + + void update(); + + void setCamera(gk::Camera &camera) { m_camera = &camera; } + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + gk::Camera *m_camera = nullptr; + + gk::BoxShape m_testBox; + + mutable entt::DefaultRegistry m_registry; +}; + +#endif // SCENE_HPP_ diff --git a/source/client/states/GameState.cpp b/source/client/states/GameState.cpp index 4bb674b8..a9146c70 100644 --- a/source/client/states/GameState.cpp +++ b/source/client/states/GameState.cpp @@ -156,7 +156,6 @@ void GameState::initShaders() { m_shader.createProgram(); m_shader.addShader(GL_VERTEX_SHADER, "resources/shaders/game.v.glsl"); - m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/color.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/light.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/fog.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/game.f.glsl"); diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index 33243e31..74dcf6f0 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -66,6 +66,8 @@ void ClientWorld::update() { World::isReloadRequested = false; sendChunkRequests(); + + m_scene.update(); } void ClientWorld::sendChunkRequests() { @@ -315,5 +317,8 @@ void ClientWorld::draw(gk::RenderTarget &target, gk::RenderStates states) const } m_camera->setDPosition(cameraPos); // Restore the camera to its original position + + states.transform = gk::Transform::Identity; + target.draw(m_scene, states); } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index fa70516e..c5b6a91c 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -34,6 +34,7 @@ #include "ClientChunk.hpp" #include "Network.hpp" +#include "Scene.hpp" #include "World.hpp" class ClientCommandHandler; @@ -60,7 +61,7 @@ class ClientWorld : public World, public gk::Drawable { Chunk *getChunk(int cx, int cy, int cz) const override; void setClient(ClientCommandHandler &client) { m_client = &client; } - void setCamera(gk::Camera &camera) { m_camera = &camera; } + void setCamera(gk::Camera &camera) { m_camera = &camera; m_scene.setCamera(camera); } std::size_t loadedChunkCount() const { return m_chunks.size(); } @@ -79,6 +80,8 @@ class ClientWorld : public World, public gk::Drawable { mutable gk::Vector4f m_closestInitializedChunk{0, 0, 0, 1000000}; const Sky *m_sky = nullptr; + + Scene m_scene; }; #endif // CLIENTWORLD_HPP_