From 3c375f861f1054a58a652c22a9c57acce8c11b38 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 4 Apr 2020 20:26:02 +0200 Subject: [PATCH 01/38] [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 000000000..367798c01 --- /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 000000000..9ac0a9068 --- /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 000000000..11cf82f99 --- /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 000000000..bea5db0ab --- /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 000000000..42503d876 --- /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 000000000..f0fa47fc0 --- /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 000000000..435aa87ba --- /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 000000000..b972c7d38 --- /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 000000000..4ef2265bc --- /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 000000000..df2cb60d9 --- /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 000000000..90eab876b --- /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 000000000..82c6bcb68 --- /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 000000000..4916ce30a --- /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 000000000..e9d1128fa --- /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 000000000..fc0e32ceb --- /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 000000000..31f138e32 --- /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 000000000..214f862af --- /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 000000000..ceb1fefa4 --- /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 000000000..2c66ca388 --- /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 000000000..2077b0434 --- /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 000000000..c7729bbe9 --- /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 000000000..032d26998 --- /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 000000000..ef54f30ae --- /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 000000000..cd2ed2ef3 --- /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 000000000..7fdd75c0f --- /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 000000000..8743ebd7a --- /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 000000000..39ef3d1b5 --- /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 000000000..d6f795fd7 --- /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 348318a72..000000000 --- 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 6a2c8a07c..18114e832 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 3f74dd31a..a1d912586 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 000000000..fd0bdbd6c --- /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 000000000..60ebddcaf --- /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 4bb674b86..a9146c703 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 33243e318..74dcf6f0d 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 fa70516e0..c5b6a91c7 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_ From c725adccc41a9d07b0e6b6748441d6d4e5d2b11a Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 4 Apr 2020 22:50:43 +0200 Subject: [PATCH 02/38] [Scene] Test item drop entity almost working. --- source/client/gui/InventoryCube.cpp | 22 ++++++++------- source/client/gui/InventoryCube.hpp | 6 +++-- source/client/scene/Scene.cpp | 42 ++++++++++++++++++++++++++++- source/client/scene/Scene.hpp | 10 ++++++- source/client/states/GameState.hpp | 2 +- source/client/world/ClientWorld.cpp | 2 +- source/client/world/ClientWorld.hpp | 6 ++--- 7 files changed, 72 insertions(+), 18 deletions(-) diff --git a/source/client/gui/InventoryCube.cpp b/source/client/gui/InventoryCube.cpp index 766b0b3db..f8da8e9c1 100644 --- a/source/client/gui/InventoryCube.cpp +++ b/source/client/gui/InventoryCube.cpp @@ -39,16 +39,20 @@ #include "TextureAtlas.hpp" #include "Vertex.hpp" -InventoryCube::InventoryCube(float size) : m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) { +InventoryCube::InventoryCube(float size, bool isEntity) + : m_textureAtlas(&gk::ResourceHandler::getInstance().get("atlas-blocks")) +{ m_size = size; - m_transform.setOrigin(size * 0.5, size * 0.5, size * 0.5); + if (!isEntity) { + m_transform.setOrigin(size * 0.5, size * 0.5, size * 0.5); - // NOTE: intrinsic rotations! The axis is the local axis of the object. - // Note also that we start looking at the bottom of the cube due to how - // glm::orto is used (see comment below). - m_transform.rotate(120.f, {1, 0, 0}); - m_transform.rotate(-45.f, {0, 0, 1}); + // NOTE: intrinsic rotations! The axis is the local axis of the object. + // Note also that we start looking at the bottom of the cube due to how + // glm::ortho is used (see comment below). + m_transform.rotate(120.f, {1, 0, 0}); + m_transform.rotate(-45.f, {0, 0, 1}); + } } using namespace BlockGeometry; @@ -96,7 +100,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) { V1 = (f <= 3) ? 1.f - boundingBox.z : (f == 4) ? boundingBox.y + boundingBox.sizeY : 1.f - boundingBox.y; } - const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block.tiles().getTextureForFace(f)); + const gk::FloatRect &blockTexCoords = m_textureAtlas->getTexCoords(block.tiles().getTextureForFace(f)); for (u8f v = 0; v < nVertsPerFace; ++v) { if (block.drawType() == BlockDrawType::Cactus) { @@ -144,7 +148,7 @@ void InventoryCube::draw(gk::RenderTarget &target, gk::RenderStates states) cons // at start. states.projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR); - states.texture = &m_textureAtlas.texture(); + states.texture = &m_textureAtlas->texture(); states.vertexAttributes = VertexAttribute::Basic; glCheck(glEnable(GL_CULL_FACE)); diff --git a/source/client/gui/InventoryCube.hpp b/source/client/gui/InventoryCube.hpp index 1419bf2d1..c668279ee 100644 --- a/source/client/gui/InventoryCube.hpp +++ b/source/client/gui/InventoryCube.hpp @@ -36,16 +36,18 @@ class TextureAtlas; class InventoryCube : public gk::Drawable, public gk::Transformable { public: - InventoryCube(float size = 1.0f); + InventoryCube(float size = 1.0f, bool isEntity = false); void updateVertexBuffer(const Block &block); + float size() const { return m_size; } + private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; float m_size = 1.0f; - const TextureAtlas &m_textureAtlas; + const TextureAtlas *m_textureAtlas; gk::VertexBuffer m_vbo; bool m_isVboInitialized = false; diff --git a/source/client/scene/Scene.cpp b/source/client/scene/Scene.cpp index fd0bdbd6c..1ea0165dd 100644 --- a/source/client/scene/Scene.cpp +++ b/source/client/scene/Scene.cpp @@ -24,6 +24,7 @@ * * ===================================================================================== */ +#include "ClientPlayer.hpp" #include "Scene.hpp" struct RotationAnimation { @@ -34,7 +35,14 @@ struct RotationAnimation { float angle; }; -Scene::Scene() { +#include "InventoryCube.hpp" +#include "ItemStack.hpp" +#include "Registry.hpp" + +Scene::Scene(ClientPlayer &player) : m_player(player) { +} + +void Scene::init() { auto testEntity = m_registry.create(); gk::BoxShape &shape = m_registry.assign(testEntity, 0.25f, 0.25f, 0.25f); @@ -42,12 +50,40 @@ Scene::Scene() { 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); + m_registry.assign(testEntity, 0., 0., 0., shape.getSize().x, shape.getSize().y, shape.getSize().z); + m_registry.assign(testEntity, "default:stick", 1); + + auto testEntity2 = m_registry.create(); + + InventoryCube &cube = m_registry.assign(testEntity2, 0.25f, true); + cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); + cube.setPosition(14 + cube.getOrigin().x, 13 + cube.getOrigin().y, 16 + cube.getOrigin().z); + cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID("default:cobblestone")); + + m_registry.assign(testEntity2, 0.f, 0.f, 1.f, 1.f); + + m_isInitialized = true; } void Scene::update() { + if (!m_isInitialized) init(); + m_registry.view().each([](auto, auto &boxShape, auto &rotation) { boxShape.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); }); + + m_registry.view().each([](auto, auto &cube, auto &rotation) { + cube.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); + }); + + m_registry.view().each([this](auto entity, auto &boxShape, auto &box, auto &itemStack) { + gk::DoubleBox hitbox = box + boxShape.getPosition(); + gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; + if (hitbox.intersects(playerHitbox)) { + m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + m_registry.destroy(entity); + } + }); } void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { @@ -60,5 +96,9 @@ void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { m_registry.view().each([&](auto, auto &boxShape) { target.draw(boxShape, states); }); + + m_registry.view().each([&](auto, auto &cube) { + target.draw(cube, states); + }); } diff --git a/source/client/scene/Scene.hpp b/source/client/scene/Scene.hpp index 60ebddcaf..170715c18 100644 --- a/source/client/scene/Scene.hpp +++ b/source/client/scene/Scene.hpp @@ -33,9 +33,13 @@ #include +class ClientPlayer; + class Scene : public gk::Drawable { public: - Scene(); + Scene(ClientPlayer &player); + + void init(); void update(); @@ -44,6 +48,10 @@ class Scene : public gk::Drawable { private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + bool m_isInitialized = false; + + ClientPlayer &m_player; + gk::Camera *m_camera = nullptr; gk::BoxShape m_testBox; diff --git a/source/client/states/GameState.hpp b/source/client/states/GameState.hpp index 52df5c93c..ad933f630 100644 --- a/source/client/states/GameState.hpp +++ b/source/client/states/GameState.hpp @@ -79,7 +79,7 @@ class GameState : public gk::ApplicationState { Client m_client; - ClientWorld m_world; + ClientWorld m_world{m_player}; std::unordered_map m_playerBoxes; diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index 74dcf6f0d..3c191108b 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -39,7 +39,7 @@ #include "TextureAtlas.hpp" #include "World.hpp" -ClientWorld::ClientWorld() : +ClientWorld::ClientWorld(ClientPlayer &player) : m_scene(player), m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) { } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index c5b6a91c7..63f9c3ba6 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -45,7 +45,7 @@ class ClientWorld : public World, public gk::Drawable { using ChunkMap = std::unordered_map>; public: - ClientWorld(); + ClientWorld(ClientPlayer &player); void update(); void sendChunkRequests(); @@ -70,6 +70,8 @@ class ClientWorld : public World, public gk::Drawable { void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + Scene m_scene; + ChunkMap m_chunks; TextureAtlas &m_textureAtlas; @@ -80,8 +82,6 @@ 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_ From 86ace0d17e7cb0103245e55630051d35b0156b8b Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 4 Apr 2020 23:58:55 +0200 Subject: [PATCH 03/38] [InventoryCube] Texture rendering fixed. --- source/client/core/ClientApplication.cpp | 2 +- source/client/core/Vertex.hpp | 2 +- source/client/graphics/PlayerBox.cpp | 2 +- source/client/gui/InventoryCube.cpp | 2 +- source/client/scene/Scene.cpp | 28 +++++------------------- 5 files changed, 10 insertions(+), 26 deletions(-) diff --git a/source/client/core/ClientApplication.cpp b/source/client/core/ClientApplication.cpp index a2e421f16..01767d99c 100644 --- a/source/client/core/ClientApplication.cpp +++ b/source/client/core/ClientApplication.cpp @@ -58,8 +58,8 @@ void ClientApplication::init() { gk::CoreApplication::init(); m_window.addVertexAttribute(VertexAttribute::Coord3d, "coord3d", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, coord3d))); - m_window.addVertexAttribute(VertexAttribute::Color, "color", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color))); m_window.addVertexAttribute(VertexAttribute::TexCoord, "texCoord", 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoord))); + m_window.addVertexAttribute(VertexAttribute::Color, "color", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color))); m_window.addVertexAttribute(VertexAttribute::Normal, "normal", 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); m_window.addVertexAttribute(VertexAttribute::LightValue, "lightValue", 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, lightValue))); m_window.addVertexAttribute(VertexAttribute::AmbientOcclusion, "ambientOcclusion", 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, ambientOcclusion))); diff --git a/source/client/core/Vertex.hpp b/source/client/core/Vertex.hpp index 59d430ebe..2dc52758e 100644 --- a/source/client/core/Vertex.hpp +++ b/source/client/core/Vertex.hpp @@ -48,7 +48,7 @@ namespace VertexAttribute { AmbientOcclusion = 256, Basic = Coord3d | TexCoord | Color, - All = 0xffff + All = Basic | Normal | LightValue | AmbientOcclusion }; } diff --git a/source/client/graphics/PlayerBox.cpp b/source/client/graphics/PlayerBox.cpp index d8dca989d..ec5dc5cbb 100644 --- a/source/client/graphics/PlayerBox.cpp +++ b/source/client/graphics/PlayerBox.cpp @@ -277,7 +277,7 @@ void PlayerBox::draw(gk::RenderTarget &target, gk::RenderStates states) const { states.transform *= getTransform(); states.texture = &m_texture; - states.vertexAttributes = VertexAttribute::Basic; + states.vertexAttributes = VertexAttribute::All; glCheck(glEnable(GL_CULL_FACE)); diff --git a/source/client/gui/InventoryCube.cpp b/source/client/gui/InventoryCube.cpp index f8da8e9c1..0c228d11b 100644 --- a/source/client/gui/InventoryCube.cpp +++ b/source/client/gui/InventoryCube.cpp @@ -149,7 +149,7 @@ void InventoryCube::draw(gk::RenderTarget &target, gk::RenderStates states) cons states.projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR); states.texture = &m_textureAtlas->texture(); - states.vertexAttributes = VertexAttribute::Basic; + states.vertexAttributes = VertexAttribute::All; glCheck(glEnable(GL_CULL_FACE)); glCheck(glEnable(GL_DEPTH_TEST)); diff --git a/source/client/scene/Scene.cpp b/source/client/scene/Scene.cpp index 1ea0165dd..f934ef980 100644 --- a/source/client/scene/Scene.cpp +++ b/source/client/scene/Scene.cpp @@ -45,22 +45,14 @@ Scene::Scene(ClientPlayer &player) : m_player(player) { void Scene::init() { 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); - m_registry.assign(testEntity, 0., 0., 0., shape.getSize().x, shape.getSize().y, shape.getSize().z); - m_registry.assign(testEntity, "default:stick", 1); - - auto testEntity2 = m_registry.create(); - - InventoryCube &cube = m_registry.assign(testEntity2, 0.25f, true); + InventoryCube &cube = m_registry.assign(testEntity, 0.25f, true); cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); cube.setPosition(14 + cube.getOrigin().x, 13 + cube.getOrigin().y, 16 + cube.getOrigin().z); cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID("default:cobblestone")); - m_registry.assign(testEntity2, 0.f, 0.f, 1.f, 1.f); + m_registry.assign(testEntity, 0.f, 0.f, 1.f, 1.f); + m_registry.assign(testEntity, 0., 0., 0., cube.size(), cube.size(), cube.size()); + m_registry.assign(testEntity, "default:stick", 1); m_isInitialized = true; } @@ -68,16 +60,12 @@ void Scene::init() { void Scene::update() { if (!m_isInitialized) init(); - m_registry.view().each([](auto, auto &boxShape, auto &rotation) { - boxShape.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); - }); - m_registry.view().each([](auto, auto &cube, auto &rotation) { cube.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); }); - m_registry.view().each([this](auto entity, auto &boxShape, auto &box, auto &itemStack) { - gk::DoubleBox hitbox = box + boxShape.getPosition(); + m_registry.view().each([this](auto entity, auto &cube, auto &box, auto &itemStack) { + gk::DoubleBox hitbox = box + cube.getPosition(); gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; if (hitbox.intersects(playerHitbox)) { m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); @@ -93,10 +81,6 @@ void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { 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); - }); - m_registry.view().each([&](auto, auto &cube) { target.draw(cube, states); }); From de980135baa56abae263c06c18dc5dc1990a18f7 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 01:10:50 +0200 Subject: [PATCH 04/38] [Scene] Splitted into Controllers. [Config] New 'useItemDrop' option. --- source/client/core/Config.cpp | 2 + source/client/core/Config.hpp | 1 + source/client/hud/BlockCursor.cpp | 7 ++- source/client/scene/AnimationComponent.hpp | 58 +++++++++++++++++++++ source/client/scene/AnimationController.cpp | 38 ++++++++++++++ source/client/scene/AnimationController.hpp | 37 +++++++++++++ source/client/scene/CollisionController.cpp | 43 +++++++++++++++ source/client/scene/CollisionController.hpp | 39 ++++++++++++++ source/client/scene/ItemDropFactory.cpp | 45 ++++++++++++++++ source/client/scene/ItemDropFactory.hpp | 39 ++++++++++++++ source/client/scene/RenderingController.cpp | 36 +++++++++++++ source/client/scene/RenderingController.hpp | 40 ++++++++++++++ source/client/scene/Scene.cpp | 50 +++--------------- source/client/scene/Scene.hpp | 6 +-- source/client/states/SettingsMenuState.cpp | 1 + source/client/world/ClientWorld.cpp | 6 +++ source/client/world/ClientWorld.hpp | 4 ++ source/common/world/Chunk.cpp | 3 ++ source/common/world/World.hpp | 3 ++ 19 files changed, 408 insertions(+), 50 deletions(-) create mode 100644 source/client/scene/AnimationComponent.hpp create mode 100644 source/client/scene/AnimationController.cpp create mode 100644 source/client/scene/AnimationController.hpp create mode 100644 source/client/scene/CollisionController.cpp create mode 100644 source/client/scene/CollisionController.hpp create mode 100644 source/client/scene/ItemDropFactory.cpp create mode 100644 source/client/scene/ItemDropFactory.hpp create mode 100644 source/client/scene/RenderingController.cpp create mode 100644 source/client/scene/RenderingController.hpp diff --git a/source/client/core/Config.cpp b/source/client/core/Config.cpp index 51d0d2d53..c3e5c5de3 100644 --- a/source/client/core/Config.cpp +++ b/source/client/core/Config.cpp @@ -29,6 +29,7 @@ // Gameplay bool Config::isFlyModeEnabled = false; bool Config::isNoClipEnabled = false; +bool Config::useItemDrops = true; // Interface bool Config::isBlockInfoWidgetEnabled = true; @@ -68,6 +69,7 @@ void Config::loadConfigFromFile(const char *file) { isFlyModeEnabled = lua["isFlyModeEnabled"].get_or(isFlyModeEnabled); isNoClipEnabled = lua["isNoClipEnabled"].get_or(isNoClipEnabled); + useItemDrops = lua["useItemDrops"].get_or(useItemDrops); isBlockInfoWidgetEnabled = lua["isBlockInfoWidgetEnabled"].get_or(isBlockInfoWidgetEnabled); isFpsCounterEnabled = lua["isFpsCounterEnabled"].get_or(isFpsCounterEnabled); diff --git a/source/client/core/Config.hpp b/source/client/core/Config.hpp index 4ee709493..94e5b5670 100644 --- a/source/client/core/Config.hpp +++ b/source/client/core/Config.hpp @@ -33,6 +33,7 @@ namespace Config { // Gameplay extern bool isFlyModeEnabled; extern bool isNoClipEnabled; + extern bool useItemDrops; // Interface extern bool isBlockInfoWidgetEnabled; diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index a1d912586..fbae46500 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -149,8 +149,11 @@ void BlockCursor::update(const Hotbar &hotbar) { timeToBreak = m_currentBlock->timeToBreak(currentStack.item().harvestCapability(), currentStack.item().miningSpeed()); if (ticks > m_animationStart + timeToBreak * 1000) { - ItemStack itemDrop = m_currentBlock->getItemDrop(); - m_player.inventory().addStack(itemDrop.item().stringID(), itemDrop.amount()); + if (!Config::useItemDrops) { + ItemStack itemDrop = m_currentBlock->getItemDrop(); + m_player.inventory().addStack(itemDrop.item().stringID(), itemDrop.amount()); + } + m_world.setBlock(m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z, 0); m_animationStart = ticks; diff --git a/source/client/scene/AnimationComponent.hpp b/source/client/scene/AnimationComponent.hpp new file mode 100644 index 000000000..e7b304c42 --- /dev/null +++ b/source/client/scene/AnimationComponent.hpp @@ -0,0 +1,58 @@ +/* + * ===================================================================================== + * + * 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 ANIMATIONCOMPONENT_HPP_ +#define ANIMATIONCOMPONENT_HPP_ + +enum class AnimationType { + Rotation, +}; + +struct AnimationComponent { + AnimationComponent(float axisX, float axisY, float axisZ, float angle) { + type = AnimationType::Rotation; + + rotation.axisX = axisX; + rotation.axisY = axisY; + rotation.axisZ = axisZ; + + rotation.angle = angle; + } + + AnimationType type; + + union { + struct { + float axisX; + float axisY; + float axisZ; + + float angle; + } rotation; + }; +}; + +#endif // ANIMATIONCOMPONENT_HPP_ diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp new file mode 100644 index 000000000..f239b965c --- /dev/null +++ b/source/client/scene/AnimationController.cpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * 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 "AnimationComponent.hpp" +#include "AnimationController.hpp" +#include "InventoryCube.hpp" + +void AnimationController::update(entt::DefaultRegistry ®istry) { + // FIXME: This shouldn't use InventoryCube but a more generic class + registry.view().each([](auto, auto &cube, auto &animation) { + if (animation.type == AnimationType::Rotation) + cube.rotate(animation.rotation.angle, {animation.rotation.axisX, animation.rotation.axisY, animation.rotation.axisZ}); + }); +} + diff --git a/source/client/scene/AnimationController.hpp b/source/client/scene/AnimationController.hpp new file mode 100644 index 000000000..8154fe021 --- /dev/null +++ b/source/client/scene/AnimationController.hpp @@ -0,0 +1,37 @@ +/* + * ===================================================================================== + * + * 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 ANIMATIONCONTROLLER_HPP_ +#define ANIMATIONCONTROLLER_HPP_ + +#include + +class AnimationController { + public: + static void update(entt::DefaultRegistry ®istry); +}; + +#endif // ANIMATIONCONTROLLER_HPP_ diff --git a/source/client/scene/CollisionController.cpp b/source/client/scene/CollisionController.cpp new file mode 100644 index 000000000..5cb0bccbc --- /dev/null +++ b/source/client/scene/CollisionController.cpp @@ -0,0 +1,43 @@ +/* + * ===================================================================================== + * + * 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 "ClientPlayer.hpp" +#include "CollisionController.hpp" +#include "InventoryCube.hpp" +#include "ItemStack.hpp" + +void CollisionController::update(entt::DefaultRegistry ®istry, ClientPlayer &player) { + // FIXME: This shouldn't use InventoryCube, but instead a callback stored in a CollisionComponent + registry.view().each([&](auto entity, auto &cube, auto &box, auto &itemStack) { + gk::DoubleBox hitbox = box + cube.getPosition(); + gk::DoubleBox playerHitbox = player.hitbox() + gk::Vector3d{player.x(), player.y(), player.z()}; + if (hitbox.intersects(playerHitbox)) { + player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + registry.destroy(entity); + } + }); +} + diff --git a/source/client/scene/CollisionController.hpp b/source/client/scene/CollisionController.hpp new file mode 100644 index 000000000..24e123ac7 --- /dev/null +++ b/source/client/scene/CollisionController.hpp @@ -0,0 +1,39 @@ +/* + * ===================================================================================== + * + * 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 COLLISIONCONTROLLER_HPP_ +#define COLLISIONCONTROLLER_HPP_ + +#include + +class ClientPlayer; + +class CollisionController { + public: + static void update(entt::DefaultRegistry ®istry, ClientPlayer &player); +}; + +#endif // COLLISIONCONTROLLER_HPP_ diff --git a/source/client/scene/ItemDropFactory.cpp b/source/client/scene/ItemDropFactory.cpp new file mode 100644 index 000000000..35c7ce26c --- /dev/null +++ b/source/client/scene/ItemDropFactory.cpp @@ -0,0 +1,45 @@ +/* + * ===================================================================================== + * + * 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 "AnimationComponent.hpp" +#include "InventoryCube.hpp" +#include "ItemDropFactory.hpp" +#include "ItemStack.hpp" +#include "Registry.hpp" + +void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { + auto entity = registry.create(); + + InventoryCube &cube = registry.assign(entity, 0.25f, true); + cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); + cube.setPosition(x + 0.5, y + 0.5, z + 0.5); + cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(itemID)); + + registry.assign(entity, 0.f, 0.f, 1.f, 0.5f); + registry.assign(entity, 0., 0., 0., cube.size(), cube.size(), cube.size()); + registry.assign(entity, itemID, amount); +} + diff --git a/source/client/scene/ItemDropFactory.hpp b/source/client/scene/ItemDropFactory.hpp new file mode 100644 index 000000000..c6ec80d70 --- /dev/null +++ b/source/client/scene/ItemDropFactory.hpp @@ -0,0 +1,39 @@ +/* + * ===================================================================================== + * + * 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 ITEMDROPFACTORY_HPP_ +#define ITEMDROPFACTORY_HPP_ + +#include + +#include + +class ItemDropFactory { + public: + static void create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount = 1); +}; + +#endif // ITEMDROPFACTORY_HPP_ diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp new file mode 100644 index 000000000..28f99f2dd --- /dev/null +++ b/source/client/scene/RenderingController.cpp @@ -0,0 +1,36 @@ +/* + * ===================================================================================== + * + * 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 "InventoryCube.hpp" +#include "RenderingController.hpp" + +void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { + // FIXME: There's probably another way to do this + registry.view().each([&](auto, auto &cube) { + target.draw(cube, states); + }); +} + diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp new file mode 100644 index 000000000..d50077a6e --- /dev/null +++ b/source/client/scene/RenderingController.hpp @@ -0,0 +1,40 @@ +/* + * ===================================================================================== + * + * 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 RENDERINGCONTROLLER_HPP_ +#define RENDERINGCONTROLLER_HPP_ + +#include +#include + +#include + +class RenderingController { + public: + static void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states); +}; + +#endif // RENDERINGCONTROLLER_HPP_ diff --git a/source/client/scene/Scene.cpp b/source/client/scene/Scene.cpp index f934ef980..a9d28c151 100644 --- a/source/client/scene/Scene.cpp +++ b/source/client/scene/Scene.cpp @@ -24,54 +24,18 @@ * * ===================================================================================== */ +#include "AnimationController.hpp" #include "ClientPlayer.hpp" +#include "CollisionController.hpp" #include "Scene.hpp" - -struct RotationAnimation { - float axisX; - float axisY; - float axisZ; - - float angle; -}; - -#include "InventoryCube.hpp" -#include "ItemStack.hpp" -#include "Registry.hpp" +#include "RenderingController.hpp" Scene::Scene(ClientPlayer &player) : m_player(player) { } -void Scene::init() { - auto testEntity = m_registry.create(); - - InventoryCube &cube = m_registry.assign(testEntity, 0.25f, true); - cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); - cube.setPosition(14 + cube.getOrigin().x, 13 + cube.getOrigin().y, 16 + cube.getOrigin().z); - cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID("default:cobblestone")); - - m_registry.assign(testEntity, 0.f, 0.f, 1.f, 1.f); - m_registry.assign(testEntity, 0., 0., 0., cube.size(), cube.size(), cube.size()); - m_registry.assign(testEntity, "default:stick", 1); - - m_isInitialized = true; -} - void Scene::update() { - if (!m_isInitialized) init(); - - m_registry.view().each([](auto, auto &cube, auto &rotation) { - cube.rotate(rotation.angle, {rotation.axisX, rotation.axisY, rotation.axisZ}); - }); - - m_registry.view().each([this](auto entity, auto &cube, auto &box, auto &itemStack) { - gk::DoubleBox hitbox = box + cube.getPosition(); - gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; - if (hitbox.intersects(playerHitbox)) { - m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); - m_registry.destroy(entity); - } - }); + AnimationController::update(m_registry); + CollisionController::update(m_registry, m_player); } void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { @@ -81,8 +45,6 @@ void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { gk::Vector3d cameraPosition = m_camera->getDPosition(); states.transform.translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z); - m_registry.view().each([&](auto, auto &cube) { - target.draw(cube, states); - }); + RenderingController::draw(m_registry, target, states); } diff --git a/source/client/scene/Scene.hpp b/source/client/scene/Scene.hpp index 170715c18..3628d262c 100644 --- a/source/client/scene/Scene.hpp +++ b/source/client/scene/Scene.hpp @@ -39,17 +39,15 @@ class Scene : public gk::Drawable { public: Scene(ClientPlayer &player); - void init(); - void update(); void setCamera(gk::Camera &camera) { m_camera = &camera; } + entt::DefaultRegistry ®istry() { return m_registry; } + private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; - bool m_isInitialized = false; - ClientPlayer &m_player; gk::Camera *m_camera = nullptr; diff --git a/source/client/states/SettingsMenuState.cpp b/source/client/states/SettingsMenuState.cpp index 4b901a5a8..7c5880672 100644 --- a/source/client/states/SettingsMenuState.cpp +++ b/source/client/states/SettingsMenuState.cpp @@ -137,6 +137,7 @@ void SettingsMenuState::addGameplayButtons() { addToggleButton("Fly Mode", Config::isFlyModeEnabled, false); addToggleButton("No Clip", Config::isNoClipEnabled, false); + addToggleButton("Use item drops", Config::useItemDrops, false); } void SettingsMenuState::addInterfaceButtons() { diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index 3c191108b..e28c8bb83 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -34,6 +34,7 @@ #include "ClientCommandHandler.hpp" #include "ClientPlayer.hpp" #include "ClientWorld.hpp" +#include "ItemDropFactory.hpp" #include "Registry.hpp" #include "Sky.hpp" #include "TextureAtlas.hpp" @@ -100,6 +101,11 @@ void ClientWorld::checkPlayerChunk(double playerX, double playerY, double player } } +void ClientWorld::onBlockDestroyed(int x, int y, int z, const Block &block) { + if (Config::useItemDrops) + ItemDropFactory::create(m_scene.registry(), x, y, z, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); +} + void ClientWorld::clear() { m_chunks.clear(); } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index 63f9c3ba6..2eb729dcf 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -51,6 +51,8 @@ class ClientWorld : public World, public gk::Drawable { void sendChunkRequests(); void checkPlayerChunk(double playerX, double playerY, double playerZ); + void onBlockDestroyed(int x, int y, int z, const Block &block) override; + void clear(); void updateSky(u16 dimensionID); @@ -60,6 +62,8 @@ class ClientWorld : public World, public gk::Drawable { Chunk *getChunk(int cx, int cy, int cz) const override; + Scene &scene() { return m_scene; } + void setClient(ClientCommandHandler &client) { m_client = &client; } void setCamera(gk::Camera &camera) { m_camera = &camera; m_scene.setCamera(camera); } diff --git a/source/common/world/Chunk.cpp b/source/common/world/Chunk.cpp index ddd74ef0c..1d543b443 100644 --- a/source/common/world/Chunk.cpp +++ b/source/common/world/Chunk.cpp @@ -31,6 +31,7 @@ #include "Chunk.hpp" #include "EngineConfig.hpp" #include "Registry.hpp" +#include "World.hpp" Chunk::Chunk(s32 x, s32 y, s32 z, World &world) : m_world(world) { m_x = x; @@ -88,10 +89,12 @@ void Chunk::setBlock(int x, int y, int z, u16 type) { } onBlockPlaced(x, y, z, block); + m_world.onBlockPlaced(x + m_x * width, y + m_y * depth, z + m_z * height, block); if (m_data[z][y][x] != 0) { const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x]); onBlockDestroyed(x, y, z, oldBlock); + m_world.onBlockDestroyed(x + m_x * width, y + m_y * depth, z + m_z * height, oldBlock); } setBlockRaw(x, y, z, type); diff --git a/source/common/world/World.hpp b/source/common/world/World.hpp index 2aebce377..56e87617e 100644 --- a/source/common/world/World.hpp +++ b/source/common/world/World.hpp @@ -46,6 +46,9 @@ class World { u16 getData(int x, int y, int z) const; void setData(int x, int y, int z, u16 data) const; + virtual void onBlockPlaced(int, int, int, const Block &) {} + virtual void onBlockDestroyed(int, int, int, const Block &) {} + static bool isReloadRequested; }; From 4e09432eea82fee4de42ab99231126d4bc142664 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 01:28:06 +0200 Subject: [PATCH 05/38] [Scene] Now using AbstractController instead of static classes. --- source/client/scene/AbstractController.hpp | 41 +++++++++++++++++++++ source/client/scene/AnimationController.hpp | 6 +-- source/client/scene/CollisionController.cpp | 6 +-- source/client/scene/CollisionController.hpp | 11 ++++-- source/client/scene/RenderingController.hpp | 9 ++--- source/client/scene/Scene.cpp | 10 +++-- source/client/scene/Scene.hpp | 10 +++-- 7 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 source/client/scene/AbstractController.hpp diff --git a/source/client/scene/AbstractController.hpp b/source/client/scene/AbstractController.hpp new file mode 100644 index 000000000..077833cc1 --- /dev/null +++ b/source/client/scene/AbstractController.hpp @@ -0,0 +1,41 @@ +/* + * ===================================================================================== + * + * 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 ABSTRACTCONTROLLER_HPP_ +#define ABSTRACTCONTROLLER_HPP_ + +#include +#include + +#include + +class AbstractController { + public: + virtual void update(entt::DefaultRegistry &) {} + virtual void draw(entt::DefaultRegistry &, gk::RenderTarget &, gk::RenderStates) {} +}; + +#endif // ABSTRACTCONTROLLER_HPP_ diff --git a/source/client/scene/AnimationController.hpp b/source/client/scene/AnimationController.hpp index 8154fe021..1870f0df6 100644 --- a/source/client/scene/AnimationController.hpp +++ b/source/client/scene/AnimationController.hpp @@ -27,11 +27,11 @@ #ifndef ANIMATIONCONTROLLER_HPP_ #define ANIMATIONCONTROLLER_HPP_ -#include +#include "AbstractController.hpp" -class AnimationController { +class AnimationController : public AbstractController { public: - static void update(entt::DefaultRegistry ®istry); + void update(entt::DefaultRegistry ®istry) override; }; #endif // ANIMATIONCONTROLLER_HPP_ diff --git a/source/client/scene/CollisionController.cpp b/source/client/scene/CollisionController.cpp index 5cb0bccbc..95cff89bc 100644 --- a/source/client/scene/CollisionController.cpp +++ b/source/client/scene/CollisionController.cpp @@ -29,13 +29,13 @@ #include "InventoryCube.hpp" #include "ItemStack.hpp" -void CollisionController::update(entt::DefaultRegistry ®istry, ClientPlayer &player) { +void CollisionController::update(entt::DefaultRegistry ®istry) { // FIXME: This shouldn't use InventoryCube, but instead a callback stored in a CollisionComponent registry.view().each([&](auto entity, auto &cube, auto &box, auto &itemStack) { gk::DoubleBox hitbox = box + cube.getPosition(); - gk::DoubleBox playerHitbox = player.hitbox() + gk::Vector3d{player.x(), player.y(), player.z()}; + gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; if (hitbox.intersects(playerHitbox)) { - player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); registry.destroy(entity); } }); diff --git a/source/client/scene/CollisionController.hpp b/source/client/scene/CollisionController.hpp index 24e123ac7..9f7741b81 100644 --- a/source/client/scene/CollisionController.hpp +++ b/source/client/scene/CollisionController.hpp @@ -27,13 +27,18 @@ #ifndef COLLISIONCONTROLLER_HPP_ #define COLLISIONCONTROLLER_HPP_ -#include +#include "AbstractController.hpp" class ClientPlayer; -class CollisionController { +class CollisionController : public AbstractController { public: - static void update(entt::DefaultRegistry ®istry, ClientPlayer &player); + CollisionController(ClientPlayer &player) : m_player(player) {} + + void update(entt::DefaultRegistry ®istry); + + private: + ClientPlayer &m_player; }; #endif // COLLISIONCONTROLLER_HPP_ diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp index d50077a6e..75af6d10e 100644 --- a/source/client/scene/RenderingController.hpp +++ b/source/client/scene/RenderingController.hpp @@ -27,14 +27,11 @@ #ifndef RENDERINGCONTROLLER_HPP_ #define RENDERINGCONTROLLER_HPP_ -#include -#include +#include "AbstractController.hpp" -#include - -class RenderingController { +class RenderingController : public AbstractController { public: - static void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states); + void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; }; #endif // RENDERINGCONTROLLER_HPP_ diff --git a/source/client/scene/Scene.cpp b/source/client/scene/Scene.cpp index a9d28c151..c6227d2a7 100644 --- a/source/client/scene/Scene.cpp +++ b/source/client/scene/Scene.cpp @@ -31,11 +31,14 @@ #include "RenderingController.hpp" Scene::Scene(ClientPlayer &player) : m_player(player) { + m_controllers.emplace_back(new AnimationController); + m_controllers.emplace_back(new CollisionController(player)); + m_controllers.emplace_back(new RenderingController); } void Scene::update() { - AnimationController::update(m_registry); - CollisionController::update(m_registry, m_player); + for (auto &controller : m_controllers) + controller->update(m_registry); } void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { @@ -45,6 +48,7 @@ void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { gk::Vector3d cameraPosition = m_camera->getDPosition(); states.transform.translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z); - RenderingController::draw(m_registry, target, states); + for (auto &controller : m_controllers) + controller->draw(m_registry, target, states); } diff --git a/source/client/scene/Scene.hpp b/source/client/scene/Scene.hpp index 3628d262c..ddbf7d321 100644 --- a/source/client/scene/Scene.hpp +++ b/source/client/scene/Scene.hpp @@ -27,12 +27,16 @@ #ifndef SCENE_HPP_ #define SCENE_HPP_ +#include +#include + #include #include -#include #include +#include "AbstractController.hpp" + class ClientPlayer; class Scene : public gk::Drawable { @@ -52,9 +56,9 @@ class Scene : public gk::Drawable { gk::Camera *m_camera = nullptr; - gk::BoxShape m_testBox; - mutable entt::DefaultRegistry m_registry; + + std::deque> m_controllers; }; #endif // SCENE_HPP_ From 0f610eac3bd0c4e6f8f6c3b99c064e9e50bde068 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 02:41:37 +0200 Subject: [PATCH 06/38] [AnimationComponent] Added new animation type: translation. [ItemDropFactory] Improved animation. --- source/client/scene/AnimationComponent.hpp | 59 +++++++++++++++++---- source/client/scene/AnimationController.cpp | 33 +++++++++++- source/client/scene/ItemDropFactory.cpp | 5 +- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/source/client/scene/AnimationComponent.hpp b/source/client/scene/AnimationComponent.hpp index e7b304c42..5fcc9f54b 100644 --- a/source/client/scene/AnimationComponent.hpp +++ b/source/client/scene/AnimationComponent.hpp @@ -27,21 +27,14 @@ #ifndef ANIMATIONCOMPONENT_HPP_ #define ANIMATIONCOMPONENT_HPP_ +#include + enum class AnimationType { Rotation, + Translation, }; -struct AnimationComponent { - AnimationComponent(float axisX, float axisY, float axisZ, float angle) { - type = AnimationType::Rotation; - - rotation.axisX = axisX; - rotation.axisY = axisY; - rotation.axisZ = axisZ; - - rotation.angle = angle; - } - +struct AnimationData { AnimationType type; union { @@ -52,7 +45,51 @@ struct AnimationComponent { float angle; } rotation; + + struct { + float dx, dy, dz; + float cx, cy, cz; + float min, max; + bool loop; + } translation; }; }; +struct AnimationComponent { + void addRotation(float axisX, float axisY, float axisZ, float angle) { + list.emplace_back(); + AnimationData &data = list.back(); + + data.type = AnimationType::Rotation; + + data.rotation.axisX = axisX; + data.rotation.axisY = axisY; + data.rotation.axisZ = axisZ; + + data.rotation.angle = angle; + } + + void addTranslation(float dx, float dy, float dz, float min, float max, bool loop) { + list.emplace_back(); + AnimationData &data = list.back(); + + data.type = AnimationType::Translation; + + data.translation.dx = dx; + data.translation.dy = dy; + data.translation.dz = dz; + + data.translation.cx = 0; + data.translation.cy = 0; + data.translation.cz = 0; + + data.translation.min = min; + data.translation.max = max; + + data.translation.loop = loop; + } + + std::vector list; +}; + #endif // ANIMATIONCOMPONENT_HPP_ diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp index f239b965c..9fd11019f 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/client/scene/AnimationController.cpp @@ -31,8 +31,37 @@ void AnimationController::update(entt::DefaultRegistry ®istry) { // FIXME: This shouldn't use InventoryCube but a more generic class registry.view().each([](auto, auto &cube, auto &animation) { - if (animation.type == AnimationType::Rotation) - cube.rotate(animation.rotation.angle, {animation.rotation.axisX, animation.rotation.axisY, animation.rotation.axisZ}); + for (auto &it : animation.list) { + if (it.type == AnimationType::Rotation) + cube.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); + else if (it.type == AnimationType::Translation) { + float dx = it.translation.dx; + float dy = it.translation.dy; + float dz = it.translation.dz; + + if (it.translation.cx + it.translation.dx > it.translation.max + || it.translation.cx + it.translation.dx < it.translation.min) + dx = (it.translation.loop) ? -dx : 0; + + if (it.translation.cy + it.translation.dy > it.translation.max + || it.translation.cy + it.translation.dy < it.translation.min) + dy = (it.translation.loop) ? -dy : 0; + + if (it.translation.cz + it.translation.dz > it.translation.max + || it.translation.cz + it.translation.dz < it.translation.min) + dz = (it.translation.loop) ? -dz : 0; + + cube.move(dx, dy, dz); + + it.translation.cx += dx; + it.translation.cy += dy; + it.translation.cz += dz; + + it.translation.dx = dx; + it.translation.dy = dy; + it.translation.dz = dz; + } + } }); } diff --git a/source/client/scene/ItemDropFactory.cpp b/source/client/scene/ItemDropFactory.cpp index 35c7ce26c..165e7b73c 100644 --- a/source/client/scene/ItemDropFactory.cpp +++ b/source/client/scene/ItemDropFactory.cpp @@ -38,7 +38,10 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y cube.setPosition(x + 0.5, y + 0.5, z + 0.5); cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(itemID)); - registry.assign(entity, 0.f, 0.f, 1.f, 0.5f); + auto &animationComponent = registry.assign(entity); + animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); + animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); + registry.assign(entity, 0., 0., 0., cube.size(), cube.size(), cube.size()); registry.assign(entity, itemID, amount); } From 4710c72f37ade0e06e60765a9bf2efdc600f7e0b Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 16:59:34 +0200 Subject: [PATCH 07/38] Working on entity serialization. --- README.md | 4 +- source/client/CMakeLists.txt | 4 +- source/client/scene/AnimationController.cpp | 7 +- .../scene/{Scene.cpp => ClientScene.cpp} | 20 ++++- .../scene/{Scene.hpp => ClientScene.hpp} | 15 ++-- source/client/scene/CollisionController.cpp | 5 +- source/client/scene/ItemDropFactory.cpp | 8 +- source/client/scene/RenderingController.cpp | 14 ++- source/client/world/ClientWorld.hpp | 6 +- source/common/network/NetworkUtils.cpp | 20 +++++ source/common/network/NetworkUtils.hpp | 31 +++++++ .../scene/AnimationComponent.hpp | 39 ++++++++- source/common/scene/DrawableComponent.hpp | 51 +++++++++++ source/common/scene/Scene.hpp | 47 ++++++++++ source/common/scene/SceneSerializer.cpp | 51 +++++++++++ source/common/scene/SceneSerializer.hpp | 87 +++++++++++++++++++ source/server/CMakeLists.txt | 4 +- 17 files changed, 377 insertions(+), 36 deletions(-) rename source/client/scene/{Scene.cpp => ClientScene.cpp} (79%) rename source/client/scene/{Scene.hpp => ClientScene.hpp} (87%) rename source/{client => common}/scene/AnimationComponent.hpp (64%) create mode 100644 source/common/scene/DrawableComponent.hpp create mode 100644 source/common/scene/Scene.hpp create mode 100644 source/common/scene/SceneSerializer.cpp create mode 100644 source/common/scene/SceneSerializer.hpp diff --git a/README.md b/README.md index d58858787..6ad6d64d7 100644 --- a/README.md +++ b/README.md @@ -104,16 +104,16 @@ This list is non exhaustive. - Player model display (currently without rotation nor animation) - Dimensions (like the Nether or the Ender in Minecraft) ([#80](https://github.com/Unarelith/OpenMiner/pull/80)) - World loading/saving (using `/save ` and `/load ` commands, see [#26](https://github.com/Unarelith/OpenMiner/issues/26)) +- Texture pack system (partially implemented, see [#34](https://github.com/Unarelith/OpenMiner/issues/34)) +- Entities (block drops, mobs, etc...) ([#90](https://github.com/Unarelith/OpenMiner/pull/90)) ### Missing features -- Texture pack system ([#34](https://github.com/Unarelith/OpenMiner/issues/34)) - Fluid propagation ([#62](https://github.com/Unarelith/OpenMiner/issues/62)) - Day/night cycle with sun/moon display ([#73](https://github.com/Unarelith/OpenMiner/issues/73)) - Real worldgen (seed-based, cave tunnels) ([#79](https://github.com/Unarelith/OpenMiner/issues/79)) - Clouds ([#52](https://github.com/Unarelith/OpenMiner/pull/52)) - Particle system -- Entities (block drops, mobs, etc...) ## Screenshots diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index bc6d9889b..474e73b44 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -49,6 +49,7 @@ endif () target_link_libraries(${PROJECT_NAME} ${CMAKE_PROJECT_NAME}_server_lib + ${CMAKE_PROJECT_NAME}_common ${GAMEKIT_LIBRARIES} ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} @@ -60,6 +61,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - ${UNIX_LIBS} - ${CMAKE_PROJECT_NAME}_common) + ${UNIX_LIBS}) diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp index 9fd11019f..4ba58485a 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/client/scene/AnimationController.cpp @@ -29,11 +29,10 @@ #include "InventoryCube.hpp" void AnimationController::update(entt::DefaultRegistry ®istry) { - // FIXME: This shouldn't use InventoryCube but a more generic class - registry.view().each([](auto, auto &cube, auto &animation) { + registry.view().each([](auto, auto &transformable, auto &animation) { for (auto &it : animation.list) { if (it.type == AnimationType::Rotation) - cube.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); + transformable.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); else if (it.type == AnimationType::Translation) { float dx = it.translation.dx; float dy = it.translation.dy; @@ -51,7 +50,7 @@ void AnimationController::update(entt::DefaultRegistry ®istry) { || it.translation.cz + it.translation.dz < it.translation.min) dz = (it.translation.loop) ? -dz : 0; - cube.move(dx, dy, dz); + transformable.move(dx, dy, dz); it.translation.cx += dx; it.translation.cy += dy; diff --git a/source/client/scene/Scene.cpp b/source/client/scene/ClientScene.cpp similarity index 79% rename from source/client/scene/Scene.cpp rename to source/client/scene/ClientScene.cpp index c6227d2a7..2d3b1883d 100644 --- a/source/client/scene/Scene.cpp +++ b/source/client/scene/ClientScene.cpp @@ -26,22 +26,34 @@ */ #include "AnimationController.hpp" #include "ClientPlayer.hpp" +#include "ClientScene.hpp" #include "CollisionController.hpp" -#include "Scene.hpp" #include "RenderingController.hpp" -Scene::Scene(ClientPlayer &player) : m_player(player) { +ClientScene::ClientScene(ClientPlayer &player) : m_player(player) { m_controllers.emplace_back(new AnimationController); m_controllers.emplace_back(new CollisionController(player)); m_controllers.emplace_back(new RenderingController); } -void Scene::update() { +void ClientScene::update() { for (auto &controller : m_controllers) controller->update(m_registry); + + static bool test = false; + if (!test && m_registry.alive() > 2) { + gkDebug() << "serializing..."; + sf::Packet packet; + serialize(packet); + gkDebug() << "deserializing..."; + deserialize(packet); + gkDebug() << "serializing..."; + serialize(packet); + test = true; + } } -void Scene::draw(gk::RenderTarget &target, gk::RenderStates states) const { +void ClientScene::draw(gk::RenderTarget &target, gk::RenderStates states) const { if (!m_camera) return; // Subtract the camera position - see comment in ClientWorld::draw() diff --git a/source/client/scene/Scene.hpp b/source/client/scene/ClientScene.hpp similarity index 87% rename from source/client/scene/Scene.hpp rename to source/client/scene/ClientScene.hpp index ddbf7d321..b4b4ebc14 100644 --- a/source/client/scene/Scene.hpp +++ b/source/client/scene/ClientScene.hpp @@ -24,8 +24,8 @@ * * ===================================================================================== */ -#ifndef SCENE_HPP_ -#define SCENE_HPP_ +#ifndef CLIENTSCENE_HPP_ +#define CLIENTSCENE_HPP_ #include #include @@ -36,19 +36,18 @@ #include #include "AbstractController.hpp" +#include "Scene.hpp" class ClientPlayer; -class Scene : public gk::Drawable { +class ClientScene : public Scene, public gk::Drawable { public: - Scene(ClientPlayer &player); + ClientScene(ClientPlayer &player); void update(); void setCamera(gk::Camera &camera) { m_camera = &camera; } - entt::DefaultRegistry ®istry() { return m_registry; } - private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; @@ -56,9 +55,7 @@ class Scene : public gk::Drawable { gk::Camera *m_camera = nullptr; - mutable entt::DefaultRegistry m_registry; - std::deque> m_controllers; }; -#endif // SCENE_HPP_ +#endif // CLIENTSCENE_HPP_ diff --git a/source/client/scene/CollisionController.cpp b/source/client/scene/CollisionController.cpp index 95cff89bc..24d76bd90 100644 --- a/source/client/scene/CollisionController.cpp +++ b/source/client/scene/CollisionController.cpp @@ -30,9 +30,8 @@ #include "ItemStack.hpp" void CollisionController::update(entt::DefaultRegistry ®istry) { - // FIXME: This shouldn't use InventoryCube, but instead a callback stored in a CollisionComponent - registry.view().each([&](auto entity, auto &cube, auto &box, auto &itemStack) { - gk::DoubleBox hitbox = box + cube.getPosition(); + registry.view().each([&](auto entity, auto &transformable, auto &box, auto &itemStack) { + gk::DoubleBox hitbox = box + transformable.getPosition(); gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; if (hitbox.intersects(playerHitbox)) { m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); diff --git a/source/client/scene/ItemDropFactory.cpp b/source/client/scene/ItemDropFactory.cpp index 165e7b73c..88a4cc97e 100644 --- a/source/client/scene/ItemDropFactory.cpp +++ b/source/client/scene/ItemDropFactory.cpp @@ -25,6 +25,7 @@ * ===================================================================================== */ #include "AnimationComponent.hpp" +#include "DrawableComponent.hpp" #include "InventoryCube.hpp" #include "ItemDropFactory.hpp" #include "ItemStack.hpp" @@ -33,11 +34,14 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); - InventoryCube &cube = registry.assign(entity, 0.25f, true); + auto &drawableComponent = registry.assign(entity); + auto &cube = drawableComponent.setDrawable(0.25f, true); cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); - cube.setPosition(x + 0.5, y + 0.5, z + 0.5); cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(itemID)); + auto &transformable = registry.assign(entity); + transformable.setPosition(x + 0.5, y + 0.5, z + 0.5); + auto &animationComponent = registry.assign(entity); animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 28f99f2dd..5bab875d0 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -24,13 +24,21 @@ * * ===================================================================================== */ +#include "DrawableComponent.hpp" #include "InventoryCube.hpp" #include "RenderingController.hpp" +#include +#include + void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { - // FIXME: There's probably another way to do this - registry.view().each([&](auto, auto &cube) { - target.draw(cube, states); + registry.view().each([&](auto entity, auto &drawable, auto &transformable) { + if (gk::GameClock::getInstance().getTicks() % 100 < 12) + gkDebug() << "Drawing entity" << entity << "at" << transformable.getPosition(); + + gk::RenderStates drawStates = states; + drawStates.transform *= transformable.getTransform(); + drawable.draw(target, drawStates); }); } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index 2eb729dcf..24ec38908 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -33,8 +33,8 @@ #include #include "ClientChunk.hpp" +#include "ClientScene.hpp" #include "Network.hpp" -#include "Scene.hpp" #include "World.hpp" class ClientCommandHandler; @@ -62,7 +62,7 @@ class ClientWorld : public World, public gk::Drawable { Chunk *getChunk(int cx, int cy, int cz) const override; - Scene &scene() { return m_scene; } + ClientScene &scene() { return m_scene; } void setClient(ClientCommandHandler &client) { m_client = &client; } void setCamera(gk::Camera &camera) { m_camera = &camera; m_scene.setCamera(camera); } @@ -74,7 +74,7 @@ class ClientWorld : public World, public gk::Drawable { void draw(gk::RenderTarget &target, gk::RenderStates states) const override; - Scene m_scene; + ClientScene m_scene; ChunkMap m_chunks; diff --git a/source/common/network/NetworkUtils.cpp b/source/common/network/NetworkUtils.cpp index 0c9e9da4b..9fa11a675 100644 --- a/source/common/network/NetworkUtils.cpp +++ b/source/common/network/NetworkUtils.cpp @@ -36,3 +36,23 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Color &color) { return packet; } +sf::Packet &operator<<(sf::Packet &packet, const gk::Transformable &transformable) { + packet << transformable.getPosition() << transformable.getOrigin() << transformable.getScale() + << transformable.getRotation() << transformable.getRotationTransform().getMatrix(); + return packet; +} + +sf::Packet &operator>>(sf::Packet &packet, gk::Transformable &transformable) { + gk::Vector3f position, origin, scale; + float rotation; + packet >> position >> origin >> scale >> rotation + >> transformable.getRotationTransform().getMatrix(); + + transformable.setPosition(position); + transformable.setOrigin(origin); + transformable.setScale(scale); + transformable.setRotation(rotation); + + return packet; +} + diff --git a/source/common/network/NetworkUtils.hpp b/source/common/network/NetworkUtils.hpp index 1e8333865..d73c1423a 100644 --- a/source/common/network/NetworkUtils.hpp +++ b/source/common/network/NetworkUtils.hpp @@ -84,6 +84,29 @@ sf::Packet &operator>>(sf::Packet &packet, std::unordered_map &map) { return packet; } +//====================================================================================== +// glm::mat4 +//====================================================================================== +#include +#include + +template +sf::Packet &operator<<(sf::Packet &packet, const glm::tmat4x4 &matrix) { + for (int i = 0 ; i < 4 * 4 ; ++i) { + packet << matrix[i % 4][i / 4]; + } + return packet; +} + +template +sf::Packet &operator>>(sf::Packet &packet, glm::tmat4x4 &matrix) { + for (int i = 0 ; i < 4 * 4 ; ++i) { + packet >> matrix[i % 4][i / 4]; + } + return packet; +} + + //====================================================================================== // gk::Rect //====================================================================================== @@ -143,4 +166,12 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Vector3 &vec) { sf::Packet &operator<<(sf::Packet &packet, const gk::Color &color); sf::Packet &operator>>(sf::Packet &packet, gk::Color &color); +//====================================================================================== +// gk::Transformable +//====================================================================================== +#include + +sf::Packet &operator<<(sf::Packet &packet, const gk::Transformable &transformable); +sf::Packet &operator>>(sf::Packet &packet, gk::Transformable &transformable); + #endif // NETWORKUTILS_HPP_ diff --git a/source/client/scene/AnimationComponent.hpp b/source/common/scene/AnimationComponent.hpp similarity index 64% rename from source/client/scene/AnimationComponent.hpp rename to source/common/scene/AnimationComponent.hpp index 5fcc9f54b..96590bcdc 100644 --- a/source/client/scene/AnimationComponent.hpp +++ b/source/common/scene/AnimationComponent.hpp @@ -29,12 +29,17 @@ #include +#include + +#include "ISerializable.hpp" +#include "NetworkUtils.hpp" + enum class AnimationType { Rotation, Translation, }; -struct AnimationData { +struct AnimationData : public ISerializable { AnimationType type; union { @@ -53,9 +58,36 @@ struct AnimationData { bool loop; } translation; }; + + void serialize(sf::Packet &packet) const override { + packet << u8(type); + if (type == AnimationType::Rotation) { + packet << rotation.axisX << rotation.axisY << rotation.axisZ << rotation.angle; + } + else if (type == AnimationType::Translation) { + packet << translation.dx << translation.dy << translation.dz + << translation.cx << translation.cy << translation.cz + << translation.min << translation.max << translation.loop; + } + } + + void deserialize(sf::Packet &packet) override { + u8 type; + packet >> type; + if (type == u8(AnimationType::Rotation)) { + packet >> rotation.axisX >> rotation.axisY >> rotation.axisZ >> rotation.angle; + } + else if (type == u8(AnimationType::Translation)) { + packet >> translation.dx >> translation.dy >> translation.dz + >> translation.cx >> translation.cy >> translation.cz + >> translation.min >> translation.max >> translation.loop; + } + + this->type = AnimationType(type); + } }; -struct AnimationComponent { +struct AnimationComponent : public ISerializable { void addRotation(float axisX, float axisY, float axisZ, float angle) { list.emplace_back(); AnimationData &data = list.back(); @@ -89,6 +121,9 @@ struct AnimationComponent { data.translation.loop = loop; } + void serialize(sf::Packet &packet) const override { packet << list; } + void deserialize(sf::Packet &packet) override { packet >> list; } + std::vector list; }; diff --git a/source/common/scene/DrawableComponent.hpp b/source/common/scene/DrawableComponent.hpp new file mode 100644 index 000000000..fbd8e43cb --- /dev/null +++ b/source/common/scene/DrawableComponent.hpp @@ -0,0 +1,51 @@ +/* + * ===================================================================================== + * + * 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 DRAWABLECOMPONENT_HPP_ +#define DRAWABLECOMPONENT_HPP_ + +#include + +#include + +class DrawableComponent { + public: + template + auto setDrawable(Args &&...args) -> typename std::enable_if::value, T &>::type { + m_drawable.reset(new T(std::forward(args)...)); + return *static_cast(m_drawable.get()); + } + + void draw(gk::RenderTarget &target, gk::RenderStates states) { + if (m_drawable) + target.draw(*m_drawable, states); + } + + private: + std::unique_ptr m_drawable; +}; + +#endif // DRAWABLECOMPONENT_HPP_ diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp new file mode 100644 index 000000000..101244291 --- /dev/null +++ b/source/common/scene/Scene.hpp @@ -0,0 +1,47 @@ +/* + * ===================================================================================== + * + * 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 "ISerializable.hpp" +#include "SceneSerializer.hpp" + +class Scene : public ISerializable { + public: + void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet); } + void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet); } + + entt::DefaultRegistry ®istry() { return m_registry; } + + protected: + mutable entt::DefaultRegistry m_registry; + + private: + SceneSerializer m_serializer{*this}; +}; + +#endif // SCENE_HPP_ diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp new file mode 100644 index 000000000..c68a2a670 --- /dev/null +++ b/source/common/scene/SceneSerializer.cpp @@ -0,0 +1,51 @@ +/* + * ===================================================================================== + * + * 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 "AnimationComponent.hpp" +#include "ItemStack.hpp" +#include "Scene.hpp" +#include "SceneSerializer.hpp" + +void SceneSerializer::serialize(sf::Packet &packet) const { + m_outputArchive.setPacket(packet); + m_scene.registry().snapshot().component(m_outputArchive); +} + +void SceneSerializer::deserialize(sf::Packet &packet) { + m_inputArchive.setPacket(packet); + m_scene.registry().restore().component(m_inputArchive); +} + +void SceneSerializer::OutputArchive::operator()(Entity entity) { + gkDebug() << entity; + (*m_packet) << entity; +} + +void SceneSerializer::InputArchive::operator()(Entity &entity) { + (*m_packet) >> entity; + gkDebug() << entity; +} + diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp new file mode 100644 index 000000000..9c85069e9 --- /dev/null +++ b/source/common/scene/SceneSerializer.hpp @@ -0,0 +1,87 @@ +/* + * ===================================================================================== + * + * 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 SCENESERIALIZER_HPP_ +#define SCENESERIALIZER_HPP_ + +#include + +#include + +#include "ISerializable.hpp" +#include "NetworkUtils.hpp" + +class Scene; + +class SceneSerializer : public ISerializable { + using Entity = entt::DefaultRegistry::entity_type; + + public: + SceneSerializer(Scene &scene) : m_scene(scene) {} + + void serialize(sf::Packet &packet) const; + void deserialize(sf::Packet &packet); + + private: + class OutputArchive { + public: + void operator()(Entity entity); + + template + void operator()(Entity entity, const T &value) { + // gkDebug() << entity << value; + (*m_packet) << entity << value; + } + + void setPacket(sf::Packet &packet) { m_packet = &packet; } + + private: + sf::Packet *m_packet = nullptr; + }; + + class InputArchive { + public: + void operator()(Entity &entity); + + template + void operator()(Entity &entity, T &value) { + (*m_packet) >> entity >> value; + // gkDebug() << entity << value; + } + + void setPacket(sf::Packet &packet) { m_packet = &packet; } + + private: + sf::Packet *m_packet = nullptr; + }; + + Scene &m_scene; + + mutable OutputArchive m_outputArchive; + mutable InputArchive m_inputArchive; +}; + +#endif // SCENESERIALIZER_HPP_ diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index 42ea96b7f..ab136739f 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -66,6 +66,7 @@ target_link_libraries(${PROJECT_NAME}_lib sfml-system sfml-network) target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib + ${CMAKE_PROJECT_NAME}_common ${GAMEKIT_LIBRARIES} ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} @@ -77,6 +78,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - ${UNIX_LIBS} - ${CMAKE_PROJECT_NAME}_common) + ${UNIX_LIBS}) From 9bf0969eec7783554cd41e452736ef035dd6e4e4 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 18:58:25 +0200 Subject: [PATCH 08/38] Entity serialization is almost complete, only rendering data left. --- .../client/network/ClientCommandHandler.cpp | 15 ++++++ source/client/scene/ClientScene.cpp | 19 +------- source/client/scene/ClientScene.hpp | 7 --- source/client/scene/DrawableFactory.cpp | 36 +++++++++++++++ source/client/scene/DrawableFactory.hpp | 38 +++++++++++++++ source/client/world/ClientWorld.cpp | 5 -- source/client/world/ClientWorld.hpp | 2 - source/common/network/Network.cpp | 5 +- source/common/network/Network.hpp | 3 ++ .../scene/AbstractController.hpp | 0 source/common/scene/DrawableComponent.hpp | 23 ++++++++-- source/common/scene/DrawableDefinitions.hpp | 46 +++++++++++++++++++ source/common/scene/Scene.hpp | 12 +++-- source/common/scene/SceneSerializer.cpp | 9 ++-- source/common/scene/SceneSerializer.hpp | 15 ++---- source/server/network/Server.cpp | 4 +- source/server/network/Server.hpp | 2 +- .../scene/ItemDropFactory.cpp | 11 +++-- .../scene/ItemDropFactory.hpp | 0 source/server/scene/ServerScene.cpp | 37 +++++++++++++++ source/server/scene/ServerScene.hpp | 39 ++++++++++++++++ source/server/world/ServerWorld.cpp | 21 +++++++++ source/server/world/ServerWorld.hpp | 5 ++ 23 files changed, 292 insertions(+), 62 deletions(-) create mode 100644 source/client/scene/DrawableFactory.cpp create mode 100644 source/client/scene/DrawableFactory.hpp rename source/{client => common}/scene/AbstractController.hpp (100%) create mode 100644 source/common/scene/DrawableDefinitions.hpp rename source/{client => server}/scene/ItemDropFactory.cpp (84%) rename source/{client => server}/scene/ItemDropFactory.hpp (100%) create mode 100644 source/server/scene/ServerScene.cpp create mode 100644 source/server/scene/ServerScene.hpp diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 2c242b830..46075b878 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -31,6 +31,7 @@ #include "ClientPlayer.hpp" #include "ClientWorld.hpp" #include "ClientCommandHandler.hpp" +#include "DrawableComponent.hpp" #include "LuaGUIState.hpp" #include "Registry.hpp" @@ -241,5 +242,19 @@ void ClientCommandHandler::setupCallbacks() { } } }); + + m_client.setCommandCallback(Network::Command::SceneState, [this](sf::Packet &packet) { + u16 dimensionID; + packet >> dimensionID; + // FIXME: Check dimension and only apply changes if it's the same as the current one + + packet >> m_world.scene(); + + // FIXME: Move this elsewhere + m_world.scene().registry().view().each([](auto, auto &drawableComponent) { + if (!drawableComponent.drawable() && drawableComponent.drawableDef()) { + } + }); + }); } diff --git a/source/client/scene/ClientScene.cpp b/source/client/scene/ClientScene.cpp index 2d3b1883d..dc70ac3fc 100644 --- a/source/client/scene/ClientScene.cpp +++ b/source/client/scene/ClientScene.cpp @@ -30,29 +30,12 @@ #include "CollisionController.hpp" #include "RenderingController.hpp" -ClientScene::ClientScene(ClientPlayer &player) : m_player(player) { +ClientScene::ClientScene(ClientPlayer &player) { m_controllers.emplace_back(new AnimationController); m_controllers.emplace_back(new CollisionController(player)); m_controllers.emplace_back(new RenderingController); } -void ClientScene::update() { - for (auto &controller : m_controllers) - controller->update(m_registry); - - static bool test = false; - if (!test && m_registry.alive() > 2) { - gkDebug() << "serializing..."; - sf::Packet packet; - serialize(packet); - gkDebug() << "deserializing..."; - deserialize(packet); - gkDebug() << "serializing..."; - serialize(packet); - test = true; - } -} - void ClientScene::draw(gk::RenderTarget &target, gk::RenderStates states) const { if (!m_camera) return; diff --git a/source/client/scene/ClientScene.hpp b/source/client/scene/ClientScene.hpp index b4b4ebc14..adadadb91 100644 --- a/source/client/scene/ClientScene.hpp +++ b/source/client/scene/ClientScene.hpp @@ -35,7 +35,6 @@ #include -#include "AbstractController.hpp" #include "Scene.hpp" class ClientPlayer; @@ -44,18 +43,12 @@ class ClientScene : public Scene, public gk::Drawable { public: ClientScene(ClientPlayer &player); - void update(); - void setCamera(gk::Camera &camera) { m_camera = &camera; } private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; - ClientPlayer &m_player; - gk::Camera *m_camera = nullptr; - - std::deque> m_controllers; }; #endif // CLIENTSCENE_HPP_ diff --git a/source/client/scene/DrawableFactory.cpp b/source/client/scene/DrawableFactory.cpp new file mode 100644 index 000000000..1a7abfac4 --- /dev/null +++ b/source/client/scene/DrawableFactory.cpp @@ -0,0 +1,36 @@ +/* + * ===================================================================================== + * + * 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 "DrawableFactory.hpp" +#include "InventoryCube.hpp" +#include "Registry.hpp" + +void DrawableFactory::createInventoryCube(DrawableComponent &component, const InventoryCubeDef &def) { + auto &cube = component.setDrawable(def.size, true); + cube.setOrigin(def.origin); + cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(def.block)); +} + diff --git a/source/client/scene/DrawableFactory.hpp b/source/client/scene/DrawableFactory.hpp new file mode 100644 index 000000000..9bda544cf --- /dev/null +++ b/source/client/scene/DrawableFactory.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * 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 DRAWABLEFACTORY_HPP_ +#define DRAWABLEFACTORY_HPP_ + +#include "DrawableComponent.hpp" +#include "DrawableDefinitions.hpp" + +class DrawableFactory { + public: + static void createInventoryCube(DrawableComponent &component, const InventoryCubeDef &def); +}; + +#endif // DRAWABLEFACTORY_HPP_ diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index e28c8bb83..bf5a8d173 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -101,11 +101,6 @@ void ClientWorld::checkPlayerChunk(double playerX, double playerY, double player } } -void ClientWorld::onBlockDestroyed(int x, int y, int z, const Block &block) { - if (Config::useItemDrops) - ItemDropFactory::create(m_scene.registry(), x, y, z, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); -} - void ClientWorld::clear() { m_chunks.clear(); } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index 24ec38908..0bbed3f73 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -51,8 +51,6 @@ class ClientWorld : public World, public gk::Drawable { void sendChunkRequests(); void checkPlayerChunk(double playerX, double playerY, double playerZ); - void onBlockDestroyed(int x, int y, int z, const Block &block) override; - void clear(); void updateSky(u16 dimensionID); diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index 4358b4552..c8fc200bc 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -59,8 +59,11 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::RegistryData, "RegistryData"}, - {Network::Command::ChatMessage, "ChatMessage"} + {Network::Command::ChatMessage, "ChatMessage"}, + + {Network::Command::SceneState, "SceneState"}, }; + return commandNames[command]; } diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index d64fba087..c4ae99f09 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -66,6 +66,9 @@ namespace Network { // Chat commands ChatMessage = 21, // [NetworkCommand][u16 client id][std::string message] (both) + + // Scene commands + SceneState = 22, // [NetworkCommand][u16 dimension id][Scene scene] (from Server only) }; std::string commandToString(Command command); diff --git a/source/client/scene/AbstractController.hpp b/source/common/scene/AbstractController.hpp similarity index 100% rename from source/client/scene/AbstractController.hpp rename to source/common/scene/AbstractController.hpp diff --git a/source/common/scene/DrawableComponent.hpp b/source/common/scene/DrawableComponent.hpp index fbd8e43cb..f2b355276 100644 --- a/source/common/scene/DrawableComponent.hpp +++ b/source/common/scene/DrawableComponent.hpp @@ -31,12 +31,20 @@ #include -class DrawableComponent { +#include "ISerializable.hpp" + +class DrawableComponent : public ISerializable { public: template auto setDrawable(Args &&...args) -> typename std::enable_if::value, T &>::type { - m_drawable.reset(new T(std::forward(args)...)); - return *static_cast(m_drawable.get()); + m_drawable = std::make_shared(std::forward(args)...); + return *std::static_pointer_cast(m_drawable); + } + + template + auto setDrawableDef(Args &&...args) -> typename std::enable_if::value, T &>::type { + m_drawableDef = std::make_shared(std::forward(args)...); + return *std::static_pointer_cast(m_drawableDef); } void draw(gk::RenderTarget &target, gk::RenderStates states) { @@ -44,8 +52,15 @@ class DrawableComponent { target.draw(*m_drawable, states); } + void serialize(sf::Packet &packet) const override { m_drawableDef->serialize(packet); } + void deserialize(sf::Packet &packet) override { m_drawableDef->deserialize(packet); } + + const gk::Drawable *drawable() const { return m_drawable.get(); } + const ISerializable *drawableDef() const { return m_drawableDef.get(); } + private: - std::unique_ptr m_drawable; + std::shared_ptr m_drawable; + std::shared_ptr m_drawableDef; }; #endif // DRAWABLECOMPONENT_HPP_ diff --git a/source/common/scene/DrawableDefinitions.hpp b/source/common/scene/DrawableDefinitions.hpp new file mode 100644 index 000000000..f60796c77 --- /dev/null +++ b/source/common/scene/DrawableDefinitions.hpp @@ -0,0 +1,46 @@ +/* + * ===================================================================================== + * + * 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 DRAWABLEDEFINITIONS_HPP_ +#define DRAWABLEDEFINITIONS_HPP_ + +#include + +#include + +#include "ISerializable.hpp" +#include "NetworkUtils.hpp" + +struct InventoryCubeDef : public ISerializable { + float size = 1.f; + gk::Vector3f origin{0, 0, 0}; + std::string block{"_:air"}; + + void serialize(sf::Packet &packet) const override { packet << size << origin << block; } + void deserialize(sf::Packet &packet) override { packet >> size >> origin >> block; } +}; + +#endif // DRAWABLEDEFINITIONS_HPP_ diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp index 101244291..039cad3aa 100644 --- a/source/common/scene/Scene.hpp +++ b/source/common/scene/Scene.hpp @@ -27,21 +27,27 @@ #ifndef SCENE_HPP_ #define SCENE_HPP_ +#include "AbstractController.hpp" #include "ISerializable.hpp" #include "SceneSerializer.hpp" class Scene : public ISerializable { public: - void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet); } - void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet); } + virtual void update() { for (auto &controller : m_controllers) controller->update(m_registry); } + void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet, *this); } + void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet, *this); } + + const entt::DefaultRegistry ®istry() const { return m_registry; } entt::DefaultRegistry ®istry() { return m_registry; } protected: mutable entt::DefaultRegistry m_registry; + std::deque> m_controllers; + private: - SceneSerializer m_serializer{*this}; + SceneSerializer m_serializer; }; #endif // SCENE_HPP_ diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index c68a2a670..71f1afa67 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -25,18 +25,19 @@ * ===================================================================================== */ #include "AnimationComponent.hpp" +#include "DrawableComponent.hpp" #include "ItemStack.hpp" #include "Scene.hpp" #include "SceneSerializer.hpp" -void SceneSerializer::serialize(sf::Packet &packet) const { +void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { m_outputArchive.setPacket(packet); - m_scene.registry().snapshot().component(m_outputArchive); + scene.registry().snapshot().component(m_outputArchive); } -void SceneSerializer::deserialize(sf::Packet &packet) { +void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { m_inputArchive.setPacket(packet); - m_scene.registry().restore().component(m_inputArchive); + scene.registry().restore().component(m_inputArchive); } void SceneSerializer::OutputArchive::operator()(Entity entity) { diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 9c85069e9..3d00e81d8 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -31,19 +31,16 @@ #include -#include "ISerializable.hpp" #include "NetworkUtils.hpp" class Scene; -class SceneSerializer : public ISerializable { +class SceneSerializer { using Entity = entt::DefaultRegistry::entity_type; public: - SceneSerializer(Scene &scene) : m_scene(scene) {} - - void serialize(sf::Packet &packet) const; - void deserialize(sf::Packet &packet); + void serialize(sf::Packet &packet, const Scene &scene) const; + void deserialize(sf::Packet &packet, Scene &scene); private: class OutputArchive { @@ -52,7 +49,7 @@ class SceneSerializer : public ISerializable { template void operator()(Entity entity, const T &value) { - // gkDebug() << entity << value; + gkDebug() << entity << (void *)&value; (*m_packet) << entity << value; } @@ -69,7 +66,7 @@ class SceneSerializer : public ISerializable { template void operator()(Entity &entity, T &value) { (*m_packet) >> entity >> value; - // gkDebug() << entity << value; + gkDebug() << entity << (void *)&value; } void setPacket(sf::Packet &packet) { m_packet = &packet; } @@ -78,8 +75,6 @@ class SceneSerializer : public ISerializable { sf::Packet *m_packet = nullptr; }; - Scene &m_scene; - mutable OutputArchive m_outputArchive; mutable InputArchive m_inputArchive; }; diff --git a/source/server/network/Server.cpp b/source/server/network/Server.cpp index 723f800a9..5b10822ba 100644 --- a/source/server/network/Server.cpp +++ b/source/server/network/Server.cpp @@ -173,8 +173,8 @@ void Server::disconnectClient(ClientInfo &client) { } } -void Server::sendToAllClients(sf::Packet &packet) { - for (ClientInfo &client : m_info.clients()) { +void Server::sendToAllClients(sf::Packet &packet) const { + for (const ClientInfo &client : m_info.clients()) { client.tcpSocket->send(packet); } } diff --git a/source/server/network/Server.hpp b/source/server/network/Server.hpp index dffe32afa..080841b48 100644 --- a/source/server/network/Server.hpp +++ b/source/server/network/Server.hpp @@ -48,7 +48,7 @@ class Server { void handleGameEvents(); - void sendToAllClients(sf::Packet &packet); + void sendToAllClients(sf::Packet &packet) const; bool isRunning() const { return m_isRunning; } diff --git a/source/client/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp similarity index 84% rename from source/client/scene/ItemDropFactory.cpp rename to source/server/scene/ItemDropFactory.cpp index 88a4cc97e..3bcbd059d 100644 --- a/source/client/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -26,7 +26,7 @@ */ #include "AnimationComponent.hpp" #include "DrawableComponent.hpp" -#include "InventoryCube.hpp" +#include "DrawableDefinitions.hpp" #include "ItemDropFactory.hpp" #include "ItemStack.hpp" #include "Registry.hpp" @@ -35,9 +35,10 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y auto entity = registry.create(); auto &drawableComponent = registry.assign(entity); - auto &cube = drawableComponent.setDrawable(0.25f, true); - cube.setOrigin(cube.size() / 2.f, cube.size() / 2.f, cube.size() / 2.f); - cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(itemID)); + auto &cube = drawableComponent.setDrawableDef(); + cube.size = 0.25f; + cube.origin = gk::Vector3f{cube.size / 2.f, cube.size / 2.f, cube.size / 2.f}; + cube.block = itemID; auto &transformable = registry.assign(entity); transformable.setPosition(x + 0.5, y + 0.5, z + 0.5); @@ -46,7 +47,7 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); - registry.assign(entity, 0., 0., 0., cube.size(), cube.size(), cube.size()); + registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); registry.assign(entity, itemID, amount); } diff --git a/source/client/scene/ItemDropFactory.hpp b/source/server/scene/ItemDropFactory.hpp similarity index 100% rename from source/client/scene/ItemDropFactory.hpp rename to source/server/scene/ItemDropFactory.hpp diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp new file mode 100644 index 000000000..5d2c60596 --- /dev/null +++ b/source/server/scene/ServerScene.cpp @@ -0,0 +1,37 @@ +/* + * ===================================================================================== + * + * 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 +#include + +#include "ServerScene.hpp" + +ServerScene::ServerScene() { +} + +void ServerScene::update() { +} + diff --git a/source/server/scene/ServerScene.hpp b/source/server/scene/ServerScene.hpp new file mode 100644 index 000000000..8ccf9d7fb --- /dev/null +++ b/source/server/scene/ServerScene.hpp @@ -0,0 +1,39 @@ +/* + * ===================================================================================== + * + * 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 SERVERSCENE_HPP_ +#define SERVERSCENE_HPP_ + +#include "Scene.hpp" + +class ServerScene : public Scene { + public: + ServerScene(); + + void update() override; +}; + +#endif // SERVERSCENE_HPP_ diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index d65b39ed0..e290c12cf 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -33,6 +33,8 @@ #include "ServerPlayer.hpp" #include "ServerWorld.hpp" +#include "Dimension.hpp" // FIXME + void ServerWorld::update() { if (m_lastTick < m_clock.getTicks() / 50) { m_lastTick = m_clock.getTicks() / 50; @@ -51,6 +53,18 @@ void ServerWorld::update() { } } } + + m_scene.update(); + + // FIXME: Should be placed somewhere else + // FIXME: Shouldn't send that often + if (m_clock.getTicks() % 100 < 12) { + sf::Packet packet; + packet << Network::Command::SceneState; // FIXME + packet << m_dimension.id(); + packet << m_scene; + m_server->server().sendToAllClients(packet); + } } void ServerWorld::createChunkNeighbours(ServerChunk &chunk) { @@ -146,6 +160,13 @@ void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) sendChunkData(client, chunk); } +#include "ItemDropFactory.hpp" // FIXME + +void ServerWorld::onBlockDestroyed(int x, int y, int z, const Block &block) { + // FIXME if (Config::useItemDrops) + ItemDropFactory::create(m_scene.registry(), x, y, z, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); +} + ServerChunk &ServerWorld::createChunk(s32 cx, s32 cy, s32 cz) { ServerChunk *chunk = (ServerChunk *)getChunk(cx, cy, cz); if (!chunk) { diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp index a24adb2e1..bed806cb0 100644 --- a/source/server/world/ServerWorld.hpp +++ b/source/server/world/ServerWorld.hpp @@ -30,6 +30,7 @@ #include #include "ServerChunk.hpp" +#include "ServerScene.hpp" #include "TerrainGenerator.hpp" #include "World.hpp" @@ -55,6 +56,8 @@ class ServerWorld : public World { void sendChunkData(const ClientInfo &client, ServerChunk &chunk); void sendRequestedData(ClientInfo &client, s32 cx, s32 cy, s32 cz); + void onBlockDestroyed(int x, int y, int z, const Block &block) override; + ServerChunk &createChunk(s32 cx, s32 cy, s32 cz); Chunk *getChunk(int cx, int cy, int cz) const override; @@ -79,6 +82,8 @@ class ServerWorld : public World { ServerCommandHandler *m_server = nullptr; gk::GameClock &m_clock; + + ServerScene m_scene; }; #endif // SERVERWORLD_HPP_ From 69b08c54be3c3882c538a2d7aa57320bee2b7ccc Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 22:29:54 +0200 Subject: [PATCH 09/38] Got rendering working. --- .../client/network/ClientCommandHandler.cpp | 8 +---- source/client/scene/RenderingController.cpp | 24 +++++++++++--- source/client/scene/RenderingController.hpp | 2 ++ source/common/scene/DrawableComponent.hpp | 17 +--------- .../scene/DrawableDef.cpp} | 26 +++++++++++---- .../scene/DrawableDef.hpp} | 32 +++++++++++++++---- ...leDefinitions.hpp => InventoryCubeDef.hpp} | 12 +++---- source/common/scene/SceneSerializer.cpp | 10 +++--- source/common/scene/SceneSerializer.hpp | 4 +-- source/server/scene/ItemDropFactory.cpp | 11 ++++--- source/server/world/ServerWorld.cpp | 9 ++++-- 11 files changed, 93 insertions(+), 62 deletions(-) rename source/{client/scene/DrawableFactory.cpp => common/scene/DrawableDef.cpp} (70%) rename source/{client/scene/DrawableFactory.hpp => common/scene/DrawableDef.hpp} (66%) rename source/common/scene/{DrawableDefinitions.hpp => InventoryCubeDef.hpp} (89%) diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 46075b878..085b0ed48 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -246,15 +246,9 @@ void ClientCommandHandler::setupCallbacks() { m_client.setCommandCallback(Network::Command::SceneState, [this](sf::Packet &packet) { u16 dimensionID; packet >> dimensionID; - // FIXME: Check dimension and only apply changes if it's the same as the current one + // FIXME: Check dimension and only apply changes if it's the same as the current one packet >> m_world.scene(); - - // FIXME: Move this elsewhere - m_world.scene().registry().view().each([](auto, auto &drawableComponent) { - if (!drawableComponent.drawable() && drawableComponent.drawableDef()) { - } - }); }); } diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 5bab875d0..692e6244a 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -24,6 +24,7 @@ * * ===================================================================================== */ +#include "DrawableDef.hpp" #include "DrawableComponent.hpp" #include "InventoryCube.hpp" #include "RenderingController.hpp" @@ -31,11 +32,26 @@ #include #include -void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { - registry.view().each([&](auto entity, auto &drawable, auto &transformable) { - if (gk::GameClock::getInstance().getTicks() % 100 < 12) - gkDebug() << "Drawing entity" << entity << "at" << transformable.getPosition(); +#include "Registry.hpp" + +void RenderingController::update(entt::DefaultRegistry ®istry) { + registry.view().each([&](auto entity, auto &drawableDef) { + const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); + + DrawableComponent &drawable = (!registry.has()) + ? registry.assign(entity) + : registry.get(entity); + + InventoryCube &cube = drawable.setDrawable(cubeDef.size, true); + cube.setOrigin(cubeDef.origin); + cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(cubeDef.blockID)); + registry.remove(entity); + }); +} + +void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { + registry.view().each([&](auto, auto &drawable, auto &transformable) { gk::RenderStates drawStates = states; drawStates.transform *= transformable.getTransform(); drawable.draw(target, drawStates); diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp index 75af6d10e..78c515f01 100644 --- a/source/client/scene/RenderingController.hpp +++ b/source/client/scene/RenderingController.hpp @@ -31,6 +31,8 @@ class RenderingController : public AbstractController { public: + void update(entt::DefaultRegistry ®istry) override; + void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; }; diff --git a/source/common/scene/DrawableComponent.hpp b/source/common/scene/DrawableComponent.hpp index f2b355276..3d80c172b 100644 --- a/source/common/scene/DrawableComponent.hpp +++ b/source/common/scene/DrawableComponent.hpp @@ -31,9 +31,7 @@ #include -#include "ISerializable.hpp" - -class DrawableComponent : public ISerializable { +class DrawableComponent { public: template auto setDrawable(Args &&...args) -> typename std::enable_if::value, T &>::type { @@ -41,26 +39,13 @@ class DrawableComponent : public ISerializable { return *std::static_pointer_cast(m_drawable); } - template - auto setDrawableDef(Args &&...args) -> typename std::enable_if::value, T &>::type { - m_drawableDef = std::make_shared(std::forward(args)...); - return *std::static_pointer_cast(m_drawableDef); - } - void draw(gk::RenderTarget &target, gk::RenderStates states) { if (m_drawable) target.draw(*m_drawable, states); } - void serialize(sf::Packet &packet) const override { m_drawableDef->serialize(packet); } - void deserialize(sf::Packet &packet) override { m_drawableDef->deserialize(packet); } - - const gk::Drawable *drawable() const { return m_drawable.get(); } - const ISerializable *drawableDef() const { return m_drawableDef.get(); } - private: std::shared_ptr m_drawable; - std::shared_ptr m_drawableDef; }; #endif // DRAWABLECOMPONENT_HPP_ diff --git a/source/client/scene/DrawableFactory.cpp b/source/common/scene/DrawableDef.cpp similarity index 70% rename from source/client/scene/DrawableFactory.cpp rename to source/common/scene/DrawableDef.cpp index 1a7abfac4..f96f05d2f 100644 --- a/source/client/scene/DrawableFactory.cpp +++ b/source/common/scene/DrawableDef.cpp @@ -24,13 +24,25 @@ * * ===================================================================================== */ -#include "DrawableFactory.hpp" -#include "InventoryCube.hpp" -#include "Registry.hpp" +#include "DrawableDef.hpp" +#include "NetworkUtils.hpp" -void DrawableFactory::createInventoryCube(DrawableComponent &component, const InventoryCubeDef &def) { - auto &cube = component.setDrawable(def.size, true); - cube.setOrigin(def.origin); - cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(def.block)); +InventoryCubeDef &DrawableDef::addInventoryCube() { + m_cubes.emplace_back(); + return m_cubes.back(); +} + +void DrawableDef::serialize(sf::Packet &packet) const { + for (auto &it : m_cubes) + packet << u8(DrawableType::InventoryCube) << it; +} + +void DrawableDef::deserialize(sf::Packet &packet) { + u8 type; + packet >> type; + if (type == DrawableType::InventoryCube) { + m_cubes.emplace_back(); + packet >> m_cubes.back(); + } } diff --git a/source/client/scene/DrawableFactory.hpp b/source/common/scene/DrawableDef.hpp similarity index 66% rename from source/client/scene/DrawableFactory.hpp rename to source/common/scene/DrawableDef.hpp index 9bda544cf..948ed2d16 100644 --- a/source/client/scene/DrawableFactory.hpp +++ b/source/common/scene/DrawableDef.hpp @@ -24,15 +24,33 @@ * * ===================================================================================== */ -#ifndef DRAWABLEFACTORY_HPP_ -#define DRAWABLEFACTORY_HPP_ +#ifndef DRAWABLEDEF_HPP_ +#define DRAWABLEDEF_HPP_ -#include "DrawableComponent.hpp" -#include "DrawableDefinitions.hpp" +#include -class DrawableFactory { +#include "ISerializable.hpp" +#include "InventoryCubeDef.hpp" + +namespace DrawableType { + enum : u8 { + Undefined = 0, + InventoryCube = 1, + }; +} + +class DrawableDef : public ISerializable { public: - static void createInventoryCube(DrawableComponent &component, const InventoryCubeDef &def); + InventoryCubeDef &addInventoryCube(); + + // FIXME + const InventoryCubeDef &getInventoryCubeDef() const { return m_cubes.back(); } + + void serialize(sf::Packet &packet) const override; + void deserialize(sf::Packet &packet) override; + + protected: + std::vector m_cubes; }; -#endif // DRAWABLEFACTORY_HPP_ +#endif // DRAWABLEDEF_HPP_ diff --git a/source/common/scene/DrawableDefinitions.hpp b/source/common/scene/InventoryCubeDef.hpp similarity index 89% rename from source/common/scene/DrawableDefinitions.hpp rename to source/common/scene/InventoryCubeDef.hpp index f60796c77..b286e45fc 100644 --- a/source/common/scene/DrawableDefinitions.hpp +++ b/source/common/scene/InventoryCubeDef.hpp @@ -24,8 +24,8 @@ * * ===================================================================================== */ -#ifndef DRAWABLEDEFINITIONS_HPP_ -#define DRAWABLEDEFINITIONS_HPP_ +#ifndef INVENTORYCUBEDEF_HPP_ +#define INVENTORYCUBEDEF_HPP_ #include @@ -37,10 +37,10 @@ struct InventoryCubeDef : public ISerializable { float size = 1.f; gk::Vector3f origin{0, 0, 0}; - std::string block{"_:air"}; + std::string blockID{"_:air"}; - void serialize(sf::Packet &packet) const override { packet << size << origin << block; } - void deserialize(sf::Packet &packet) override { packet >> size >> origin >> block; } + void serialize(sf::Packet &packet) const override { packet << size << origin << blockID; } + void deserialize(sf::Packet &packet) override { packet >> size >> origin >> blockID; } }; -#endif // DRAWABLEDEFINITIONS_HPP_ +#endif // INVENTORYCUBEDEF_HPP_ diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index 71f1afa67..09968bed1 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -25,28 +25,28 @@ * ===================================================================================== */ #include "AnimationComponent.hpp" -#include "DrawableComponent.hpp" +#include "DrawableDef.hpp" #include "ItemStack.hpp" #include "Scene.hpp" #include "SceneSerializer.hpp" void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { m_outputArchive.setPacket(packet); - scene.registry().snapshot().component(m_outputArchive); + scene.registry().snapshot().component(m_outputArchive); } void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { m_inputArchive.setPacket(packet); - scene.registry().restore().component(m_inputArchive); + scene.registry().restore().component(m_inputArchive); } void SceneSerializer::OutputArchive::operator()(Entity entity) { - gkDebug() << entity; + // gkDebug() << entity; (*m_packet) << entity; } void SceneSerializer::InputArchive::operator()(Entity &entity) { (*m_packet) >> entity; - gkDebug() << entity; + // gkDebug() << entity; } diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 3d00e81d8..7bdc45ebc 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -49,7 +49,7 @@ class SceneSerializer { template void operator()(Entity entity, const T &value) { - gkDebug() << entity << (void *)&value; + // gkDebug() << entity << (void *)&value << typeid(T).name(); (*m_packet) << entity << value; } @@ -66,7 +66,7 @@ class SceneSerializer { template void operator()(Entity &entity, T &value) { (*m_packet) >> entity >> value; - gkDebug() << entity << (void *)&value; + // gkDebug() << entity << (void *)&value << typeid(T).name(); } void setPacket(sf::Packet &packet) { m_packet = &packet; } diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 3bcbd059d..7acadccbf 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -26,7 +26,7 @@ */ #include "AnimationComponent.hpp" #include "DrawableComponent.hpp" -#include "DrawableDefinitions.hpp" +#include "DrawableDef.hpp" #include "ItemDropFactory.hpp" #include "ItemStack.hpp" #include "Registry.hpp" @@ -34,11 +34,11 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); - auto &drawableComponent = registry.assign(entity); - auto &cube = drawableComponent.setDrawableDef(); + auto &drawableDef = registry.assign(entity); + auto &cube = drawableDef.addInventoryCube(); cube.size = 0.25f; cube.origin = gk::Vector3f{cube.size / 2.f, cube.size / 2.f, cube.size / 2.f}; - cube.block = itemID; + cube.blockID = itemID; auto &transformable = registry.assign(entity); transformable.setPosition(x + 0.5, y + 0.5, z + 0.5); @@ -47,7 +47,8 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); - registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); + // FIXME + // registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); registry.assign(entity, itemID, amount); } diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index e290c12cf..84bfef7a8 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -57,10 +57,13 @@ void ServerWorld::update() { m_scene.update(); // FIXME: Should be placed somewhere else - // FIXME: Shouldn't send that often - if (m_clock.getTicks() % 100 < 12) { + static int lastTime = m_clock.getTicks(true); + int now = m_clock.getTicks(true); + if (now - lastTime > 1000) { + lastTime = now; + sf::Packet packet; - packet << Network::Command::SceneState; // FIXME + packet << Network::Command::SceneState; packet << m_dimension.id(); packet << m_scene; m_server->server().sendToAllClients(packet); From 9738bbed21af0817ea47c574733bae97b93270f3 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 23:11:34 +0200 Subject: [PATCH 10/38] [PlayerList] Added to store players in server. --- source/server/core/PlayerList.cpp | 55 +++++++++ source/server/core/PlayerList.hpp | 56 +++++++++ source/server/core/ServerApplication.hpp | 4 +- .../server/network/ServerCommandHandler.cpp | 116 +++++++++++------- .../server/network/ServerCommandHandler.hpp | 6 +- 5 files changed, 186 insertions(+), 51 deletions(-) create mode 100644 source/server/core/PlayerList.cpp create mode 100644 source/server/core/PlayerList.hpp diff --git a/source/server/core/PlayerList.cpp b/source/server/core/PlayerList.cpp new file mode 100644 index 000000000..d67bef2d5 --- /dev/null +++ b/source/server/core/PlayerList.cpp @@ -0,0 +1,55 @@ +/* + * ===================================================================================== + * + * 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 "PlayerList.hpp" + +ServerPlayer &PlayerList::addPlayer(ClientInfo &client) { + m_players.emplace(client.id, client); + return m_players.at(client.id); +} + +void PlayerList::removePlayer(u16 id) { + auto it = m_players.find(id); + if (it != m_players.end()) + m_players.erase(it); +} + +const ServerPlayer *PlayerList::getPlayer(u16 id) const { + auto it = m_players.find(id); + if (it == m_players.end()) + return nullptr; + + return &it->second; +} + +ServerPlayer *PlayerList::getPlayer(u16 id) { + auto it = m_players.find(id); + if (it == m_players.end()) + return nullptr; + + return &it->second; +} + diff --git a/source/server/core/PlayerList.hpp b/source/server/core/PlayerList.hpp new file mode 100644 index 000000000..b2c8ac851 --- /dev/null +++ b/source/server/core/PlayerList.hpp @@ -0,0 +1,56 @@ +/* + * ===================================================================================== + * + * 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 PLAYERLIST_HPP_ +#define PLAYERLIST_HPP_ + +#include + +#include "ServerPlayer.hpp" + +class PlayerList { + using Container = std::unordered_map; + using Iterator = Container::iterator; + using ConstIterator = Container::const_iterator; + + public: + ServerPlayer &addPlayer(ClientInfo &client); + void removePlayer(u16 id); + + const ServerPlayer *getPlayer(u16 id) const; + ServerPlayer *getPlayer(u16 id); + + Iterator begin() { return m_players.begin(); } + Iterator end() { return m_players.end(); } + + ConstIterator begin() const { return m_players.begin(); } + ConstIterator end() const { return m_players.end(); } + + private: + Container m_players; +}; + +#endif // PLAYERLIST_HPP_ diff --git a/source/server/core/ServerApplication.hpp b/source/server/core/ServerApplication.hpp index 60bb12564..4fd895cf2 100644 --- a/source/server/core/ServerApplication.hpp +++ b/source/server/core/ServerApplication.hpp @@ -37,7 +37,7 @@ #include "Server.hpp" #include "ServerCommandHandler.hpp" #include "ServerModLoader.hpp" -#include "ServerPlayer.hpp" +#include "PlayerList.hpp" #include "WorldController.hpp" struct ServerOnlineEvent { @@ -73,7 +73,7 @@ class ServerApplication { u16 m_port = 4242; WorldController m_worldController{m_registry, m_clock}; - std::unordered_map m_players; + PlayerList m_players; Server m_server; ServerCommandHandler m_serverCommandHandler{m_scriptEngine, m_server, m_worldController, m_players, m_registry}; diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 3804ce5f7..1b22e7df5 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -25,11 +25,11 @@ * ===================================================================================== */ #include "BlockData.hpp" +#include "PlayerList.hpp" #include "Registry.hpp" #include "ScriptEngine.hpp" #include "Server.hpp" #include "ServerBlock.hpp" -#include "ServerPlayer.hpp" #include "ServerCommandHandler.hpp" #include "WorldController.hpp" @@ -55,18 +55,21 @@ void ServerCommandHandler::sendBlockInvUpdate(s32 x, s32 y, s32 z, const Invento } void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportation, const ClientInfo *client) const { - const ServerPlayer &player = m_players.at(clientID); - - sf::Packet packet; - packet << Network::Command::PlayerPosUpdate; - packet << clientID; - packet << player.x() << player.y() << player.z(); - packet << isTeleportation; + const ServerPlayer *player = m_players.getPlayer(clientID); + if (player) { + sf::Packet packet; + packet << Network::Command::PlayerPosUpdate; + packet << clientID; + packet << player->x() << player->y() << player->z(); + packet << isTeleportation; - if (!client) - m_server.sendToAllClients(packet); + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); + } else - client->tcpSocket->send(packet); + gkError() << ("Failed to send pos update for player " + std::to_string(clientID) + ": Player not found").c_str(); } void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client) const { @@ -105,9 +108,7 @@ void ServerCommandHandler::setupCallbacks() { client.tcpSocket->send(spawnPacket); } - m_players.emplace(client.id, client); - - auto &player = m_players.at(client.id); + auto &player = m_players.addPlayer(client); player.setPosition(m_spawnPosition.x, m_spawnPosition.y, m_spawnPosition.z); // FIXME: Find a better way to give starting items @@ -115,7 +116,7 @@ void ServerCommandHandler::setupCallbacks() { sf::Packet invPacket; invPacket << Network::Command::PlayerInvUpdate << client.id; - invPacket << m_players.at(client.id).inventory(); + invPacket << player.inventory(); client.tcpSocket->send(invPacket); // Send spawn packet to all clients for this player @@ -126,9 +127,7 @@ void ServerCommandHandler::setupCallbacks() { }); m_server.setCommandCallback(Network::Command::ClientDisconnect, [this](ClientInfo &client, sf::Packet &) { - auto it = m_players.find(client.id); - if (it != m_players.end()) - m_players.erase(it); + m_players.removePlayer(client.id); }); m_server.setCommandCallback(Network::Command::ChunkRequest, [this](ClientInfo &client, sf::Packet &packet) { @@ -141,9 +140,15 @@ void ServerCommandHandler::setupCallbacks() { m_server.setCommandCallback(Network::Command::PlayerInvUpdate, [this](ClientInfo &client, sf::Packet &packet) { u16 clientId; packet >> clientId; - if (clientId == client.id) { - packet >> m_players.at(client.id).inventory(); + + ServerPlayer *player = m_players.getPlayer(clientId); + if (player) { + if (clientId == client.id) { + packet >> player->inventory(); + } } + else + gkError() << ("Failed to update inventory of player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerPosUpdate, [this](ClientInfo &client, sf::Packet &packet) { @@ -152,24 +157,34 @@ void ServerCommandHandler::setupCallbacks() { packet >> clientId; packet >> x >> y >> z; - if (clientId == client.id) - m_players.at(client.id).setPosition(x, y, z); + ServerPlayer *player = m_players.getPlayer(clientId); + if (player) { + if (clientId == client.id) + player->setPosition(x, y, z); + } + else + gkError() << ("Failed to update position of player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerPlaceBlock, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - u32 block; - packet >> x >> y >> z >> block; + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + u32 block; + packet >> x >> y >> z >> block; - ServerWorld &world = getWorldForClient(client.id); - world.setData(x, y, z, block >> 16); - world.setBlock(x, y, z, block & 0xffff); + ServerWorld &world = getWorldForClient(client.id); + world.setData(x, y, z, block >> 16); + world.setBlock(x, y, z, block & 0xffff); - m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, m_players.at(client.id), world, client, *this); + m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, *player, world, client, *this); - sf::Packet answer; - answer << Network::Command::BlockUpdate << x << y << z << block; - m_server.sendToAllClients(answer); + sf::Packet answer; + answer << Network::Command::BlockUpdate << x << y << z << block; + m_server.sendToAllClients(answer); + } + else + gkError() << ("Failed to place block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerDigBlock, [this](ClientInfo &client, sf::Packet &packet) { @@ -213,19 +228,24 @@ void ServerCommandHandler::setupCallbacks() { }); m_server.setCommandCallback(Network::Command::BlockActivated, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - u16 screenWidth, screenHeight; - u8 guiScale; - packet >> x >> y >> z >> screenWidth >> screenHeight >> guiScale; + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + u16 screenWidth, screenHeight; + u8 guiScale; + packet >> x >> y >> z >> screenWidth >> screenHeight >> guiScale; - ServerWorld &world = getWorldForClient(client.id); + ServerWorld &world = getWorldForClient(client.id); - u16 id = world.getBlock(x, y, z); - ServerBlock &block = (ServerBlock &)(m_registry.getBlock(id)); - bool hasBeenActivated = block.onBlockActivated({x, y, z}, m_players.at(client.id), world, client, *this, screenWidth, screenHeight, guiScale); + u16 id = world.getBlock(x, y, z); + ServerBlock &block = (ServerBlock &)(m_registry.getBlock(id)); + bool hasBeenActivated = block.onBlockActivated({x, y, z}, *player, world, client, *this, screenWidth, screenHeight, guiScale); - if (hasBeenActivated) - m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockActivated, glm::ivec3{x, y, z}, block, m_players.at(client.id), world, client, *this); + if (hasBeenActivated) + m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockActivated, glm::ivec3{x, y, z}, block, *player, world, client, *this); + } + else + gkError() << ("Failed to activate block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::BlockInvUpdate, [this](ClientInfo &client, sf::Packet &packet) { @@ -267,14 +287,18 @@ void ServerCommandHandler::setupCallbacks() { } void ServerCommandHandler::setPlayerPosition(u16 clientID, s32 x, s32 y, s32 z) { - m_players.at(clientID).setPosition(x, y, z); + ServerPlayer *player = m_players.getPlayer(clientID); + if (player) + player->setPosition(x, y, z); + else + gkError() << ("Failed to set position for player " + std::to_string(clientID) + ": Player not found").c_str(); } inline ServerWorld &ServerCommandHandler::getWorldForClient(u16 clientID) { - auto it = m_players.find(clientID); - if (it == m_players.end()) + ServerPlayer *player = m_players.getPlayer(clientID); + if (!player) throw EXCEPTION("Player instance not found for client", clientID); - return m_worldController.getWorld(it->second.dimension()); + return m_worldController.getWorld(player->dimension()); } diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index aff2807e1..58aad0925 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -37,16 +37,16 @@ struct BlockData; class ClientInfo; class Inventory; +class PlayerList; class Registry; class ScriptEngine; class Server; -class ServerPlayer; class ServerWorld; class WorldController; class ServerCommandHandler { public: - ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, std::unordered_map &players, Registry ®istry) + ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, PlayerList &players, Registry ®istry) : m_scriptEngine(scriptEngine), m_server(server), m_worldController(worldController), m_players(players), m_registry(registry) {} void sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const ClientInfo *client = nullptr) const; @@ -68,7 +68,7 @@ class ServerCommandHandler { Server &m_server; WorldController &m_worldController; - std::unordered_map &m_players; + PlayerList &m_players; Registry &m_registry; From dd81f09f07d252899bb77b54cef8d5f048738baa Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 5 Apr 2020 23:27:18 +0200 Subject: [PATCH 11/38] [CollisionController] Now server-side. --- source/client/scene/ClientScene.cpp | 2 -- source/server/core/ServerApplication.cpp | 2 +- .../scene/CollisionController.cpp | 17 ++++++++++------- .../scene/CollisionController.hpp | 6 +++--- source/server/scene/ItemDropFactory.cpp | 3 +-- source/server/scene/ServerScene.cpp | 7 +++---- source/server/scene/ServerScene.hpp | 6 +++--- source/server/world/ServerWorld.hpp | 5 +++-- source/server/world/WorldController.cpp | 4 ++-- source/server/world/WorldController.hpp | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) rename source/{client => server}/scene/CollisionController.cpp (76%) rename source/{client => server}/scene/CollisionController.hpp (92%) diff --git a/source/client/scene/ClientScene.cpp b/source/client/scene/ClientScene.cpp index dc70ac3fc..cbbf47a06 100644 --- a/source/client/scene/ClientScene.cpp +++ b/source/client/scene/ClientScene.cpp @@ -27,12 +27,10 @@ #include "AnimationController.hpp" #include "ClientPlayer.hpp" #include "ClientScene.hpp" -#include "CollisionController.hpp" #include "RenderingController.hpp" ClientScene::ClientScene(ClientPlayer &player) { m_controllers.emplace_back(new AnimationController); - m_controllers.emplace_back(new CollisionController(player)); m_controllers.emplace_back(new RenderingController); } diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp index 3655d704b..5434a5b66 100644 --- a/source/server/core/ServerApplication.cpp +++ b/source/server/core/ServerApplication.cpp @@ -72,7 +72,7 @@ void ServerApplication::init() { m_serverCommandHandler.setupCallbacks(); m_worldController.setServer(m_serverCommandHandler); - m_worldController.init(); + m_worldController.init(m_players); m_scriptEngine.luaCore().setRegistry(&m_registry); diff --git a/source/client/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp similarity index 76% rename from source/client/scene/CollisionController.cpp rename to source/server/scene/CollisionController.cpp index 24d76bd90..8bd2212e9 100644 --- a/source/client/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -24,18 +24,21 @@ * * ===================================================================================== */ -#include "ClientPlayer.hpp" +#include + #include "CollisionController.hpp" -#include "InventoryCube.hpp" #include "ItemStack.hpp" +#include "PlayerList.hpp" void CollisionController::update(entt::DefaultRegistry ®istry) { registry.view().each([&](auto entity, auto &transformable, auto &box, auto &itemStack) { - gk::DoubleBox hitbox = box + transformable.getPosition(); - gk::DoubleBox playerHitbox = m_player.hitbox() + gk::Vector3d{m_player.x(), m_player.y(), m_player.z()}; - if (hitbox.intersects(playerHitbox)) { - m_player.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); - registry.destroy(entity); + for (auto &it : m_players) { + gk::DoubleBox hitbox = box + transformable.getPosition(); + gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; + if (hitbox.intersects(playerHitbox)) { + it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + registry.destroy(entity); + } } }); } diff --git a/source/client/scene/CollisionController.hpp b/source/server/scene/CollisionController.hpp similarity index 92% rename from source/client/scene/CollisionController.hpp rename to source/server/scene/CollisionController.hpp index 9f7741b81..c07731d92 100644 --- a/source/client/scene/CollisionController.hpp +++ b/source/server/scene/CollisionController.hpp @@ -29,16 +29,16 @@ #include "AbstractController.hpp" -class ClientPlayer; +class PlayerList; class CollisionController : public AbstractController { public: - CollisionController(ClientPlayer &player) : m_player(player) {} + CollisionController(PlayerList &players) : m_players(players) {} void update(entt::DefaultRegistry ®istry); private: - ClientPlayer &m_player; + PlayerList &m_players; }; #endif // COLLISIONCONTROLLER_HPP_ diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 7acadccbf..cab9c6812 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -47,8 +47,7 @@ void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); - // FIXME - // registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); + registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); registry.assign(entity, itemID, amount); } diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp index 5d2c60596..9e6572266 100644 --- a/source/server/scene/ServerScene.cpp +++ b/source/server/scene/ServerScene.cpp @@ -27,11 +27,10 @@ #include #include +#include "CollisionController.hpp" #include "ServerScene.hpp" -ServerScene::ServerScene() { -} - -void ServerScene::update() { +ServerScene::ServerScene(PlayerList &players) { + m_controllers.emplace_back(new CollisionController(players)); } diff --git a/source/server/scene/ServerScene.hpp b/source/server/scene/ServerScene.hpp index 8ccf9d7fb..329b8b13c 100644 --- a/source/server/scene/ServerScene.hpp +++ b/source/server/scene/ServerScene.hpp @@ -29,11 +29,11 @@ #include "Scene.hpp" +class PlayerList; + class ServerScene : public Scene { public: - ServerScene(); - - void update() override; + ServerScene(PlayerList &players); }; #endif // SERVERSCENE_HPP_ diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp index bed806cb0..9d65c5000 100644 --- a/source/server/world/ServerWorld.hpp +++ b/source/server/world/ServerWorld.hpp @@ -40,6 +40,7 @@ namespace gk { class ClientInfo; class Dimension; +class PlayerList; class ServerCommandHandler; class ServerPlayer; @@ -47,8 +48,8 @@ class ServerWorld : public World { using ChunkMap = std::unordered_map>; public: - ServerWorld(const Dimension &dimension, gk::GameClock &clock) - : m_dimension(dimension), m_terrainGenerator(dimension), m_clock(clock) {} + ServerWorld(PlayerList &players, const Dimension &dimension, gk::GameClock &clock) + : m_dimension(dimension), m_terrainGenerator(dimension), m_clock(clock), m_scene(players) {} void update(); diff --git a/source/server/world/WorldController.cpp b/source/server/world/WorldController.cpp index 73170b2f5..ec5e8229d 100644 --- a/source/server/world/WorldController.cpp +++ b/source/server/world/WorldController.cpp @@ -29,9 +29,9 @@ #include "Registry.hpp" #include "WorldController.hpp" -void WorldController::init() { +void WorldController::init(PlayerList &players) { for (const Dimension &dimension : m_registry.dimensions()) { - m_worldList.emplace_back(dimension, m_clock); + m_worldList.emplace_back(players, dimension, m_clock); m_worldList.back().setServer(m_server); } } diff --git a/source/server/world/WorldController.hpp b/source/server/world/WorldController.hpp index e691b1f80..c4830f5c2 100644 --- a/source/server/world/WorldController.hpp +++ b/source/server/world/WorldController.hpp @@ -41,7 +41,7 @@ class WorldController { public: WorldController(Registry ®istry, gk::GameClock &clock) : m_registry(registry), m_clock(clock) {} - void init(); + void init(PlayerList &players); void update(); From fd0702c595b85e5fec42672185a2efe96b6d3cc1 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 6 Apr 2020 01:01:34 +0200 Subject: [PATCH 12/38] Entities completely serialized. --- source/client/scene/ClientScene.cpp | 5 +---- source/client/scene/ClientScene.hpp | 2 +- source/client/states/GameState.hpp | 2 +- source/client/world/ClientWorld.cpp | 3 +-- source/client/world/ClientWorld.hpp | 2 +- source/common/network/NetworkUtils.cpp | 5 +++-- source/{client => server}/scene/AnimationController.cpp | 1 - source/{client => server}/scene/AnimationController.hpp | 0 source/server/scene/CollisionController.cpp | 1 + source/server/scene/ServerScene.cpp | 2 ++ source/server/world/ServerWorld.cpp | 2 +- 11 files changed, 12 insertions(+), 13 deletions(-) rename source/{client => server}/scene/AnimationController.cpp (98%) rename source/{client => server}/scene/AnimationController.hpp (100%) diff --git a/source/client/scene/ClientScene.cpp b/source/client/scene/ClientScene.cpp index cbbf47a06..2797928df 100644 --- a/source/client/scene/ClientScene.cpp +++ b/source/client/scene/ClientScene.cpp @@ -24,13 +24,10 @@ * * ===================================================================================== */ -#include "AnimationController.hpp" -#include "ClientPlayer.hpp" #include "ClientScene.hpp" #include "RenderingController.hpp" -ClientScene::ClientScene(ClientPlayer &player) { - m_controllers.emplace_back(new AnimationController); +ClientScene::ClientScene() { m_controllers.emplace_back(new RenderingController); } diff --git a/source/client/scene/ClientScene.hpp b/source/client/scene/ClientScene.hpp index adadadb91..2b4922905 100644 --- a/source/client/scene/ClientScene.hpp +++ b/source/client/scene/ClientScene.hpp @@ -41,7 +41,7 @@ class ClientPlayer; class ClientScene : public Scene, public gk::Drawable { public: - ClientScene(ClientPlayer &player); + ClientScene(); void setCamera(gk::Camera &camera) { m_camera = &camera; } diff --git a/source/client/states/GameState.hpp b/source/client/states/GameState.hpp index ad933f630..52df5c93c 100644 --- a/source/client/states/GameState.hpp +++ b/source/client/states/GameState.hpp @@ -79,7 +79,7 @@ class GameState : public gk::ApplicationState { Client m_client; - ClientWorld m_world{m_player}; + ClientWorld m_world; std::unordered_map m_playerBoxes; diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index bf5a8d173..9cf88c0ba 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -40,8 +40,7 @@ #include "TextureAtlas.hpp" #include "World.hpp" -ClientWorld::ClientWorld(ClientPlayer &player) : m_scene(player), - m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) +ClientWorld::ClientWorld() : m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) { } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index 0bbed3f73..e4c228b0f 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -45,7 +45,7 @@ class ClientWorld : public World, public gk::Drawable { using ChunkMap = std::unordered_map>; public: - ClientWorld(ClientPlayer &player); + ClientWorld(); void update(); void sendChunkRequests(); diff --git a/source/common/network/NetworkUtils.cpp b/source/common/network/NetworkUtils.cpp index 9fa11a675..29a0b4c79 100644 --- a/source/common/network/NetworkUtils.cpp +++ b/source/common/network/NetworkUtils.cpp @@ -45,14 +45,15 @@ sf::Packet &operator<<(sf::Packet &packet, const gk::Transformable &transformabl sf::Packet &operator>>(sf::Packet &packet, gk::Transformable &transformable) { gk::Vector3f position, origin, scale; float rotation; - packet >> position >> origin >> scale >> rotation - >> transformable.getRotationTransform().getMatrix(); + packet >> position >> origin >> scale >> rotation; transformable.setPosition(position); transformable.setOrigin(origin); transformable.setScale(scale); transformable.setRotation(rotation); + packet >> transformable.getRotationTransform().getMatrix(); + return packet; } diff --git a/source/client/scene/AnimationController.cpp b/source/server/scene/AnimationController.cpp similarity index 98% rename from source/client/scene/AnimationController.cpp rename to source/server/scene/AnimationController.cpp index 4ba58485a..b319bedba 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/server/scene/AnimationController.cpp @@ -26,7 +26,6 @@ */ #include "AnimationComponent.hpp" #include "AnimationController.hpp" -#include "InventoryCube.hpp" void AnimationController::update(entt::DefaultRegistry ®istry) { registry.view().each([](auto, auto &transformable, auto &animation) { diff --git a/source/client/scene/AnimationController.hpp b/source/server/scene/AnimationController.hpp similarity index 100% rename from source/client/scene/AnimationController.hpp rename to source/server/scene/AnimationController.hpp diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp index 8bd2212e9..9728d4bc1 100644 --- a/source/server/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -37,6 +37,7 @@ void CollisionController::update(entt::DefaultRegistry ®istry) { gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; if (hitbox.intersects(playerHitbox)) { it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + // FIXME: Send inventory update here registry.destroy(entity); } } diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp index 9e6572266..19114cdec 100644 --- a/source/server/scene/ServerScene.cpp +++ b/source/server/scene/ServerScene.cpp @@ -27,10 +27,12 @@ #include #include +#include "AnimationController.hpp" #include "CollisionController.hpp" #include "ServerScene.hpp" ServerScene::ServerScene(PlayerList &players) { + m_controllers.emplace_back(new AnimationController); m_controllers.emplace_back(new CollisionController(players)); } diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index 84bfef7a8..334c5d3a1 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -59,7 +59,7 @@ void ServerWorld::update() { // FIXME: Should be placed somewhere else static int lastTime = m_clock.getTicks(true); int now = m_clock.getTicks(true); - if (now - lastTime > 1000) { + if (now - lastTime > 100) { lastTime = now; sf::Packet packet; From 673e20edd47134fba3675b7732607fdbbd1596f4 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 6 Apr 2020 01:24:49 +0200 Subject: [PATCH 13/38] [ServerWorld] Sending Scene state more often. --- source/client/scene/RenderingController.cpp | 4 +--- source/client/scene/RenderingController.hpp | 2 -- source/server/world/ServerWorld.cpp | 3 ++- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 692e6244a..b4cae4095 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -34,7 +34,7 @@ #include "Registry.hpp" -void RenderingController::update(entt::DefaultRegistry ®istry) { +void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { registry.view().each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); @@ -48,9 +48,7 @@ void RenderingController::update(entt::DefaultRegistry ®istry) { registry.remove(entity); }); -} -void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { registry.view().each([&](auto, auto &drawable, auto &transformable) { gk::RenderStates drawStates = states; drawStates.transform *= transformable.getTransform(); diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp index 78c515f01..75af6d10e 100644 --- a/source/client/scene/RenderingController.hpp +++ b/source/client/scene/RenderingController.hpp @@ -31,8 +31,6 @@ class RenderingController : public AbstractController { public: - void update(entt::DefaultRegistry ®istry) override; - void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; }; diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index 334c5d3a1..bf38ff853 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -57,9 +57,10 @@ void ServerWorld::update() { m_scene.update(); // FIXME: Should be placed somewhere else + // FIXME: Shouldn't be sent that often static int lastTime = m_clock.getTicks(true); int now = m_clock.getTicks(true); - if (now - lastTime > 100) { + if (now - lastTime > 10) { lastTime = now; sf::Packet packet; From b4a0ea60ece338e09c6840d2fb1bb7e9db23233c Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 8 Apr 2020 10:50:56 +0200 Subject: [PATCH 14/38] [entt] Small tweaks for compatibility with C++17 version. --- external/entt/entity/actor.hpp | 2 +- external/entt/entity/helper.hpp | 6 +++--- external/entt/entity/prototype.hpp | 2 +- external/entt/entity/registry.hpp | 2 +- source/client/scene/RenderingController.cpp | 2 +- source/client/scene/RenderingController.hpp | 2 +- source/common/scene/AbstractController.hpp | 4 ++-- source/common/scene/Scene.hpp | 6 +++--- source/common/scene/SceneSerializer.cpp | 18 ++++++++++++++++-- source/common/scene/SceneSerializer.hpp | 5 ++++- source/server/scene/AnimationController.cpp | 2 +- source/server/scene/AnimationController.hpp | 2 +- source/server/scene/CollisionController.cpp | 2 +- source/server/scene/CollisionController.hpp | 2 +- source/server/scene/ItemDropFactory.cpp | 2 +- source/server/scene/ItemDropFactory.hpp | 2 +- 16 files changed, 39 insertions(+), 22 deletions(-) diff --git a/external/entt/entity/actor.hpp b/external/entt/entity/actor.hpp index 435aa87ba..f1c8d99a1 100644 --- a/external/entt/entity/actor.hpp +++ b/external/entt/entity/actor.hpp @@ -234,7 +234,7 @@ struct Actor { * 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; +using actor = Actor; } diff --git a/external/entt/entity/helper.hpp b/external/entt/entity/helper.hpp index 90eab876b..7445f15f1 100644 --- a/external/entt/entity/helper.hpp +++ b/external/entt/entity/helper.hpp @@ -43,7 +43,7 @@ void dependency(Registry ®istry, const Entity entity) { * The following adds components `AType` and `AnotherType` whenever `MyType` is * assigned to an entity: * @code{.cpp} - * entt::DefaultRegistry registry; + * entt::registry registry; * entt::connect(registry.construction()); * @endcode * @@ -66,7 +66,7 @@ inline void connect(Sink &, const Entity)> sink) { * The following breaks the dependency between the component `MyType` and the * components `AType` and `AnotherType`: * @code{.cpp} - * entt::DefaultRegistry registry; + * entt::registry registry; * entt::disconnect(registry.construction()); * @endcode * @@ -89,7 +89,7 @@ inline void disconnect(Sink &, const Entity)> sink) { * As an example and where the user defined literal for hashed strings hasn't * been changed: * @code{.cpp} - * entt::DefaultRegistry registry; + * entt::registry registry; * registry.assign>(entity); * @endcode * diff --git a/external/entt/entity/prototype.hpp b/external/entt/entity/prototype.hpp index 82c6bcb68..cac3d80c5 100644 --- a/external/entt/entity/prototype.hpp +++ b/external/entt/entity/prototype.hpp @@ -488,7 +488,7 @@ class Prototype final { * applications.
* Users should have a really good reason to choose something different. */ -using DefaultPrototype = Prototype; +using prototype = Prototype; } diff --git a/external/entt/entity/registry.hpp b/external/entt/entity/registry.hpp index 4916ce30a..a472267a1 100644 --- a/external/entt/entity/registry.hpp +++ b/external/entt/entity/registry.hpp @@ -1653,7 +1653,7 @@ class Registry { * 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; +using registry = Registry; } diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index b4cae4095..696bf119f 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -34,7 +34,7 @@ #include "Registry.hpp" -void RenderingController::draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) { +void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { registry.view().each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp index 75af6d10e..f877453be 100644 --- a/source/client/scene/RenderingController.hpp +++ b/source/client/scene/RenderingController.hpp @@ -31,7 +31,7 @@ class RenderingController : public AbstractController { public: - void draw(entt::DefaultRegistry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; + void draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; }; #endif // RENDERINGCONTROLLER_HPP_ diff --git a/source/common/scene/AbstractController.hpp b/source/common/scene/AbstractController.hpp index 077833cc1..59ec318c5 100644 --- a/source/common/scene/AbstractController.hpp +++ b/source/common/scene/AbstractController.hpp @@ -34,8 +34,8 @@ class AbstractController { public: - virtual void update(entt::DefaultRegistry &) {} - virtual void draw(entt::DefaultRegistry &, gk::RenderTarget &, gk::RenderStates) {} + virtual void update(entt::registry &) {} + virtual void draw(entt::registry &, gk::RenderTarget &, gk::RenderStates) {} }; #endif // ABSTRACTCONTROLLER_HPP_ diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp index 039cad3aa..564576092 100644 --- a/source/common/scene/Scene.hpp +++ b/source/common/scene/Scene.hpp @@ -38,11 +38,11 @@ class Scene : public ISerializable { void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet, *this); } void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet, *this); } - const entt::DefaultRegistry ®istry() const { return m_registry; } - entt::DefaultRegistry ®istry() { return m_registry; } + const entt::registry ®istry() const { return m_registry; } + entt::registry ®istry() { return m_registry; } protected: - mutable entt::DefaultRegistry m_registry; + mutable entt::registry m_registry; std::deque> m_controllers; diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index 09968bed1..bf1d90129 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -32,12 +32,26 @@ void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { m_outputArchive.setPacket(packet); - scene.registry().snapshot().component(m_outputArchive); + + scene.registry().snapshot().component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_outputArchive); } void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { m_inputArchive.setPacket(packet); - scene.registry().restore().component(m_inputArchive); + + scene.registry().restore().component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_inputArchive); } void SceneSerializer::OutputArchive::operator()(Entity entity) { diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 7bdc45ebc..2243186f7 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -36,7 +36,7 @@ class Scene; class SceneSerializer { - using Entity = entt::DefaultRegistry::entity_type; + using Entity = entt::registry::entity_type; public: void serialize(sf::Packet &packet, const Scene &scene) const; @@ -51,6 +51,9 @@ class SceneSerializer { void operator()(Entity entity, const T &value) { // gkDebug() << entity << (void *)&value << typeid(T).name(); (*m_packet) << entity << value; + // FIXME: It should be possible to check the type here and to create + // a defintion struct to serialize for some of them + // instead of sending the component } void setPacket(sf::Packet &packet) { m_packet = &packet; } diff --git a/source/server/scene/AnimationController.cpp b/source/server/scene/AnimationController.cpp index b319bedba..359602764 100644 --- a/source/server/scene/AnimationController.cpp +++ b/source/server/scene/AnimationController.cpp @@ -27,7 +27,7 @@ #include "AnimationComponent.hpp" #include "AnimationController.hpp" -void AnimationController::update(entt::DefaultRegistry ®istry) { +void AnimationController::update(entt::registry ®istry) { registry.view().each([](auto, auto &transformable, auto &animation) { for (auto &it : animation.list) { if (it.type == AnimationType::Rotation) diff --git a/source/server/scene/AnimationController.hpp b/source/server/scene/AnimationController.hpp index 1870f0df6..eae10be26 100644 --- a/source/server/scene/AnimationController.hpp +++ b/source/server/scene/AnimationController.hpp @@ -31,7 +31,7 @@ class AnimationController : public AbstractController { public: - void update(entt::DefaultRegistry ®istry) override; + void update(entt::registry ®istry) override; }; #endif // ANIMATIONCONTROLLER_HPP_ diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp index 9728d4bc1..49bbc4926 100644 --- a/source/server/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -30,7 +30,7 @@ #include "ItemStack.hpp" #include "PlayerList.hpp" -void CollisionController::update(entt::DefaultRegistry ®istry) { +void CollisionController::update(entt::registry ®istry) { registry.view().each([&](auto entity, auto &transformable, auto &box, auto &itemStack) { for (auto &it : m_players) { gk::DoubleBox hitbox = box + transformable.getPosition(); diff --git a/source/server/scene/CollisionController.hpp b/source/server/scene/CollisionController.hpp index c07731d92..cabd32d0d 100644 --- a/source/server/scene/CollisionController.hpp +++ b/source/server/scene/CollisionController.hpp @@ -35,7 +35,7 @@ class CollisionController : public AbstractController { public: CollisionController(PlayerList &players) : m_players(players) {} - void update(entt::DefaultRegistry ®istry); + void update(entt::registry ®istry); private: PlayerList &m_players; diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index cab9c6812..73979898a 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -31,7 +31,7 @@ #include "ItemStack.hpp" #include "Registry.hpp" -void ItemDropFactory::create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { +void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); auto &drawableDef = registry.assign(entity); diff --git a/source/server/scene/ItemDropFactory.hpp b/source/server/scene/ItemDropFactory.hpp index c6ec80d70..9970c48da 100644 --- a/source/server/scene/ItemDropFactory.hpp +++ b/source/server/scene/ItemDropFactory.hpp @@ -33,7 +33,7 @@ class ItemDropFactory { public: - static void create(entt::DefaultRegistry ®istry, double x, double y, double z, const std::string &itemID, u16 amount = 1); + static void create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount = 1); }; #endif // ITEMDROPFACTORY_HPP_ From fd7f8803e551c0d0da5eb27cdd17bb8c56613f5e Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sun, 12 Apr 2020 14:38:26 +0200 Subject: [PATCH 15/38] Now using C++17 entt version. --- .gitmodules | 3 + CMakeLists.txt | 4 +- README.md | 1 + external/entt | 1 + 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 ----- source/client/CMakeLists.txt | 4 +- source/client/scene/RenderingController.cpp | 4 +- source/common/CMakeLists.txt | 5 +- source/common/scene/SceneSerializer.cpp | 50 +- source/common/scene/SceneSerializer.hpp | 74 +- source/server/CMakeLists.txt | 6 +- 38 files changed, 82 insertions(+), 9502 deletions(-) create mode 100644 .gitmodules create mode 160000 external/entt delete mode 100644 external/entt/config/config.h delete mode 100644 external/entt/core/algorithm.hpp delete mode 100644 external/entt/core/family.hpp delete mode 100644 external/entt/core/hashed_string.hpp delete mode 100644 external/entt/core/ident.hpp delete mode 100644 external/entt/core/monostate.hpp delete mode 100644 external/entt/entity/actor.hpp delete mode 100644 external/entt/entity/attachee.hpp delete mode 100644 external/entt/entity/entity.hpp delete mode 100644 external/entt/entity/entt_traits.hpp delete mode 100644 external/entt/entity/helper.hpp delete mode 100644 external/entt/entity/prototype.hpp delete mode 100644 external/entt/entity/registry.hpp delete mode 100644 external/entt/entity/snapshot.hpp delete mode 100644 external/entt/entity/sparse_set.hpp delete mode 100644 external/entt/entity/utility.hpp delete mode 100644 external/entt/entity/view.hpp delete mode 100644 external/entt/entt.hpp delete mode 100644 external/entt/locator/locator.hpp delete mode 100644 external/entt/process/process.hpp delete mode 100644 external/entt/process/scheduler.hpp delete mode 100644 external/entt/resource/cache.hpp delete mode 100644 external/entt/resource/handle.hpp delete mode 100644 external/entt/resource/loader.hpp delete mode 100644 external/entt/signal/delegate.hpp delete mode 100644 external/entt/signal/dispatcher.hpp delete mode 100644 external/entt/signal/emitter.hpp delete mode 100644 external/entt/signal/sigh.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..533fba78a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/entt"] + path = external/entt + url = git://github.com/Unarelith/entt.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 25af3a8be..d50dd9470 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ set(DEBUG_GCC_FLAGS -g -Og -Wall -Wextra -Wfatal-errors -Wno-variadic-macros) set(RELEASE_GCC_FLAGS -O3) set(RELWITHDEB_GCC_FLAGS -g -O3 -Wall -Wextra -Wfatal-errors -Wno-variadic-macros) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) #------------------------------------------------------------------------------ # Setting default build type @@ -185,6 +185,8 @@ endif () #------------------------------------------------------------------------------ # Subdirectories #------------------------------------------------------------------------------ +add_subdirectory(external/entt) + add_subdirectory(source/common) add_subdirectory(source/server) add_subdirectory(source/client) diff --git a/README.md b/README.md index 6ad6d64d7..1adfa8eb1 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ The long-term goal of this project is to provide a viable alternative to Minecra - [SFML](https://www.sfml-dev.org/) (only used for network) - [Lua](http://www.lua.org) - _Linux users: Check your distribution repositories for packages._ +- Run `git submodule update --init --recursive` - Run `cmake .` - Run `make -j8` - Run the client with `./openminer` diff --git a/external/entt b/external/entt new file mode 160000 index 000000000..76707f0c1 --- /dev/null +++ b/external/entt @@ -0,0 +1 @@ +Subproject commit 76707f0c1a73bf5918c56ffd860c3b83b6a9d120 diff --git a/external/entt/config/config.h b/external/entt/config/config.h deleted file mode 100644 index 367798c01..000000000 --- a/external/entt/config/config.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 deleted file mode 100644 index 9ac0a9068..000000000 --- a/external/entt/core/algorithm.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#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 deleted file mode 100644 index 11cf82f99..000000000 --- a/external/entt/core/family.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#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 deleted file mode 100644 index bea5db0ab..000000000 --- a/external/entt/core/hashed_string.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#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 deleted file mode 100644 index 42503d876..000000000 --- a/external/entt/core/ident.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#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 deleted file mode 100644 index f0fa47fc0..000000000 --- a/external/entt/core/monostate.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#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 deleted file mode 100644 index f1c8d99a1..000000000 --- a/external/entt/entity/actor.hpp +++ /dev/null @@ -1,243 +0,0 @@ -#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 actor = Actor; - - -} - - -#endif // ENTT_ENTITY_ACTOR_HPP diff --git a/external/entt/entity/attachee.hpp b/external/entt/entity/attachee.hpp deleted file mode 100644 index b972c7d38..000000000 --- a/external/entt/entity/attachee.hpp +++ /dev/null @@ -1,230 +0,0 @@ -#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 deleted file mode 100644 index 4ef2265bc..000000000 --- a/external/entt/entity/entity.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#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 deleted file mode 100644 index df2cb60d9..000000000 --- a/external/entt/entity/entt_traits.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#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 deleted file mode 100644 index 7445f15f1..000000000 --- a/external/entt/entity/helper.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#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::registry 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::registry 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::registry 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 deleted file mode 100644 index cac3d80c5..000000000 --- a/external/entt/entity/prototype.hpp +++ /dev/null @@ -1,497 +0,0 @@ -#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 prototype = Prototype; - - -} - - -#endif // ENTT_ENTITY_PROTOTYPE_HPP diff --git a/external/entt/entity/registry.hpp b/external/entt/entity/registry.hpp deleted file mode 100644 index a472267a1..000000000 --- a/external/entt/entity/registry.hpp +++ /dev/null @@ -1,1662 +0,0 @@ -#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 registry = Registry; - - -} - - -#endif // ENTT_ENTITY_REGISTRY_HPP diff --git a/external/entt/entity/snapshot.hpp b/external/entt/entity/snapshot.hpp deleted file mode 100644 index e9d1128fa..000000000 --- a/external/entt/entity/snapshot.hpp +++ /dev/null @@ -1,724 +0,0 @@ -#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 deleted file mode 100644 index fc0e32ceb..000000000 --- a/external/entt/entity/sparse_set.hpp +++ /dev/null @@ -1,1120 +0,0 @@ -#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 deleted file mode 100644 index 31f138e32..000000000 --- a/external/entt/entity/utility.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#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 deleted file mode 100644 index 214f862af..000000000 --- a/external/entt/entity/view.hpp +++ /dev/null @@ -1,1889 +0,0 @@ -#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 deleted file mode 100644 index ceb1fefa4..000000000 --- a/external/entt/entt.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#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 deleted file mode 100644 index 2c66ca388..000000000 --- a/external/entt/locator/locator.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#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 deleted file mode 100644 index 2077b0434..000000000 --- a/external/entt/process/process.hpp +++ /dev/null @@ -1,339 +0,0 @@ -#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 deleted file mode 100644 index c7729bbe9..000000000 --- a/external/entt/process/scheduler.hpp +++ /dev/null @@ -1,311 +0,0 @@ -#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 deleted file mode 100644 index 032d26998..000000000 --- a/external/entt/resource/cache.hpp +++ /dev/null @@ -1,201 +0,0 @@ -#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 deleted file mode 100644 index ef54f30ae..000000000 --- a/external/entt/resource/handle.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#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 deleted file mode 100644 index cd2ed2ef3..000000000 --- a/external/entt/resource/loader.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#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 deleted file mode 100644 index 7fdd75c0f..000000000 --- a/external/entt/signal/delegate.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#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 deleted file mode 100644 index 8743ebd7a..000000000 --- a/external/entt/signal/dispatcher.hpp +++ /dev/null @@ -1,188 +0,0 @@ -#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 deleted file mode 100644 index 39ef3d1b5..000000000 --- a/external/entt/signal/emitter.hpp +++ /dev/null @@ -1,336 +0,0 @@ -#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 deleted file mode 100644 index d6f795fd7..000000000 --- a/external/entt/signal/sigh.hpp +++ /dev/null @@ -1,426 +0,0 @@ -#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/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index 474e73b44..17eb259a1 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -7,7 +7,9 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}) # Get source files #------------------------------------------------------------------------------ file(GLOB_RECURSE SOURCE_FILES *.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../server/*.hpp ../common/*.hpp ../../external/*.hpp) +file(GLOB_RECURSE HEADER_FILES *.hpp ../server/*.hpp ../common/*.hpp) + +include_directories(../../external ../../external/entt/single_include) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 696bf119f..b86c9e5ef 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -38,9 +38,7 @@ void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &targe registry.view().each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); - DrawableComponent &drawable = (!registry.has()) - ? registry.assign(entity) - : registry.get(entity); + DrawableComponent &drawable = registry.get_or_assign(entity); InventoryCube &cube = drawable.setDrawable(cubeDef.size, true); cube.setOrigin(cubeDef.origin); diff --git a/source/common/CMakeLists.txt b/source/common/CMakeLists.txt index 35235c0f6..bfadc9203 100644 --- a/source/common/CMakeLists.txt +++ b/source/common/CMakeLists.txt @@ -7,7 +7,9 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}_common) # Get source files #------------------------------------------------------------------------------ file(GLOB_RECURSE SOURCE_FILES *.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../../external/*.hpp) +file(GLOB_RECURSE HEADER_FILES *.hpp) + +include_directories(../../external ../../external/entt/single_include) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) @@ -18,6 +20,7 @@ endforeach(HEADER_FILE) # Add library #------------------------------------------------------------------------------ add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) +add_dependencies(${PROJECT_NAME} EnTT) #------------------------------------------------------------------------------ # Compiler flags diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index bf1d90129..75888f9b1 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -31,36 +31,36 @@ #include "SceneSerializer.hpp" void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { - m_outputArchive.setPacket(packet); + // m_outputArchive.setPacket(packet); - scene.registry().snapshot().component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_outputArchive); + // scene.registry().snapshot().component< + // AnimationComponent, + // gk::DoubleBox, + // ItemStack, + // gk::Transformable, + // DrawableDef + // >(m_outputArchive); } void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { - m_inputArchive.setPacket(packet); + // m_inputArchive.setPacket(packet); - scene.registry().restore().component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_inputArchive); + // scene.registry().restore().component< + // AnimationComponent, + // gk::DoubleBox, + // ItemStack, + // gk::Transformable, + // DrawableDef + // >(m_inputArchive); } -void SceneSerializer::OutputArchive::operator()(Entity entity) { - // gkDebug() << entity; - (*m_packet) << entity; -} - -void SceneSerializer::InputArchive::operator()(Entity &entity) { - (*m_packet) >> entity; - // gkDebug() << entity; -} +// void SceneSerializer::OutputArchive::operator()(Entity entity) { +// // gkDebug() << entity; +// // (*m_packet) << entity; +// } +// +// void SceneSerializer::InputArchive::operator()(Entity &entity) { +// // (*m_packet) >> entity; +// // gkDebug() << entity; +// } diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 2243186f7..419f10a85 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -43,43 +43,43 @@ class SceneSerializer { void deserialize(sf::Packet &packet, Scene &scene); private: - class OutputArchive { - public: - void operator()(Entity entity); - - template - void operator()(Entity entity, const T &value) { - // gkDebug() << entity << (void *)&value << typeid(T).name(); - (*m_packet) << entity << value; - // FIXME: It should be possible to check the type here and to create - // a defintion struct to serialize for some of them - // instead of sending the component - } - - void setPacket(sf::Packet &packet) { m_packet = &packet; } - - private: - sf::Packet *m_packet = nullptr; - }; - - class InputArchive { - public: - void operator()(Entity &entity); - - template - void operator()(Entity &entity, T &value) { - (*m_packet) >> entity >> value; - // gkDebug() << entity << (void *)&value << typeid(T).name(); - } - - void setPacket(sf::Packet &packet) { m_packet = &packet; } - - private: - sf::Packet *m_packet = nullptr; - }; - - mutable OutputArchive m_outputArchive; - mutable InputArchive m_inputArchive; + // class OutputArchive { + // public: + // void operator()(Entity entity); + // + // template + // void operator()(Entity entity, const T &value) { + // // gkDebug() << entity << (void *)&value << typeid(T).name(); + // // (*m_packet) << entity << value; + // // FIXME: It should be possible to check the type here and to create + // // a defintion struct to serialize for some of them + // // instead of sending the component + // } + // + // void setPacket(sf::Packet &packet) { m_packet = &packet; } + // + // private: + // sf::Packet *m_packet = nullptr; + // }; + // + // class InputArchive { + // public: + // void operator()(Entity &entity); + // + // template + // void operator()(Entity &entity, T &value) { + // // (*m_packet) >> entity >> value; + // // gkDebug() << entity << (void *)&value << typeid(T).name(); + // } + // + // void setPacket(sf::Packet &packet) { m_packet = &packet; } + // + // private: + // sf::Packet *m_packet = nullptr; + // }; + // + // mutable OutputArchive m_outputArchive; + // mutable InputArchive m_inputArchive; }; #endif // SCENESERIALIZER_HPP_ diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index ab136739f..1914331bf 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -6,8 +6,10 @@ set(PROJECT_NAME ${PROJECT_NAME}_server) #------------------------------------------------------------------------------ # Get source files #------------------------------------------------------------------------------ -file(GLOB_RECURSE SOURCE_FILES */*.cpp ../../external/*.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../common/*.hpp ../../external/*.hpp) +file(GLOB_RECURSE SOURCE_FILES */*.cpp ../../external/FastNoise.cpp) +file(GLOB_RECURSE HEADER_FILES *.hpp ../common/*.hpp) + +include_directories(../../external ../../external/entt/single_include) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) From 096a9d3458f0d0751ef12c59ad6d296006cc7ab7 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 13 Apr 2020 15:01:10 +0200 Subject: [PATCH 16/38] SFML and GameKit are now used as git submodules. --- .gitmodules | 8 ++++ CMakeLists.txt | 81 ++++++++++++++++++++---------------- external/SFML | 1 + external/gamekit | 1 + source/client/CMakeLists.txt | 2 - source/common/CMakeLists.txt | 4 +- 6 files changed, 57 insertions(+), 40 deletions(-) create mode 160000 external/SFML create mode 160000 external/gamekit diff --git a/.gitmodules b/.gitmodules index 533fba78a..dd37435a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,11 @@ [submodule "external/entt"] path = external/entt url = git://github.com/Unarelith/entt.git + ignore = dirty +[submodule "external/SFML"] + path = external/SFML + url = git://github.com/SFML/SFML.git + ignore = dirty +[submodule "external/gamekit"] + path = external/gamekit + url = git://github.com/Unarelith/GameKit.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d50dd9470..3a893d60c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ endif () set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +include_directories(external) + #------------------------------------------------------------------------------ # Compiler flags #------------------------------------------------------------------------------ @@ -55,21 +57,6 @@ endif() include_directories(${LUA_INCLUDE_DIR}) link_directories(${LUA_LIBRARY_DIRS}) -#------------------------------------------------------------------------------ -# - gamekit -#------------------------------------------------------------------------------ -if (WIN32) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/gamekit) -endif () - -find_package(GameKit REQUIRED) - -if(NOT GAMEKIT_FOUND) - message(FATAL_ERROR "gamekit is needed to build the project. Please install it correctly.") -endif() - -include_directories(${GAMEKIT_INCLUDE_DIR}) - #------------------------------------------------------------------------------ # - tinyxml2 #------------------------------------------------------------------------------ @@ -148,25 +135,6 @@ include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_MIXER_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIRS}) -#------------------------------------------------------------------------------ -# - SFML network -#------------------------------------------------------------------------------ -if (WIN32) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/SFML) -endif () - -if (MINGW) - set(SFML_STATIC_LIBRARIES TRUE) -endif () - -find_package(SFML COMPONENTS system network) - -if(NOT SFML_FOUND) - message(FATAL_ERROR "SFML is needed to build the project. Please install it correctly.") -endif() - -include_directories(${SFML_INCLUDE_DIRS}) - #------------------------------------------------------------------------------ # - GLEW #------------------------------------------------------------------------------ @@ -183,10 +151,53 @@ if (WIN32) endif () #------------------------------------------------------------------------------ -# Subdirectories +# Submodules +# from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html +#------------------------------------------------------------------------------ +find_package(Git QUIET) + +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() +endif() + +if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/gamekit/CMakeLists.txt") + message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") +endif() + +#------------------------------------------------------------------------------ +# - EnTT #------------------------------------------------------------------------------ add_subdirectory(external/entt) +include_directories(external/entt/single_include) + +#------------------------------------------------------------------------------ +# - gamekit +#------------------------------------------------------------------------------ +add_subdirectory(external/gamekit) + +#------------------------------------------------------------------------------ +# - SFML network +#------------------------------------------------------------------------------ +set(SFML_BUILD_AUDIO FALSE) +set(SFML_BUILD_GRAPHICS FALSE) +set(SFML_BUILD_WINDOW FALSE) +set(BUILD_SHARED_LIBS ON) + +add_subdirectory(external/SFML) + +#------------------------------------------------------------------------------ +# Subdirectories +#------------------------------------------------------------------------------ add_subdirectory(source/common) add_subdirectory(source/server) add_subdirectory(source/client) diff --git a/external/SFML b/external/SFML new file mode 160000 index 000000000..50e173e40 --- /dev/null +++ b/external/SFML @@ -0,0 +1 @@ +Subproject commit 50e173e403ef8912e3d8ac3c7ab3e27e32243339 diff --git a/external/gamekit b/external/gamekit new file mode 160000 index 000000000..d39510fa3 --- /dev/null +++ b/external/gamekit @@ -0,0 +1 @@ +Subproject commit d39510fa3920197ddff588aacf5a15175e67887a diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index 17eb259a1..ad7b23ae2 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -9,8 +9,6 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}) file(GLOB_RECURSE SOURCE_FILES *.cpp) file(GLOB_RECURSE HEADER_FILES *.hpp ../server/*.hpp ../common/*.hpp) -include_directories(../../external ../../external/entt/single_include) - foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) include_directories(${HEADER_DIRECTORY}) diff --git a/source/common/CMakeLists.txt b/source/common/CMakeLists.txt index bfadc9203..a86c3abea 100644 --- a/source/common/CMakeLists.txt +++ b/source/common/CMakeLists.txt @@ -9,8 +9,6 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}_common) file(GLOB_RECURSE SOURCE_FILES *.cpp) file(GLOB_RECURSE HEADER_FILES *.hpp) -include_directories(../../external ../../external/entt/single_include) - foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) include_directories(${HEADER_DIRECTORY}) @@ -20,7 +18,7 @@ endforeach(HEADER_FILE) # Add library #------------------------------------------------------------------------------ add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) -add_dependencies(${PROJECT_NAME} EnTT) +add_dependencies(${PROJECT_NAME} EnTT sfml-network gamekit) #------------------------------------------------------------------------------ # Compiler flags From 51c3bf93716df7057c7a0a2672d6d69499abad41 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 13 Apr 2020 15:16:10 +0200 Subject: [PATCH 17/38] [README.md] Updated with new compilation instructions. --- .gitignore | 1 + README.md | 9 ++++----- source/client/CMakeLists.txt | 2 +- source/server/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 21aecb4b0..21465a5b2 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ doc/html doc/latex # Binaries +build openminer openminer_server *.exe diff --git a/README.md b/README.md index 1adfa8eb1..82b1f43c4 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,10 @@ The long-term goal of this project is to provide a viable alternative to Minecra - [SFML](https://www.sfml-dev.org/) (only used for network) - [Lua](http://www.lua.org) - _Linux users: Check your distribution repositories for packages._ -- Run `git submodule update --init --recursive` -- Run `cmake .` -- Run `make -j8` -- Run the client with `./openminer` -- If you want a multiplayer game, run the server with `./openminer_server` +- Run `mkdir build && cd build` +- Run `cmake .. && make -j8` +- Run the client with `./openminer -w ..` +- If you want a multiplayer game, run the server with `./openminer_server -w ..` ### Using Windows diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index ad7b23ae2..578c9f29e 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -50,7 +50,6 @@ endif () target_link_libraries(${PROJECT_NAME} ${CMAKE_PROJECT_NAME}_server_lib ${CMAKE_PROJECT_NAME}_common - ${GAMEKIT_LIBRARIES} ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -61,5 +60,6 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network + gamekit ${UNIX_LIBS}) diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index 1914331bf..2bcedf3ba 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -69,7 +69,6 @@ target_link_libraries(${PROJECT_NAME}_lib sfml-system sfml-network) target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib ${CMAKE_PROJECT_NAME}_common - ${GAMEKIT_LIBRARIES} ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -80,5 +79,6 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network + gamekit ${UNIX_LIBS}) From cec600196188ee696ac927f60bf37df9a725eae3 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 13 Apr 2020 16:59:31 +0200 Subject: [PATCH 18/38] [SceneSerializer] Adapted to latest EnTT version. --- source/common/scene/SceneSerializer.cpp | 62 +++++++++++-------- source/common/scene/SceneSerializer.hpp | 82 +++++++++++++------------ 2 files changed, 80 insertions(+), 64 deletions(-) diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index 75888f9b1..67d529b0d 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -31,36 +31,48 @@ #include "SceneSerializer.hpp" void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { - // m_outputArchive.setPacket(packet); + m_outputArchive.setPacket(packet); - // scene.registry().snapshot().component< - // AnimationComponent, - // gk::DoubleBox, - // ItemStack, - // gk::Transformable, - // DrawableDef - // >(m_outputArchive); + scene.registry().snapshot().component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_outputArchive); } void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { - // m_inputArchive.setPacket(packet); + m_inputArchive.setPacket(packet); - // scene.registry().restore().component< - // AnimationComponent, - // gk::DoubleBox, - // ItemStack, - // gk::Transformable, - // DrawableDef - // >(m_inputArchive); + scene.registry().loader().component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_inputArchive); } -// void SceneSerializer::OutputArchive::operator()(Entity entity) { -// // gkDebug() << entity; -// // (*m_packet) << entity; -// } -// -// void SceneSerializer::InputArchive::operator()(Entity &entity) { -// // (*m_packet) >> entity; -// // gkDebug() << entity; -// } +void SceneSerializer::OutputArchive::operator()(entt::entity entity) { + // gkDebug() << "Entity:" << (u32)entity; + (*m_packet) << (u32)entity; +} + +void SceneSerializer::OutputArchive::operator()(std::underlying_type_t size) { + // gkDebug() << "Size:" << size; + (*m_packet) << size; +} + +void SceneSerializer::InputArchive::operator()(entt::entity &entity) { + u32 entityID; + (*m_packet) >> entityID; + entity = (entt::entity)entityID; + // gkDebug() << "Entity:" << (u32)entity; +} + +void SceneSerializer::InputArchive::operator()(std::underlying_type_t &size) { + // gkDebug() << "Size:" << size; + (*m_packet) >> size; +} diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 419f10a85..8510611ac 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -36,50 +36,54 @@ class Scene; class SceneSerializer { - using Entity = entt::registry::entity_type; - public: void serialize(sf::Packet &packet, const Scene &scene) const; void deserialize(sf::Packet &packet, Scene &scene); private: - // class OutputArchive { - // public: - // void operator()(Entity entity); - // - // template - // void operator()(Entity entity, const T &value) { - // // gkDebug() << entity << (void *)&value << typeid(T).name(); - // // (*m_packet) << entity << value; - // // FIXME: It should be possible to check the type here and to create - // // a defintion struct to serialize for some of them - // // instead of sending the component - // } - // - // void setPacket(sf::Packet &packet) { m_packet = &packet; } - // - // private: - // sf::Packet *m_packet = nullptr; - // }; - // - // class InputArchive { - // public: - // void operator()(Entity &entity); - // - // template - // void operator()(Entity &entity, T &value) { - // // (*m_packet) >> entity >> value; - // // gkDebug() << entity << (void *)&value << typeid(T).name(); - // } - // - // void setPacket(sf::Packet &packet) { m_packet = &packet; } - // - // private: - // sf::Packet *m_packet = nullptr; - // }; - // - // mutable OutputArchive m_outputArchive; - // mutable InputArchive m_inputArchive; + class OutputArchive { + public: + void operator()(entt::entity entity); + void operator()(std::underlying_type_t size); + + template + void operator()(entt::entity entity, const T &value) { + // gkDebug() << (u32)entity << (void *)&value << typeid(T).name(); + (*m_packet) << (u32)entity << value; + + // FIXME: It should be possible to check the type here and to create + // a defintion struct to serialize for some of them + // instead of sending the component + } + + void setPacket(sf::Packet &packet) { m_packet = &packet; } + + private: + sf::Packet *m_packet = nullptr; + }; + + class InputArchive { + public: + void operator()(entt::entity &entity); + void operator()(std::underlying_type_t &size); + + template + void operator()(entt::entity &entity, T &value) { + u32 entityID; + (*m_packet) >> entityID >> value; + entity = (entt::entity)entityID; + + // gkDebug() << (u32)entity << (void *)&value << typeid(T).name(); + } + + void setPacket(sf::Packet &packet) { m_packet = &packet; } + + private: + sf::Packet *m_packet = nullptr; + }; + + mutable OutputArchive m_outputArchive; + mutable InputArchive m_inputArchive; }; #endif // SCENESERIALIZER_HPP_ From 61a70126e63eae82b77b2c3bd2a8c9a116e814a1 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Mon, 13 Apr 2020 18:39:51 +0200 Subject: [PATCH 19/38] [SceneSerializer] Now using entt::continuous_loader. --- source/client/hud/DebugOverlay.cpp | 3 ++ source/client/scene/RenderingController.cpp | 4 +- source/client/scene/RenderingController.hpp | 2 + source/client/world/ClientWorld.hpp | 1 + source/common/scene/AbstractController.hpp | 2 + source/common/scene/Scene.hpp | 6 +-- source/common/scene/SceneSerializer.cpp | 43 +++++++++++++-------- source/common/scene/SceneSerializer.hpp | 9 ++++- source/server/core/ServerApplication.cpp | 2 +- source/server/world/WorldController.hpp | 2 +- 10 files changed, 50 insertions(+), 24 deletions(-) diff --git a/source/client/hud/DebugOverlay.cpp b/source/client/hud/DebugOverlay.cpp index fbe58910f..609fa875a 100644 --- a/source/client/hud/DebugOverlay.cpp +++ b/source/client/hud/DebugOverlay.cpp @@ -27,6 +27,7 @@ #include #include "ClientPlayer.hpp" +#include "ClientScene.hpp" #include "ClientWorld.hpp" #include "Config.hpp" #include "DebugOverlay.hpp" @@ -70,6 +71,8 @@ void DebugOverlay::update() { stream << "dimension: " << m_player.dimension(); stream << '\n'; stream << "Loaded chunks: " << m_world.loadedChunkCount(); + stream << '\n'; + stream << "Alive entities: " << m_world.scene().registry().alive(); m_positionText.setText(stream.str()); } diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index b86c9e5ef..3d96ae899 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -34,7 +34,7 @@ #include "Registry.hpp" -void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { +void RenderingController::update(entt::registry ®istry) { registry.view().each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); @@ -46,7 +46,9 @@ void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &targe registry.remove(entity); }); +} +void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { registry.view().each([&](auto, auto &drawable, auto &transformable) { gk::RenderStates drawStates = states; drawStates.transform *= transformable.getTransform(); diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/RenderingController.hpp index f877453be..21bbe4ceb 100644 --- a/source/client/scene/RenderingController.hpp +++ b/source/client/scene/RenderingController.hpp @@ -31,6 +31,8 @@ class RenderingController : public AbstractController { public: + void update(entt::registry ®istry) override; + void draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; }; diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index e4c228b0f..00ae9bd54 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -60,6 +60,7 @@ class ClientWorld : public World, public gk::Drawable { Chunk *getChunk(int cx, int cy, int cz) const override; + const ClientScene &scene() const { return m_scene; } ClientScene &scene() { return m_scene; } void setClient(ClientCommandHandler &client) { m_client = &client; } diff --git a/source/common/scene/AbstractController.hpp b/source/common/scene/AbstractController.hpp index 59ec318c5..908ef453d 100644 --- a/source/common/scene/AbstractController.hpp +++ b/source/common/scene/AbstractController.hpp @@ -34,6 +34,8 @@ class AbstractController { public: + virtual ~AbstractController() = default; + virtual void update(entt::registry &) {} virtual void draw(entt::registry &, gk::RenderTarget &, gk::RenderStates) {} }; diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp index 564576092..b94790f75 100644 --- a/source/common/scene/Scene.hpp +++ b/source/common/scene/Scene.hpp @@ -35,8 +35,8 @@ class Scene : public ISerializable { public: virtual void update() { for (auto &controller : m_controllers) controller->update(m_registry); } - void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet, *this); } - void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet, *this); } + void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet); } + void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet); } const entt::registry ®istry() const { return m_registry; } entt::registry ®istry() { return m_registry; } @@ -47,7 +47,7 @@ class Scene : public ISerializable { std::deque> m_controllers; private: - SceneSerializer m_serializer; + SceneSerializer m_serializer{*this}; }; #endif // SCENE_HPP_ diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp index 67d529b0d..d8c54d940 100644 --- a/source/common/scene/SceneSerializer.cpp +++ b/source/common/scene/SceneSerializer.cpp @@ -30,28 +30,39 @@ #include "Scene.hpp" #include "SceneSerializer.hpp" -void SceneSerializer::serialize(sf::Packet &packet, const Scene &scene) const { +SceneSerializer::SceneSerializer(Scene &scene) : m_scene(scene), m_loader(m_scene.registry()) { +} + +void SceneSerializer::serialize(sf::Packet &packet) const { m_outputArchive.setPacket(packet); - scene.registry().snapshot().component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_outputArchive); + m_scene.registry().snapshot() + .entities(m_outputArchive) + .destroyed(m_outputArchive) + .component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_outputArchive); } -void SceneSerializer::deserialize(sf::Packet &packet, Scene &scene) { +void SceneSerializer::deserialize(sf::Packet &packet) { m_inputArchive.setPacket(packet); - scene.registry().loader().component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_inputArchive); + m_loader + .entities(m_inputArchive) + .destroyed(m_inputArchive) + .component< + AnimationComponent, + gk::DoubleBox, + ItemStack, + gk::Transformable, + DrawableDef + >(m_inputArchive) + .orphans() + .shrink(); } void SceneSerializer::OutputArchive::operator()(entt::entity entity) { diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 8510611ac..50b2f6e71 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -37,10 +37,15 @@ class Scene; class SceneSerializer { public: - void serialize(sf::Packet &packet, const Scene &scene) const; - void deserialize(sf::Packet &packet, Scene &scene); + SceneSerializer(Scene &scene); + + void serialize(sf::Packet &packet) const; + void deserialize(sf::Packet &packet); private: + Scene &m_scene; + entt::continuous_loader m_loader; + class OutputArchive { public: void operator()(entt::entity entity); diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp index 5434a5b66..7d46d2356 100644 --- a/source/server/core/ServerApplication.cpp +++ b/source/server/core/ServerApplication.cpp @@ -90,7 +90,7 @@ int ServerApplication::run(bool isProtected) { } catch(const gk::Exception &e) { if (m_eventHandler) - m_eventHandler->emplaceEvent(false); + m_eventHandler->emplaceEvent(false, 0); std::cerr << "Fatal error " << e.what() << std::endl; return 1; diff --git a/source/server/world/WorldController.hpp b/source/server/world/WorldController.hpp index c4830f5c2..59538a911 100644 --- a/source/server/world/WorldController.hpp +++ b/source/server/world/WorldController.hpp @@ -53,7 +53,7 @@ class WorldController { void setServer(ServerCommandHandler &server) { m_server = &server; } private: - std::vector m_worldList; + std::deque m_worldList; Registry &m_registry; From 5ea804739a74a2f996756adc2dfe325f83bf7cf1 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 25 Apr 2020 17:05:51 +0200 Subject: [PATCH 20/38] [AnimationController] Moved client-side. --- source/{server => client}/scene/AnimationController.cpp | 0 source/{server => client}/scene/AnimationController.hpp | 0 source/client/scene/ClientScene.cpp | 2 ++ source/client/scene/RenderingController.cpp | 2 +- source/common/scene/AnimationComponent.hpp | 3 +++ source/common/scene/SceneSerializer.hpp | 4 ---- source/server/scene/ServerScene.cpp | 5 ----- 7 files changed, 6 insertions(+), 10 deletions(-) rename source/{server => client}/scene/AnimationController.cpp (100%) rename source/{server => client}/scene/AnimationController.hpp (100%) diff --git a/source/server/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp similarity index 100% rename from source/server/scene/AnimationController.cpp rename to source/client/scene/AnimationController.cpp diff --git a/source/server/scene/AnimationController.hpp b/source/client/scene/AnimationController.hpp similarity index 100% rename from source/server/scene/AnimationController.hpp rename to source/client/scene/AnimationController.hpp diff --git a/source/client/scene/ClientScene.cpp b/source/client/scene/ClientScene.cpp index 2797928df..dd08fff63 100644 --- a/source/client/scene/ClientScene.cpp +++ b/source/client/scene/ClientScene.cpp @@ -24,10 +24,12 @@ * * ===================================================================================== */ +#include "AnimationController.hpp" #include "ClientScene.hpp" #include "RenderingController.hpp" ClientScene::ClientScene() { + m_controllers.emplace_back(new AnimationController); m_controllers.emplace_back(new RenderingController); } diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 3d96ae899..3ca9628a3 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -35,7 +35,7 @@ #include "Registry.hpp" void RenderingController::update(entt::registry ®istry) { - registry.view().each([&](auto entity, auto &drawableDef) { + registry.view(entt::exclude).each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); DrawableComponent &drawable = registry.get_or_assign(entity); diff --git a/source/common/scene/AnimationComponent.hpp b/source/common/scene/AnimationComponent.hpp index 96590bcdc..b51696b17 100644 --- a/source/common/scene/AnimationComponent.hpp +++ b/source/common/scene/AnimationComponent.hpp @@ -125,6 +125,9 @@ struct AnimationComponent : public ISerializable { void deserialize(sf::Packet &packet) override { packet >> list; } std::vector list; + + private: + bool m_isInitialized = false; }; #endif // ANIMATIONCOMPONENT_HPP_ diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp index 50b2f6e71..1ff2913c1 100644 --- a/source/common/scene/SceneSerializer.hpp +++ b/source/common/scene/SceneSerializer.hpp @@ -55,10 +55,6 @@ class SceneSerializer { void operator()(entt::entity entity, const T &value) { // gkDebug() << (u32)entity << (void *)&value << typeid(T).name(); (*m_packet) << (u32)entity << value; - - // FIXME: It should be possible to check the type here and to create - // a defintion struct to serialize for some of them - // instead of sending the component } void setPacket(sf::Packet &packet) { m_packet = &packet; } diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp index 19114cdec..eab5e2fc1 100644 --- a/source/server/scene/ServerScene.cpp +++ b/source/server/scene/ServerScene.cpp @@ -24,15 +24,10 @@ * * ===================================================================================== */ -#include -#include - -#include "AnimationController.hpp" #include "CollisionController.hpp" #include "ServerScene.hpp" ServerScene::ServerScene(PlayerList &players) { - m_controllers.emplace_back(new AnimationController); m_controllers.emplace_back(new CollisionController(players)); } From 5f76f3c0c724ba1ffce79b86db118c2c59c366c4 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 14:23:23 +0200 Subject: [PATCH 21/38] [SceneSerializer] Removed. [CMake] Small fix for 3.17. --- cmake/FindTinyXml2.cmake | 2 +- cmake/sdl2/FindSDL2.cmake | 2 + .../client/network/ClientCommandHandler.cpp | 8 -- source/common/network/Network.cpp | 2 - source/common/network/Network.hpp | 3 - source/common/scene/Scene.hpp | 11 +-- source/common/scene/SceneSerializer.cpp | 89 ------------------ source/common/scene/SceneSerializer.hpp | 90 ------------------- source/server/world/ServerWorld.cpp | 14 --- 9 files changed, 6 insertions(+), 215 deletions(-) delete mode 100644 source/common/scene/SceneSerializer.cpp delete mode 100644 source/common/scene/SceneSerializer.hpp diff --git a/cmake/FindTinyXml2.cmake b/cmake/FindTinyXml2.cmake index 3f8850f38..e51aefef9 100644 --- a/cmake/FindTinyXml2.cmake +++ b/cmake/FindTinyXml2.cmake @@ -10,7 +10,7 @@ find_path(TINYXML2_INCLUDE_DIRS NAMES tinyxml2.h) find_library(TINYXML2_LIBRARIES NAMES tinyxml2) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(tinyxml2 DEFAULT_MSG TINYXML2_LIBRARIES TINYXML2_INCLUDE_DIRS) +find_package_handle_standard_args(TinyXml2 DEFAULT_MSG TINYXML2_LIBRARIES TINYXML2_INCLUDE_DIRS) mark_as_advanced(TINYXML2_INCLUDE_DIRS TINYXML2_LIBRARIES) diff --git a/cmake/sdl2/FindSDL2.cmake b/cmake/sdl2/FindSDL2.cmake index 8c73f58e7..f44dc406e 100644 --- a/cmake/sdl2/FindSDL2.cmake +++ b/cmake/sdl2/FindSDL2.cmake @@ -315,9 +315,11 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 VERSION_VAR SDL2_VERSION_STRING) if(SDL2MAIN_LIBRARY) + SET(FPHSA_NAME_MISMATCHED ON) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION_STRING) + SET(FPHSA_NAME_MISMATCHED OFF) endif() diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 085b0ed48..e9a5f3a17 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -242,13 +242,5 @@ void ClientCommandHandler::setupCallbacks() { } } }); - - m_client.setCommandCallback(Network::Command::SceneState, [this](sf::Packet &packet) { - u16 dimensionID; - packet >> dimensionID; - - // FIXME: Check dimension and only apply changes if it's the same as the current one - packet >> m_world.scene(); - }); } diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index c8fc200bc..9381c8f71 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -60,8 +60,6 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::RegistryData, "RegistryData"}, {Network::Command::ChatMessage, "ChatMessage"}, - - {Network::Command::SceneState, "SceneState"}, }; return commandNames[command]; diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index c4ae99f09..d64fba087 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -66,9 +66,6 @@ namespace Network { // Chat commands ChatMessage = 21, // [NetworkCommand][u16 client id][std::string message] (both) - - // Scene commands - SceneState = 22, // [NetworkCommand][u16 dimension id][Scene scene] (from Server only) }; std::string commandToString(Command command); diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp index b94790f75..01ad40386 100644 --- a/source/common/scene/Scene.hpp +++ b/source/common/scene/Scene.hpp @@ -27,17 +27,15 @@ #ifndef SCENE_HPP_ #define SCENE_HPP_ +#include + #include "AbstractController.hpp" #include "ISerializable.hpp" -#include "SceneSerializer.hpp" -class Scene : public ISerializable { +class Scene { public: virtual void update() { for (auto &controller : m_controllers) controller->update(m_registry); } - void serialize(sf::Packet &packet) const override { m_serializer.serialize(packet); } - void deserialize(sf::Packet &packet) override { m_serializer.deserialize(packet); } - const entt::registry ®istry() const { return m_registry; } entt::registry ®istry() { return m_registry; } @@ -45,9 +43,6 @@ class Scene : public ISerializable { mutable entt::registry m_registry; std::deque> m_controllers; - - private: - SceneSerializer m_serializer{*this}; }; #endif // SCENE_HPP_ diff --git a/source/common/scene/SceneSerializer.cpp b/source/common/scene/SceneSerializer.cpp deleted file mode 100644 index d8c54d940..000000000 --- a/source/common/scene/SceneSerializer.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ===================================================================================== - * - * 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 "AnimationComponent.hpp" -#include "DrawableDef.hpp" -#include "ItemStack.hpp" -#include "Scene.hpp" -#include "SceneSerializer.hpp" - -SceneSerializer::SceneSerializer(Scene &scene) : m_scene(scene), m_loader(m_scene.registry()) { -} - -void SceneSerializer::serialize(sf::Packet &packet) const { - m_outputArchive.setPacket(packet); - - m_scene.registry().snapshot() - .entities(m_outputArchive) - .destroyed(m_outputArchive) - .component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_outputArchive); -} - -void SceneSerializer::deserialize(sf::Packet &packet) { - m_inputArchive.setPacket(packet); - - m_loader - .entities(m_inputArchive) - .destroyed(m_inputArchive) - .component< - AnimationComponent, - gk::DoubleBox, - ItemStack, - gk::Transformable, - DrawableDef - >(m_inputArchive) - .orphans() - .shrink(); -} - -void SceneSerializer::OutputArchive::operator()(entt::entity entity) { - // gkDebug() << "Entity:" << (u32)entity; - (*m_packet) << (u32)entity; -} - -void SceneSerializer::OutputArchive::operator()(std::underlying_type_t size) { - // gkDebug() << "Size:" << size; - (*m_packet) << size; -} - -void SceneSerializer::InputArchive::operator()(entt::entity &entity) { - u32 entityID; - (*m_packet) >> entityID; - entity = (entt::entity)entityID; - // gkDebug() << "Entity:" << (u32)entity; -} - -void SceneSerializer::InputArchive::operator()(std::underlying_type_t &size) { - // gkDebug() << "Size:" << size; - (*m_packet) >> size; -} - diff --git a/source/common/scene/SceneSerializer.hpp b/source/common/scene/SceneSerializer.hpp deleted file mode 100644 index 1ff2913c1..000000000 --- a/source/common/scene/SceneSerializer.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * ===================================================================================== - * - * 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 SCENESERIALIZER_HPP_ -#define SCENESERIALIZER_HPP_ - -#include - -#include - -#include "NetworkUtils.hpp" - -class Scene; - -class SceneSerializer { - public: - SceneSerializer(Scene &scene); - - void serialize(sf::Packet &packet) const; - void deserialize(sf::Packet &packet); - - private: - Scene &m_scene; - entt::continuous_loader m_loader; - - class OutputArchive { - public: - void operator()(entt::entity entity); - void operator()(std::underlying_type_t size); - - template - void operator()(entt::entity entity, const T &value) { - // gkDebug() << (u32)entity << (void *)&value << typeid(T).name(); - (*m_packet) << (u32)entity << value; - } - - void setPacket(sf::Packet &packet) { m_packet = &packet; } - - private: - sf::Packet *m_packet = nullptr; - }; - - class InputArchive { - public: - void operator()(entt::entity &entity); - void operator()(std::underlying_type_t &size); - - template - void operator()(entt::entity &entity, T &value) { - u32 entityID; - (*m_packet) >> entityID >> value; - entity = (entt::entity)entityID; - - // gkDebug() << (u32)entity << (void *)&value << typeid(T).name(); - } - - void setPacket(sf::Packet &packet) { m_packet = &packet; } - - private: - sf::Packet *m_packet = nullptr; - }; - - mutable OutputArchive m_outputArchive; - mutable InputArchive m_inputArchive; -}; - -#endif // SCENESERIALIZER_HPP_ diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index bf38ff853..f6ec203e6 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -55,20 +55,6 @@ void ServerWorld::update() { } m_scene.update(); - - // FIXME: Should be placed somewhere else - // FIXME: Shouldn't be sent that often - static int lastTime = m_clock.getTicks(true); - int now = m_clock.getTicks(true); - if (now - lastTime > 10) { - lastTime = now; - - sf::Packet packet; - packet << Network::Command::SceneState; - packet << m_dimension.id(); - packet << m_scene; - m_server->server().sendToAllClients(packet); - } } void ServerWorld::createChunkNeighbours(ServerChunk &chunk) { From aa80cd49f546f1c9ecf9772c89a0a2ebc54925fe Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 14:31:48 +0200 Subject: [PATCH 22/38] [README.md] Updated compiler support line for C++17. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 82b1f43c4..5fd34178c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The long-term goal of this project is to provide a viable alternative to Minecra ### Using Linux - Dependencies: - - A compiler with C++14 support (GCC >= 5.0 or clang >= 3.4) + - A compiler with C++17 support (GCC >= 7.0 or clang >= 5.0) - [CMake](http://www.cmake.org/download/) - [GameKit](http://github.com/Unarelith/GameKit) (requires `SDL2` + `tinyxml2`, will switch to `SFML` starting from 2.6) - [SFML](https://www.sfml-dev.org/) (only used for network) From 26d65dbcdc6c618a834dd05aee2846c43cf861e8 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 18:16:40 +0200 Subject: [PATCH 23/38] [NetworkComponent|NetworkController] Added. --- source/client/core/Config.cpp | 2 - source/client/core/Config.hpp | 1 - source/client/hud/BlockCursor.cpp | 5 -- .../client/network/ClientCommandHandler.cpp | 49 +++++++++++++++ .../client/network/ClientCommandHandler.hpp | 4 ++ source/client/scene/AnimationController.cpp | 13 ++-- source/client/scene/RenderingController.cpp | 8 +-- source/client/states/SettingsMenuState.cpp | 1 - source/common/network/Network.cpp | 4 ++ source/common/network/Network.hpp | 5 ++ source/common/network/NetworkUtils.cpp | 21 ------- source/common/network/NetworkUtils.hpp | 31 --------- source/common/scene/DrawableDef.hpp | 2 + source/common/scene/NetworkComponent.hpp | 38 +++++++++++ source/common/scene/PositionComponent.hpp | 38 +++++++++++ .../server/network/ServerCommandHandler.cpp | 35 +++++++++++ .../server/network/ServerCommandHandler.hpp | 5 ++ source/server/scene/ItemDropFactory.cpp | 9 ++- source/server/scene/NetworkController.cpp | 63 +++++++++++++++++++ source/server/scene/NetworkController.hpp | 47 ++++++++++++++ source/server/scene/ServerScene.cpp | 10 +++ source/server/scene/ServerScene.hpp | 10 +++ source/server/world/ServerWorld.cpp | 3 +- source/server/world/ServerWorld.hpp | 4 +- 24 files changed, 331 insertions(+), 77 deletions(-) create mode 100644 source/common/scene/NetworkComponent.hpp create mode 100644 source/common/scene/PositionComponent.hpp create mode 100644 source/server/scene/NetworkController.cpp create mode 100644 source/server/scene/NetworkController.hpp diff --git a/source/client/core/Config.cpp b/source/client/core/Config.cpp index c3e5c5de3..51d0d2d53 100644 --- a/source/client/core/Config.cpp +++ b/source/client/core/Config.cpp @@ -29,7 +29,6 @@ // Gameplay bool Config::isFlyModeEnabled = false; bool Config::isNoClipEnabled = false; -bool Config::useItemDrops = true; // Interface bool Config::isBlockInfoWidgetEnabled = true; @@ -69,7 +68,6 @@ void Config::loadConfigFromFile(const char *file) { isFlyModeEnabled = lua["isFlyModeEnabled"].get_or(isFlyModeEnabled); isNoClipEnabled = lua["isNoClipEnabled"].get_or(isNoClipEnabled); - useItemDrops = lua["useItemDrops"].get_or(useItemDrops); isBlockInfoWidgetEnabled = lua["isBlockInfoWidgetEnabled"].get_or(isBlockInfoWidgetEnabled); isFpsCounterEnabled = lua["isFpsCounterEnabled"].get_or(isFpsCounterEnabled); diff --git a/source/client/core/Config.hpp b/source/client/core/Config.hpp index 94e5b5670..4ee709493 100644 --- a/source/client/core/Config.hpp +++ b/source/client/core/Config.hpp @@ -33,7 +33,6 @@ namespace Config { // Gameplay extern bool isFlyModeEnabled; extern bool isNoClipEnabled; - extern bool useItemDrops; // Interface extern bool isBlockInfoWidgetEnabled; diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index fbae46500..fc4da82df 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -149,11 +149,6 @@ void BlockCursor::update(const Hotbar &hotbar) { timeToBreak = m_currentBlock->timeToBreak(currentStack.item().harvestCapability(), currentStack.item().miningSpeed()); if (ticks > m_animationStart + timeToBreak * 1000) { - if (!Config::useItemDrops) { - ItemStack itemDrop = m_currentBlock->getItemDrop(); - m_player.inventory().addStack(itemDrop.item().stringID(), itemDrop.amount()); - } - m_world.setBlock(m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z, 0); m_animationStart = ticks; diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index e9a5f3a17..183011489 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -32,7 +32,10 @@ #include "ClientWorld.hpp" #include "ClientCommandHandler.hpp" #include "DrawableComponent.hpp" +#include "DrawableDef.hpp" #include "LuaGUIState.hpp" +#include "NetworkComponent.hpp" +#include "PositionComponent.hpp" #include "Registry.hpp" void ClientCommandHandler::sendPlayerInvUpdate() { @@ -242,5 +245,51 @@ void ClientCommandHandler::setupCallbacks() { } } }); + + m_client.setCommandCallback(Network::Command::EntitySpawn, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; + + auto ®istry = m_world.scene().registry(); + + auto it = m_entityMap.find(entityID); + if (it == m_entityMap.end()) { + entt::entity entity = registry.create(); + m_entityMap.emplace(entityID, entity); + registry.assign(entity, entityID); + } + else if (registry.get(it->second).entityID != entityID) { + gkError() << "EntitySpawn: Entity ID" << entityID << "is invalid"; + } + }); + + m_client.setCommandCallback(Network::Command::EntityPosUpdate, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; + + auto ®istry = m_world.scene().registry(); + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + auto &position = registry.get_or_assign(it->second); + packet >> position.x >> position.y >> position.z; + } + else + gkError() << "EntityPosUpdate: Entity ID" << entityID << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; + + auto ®istry = m_world.scene().registry(); + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + packet >> registry.get_or_assign(it->second); + } + else + gkError() << "EntityDrawableDef: Entity ID" << entityID << "is invalid"; + }); } diff --git a/source/client/network/ClientCommandHandler.hpp b/source/client/network/ClientCommandHandler.hpp index 9b3b5e5ab..3f5a75d60 100644 --- a/source/client/network/ClientCommandHandler.hpp +++ b/source/client/network/ClientCommandHandler.hpp @@ -29,6 +29,8 @@ #include +#include + #include "PlayerBox.hpp" namespace gk { @@ -70,6 +72,8 @@ class ClientCommandHandler { std::unordered_map &m_playerBoxes; + std::unordered_map m_entityMap; + bool m_isRegistryInitialized = false; bool m_isSingleplayer = false; diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp index 359602764..c45e86528 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/client/scene/AnimationController.cpp @@ -26,13 +26,12 @@ */ #include "AnimationComponent.hpp" #include "AnimationController.hpp" +#include "PositionComponent.hpp" void AnimationController::update(entt::registry ®istry) { - registry.view().each([](auto, auto &transformable, auto &animation) { + registry.view().each([](auto, auto &position, auto &animation) { for (auto &it : animation.list) { - if (it.type == AnimationType::Rotation) - transformable.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); - else if (it.type == AnimationType::Translation) { + if (it.type == AnimationType::Translation) { float dx = it.translation.dx; float dy = it.translation.dy; float dz = it.translation.dz; @@ -49,7 +48,9 @@ void AnimationController::update(entt::registry ®istry) { || it.translation.cz + it.translation.dz < it.translation.min) dz = (it.translation.loop) ? -dz : 0; - transformable.move(dx, dy, dz); + position.x += dx; + position.y += dy; + position.z += dz; it.translation.cx += dx; it.translation.cy += dy; @@ -59,6 +60,8 @@ void AnimationController::update(entt::registry ®istry) { it.translation.dy = dy; it.translation.dz = dz; } + // else if (it.type == AnimationType::Rotation) + // transformable.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); } }); } diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 3ca9628a3..62d04ec71 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -27,11 +27,9 @@ #include "DrawableDef.hpp" #include "DrawableComponent.hpp" #include "InventoryCube.hpp" +#include "PositionComponent.hpp" #include "RenderingController.hpp" -#include -#include - #include "Registry.hpp" void RenderingController::update(entt::registry ®istry) { @@ -49,9 +47,9 @@ void RenderingController::update(entt::registry ®istry) { } void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { - registry.view().each([&](auto, auto &drawable, auto &transformable) { + registry.view().each([&](auto, auto &drawable, auto &position) { gk::RenderStates drawStates = states; - drawStates.transform *= transformable.getTransform(); + drawStates.transform.translate(position.x, position.y, position.z); drawable.draw(target, drawStates); }); } diff --git a/source/client/states/SettingsMenuState.cpp b/source/client/states/SettingsMenuState.cpp index 7c5880672..4b901a5a8 100644 --- a/source/client/states/SettingsMenuState.cpp +++ b/source/client/states/SettingsMenuState.cpp @@ -137,7 +137,6 @@ void SettingsMenuState::addGameplayButtons() { addToggleButton("Fly Mode", Config::isFlyModeEnabled, false); addToggleButton("No Clip", Config::isNoClipEnabled, false); - addToggleButton("Use item drops", Config::useItemDrops, false); } void SettingsMenuState::addInterfaceButtons() { diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index 9381c8f71..bc0900ddd 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -60,6 +60,10 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::RegistryData, "RegistryData"}, {Network::Command::ChatMessage, "ChatMessage"}, + + {Network::Command::EntitySpawn, "EntitySpawn"}, + {Network::Command::EntityPosUpdate, "EntityPosUpdate"}, + {Network::Command::EntityDrawableDef, "EntityDrawableDef"}, }; return commandNames[command]; diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index d64fba087..1b5011731 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -66,6 +66,11 @@ namespace Network { // Chat commands ChatMessage = 21, // [NetworkCommand][u16 client id][std::string message] (both) + + // Entity commands + EntitySpawn = 22, // [NetworkCommand][u32 entity id] (from Server only) + EntityPosUpdate = 23, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) + EntityDrawableDef = 24, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) }; std::string commandToString(Command command); diff --git a/source/common/network/NetworkUtils.cpp b/source/common/network/NetworkUtils.cpp index 29a0b4c79..0c9e9da4b 100644 --- a/source/common/network/NetworkUtils.cpp +++ b/source/common/network/NetworkUtils.cpp @@ -36,24 +36,3 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Color &color) { return packet; } -sf::Packet &operator<<(sf::Packet &packet, const gk::Transformable &transformable) { - packet << transformable.getPosition() << transformable.getOrigin() << transformable.getScale() - << transformable.getRotation() << transformable.getRotationTransform().getMatrix(); - return packet; -} - -sf::Packet &operator>>(sf::Packet &packet, gk::Transformable &transformable) { - gk::Vector3f position, origin, scale; - float rotation; - packet >> position >> origin >> scale >> rotation; - - transformable.setPosition(position); - transformable.setOrigin(origin); - transformable.setScale(scale); - transformable.setRotation(rotation); - - packet >> transformable.getRotationTransform().getMatrix(); - - return packet; -} - diff --git a/source/common/network/NetworkUtils.hpp b/source/common/network/NetworkUtils.hpp index d73c1423a..1e8333865 100644 --- a/source/common/network/NetworkUtils.hpp +++ b/source/common/network/NetworkUtils.hpp @@ -84,29 +84,6 @@ sf::Packet &operator>>(sf::Packet &packet, std::unordered_map &map) { return packet; } -//====================================================================================== -// glm::mat4 -//====================================================================================== -#include -#include - -template -sf::Packet &operator<<(sf::Packet &packet, const glm::tmat4x4 &matrix) { - for (int i = 0 ; i < 4 * 4 ; ++i) { - packet << matrix[i % 4][i / 4]; - } - return packet; -} - -template -sf::Packet &operator>>(sf::Packet &packet, glm::tmat4x4 &matrix) { - for (int i = 0 ; i < 4 * 4 ; ++i) { - packet >> matrix[i % 4][i / 4]; - } - return packet; -} - - //====================================================================================== // gk::Rect //====================================================================================== @@ -166,12 +143,4 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Vector3 &vec) { sf::Packet &operator<<(sf::Packet &packet, const gk::Color &color); sf::Packet &operator>>(sf::Packet &packet, gk::Color &color); -//====================================================================================== -// gk::Transformable -//====================================================================================== -#include - -sf::Packet &operator<<(sf::Packet &packet, const gk::Transformable &transformable); -sf::Packet &operator>>(sf::Packet &packet, gk::Transformable &transformable); - #endif // NETWORKUTILS_HPP_ diff --git a/source/common/scene/DrawableDef.hpp b/source/common/scene/DrawableDef.hpp index 948ed2d16..bdafa21d8 100644 --- a/source/common/scene/DrawableDef.hpp +++ b/source/common/scene/DrawableDef.hpp @@ -49,6 +49,8 @@ class DrawableDef : public ISerializable { void serialize(sf::Packet &packet) const override; void deserialize(sf::Packet &packet) override; + bool isUpdated = true; + protected: std::vector m_cubes; }; diff --git a/source/common/scene/NetworkComponent.hpp b/source/common/scene/NetworkComponent.hpp new file mode 100644 index 000000000..df70ba398 --- /dev/null +++ b/source/common/scene/NetworkComponent.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * 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 NETWORKCOMPONENT_HPP_ +#define NETWORKCOMPONENT_HPP_ + +#include + +struct NetworkComponent { + u32 entityID = 0; + + bool hasSpawned = false; +}; + +#endif // NETWORKCOMPONENT_HPP_ diff --git a/source/common/scene/PositionComponent.hpp b/source/common/scene/PositionComponent.hpp new file mode 100644 index 000000000..c6b7ab9ea --- /dev/null +++ b/source/common/scene/PositionComponent.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * 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 POSITIONCOMPONENT_HPP_ +#define POSITIONCOMPONENT_HPP_ + +struct PositionComponent { + double x = 0; + double y = 0; + double z = 0; + + bool isUpdated = true; +}; + +#endif // POSITIONCOMPONENT_HPP_ diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 1b22e7df5..ddb33f419 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -25,6 +25,8 @@ * ===================================================================================== */ #include "BlockData.hpp" +#include "DrawableDef.hpp" +#include "NetworkComponent.hpp" #include "PlayerList.hpp" #include "Registry.hpp" #include "ScriptEngine.hpp" @@ -93,6 +95,36 @@ void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &mess client->tcpSocket->send(packet); } +void ServerCommandHandler::sendEntitySpawn(u32 entityID, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntitySpawn << entityID; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityPosUpdate << entityID << x << y << z; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityDrawableDef << entityID << drawableDef; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + void ServerCommandHandler::setupCallbacks() { m_server.setConnectionCallback([this](ClientInfo &client) { sf::Packet packet; @@ -124,6 +156,9 @@ void ServerCommandHandler::setupCallbacks() { spawnPacket << Network::Command::PlayerSpawn << client.id; spawnPacket << m_spawnPosition.x << m_spawnPosition.y << m_spawnPosition.z; m_server.sendToAllClients(spawnPacket); + + // Send entities to the client + m_worldController.getWorld(player.dimension()).scene().sendEntities(client); }); m_server.setCommandCallback(Network::Command::ClientDisconnect, [this](ClientInfo &client, sf::Packet &) { diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 58aad0925..7bf1d5425 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -36,11 +36,13 @@ struct BlockData; class ClientInfo; +class DrawableDef; class Inventory; class PlayerList; class Registry; class ScriptEngine; class Server; +class ServerPlayer; class ServerWorld; class WorldController; @@ -54,6 +56,9 @@ class ServerCommandHandler { void sendPlayerPosUpdate(u16 clientID, bool isTeleportation = false, const ClientInfo *client = nullptr) const; void sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client = nullptr) const; void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; + void sendEntitySpawn(u32 entityID, const ClientInfo *client = nullptr) const; + void sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; + void sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 73979898a..020420ad5 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -29,10 +29,16 @@ #include "DrawableDef.hpp" #include "ItemDropFactory.hpp" #include "ItemStack.hpp" +#include "NetworkComponent.hpp" +#include "PositionComponent.hpp" #include "Registry.hpp" +static u32 counter = 0; // FIXME: TEMPORARY + void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); + registry.assign(entity, x, y, z); + registry.assign(entity, counter++); auto &drawableDef = registry.assign(entity); auto &cube = drawableDef.addInventoryCube(); @@ -40,9 +46,6 @@ void ItemDropFactory::create(entt::registry ®istry, double x, double y, doubl cube.origin = gk::Vector3f{cube.size / 2.f, cube.size / 2.f, cube.size / 2.f}; cube.blockID = itemID; - auto &transformable = registry.assign(entity); - transformable.setPosition(x + 0.5, y + 0.5, z + 0.5); - auto &animationComponent = registry.assign(entity); animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); diff --git a/source/server/scene/NetworkController.cpp b/source/server/scene/NetworkController.cpp new file mode 100644 index 000000000..9dfc64ada --- /dev/null +++ b/source/server/scene/NetworkController.cpp @@ -0,0 +1,63 @@ +/* + * ===================================================================================== + * + * 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 "DrawableDef.hpp" +#include "NetworkComponent.hpp" +#include "NetworkController.hpp" +#include "PositionComponent.hpp" +#include "ServerCommandHandler.hpp" + +void NetworkController::update(entt::registry ®istry) { + registry.view().each([this] (auto, auto &network) { + if (!network.hasSpawned) + m_server->sendEntitySpawn(network.entityID); + }); + + registry.view().each([this] (auto, auto &network, auto &position) { + if (position.isUpdated) + m_server->sendEntityPosUpdate(network.entityID, position.x, position.y, position.z); + }); + + registry.view().each([this] (auto, auto &network, auto &drawableDef) { + if (drawableDef.isUpdated) + m_server->sendEntityDrawableDef(network.entityID, drawableDef); + }); +} + +void NetworkController::sendEntities(entt::registry ®istry, const ClientInfo &client) { + registry.view().each([&] (auto entity, auto &network) { + m_server->sendEntitySpawn(network.entityID, &client); + + if (auto *position = registry.try_get(entity) ; position) { + m_server->sendEntityPosUpdate(network.entityID, position->x, position->y, position->z); + } + + if (auto *drawableDef = registry.try_get(entity) ; drawableDef) { + m_server->sendEntityDrawableDef(network.entityID, *drawableDef); + } + }); +} + diff --git a/source/server/scene/NetworkController.hpp b/source/server/scene/NetworkController.hpp new file mode 100644 index 000000000..51572038c --- /dev/null +++ b/source/server/scene/NetworkController.hpp @@ -0,0 +1,47 @@ +/* + * ===================================================================================== + * + * 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 NETWORKCONTROLLER_HPP_ +#define NETWORKCONTROLLER_HPP_ + +#include "AbstractController.hpp" + +class ClientInfo; +class ServerCommandHandler; + +class NetworkController : public AbstractController { + public: + void update(entt::registry ®istry) override; + + void sendEntities(entt::registry ®istry, const ClientInfo &client); + + void setServer(ServerCommandHandler *server) { m_server = server; } + + private: + ServerCommandHandler *m_server = nullptr; +}; + +#endif // NETWORKCONTROLLER_HPP_ diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp index eab5e2fc1..ead10e98f 100644 --- a/source/server/scene/ServerScene.cpp +++ b/source/server/scene/ServerScene.cpp @@ -25,9 +25,19 @@ * ===================================================================================== */ #include "CollisionController.hpp" +#include "NetworkController.hpp" #include "ServerScene.hpp" ServerScene::ServerScene(PlayerList &players) { m_controllers.emplace_back(new CollisionController(players)); + m_network = static_cast(m_controllers.emplace_back(new NetworkController).get()); +} + +void ServerScene::sendEntities(const ClientInfo &client) { + m_network->sendEntities(m_registry, client); +} + +void ServerScene::setServer(ServerCommandHandler *server) { + m_network->setServer(server); } diff --git a/source/server/scene/ServerScene.hpp b/source/server/scene/ServerScene.hpp index 329b8b13c..7016fe7fa 100644 --- a/source/server/scene/ServerScene.hpp +++ b/source/server/scene/ServerScene.hpp @@ -29,11 +29,21 @@ #include "Scene.hpp" +class ClientInfo; +class NetworkController; class PlayerList; +class ServerCommandHandler; class ServerScene : public Scene { public: ServerScene(PlayerList &players); + + void sendEntities(const ClientInfo &client); + + void setServer(ServerCommandHandler *server); + + private: + NetworkController *m_network = nullptr; }; #endif // SERVERSCENE_HPP_ diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index f6ec203e6..574aa97a7 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -153,8 +153,7 @@ void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) #include "ItemDropFactory.hpp" // FIXME void ServerWorld::onBlockDestroyed(int x, int y, int z, const Block &block) { - // FIXME if (Config::useItemDrops) - ItemDropFactory::create(m_scene.registry(), x, y, z, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); } ServerChunk &ServerWorld::createChunk(s32 cx, s32 cy, s32 cz) { diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp index 9d65c5000..68fb591d8 100644 --- a/source/server/world/ServerWorld.hpp +++ b/source/server/world/ServerWorld.hpp @@ -69,7 +69,9 @@ class ServerWorld : public World { TerrainGenerator &terrainGenerator() { return m_terrainGenerator; } - void setServer(ServerCommandHandler *server) { m_server = server; } + ServerScene &scene() { return m_scene; } + + void setServer(ServerCommandHandler *server) { m_server = server; m_scene.setServer(server); } private: const Dimension &m_dimension; From f9012fc149d90f3b46a649c37285b3821608da1a Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 19:18:08 +0200 Subject: [PATCH 24/38] [Network] 'EntityDespawn' packet added. --- .../client/network/ClientCommandHandler.cpp | 20 +++++++++++++------ source/common/network/Network.cpp | 1 + source/common/network/Network.hpp | 5 +++-- .../server/network/ServerCommandHandler.cpp | 10 ++++++++++ .../server/network/ServerCommandHandler.hpp | 1 + source/server/scene/CollisionController.cpp | 10 ++++++---- source/server/scene/CollisionController.hpp | 5 +++++ source/server/scene/ServerScene.cpp | 9 +++++---- source/server/scene/ServerScene.hpp | 4 +++- 9 files changed, 48 insertions(+), 17 deletions(-) diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 183011489..50916b4a5 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -263,15 +263,25 @@ void ClientCommandHandler::setupCallbacks() { } }); - m_client.setCommandCallback(Network::Command::EntityPosUpdate, [this](sf::Packet &packet) { + m_client.setCommandCallback(Network::Command::EntityDespawn, [this](sf::Packet &packet) { u32 entityID; packet >> entityID; - auto ®istry = m_world.scene().registry(); + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + m_world.scene().registry().destroy(it->second); + } + else + gkError() << "EntityDespawn: Entity ID" << entityID << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityPosUpdate, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; auto it = m_entityMap.find(entityID); if (it != m_entityMap.end()) { - auto &position = registry.get_or_assign(it->second); + auto &position = m_world.scene().registry().get_or_assign(it->second); packet >> position.x >> position.y >> position.z; } else @@ -282,11 +292,9 @@ void ClientCommandHandler::setupCallbacks() { u32 entityID; packet >> entityID; - auto ®istry = m_world.scene().registry(); - auto it = m_entityMap.find(entityID); if (it != m_entityMap.end()) { - packet >> registry.get_or_assign(it->second); + packet >> m_world.scene().registry().get_or_assign(it->second); } else gkError() << "EntityDrawableDef: Entity ID" << entityID << "is invalid"; diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index bc0900ddd..060739cb3 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -62,6 +62,7 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::ChatMessage, "ChatMessage"}, {Network::Command::EntitySpawn, "EntitySpawn"}, + {Network::Command::EntityDespawn, "EntityDespawn"}, {Network::Command::EntityPosUpdate, "EntityPosUpdate"}, {Network::Command::EntityDrawableDef, "EntityDrawableDef"}, }; diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index 1b5011731..ce0ca5e13 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -69,8 +69,9 @@ namespace Network { // Entity commands EntitySpawn = 22, // [NetworkCommand][u32 entity id] (from Server only) - EntityPosUpdate = 23, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) - EntityDrawableDef = 24, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) + EntityDespawn = 23, // [NetworkCommand][u32 entity id] (from Server only) + EntityPosUpdate = 24, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) + EntityDrawableDef = 25, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) }; std::string commandToString(Command command); diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index ddb33f419..f4d07390b 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -105,6 +105,16 @@ void ServerCommandHandler::sendEntitySpawn(u32 entityID, const ClientInfo *clien client->tcpSocket->send(packet); } +void ServerCommandHandler::sendEntityDespawn(u32 entityID, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityDespawn << entityID; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + void ServerCommandHandler::sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityPosUpdate << entityID << x << y << z; diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 7bf1d5425..1c85ba249 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -57,6 +57,7 @@ class ServerCommandHandler { void sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client = nullptr) const; void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; void sendEntitySpawn(u32 entityID, const ClientInfo *client = nullptr) const; + void sendEntityDespawn(u32 entityID, const ClientInfo *client = nullptr) const; void sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; void sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp index 49bbc4926..726eaeeca 100644 --- a/source/server/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -24,20 +24,22 @@ * * ===================================================================================== */ -#include - #include "CollisionController.hpp" #include "ItemStack.hpp" +#include "NetworkComponent.hpp" #include "PlayerList.hpp" +#include "PositionComponent.hpp" +#include "ServerCommandHandler.hpp" void CollisionController::update(entt::registry ®istry) { - registry.view().each([&](auto entity, auto &transformable, auto &box, auto &itemStack) { + registry.view().each([&](auto entity, auto &position, auto &box, auto &itemStack, auto &network) { for (auto &it : m_players) { - gk::DoubleBox hitbox = box + transformable.getPosition(); + gk::DoubleBox hitbox = box + gk::Vector3d{position.x, position.y, position.z}; gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; if (hitbox.intersects(playerHitbox)) { it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); // FIXME: Send inventory update here + m_server->sendEntityDespawn(network.entityID); registry.destroy(entity); } } diff --git a/source/server/scene/CollisionController.hpp b/source/server/scene/CollisionController.hpp index cabd32d0d..af9620a53 100644 --- a/source/server/scene/CollisionController.hpp +++ b/source/server/scene/CollisionController.hpp @@ -30,6 +30,7 @@ #include "AbstractController.hpp" class PlayerList; +class ServerCommandHandler; class CollisionController : public AbstractController { public: @@ -37,7 +38,11 @@ class CollisionController : public AbstractController { void update(entt::registry ®istry); + void setServer(ServerCommandHandler *server) { m_server = server; } + private: + ServerCommandHandler *m_server = nullptr; + PlayerList &m_players; }; diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp index ead10e98f..2abe93d15 100644 --- a/source/server/scene/ServerScene.cpp +++ b/source/server/scene/ServerScene.cpp @@ -29,15 +29,16 @@ #include "ServerScene.hpp" ServerScene::ServerScene(PlayerList &players) { - m_controllers.emplace_back(new CollisionController(players)); - m_network = static_cast(m_controllers.emplace_back(new NetworkController).get()); + m_collisionController = static_cast(m_controllers.emplace_back(new CollisionController(players)).get()); + m_networkController = static_cast(m_controllers.emplace_back(new NetworkController).get()); } void ServerScene::sendEntities(const ClientInfo &client) { - m_network->sendEntities(m_registry, client); + m_networkController->sendEntities(m_registry, client); } void ServerScene::setServer(ServerCommandHandler *server) { - m_network->setServer(server); + m_collisionController->setServer(server); + m_networkController->setServer(server); } diff --git a/source/server/scene/ServerScene.hpp b/source/server/scene/ServerScene.hpp index 7016fe7fa..c911dbe01 100644 --- a/source/server/scene/ServerScene.hpp +++ b/source/server/scene/ServerScene.hpp @@ -30,6 +30,7 @@ #include "Scene.hpp" class ClientInfo; +class CollisionController; class NetworkController; class PlayerList; class ServerCommandHandler; @@ -43,7 +44,8 @@ class ServerScene : public Scene { void setServer(ServerCommandHandler *server); private: - NetworkController *m_network = nullptr; + CollisionController *m_collisionController = nullptr; + NetworkController *m_networkController = nullptr; }; #endif // SERVERSCENE_HPP_ From 6ad4695f1b013349e5bf8c0943908b1d1ee95c8c Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 19:33:18 +0200 Subject: [PATCH 25/38] [README.md] Updated. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5fd34178c..237f6a32b 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,9 @@ The long-term goal of this project is to provide a viable alternative to Minecra - [SFML](https://www.sfml-dev.org/) (only used for network) - [Lua](http://www.lua.org) - _Linux users: Check your distribution repositories for packages._ -- Run `mkdir build && cd build` -- Run `cmake .. && make -j8` -- Run the client with `./openminer -w ..` -- If you want a multiplayer game, run the server with `./openminer_server -w ..` +- Run `cmake -B build . && cmake --build build -j8` +- Run the client with `./build/openminer` +- If you want a multiplayer game, run the server with `./build/openminer_server` ### Using Windows From 1420e133b5f5363aa1f7359e4f208bbf69fa47e4 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Tue, 28 Apr 2020 22:57:24 +0200 Subject: [PATCH 26/38] [RotationComponent] Added. --- .../client/network/ClientCommandHandler.cpp | 21 +++++++++- source/client/scene/AnimationController.cpp | 13 ++++++- source/common/network/Network.cpp | 3 +- source/common/network/Network.hpp | 5 ++- source/common/scene/RotationComponent.hpp | 38 +++++++++++++++++++ .../server/network/ServerCommandHandler.cpp | 14 ++++++- .../server/network/ServerCommandHandler.hpp | 3 +- source/server/scene/NetworkController.cpp | 28 +++++++++++--- 8 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 source/common/scene/RotationComponent.hpp diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 50916b4a5..b02d93473 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -36,6 +36,7 @@ #include "LuaGUIState.hpp" #include "NetworkComponent.hpp" #include "PositionComponent.hpp" +#include "RotationComponent.hpp" #include "Registry.hpp" void ClientCommandHandler::sendPlayerInvUpdate() { @@ -275,7 +276,7 @@ void ClientCommandHandler::setupCallbacks() { gkError() << "EntityDespawn: Entity ID" << entityID << "is invalid"; }); - m_client.setCommandCallback(Network::Command::EntityPosUpdate, [this](sf::Packet &packet) { + m_client.setCommandCallback(Network::Command::EntityPosition, [this](sf::Packet &packet) { u32 entityID; packet >> entityID; @@ -285,7 +286,23 @@ void ClientCommandHandler::setupCallbacks() { packet >> position.x >> position.y >> position.z; } else - gkError() << "EntityPosUpdate: Entity ID" << entityID << "is invalid"; + gkError() << "EntityPosition: Entity ID" << entityID << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityRotation, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + float w, x, y, z; + packet >> w >> x >> y >> z; + + auto &rotation = m_world.scene().registry().get_or_assign(it->second); + rotation.quat = glm::quat(w, x, y, z); + } + else + gkError() << "EntityRotation: Entity ID" << entityID << "is invalid"; }); m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) { diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp index c45e86528..65248b13c 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/client/scene/AnimationController.cpp @@ -24,9 +24,12 @@ * * ===================================================================================== */ +#include + #include "AnimationComponent.hpp" #include "AnimationController.hpp" #include "PositionComponent.hpp" +#include "RotationComponent.hpp" void AnimationController::update(entt::registry ®istry) { registry.view().each([](auto, auto &position, auto &animation) { @@ -60,8 +63,14 @@ void AnimationController::update(entt::registry ®istry) { it.translation.dy = dy; it.translation.dz = dz; } - // else if (it.type == AnimationType::Rotation) - // transformable.rotate(it.rotation.angle, {it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); + } + }); + + registry.view().each([](auto, auto &rotation, auto &animation) { + for (auto &it : animation.list) { + if (it.type == AnimationType::Rotation) { + rotation.quat += glm::angleAxis(it.rotation.angle, glm::vec3{it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); + } } }); } diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index 060739cb3..a530e234b 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -63,7 +63,8 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::EntitySpawn, "EntitySpawn"}, {Network::Command::EntityDespawn, "EntityDespawn"}, - {Network::Command::EntityPosUpdate, "EntityPosUpdate"}, + {Network::Command::EntityPosition, "EntityPosition"}, + {Network::Command::EntityRotation, "EntityRotation"}, {Network::Command::EntityDrawableDef, "EntityDrawableDef"}, }; diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index ce0ca5e13..7af313caf 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -70,8 +70,9 @@ namespace Network { // Entity commands EntitySpawn = 22, // [NetworkCommand][u32 entity id] (from Server only) EntityDespawn = 23, // [NetworkCommand][u32 entity id] (from Server only) - EntityPosUpdate = 24, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) - EntityDrawableDef = 25, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) + EntityPosition = 24, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) + EntityRotation = 25, // [NetworkCommand][u32 entity id][Rotation rot] (from Server only) + EntityDrawableDef = 26, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) }; std::string commandToString(Command command); diff --git a/source/common/scene/RotationComponent.hpp b/source/common/scene/RotationComponent.hpp new file mode 100644 index 000000000..903a52bf3 --- /dev/null +++ b/source/common/scene/RotationComponent.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * 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 ROTATIONCOMPONENT_HPP_ +#define ROTATIONCOMPONENT_HPP_ + +#include + +struct RotationComponent { + glm::quat quat; + + bool isUpdated = true; +}; + +#endif // ROTATIONCOMPONENT_HPP_ diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index f4d07390b..2b044bc6d 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -115,9 +115,19 @@ void ServerCommandHandler::sendEntityDespawn(u32 entityID, const ClientInfo *cli client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client) const { sf::Packet packet; - packet << Network::Command::EntityPosUpdate << entityID << x << y << z; + packet << Network::Command::EntityPosition << entityID << x << y << z; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityRotation << entityID << w << x << y << z; if (!client) m_server.sendToAllClients(packet); diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 1c85ba249..ceffe48ca 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -58,7 +58,8 @@ class ServerCommandHandler { void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; void sendEntitySpawn(u32 entityID, const ClientInfo *client = nullptr) const; void sendEntityDespawn(u32 entityID, const ClientInfo *client = nullptr) const; - void sendEntityPosUpdate(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; + void sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; + void sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; void sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); diff --git a/source/server/scene/NetworkController.cpp b/source/server/scene/NetworkController.cpp index 9dfc64ada..7224873a1 100644 --- a/source/server/scene/NetworkController.cpp +++ b/source/server/scene/NetworkController.cpp @@ -28,22 +28,36 @@ #include "NetworkComponent.hpp" #include "NetworkController.hpp" #include "PositionComponent.hpp" +#include "RotationComponent.hpp" #include "ServerCommandHandler.hpp" void NetworkController::update(entt::registry ®istry) { registry.view().each([this] (auto, auto &network) { - if (!network.hasSpawned) + if (!network.hasSpawned) { m_server->sendEntitySpawn(network.entityID); + network.hasSpawned = true; + } }); registry.view().each([this] (auto, auto &network, auto &position) { - if (position.isUpdated) - m_server->sendEntityPosUpdate(network.entityID, position.x, position.y, position.z); + if (position.isUpdated) { + m_server->sendEntityPosition(network.entityID, position.x, position.y, position.z); + position.isUpdated = false; + } + }); + + registry.view().each([this] (auto, auto &network, auto &rotation) { + if (rotation.isUpdated) { + m_server->sendEntityRotation(network.entityID, rotation.quat.w, rotation.quat.x, rotation.quat.y, rotation.quat.z); + rotation.isUpdated = false; + } }); registry.view().each([this] (auto, auto &network, auto &drawableDef) { - if (drawableDef.isUpdated) + if (drawableDef.isUpdated) { m_server->sendEntityDrawableDef(network.entityID, drawableDef); + drawableDef.isUpdated = false; + } }); } @@ -52,7 +66,11 @@ void NetworkController::sendEntities(entt::registry ®istry, const ClientInfo m_server->sendEntitySpawn(network.entityID, &client); if (auto *position = registry.try_get(entity) ; position) { - m_server->sendEntityPosUpdate(network.entityID, position->x, position->y, position->z); + m_server->sendEntityPosition(network.entityID, position->x, position->y, position->z); + } + + if (auto *rotation = registry.try_get(entity) ; rotation) { + m_server->sendEntityRotation(network.entityID, rotation->quat.w, rotation->quat.x, rotation->quat.y, rotation->quat.z); } if (auto *drawableDef = registry.try_get(entity) ; drawableDef) { From 063044015946067f5bd1f37735adcbf4a844eb4a Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 00:27:05 +0200 Subject: [PATCH 27/38] [AnimationComponent] Now serialized. --- README.md | 1 + source/client/network/ClientCommandHandler.cpp | 14 ++++++++++++++ source/client/scene/AnimationController.cpp | 2 +- source/client/scene/RenderingController.cpp | 11 +++++++++-- source/common/network/Network.cpp | 1 + source/common/network/Network.hpp | 5 +++-- source/common/scene/AnimationComponent.hpp | 3 +-- source/common/scene/RotationComponent.hpp | 2 +- source/server/network/ServerCommandHandler.cpp | 11 +++++++++++ source/server/network/ServerCommandHandler.hpp | 2 ++ source/server/scene/ItemDropFactory.cpp | 2 ++ source/server/scene/NetworkController.cpp | 18 +++++++++++++++--- 12 files changed, 61 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 237f6a32b..da5084999 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ The long-term goal of this project is to provide a viable alternative to Minecra - [Lua](http://www.lua.org) - _Linux users: Check your distribution repositories for packages._ - Run `cmake -B build . && cmake --build build -j8` +- Or `mkdir build && cd build && cmake .. && make -j8 && cmake ..` (for old CMake versions) - Run the client with `./build/openminer` - If you want a multiplayer game, run the server with `./build/openminer_server` diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index b02d93473..16a15e537 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -27,6 +27,7 @@ #include #include +#include "AnimationComponent.hpp" #include "Client.hpp" #include "ClientPlayer.hpp" #include "ClientWorld.hpp" @@ -305,6 +306,19 @@ void ClientCommandHandler::setupCallbacks() { gkError() << "EntityRotation: Entity ID" << entityID << "is invalid"; }); + m_client.setCommandCallback(Network::Command::EntityAnimation, [this](sf::Packet &packet) { + u32 entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + auto &animation = m_world.scene().registry().get_or_assign(it->second); + animation.deserialize(packet); + } + else + gkError() << "EntityAnimation: Entity ID" << entityID << "is invalid"; + }); + m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) { u32 entityID; packet >> entityID; diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/AnimationController.cpp index 65248b13c..4deebc56d 100644 --- a/source/client/scene/AnimationController.cpp +++ b/source/client/scene/AnimationController.cpp @@ -69,7 +69,7 @@ void AnimationController::update(entt::registry ®istry) { registry.view().each([](auto, auto &rotation, auto &animation) { for (auto &it : animation.list) { if (it.type == AnimationType::Rotation) { - rotation.quat += glm::angleAxis(it.rotation.angle, glm::vec3{it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}); + rotation.quat = glm::angleAxis(glm::radians(it.rotation.angle), glm::vec3{it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}) * rotation.quat; } } }); diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 62d04ec71..5299cad44 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -24,11 +24,14 @@ * * ===================================================================================== */ +#include + #include "DrawableDef.hpp" #include "DrawableComponent.hpp" #include "InventoryCube.hpp" #include "PositionComponent.hpp" #include "RenderingController.hpp" +#include "RotationComponent.hpp" #include "Registry.hpp" @@ -47,9 +50,13 @@ void RenderingController::update(entt::registry ®istry) { } void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { - registry.view().each([&](auto, auto &drawable, auto &position) { + registry.view().each([&](auto, auto &drawable, auto &position, auto &rotation) { + gk::Transformable transformable; + transformable.setPosition(position.x, position.y, position.z); + transformable.getRotationTransform().getMatrix() = glm::toMat4(rotation.quat); + gk::RenderStates drawStates = states; - drawStates.transform.translate(position.x, position.y, position.z); + drawStates.transform *= transformable.getTransform(); drawable.draw(target, drawStates); }); } diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index a530e234b..f623db0b1 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -65,6 +65,7 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::EntityDespawn, "EntityDespawn"}, {Network::Command::EntityPosition, "EntityPosition"}, {Network::Command::EntityRotation, "EntityRotation"}, + {Network::Command::EntityAnimation, "EntityRotation"}, {Network::Command::EntityDrawableDef, "EntityDrawableDef"}, }; diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index 7af313caf..3c07b4624 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -71,8 +71,9 @@ namespace Network { EntitySpawn = 22, // [NetworkCommand][u32 entity id] (from Server only) EntityDespawn = 23, // [NetworkCommand][u32 entity id] (from Server only) EntityPosition = 24, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) - EntityRotation = 25, // [NetworkCommand][u32 entity id][Rotation rot] (from Server only) - EntityDrawableDef = 26, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) + EntityRotation = 25, // [NetworkCommand][u32 entity id][float w, float x, float y, float z] (from Server only) + EntityAnimation = 26, // [NetworkCommand][u32 entity id][AnimationComponent anim] (from Server only) + EntityDrawableDef = 27, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) }; std::string commandToString(Command command); diff --git a/source/common/scene/AnimationComponent.hpp b/source/common/scene/AnimationComponent.hpp index b51696b17..a730a5c04 100644 --- a/source/common/scene/AnimationComponent.hpp +++ b/source/common/scene/AnimationComponent.hpp @@ -126,8 +126,7 @@ struct AnimationComponent : public ISerializable { std::vector list; - private: - bool m_isInitialized = false; + bool isUpdated = true; }; #endif // ANIMATIONCOMPONENT_HPP_ diff --git a/source/common/scene/RotationComponent.hpp b/source/common/scene/RotationComponent.hpp index 903a52bf3..010fb2bf6 100644 --- a/source/common/scene/RotationComponent.hpp +++ b/source/common/scene/RotationComponent.hpp @@ -30,7 +30,7 @@ #include struct RotationComponent { - glm::quat quat; + glm::quat quat{1, 0, 0, 0}; bool isUpdated = true; }; diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 2b044bc6d..e35487011 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -24,6 +24,7 @@ * * ===================================================================================== */ +#include "AnimationComponent.hpp" #include "BlockData.hpp" #include "DrawableDef.hpp" #include "NetworkComponent.hpp" @@ -135,6 +136,16 @@ void ServerCommandHandler::sendEntityRotation(u32 entityID, float w, float x, fl client->tcpSocket->send(packet); } +void ServerCommandHandler::sendEntityAnimation(u32 entityID, const AnimationComponent &animation, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityAnimation << entityID << animation; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + void ServerCommandHandler::sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityDrawableDef << entityID << drawableDef; diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index ceffe48ca..475e4942b 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -35,6 +35,7 @@ struct BlockData; +struct AnimationComponent; class ClientInfo; class DrawableDef; class Inventory; @@ -60,6 +61,7 @@ class ServerCommandHandler { void sendEntityDespawn(u32 entityID, const ClientInfo *client = nullptr) const; void sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; void sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; + void sendEntityAnimation(u32 entityID, const AnimationComponent &animation, const ClientInfo *client = nullptr) const; void sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 020420ad5..6e8f92434 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -32,12 +32,14 @@ #include "NetworkComponent.hpp" #include "PositionComponent.hpp" #include "Registry.hpp" +#include "RotationComponent.hpp" static u32 counter = 0; // FIXME: TEMPORARY void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); registry.assign(entity, x, y, z); + registry.assign(entity); registry.assign(entity, counter++); auto &drawableDef = registry.assign(entity); diff --git a/source/server/scene/NetworkController.cpp b/source/server/scene/NetworkController.cpp index 7224873a1..930b1a193 100644 --- a/source/server/scene/NetworkController.cpp +++ b/source/server/scene/NetworkController.cpp @@ -24,6 +24,7 @@ * * ===================================================================================== */ +#include "AnimationComponent.hpp" #include "DrawableDef.hpp" #include "NetworkComponent.hpp" #include "NetworkController.hpp" @@ -53,6 +54,13 @@ void NetworkController::update(entt::registry ®istry) { } }); + registry.view().each([this] (auto, auto &network, auto &animation) { + if (animation.isUpdated) { + m_server->sendEntityAnimation(network.entityID, animation); + animation.isUpdated = false; + } + }); + registry.view().each([this] (auto, auto &network, auto &drawableDef) { if (drawableDef.isUpdated) { m_server->sendEntityDrawableDef(network.entityID, drawableDef); @@ -66,15 +74,19 @@ void NetworkController::sendEntities(entt::registry ®istry, const ClientInfo m_server->sendEntitySpawn(network.entityID, &client); if (auto *position = registry.try_get(entity) ; position) { - m_server->sendEntityPosition(network.entityID, position->x, position->y, position->z); + m_server->sendEntityPosition(network.entityID, position->x, position->y, position->z, &client); } if (auto *rotation = registry.try_get(entity) ; rotation) { - m_server->sendEntityRotation(network.entityID, rotation->quat.w, rotation->quat.x, rotation->quat.y, rotation->quat.z); + m_server->sendEntityRotation(network.entityID, rotation->quat.w, rotation->quat.x, rotation->quat.y, rotation->quat.z, &client); + } + + if (auto *animation = registry.try_get(entity) ; animation) { + m_server->sendEntityAnimation(network.entityID, *animation, &client); } if (auto *drawableDef = registry.try_get(entity) ; drawableDef) { - m_server->sendEntityDrawableDef(network.entityID, *drawableDef); + m_server->sendEntityDrawableDef(network.entityID, *drawableDef, &client); } }); } From c8a289e207c30c61a406809d23c1ffec5125194c Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 01:09:07 +0200 Subject: [PATCH 28/38] [CollisionController] Now sends inventory update. --- README.md | 7 +++++++ source/server/network/ServerCommandHandler.cpp | 16 ++++++++++++++++ source/server/network/ServerCommandHandler.hpp | 1 + source/server/scene/CollisionController.cpp | 2 +- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da5084999..f5632dae9 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ - [Wiki](#wiki) - [Keys](#keys) - [How to compile](#how-to-compile) + - [Using Linux](#using-linux) + - [Using Windows](#using-windows) + - [Using OSX](#using-osx) - [Discussion](#discussion) - [Project status](#project-status) - [Screenshots](#screenshots) @@ -79,6 +82,10 @@ The long-term goal of this project is to provide a viable alternative to Minecra - [Wiki: Compiling on Windows using CMake and MinGW-w64](https://github.com/Unarelith/OpenMiner/wiki/Compiling-on-Windows-with-MinGW-w64) - [Wiki: Compiling on Windows using CMake and Visual Studio 2017](https://github.com/Unarelith/OpenMiner/wiki/Compiling-on-Windows-with-Visual-Studio-2017) +### Using OSX + +I don't have an OSX system at the moment. But you can help me create a guide for this OS if you have one! + ## Discussion - Discord: [join server](https://discord.gg/eN8k8wt) diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index e35487011..8f9000506 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -75,6 +75,22 @@ void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportatio gkError() << ("Failed to send pos update for player " + std::to_string(clientID) + ": Player not found").c_str(); } +void ServerCommandHandler::sendPlayerInvUpdate(u16 clientID, const ClientInfo *client) const { + ServerPlayer *player = m_players.getPlayer(clientID); + if (player) { + sf::Packet packet; + packet << Network::Command::PlayerInvUpdate; + packet << clientID << player->inventory(); + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); + } + else + gkError() << ("Failed to send inv update for player " + std::to_string(clientID) + ": Player not found").c_str(); +} + void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::PlayerChangeDimension; diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 475e4942b..29d8ca2cf 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -55,6 +55,7 @@ class ServerCommandHandler { void sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const ClientInfo *client = nullptr) const; void sendBlockInvUpdate(s32 x, s32 y, s32 z, const Inventory &inventory, const ClientInfo *client = nullptr) const; void sendPlayerPosUpdate(u16 clientID, bool isTeleportation = false, const ClientInfo *client = nullptr) const; + void sendPlayerInvUpdate(u16 clientID, const ClientInfo *client = nullptr) const; void sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client = nullptr) const; void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; void sendEntitySpawn(u32 entityID, const ClientInfo *client = nullptr) const; diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp index 726eaeeca..3f4a2c923 100644 --- a/source/server/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -38,7 +38,7 @@ void CollisionController::update(entt::registry ®istry) { gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; if (hitbox.intersects(playerHitbox)) { it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); - // FIXME: Send inventory update here + m_server->sendPlayerInvUpdate(it.second.clientID(), &it.second.client()); m_server->sendEntityDespawn(network.entityID); registry.destroy(entity); } From 7d5b00ed888dfbf0f1c3e9b797190177d529a264 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 01:55:14 +0200 Subject: [PATCH 29/38] [ServerConfig] Added for option 'useItemDrops'. --- .gitignore | 1 + config.example.lua | 2 +- server_config.example.lua | 7 +++ source/client/hud/BlockCursor.cpp | 1 - source/client/network/Client.hpp | 1 + source/common/world/Chunk.cpp | 1 - source/common/world/World.hpp | 1 - source/server/core/ServerApplication.cpp | 3 ++ source/server/core/ServerConfig.cpp | 53 +++++++++++++++++++ source/server/core/ServerConfig.hpp | 37 +++++++++++++ .../server/network/ServerCommandHandler.cpp | 22 +++++--- .../server/network/ServerCommandHandler.hpp | 2 +- source/server/world/ServerWorld.cpp | 18 ++++--- source/server/world/ServerWorld.hpp | 2 +- 14 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 server_config.example.lua create mode 100644 source/server/core/ServerConfig.cpp create mode 100644 source/server/core/ServerConfig.hpp diff --git a/.gitignore b/.gitignore index 21465a5b2..60c377b0a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,5 +64,6 @@ openminer_server *.zip test_atlas.png config.lua +server_config.lua TODO diff --git a/config.example.lua b/config.example.lua index 1f2754455..e93461ee0 100644 --- a/config.example.lua +++ b/config.example.lua @@ -1,6 +1,6 @@ -- You can copy this file as 'config.lua' to load automatically those settings at client startup -- Here the default values are set, you can remove options or change them accordingly --- See client/source/Config.cpp for more details +-- See source/client/core/Config.cpp for more details -- Gameplay isFlyModeEnabled = false diff --git a/server_config.example.lua b/server_config.example.lua new file mode 100644 index 000000000..6a0c7986c --- /dev/null +++ b/server_config.example.lua @@ -0,0 +1,7 @@ +-- You can copy this file as 'server_config.lua' to load automatically those settings at server startup +-- Here the default values are set, you can remove options or change them accordingly +-- See source/server/core/ServerConfig.cpp for more details + +-- Gameplay +useItemDrops = true + diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index fc4da82df..d79552e8c 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -153,7 +153,6 @@ void BlockCursor::update(const Hotbar &hotbar) { m_animationStart = ticks; m_client.sendPlayerDigBlock(m_selectedBlock); - m_client.sendPlayerInvUpdate(); } } } diff --git a/source/client/network/Client.hpp b/source/client/network/Client.hpp index 31e2a07e3..5ccb5825a 100644 --- a/source/client/network/Client.hpp +++ b/source/client/network/Client.hpp @@ -30,6 +30,7 @@ #include #include +#include #include #include diff --git a/source/common/world/Chunk.cpp b/source/common/world/Chunk.cpp index 1d543b443..19cad394c 100644 --- a/source/common/world/Chunk.cpp +++ b/source/common/world/Chunk.cpp @@ -94,7 +94,6 @@ void Chunk::setBlock(int x, int y, int z, u16 type) { if (m_data[z][y][x] != 0) { const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x]); onBlockDestroyed(x, y, z, oldBlock); - m_world.onBlockDestroyed(x + m_x * width, y + m_y * depth, z + m_z * height, oldBlock); } setBlockRaw(x, y, z, type); diff --git a/source/common/world/World.hpp b/source/common/world/World.hpp index 56e87617e..e96e176ab 100644 --- a/source/common/world/World.hpp +++ b/source/common/world/World.hpp @@ -47,7 +47,6 @@ class World { void setData(int x, int y, int z, u16 data) const; virtual void onBlockPlaced(int, int, int, const Block &) {} - virtual void onBlockDestroyed(int, int, int, const Block &) {} static bool isReloadRequested; }; diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp index 7d46d2356..10df83824 100644 --- a/source/server/core/ServerApplication.cpp +++ b/source/server/core/ServerApplication.cpp @@ -29,6 +29,7 @@ #include "BlockGeometry.hpp" #include "ServerApplication.hpp" #include "ServerBlock.hpp" +#include "ServerConfig.hpp" namespace fs = ghc::filesystem; @@ -58,6 +59,8 @@ void ServerApplication::init() { if (m_argumentParser.getArgument("port").isFound) m_port = std::stoi(m_argumentParser.getArgument("port").parameter); + ServerConfig::loadConfigFromFile("server_config.lua"); + m_server.init(m_port); m_server.setRunning(true); diff --git a/source/server/core/ServerConfig.cpp b/source/server/core/ServerConfig.cpp new file mode 100644 index 000000000..3939fb12b --- /dev/null +++ b/source/server/core/ServerConfig.cpp @@ -0,0 +1,53 @@ +/* + * ===================================================================================== + * + * 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 "ServerConfig.hpp" + +// Gameplay +bool ServerConfig::useItemDrops = true; + +#include +#include + +#include + +void ServerConfig::loadConfigFromFile(const char *file) { + if (gk::Filesystem::fileExists(file)) { + sol::state lua; + + try { + lua.safe_script_file(file); + + useItemDrops = lua["useItemDrops"].get_or(useItemDrops); + + gkInfo() << "Config file loaded successfully"; + } + catch (sol::error &e) { + gkError() << e.what(); + } + } +} + diff --git a/source/server/core/ServerConfig.hpp b/source/server/core/ServerConfig.hpp new file mode 100644 index 000000000..4ee378fa7 --- /dev/null +++ b/source/server/core/ServerConfig.hpp @@ -0,0 +1,37 @@ +/* + * ===================================================================================== + * + * 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 SERVERCONFIG_HPP_ +#define SERVERCONFIG_HPP_ + +namespace ServerConfig { + // Gameplay + extern bool useItemDrops; + + void loadConfigFromFile(const char *file); +} + +#endif // SERVERCONFIG_HPP_ diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 8f9000506..25c7e1706 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -162,7 +162,7 @@ void ServerCommandHandler::sendEntityAnimation(u32 entityID, const AnimationComp client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityDrawableDef(u32 entityID, const DrawableDef &drawableDef, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityDrawableDef << entityID << drawableDef; @@ -270,13 +270,21 @@ void ServerCommandHandler::setupCallbacks() { }); m_server.setCommandCallback(Network::Command::PlayerDigBlock, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - packet >> x >> y >> z; - getWorldForClient(client.id).setBlock(x, y, z, 0); + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + packet >> x >> y >> z; + + ServerWorld &world = getWorldForClient(client.id); + world.onBlockDigged(x, y, z, Registry::getInstance().getBlock(world.getBlock(x, y, z)), *player); + world.setBlock(x, y, z, 0); - sf::Packet answer; - answer << Network::Command::BlockUpdate << x << y << z << u32(0); - m_server.sendToAllClients(answer); + sf::Packet answer; + answer << Network::Command::BlockUpdate << x << y << z << u32(0); + m_server.sendToAllClients(answer); + } + else + gkError() << ("Failed to dig block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerInventory, [this](ClientInfo &client, sf::Packet &packet) { diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 29d8ca2cf..9143e2228 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -63,7 +63,7 @@ class ServerCommandHandler { void sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; void sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; void sendEntityAnimation(u32 entityID, const AnimationComponent &animation, const ClientInfo *client = nullptr) const; - void sendEntityDrawableDef(u32 entityID, DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; + void sendEntityDrawableDef(u32 entityID, const DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index 574aa97a7..fa7408f94 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -26,15 +26,16 @@ */ #include +#include "Dimension.hpp" #include "EngineConfig.hpp" +#include "ItemDropFactory.hpp" #include "Network.hpp" #include "Server.hpp" #include "ServerCommandHandler.hpp" +#include "ServerConfig.hpp" #include "ServerPlayer.hpp" #include "ServerWorld.hpp" -#include "Dimension.hpp" // FIXME - void ServerWorld::update() { if (m_lastTick < m_clock.getTicks() / 50) { m_lastTick = m_clock.getTicks() / 50; @@ -150,10 +151,15 @@ void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) sendChunkData(client, chunk); } -#include "ItemDropFactory.hpp" // FIXME - -void ServerWorld::onBlockDestroyed(int x, int y, int z, const Block &block) { - ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); +void ServerWorld::onBlockDigged(int x, int y, int z, const Block &block, ServerPlayer &player) { + if (ServerConfig::useItemDrops) { + ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + } + else { + player.inventory().addStack(block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + // gkDebug() << player.inventory().getStack(2, 2).item().stringID() << player.inventory().getStack(2, 2).amount(); + m_server->sendPlayerInvUpdate(player.clientID(), &player.client()); + } } ServerChunk &ServerWorld::createChunk(s32 cx, s32 cy, s32 cz) { diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp index 68fb591d8..6409aa685 100644 --- a/source/server/world/ServerWorld.hpp +++ b/source/server/world/ServerWorld.hpp @@ -57,7 +57,7 @@ class ServerWorld : public World { void sendChunkData(const ClientInfo &client, ServerChunk &chunk); void sendRequestedData(ClientInfo &client, s32 cx, s32 cy, s32 cz); - void onBlockDestroyed(int x, int y, int z, const Block &block) override; + void onBlockDigged(int x, int y, int z, const Block &block, ServerPlayer &player); ServerChunk &createChunk(s32 cx, s32 cy, s32 cz); From c1097d4fcd8925b0f61bc6c2163e0ce792d115fe Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 02:10:55 +0200 Subject: [PATCH 30/38] [README.md] Updated. --- .gitmodules | 2 +- .travis.yml | 2 +- README.md | 3 +-- external/entt | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index dd37435a0..faae46e10 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "external/entt"] path = external/entt - url = git://github.com/Unarelith/entt.git + url = git://github.com/skypjack/entt.git ignore = dirty [submodule "external/SFML"] path = external/SFML diff --git a/.travis.yml b/.travis.yml index c82373c18..7991b920e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux -dist: trusty +dist: xenial language: cpp compiler: diff --git a/README.md b/README.md index f5632dae9..ecd61215b 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,8 @@ The long-term goal of this project is to provide a viable alternative to Minecra - Dependencies: - A compiler with C++17 support (GCC >= 7.0 or clang >= 5.0) - [CMake](http://www.cmake.org/download/) - - [GameKit](http://github.com/Unarelith/GameKit) (requires `SDL2` + `tinyxml2`, will switch to `SFML` starting from 2.6) - - [SFML](https://www.sfml-dev.org/) (only used for network) - [Lua](http://www.lua.org) + - [GameKit dependencies](https://github.com/Unarelith/GameKit#how-to-compile) - _Linux users: Check your distribution repositories for packages._ - Run `cmake -B build . && cmake --build build -j8` - Or `mkdir build && cd build && cmake .. && make -j8 && cmake ..` (for old CMake versions) diff --git a/external/entt b/external/entt index 76707f0c1..89dc7f817 160000 --- a/external/entt +++ b/external/entt @@ -1 +1 @@ -Subproject commit 76707f0c1a73bf5918c56ffd860c3b83b6a9d120 +Subproject commit 89dc7f817f224f368bb6e4fbfb3c5cdeb1daf433 From dd04ed943160d98e13a1f6d4afff4d9e73bce785 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 14:05:27 +0200 Subject: [PATCH 31/38] [.travis.yml] Small fix. [entt] Updated. --- .travis.yml | 4 +++- README.md | 2 +- source/client/network/ClientCommandHandler.cpp | 10 +++++----- source/client/scene/RenderingController.cpp | 2 +- source/server/scene/CollisionController.cpp | 1 + source/server/scene/ItemDropFactory.cpp | 14 +++++++------- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7991b920e..e26227d1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,9 @@ before_install: - cd .. script: - - cmake . + - mkdir build + - cd build + - cmake .. - make -j8 notifications: diff --git a/README.md b/README.md index ecd61215b..9433341d0 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The long-term goal of this project is to provide a viable alternative to Minecra - Dependencies: - A compiler with C++17 support (GCC >= 7.0 or clang >= 5.0) - - [CMake](http://www.cmake.org/download/) + - [CMake](http://www.cmake.org/download/) (>= 3.12.4) - [Lua](http://www.lua.org) - [GameKit dependencies](https://github.com/Unarelith/GameKit#how-to-compile) - _Linux users: Check your distribution repositories for packages._ diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 16a15e537..e51fa03c5 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -258,7 +258,7 @@ void ClientCommandHandler::setupCallbacks() { if (it == m_entityMap.end()) { entt::entity entity = registry.create(); m_entityMap.emplace(entityID, entity); - registry.assign(entity, entityID); + registry.emplace(entity, entityID); } else if (registry.get(it->second).entityID != entityID) { gkError() << "EntitySpawn: Entity ID" << entityID << "is invalid"; @@ -283,7 +283,7 @@ void ClientCommandHandler::setupCallbacks() { auto it = m_entityMap.find(entityID); if (it != m_entityMap.end()) { - auto &position = m_world.scene().registry().get_or_assign(it->second); + auto &position = m_world.scene().registry().get_or_emplace(it->second); packet >> position.x >> position.y >> position.z; } else @@ -299,7 +299,7 @@ void ClientCommandHandler::setupCallbacks() { float w, x, y, z; packet >> w >> x >> y >> z; - auto &rotation = m_world.scene().registry().get_or_assign(it->second); + auto &rotation = m_world.scene().registry().get_or_emplace(it->second); rotation.quat = glm::quat(w, x, y, z); } else @@ -312,7 +312,7 @@ void ClientCommandHandler::setupCallbacks() { auto it = m_entityMap.find(entityID); if (it != m_entityMap.end()) { - auto &animation = m_world.scene().registry().get_or_assign(it->second); + auto &animation = m_world.scene().registry().get_or_emplace(it->second); animation.deserialize(packet); } else @@ -325,7 +325,7 @@ void ClientCommandHandler::setupCallbacks() { auto it = m_entityMap.find(entityID); if (it != m_entityMap.end()) { - packet >> m_world.scene().registry().get_or_assign(it->second); + packet >> m_world.scene().registry().get_or_emplace(it->second); } else gkError() << "EntityDrawableDef: Entity ID" << entityID << "is invalid"; diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/RenderingController.cpp index 5299cad44..c17b25d25 100644 --- a/source/client/scene/RenderingController.cpp +++ b/source/client/scene/RenderingController.cpp @@ -39,7 +39,7 @@ void RenderingController::update(entt::registry ®istry) { registry.view(entt::exclude).each([&](auto entity, auto &drawableDef) { const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); - DrawableComponent &drawable = registry.get_or_assign(entity); + DrawableComponent &drawable = registry.get_or_emplace(entity); InventoryCube &cube = drawable.setDrawable(cubeDef.size, true); cube.setOrigin(cubeDef.origin); diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/CollisionController.cpp index 3f4a2c923..984c031a7 100644 --- a/source/server/scene/CollisionController.cpp +++ b/source/server/scene/CollisionController.cpp @@ -32,6 +32,7 @@ #include "ServerCommandHandler.hpp" void CollisionController::update(entt::registry ®istry) { + // FIXME: This should be a callback in a CollisionComponent registry.view().each([&](auto entity, auto &position, auto &box, auto &itemStack, auto &network) { for (auto &it : m_players) { gk::DoubleBox hitbox = box + gk::Vector3d{position.x, position.y, position.z}; diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 6e8f92434..7dbc2e277 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -38,21 +38,21 @@ static u32 counter = 0; // FIXME: TEMPORARY void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); - registry.assign(entity, x, y, z); - registry.assign(entity); - registry.assign(entity, counter++); + registry.emplace(entity, x, y, z); + registry.emplace(entity); + registry.emplace(entity, counter++); - auto &drawableDef = registry.assign(entity); + auto &drawableDef = registry.emplace(entity); auto &cube = drawableDef.addInventoryCube(); cube.size = 0.25f; cube.origin = gk::Vector3f{cube.size / 2.f, cube.size / 2.f, cube.size / 2.f}; cube.blockID = itemID; - auto &animationComponent = registry.assign(entity); + auto &animationComponent = registry.emplace(entity); animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); - registry.assign(entity, 0., 0., 0., cube.size, cube.size, cube.size); - registry.assign(entity, itemID, amount); + registry.emplace(entity, 0., 0., 0., cube.size, cube.size, cube.size); + registry.emplace(entity, itemID, amount); } From 88041e5e7f7d92a0ef5ddf372b22aadce1af01f4 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 17:58:07 +0200 Subject: [PATCH 32/38] [NetworkComponent] Now using server-side entity ID instead of an arbitrary one. --- .../client/network/ClientCommandHandler.cpp | 26 ++++++++++--------- .../client/network/ClientCommandHandler.hpp | 2 +- source/common/network/NetworkUtils.cpp | 12 +++++++++ source/common/network/NetworkUtils.hpp | 8 ++++++ source/common/scene/NetworkComponent.hpp | 4 +-- .../server/network/ServerCommandHandler.cpp | 12 ++++----- .../server/network/ServerCommandHandler.hpp | 14 +++++----- source/server/scene/ItemDropFactory.cpp | 4 +-- 8 files changed, 52 insertions(+), 30 deletions(-) diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index e51fa03c5..663878857 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include "AnimationComponent.hpp" #include "Client.hpp" #include "ClientPlayer.hpp" @@ -249,7 +251,7 @@ void ClientCommandHandler::setupCallbacks() { }); m_client.setCommandCallback(Network::Command::EntitySpawn, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto ®istry = m_world.scene().registry(); @@ -261,12 +263,12 @@ void ClientCommandHandler::setupCallbacks() { registry.emplace(entity, entityID); } else if (registry.get(it->second).entityID != entityID) { - gkError() << "EntitySpawn: Entity ID" << entityID << "is invalid"; + gkError() << "EntitySpawn: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; } }); m_client.setCommandCallback(Network::Command::EntityDespawn, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto it = m_entityMap.find(entityID); @@ -274,11 +276,11 @@ void ClientCommandHandler::setupCallbacks() { m_world.scene().registry().destroy(it->second); } else - gkError() << "EntityDespawn: Entity ID" << entityID << "is invalid"; + gkError() << "EntityDespawn: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; }); m_client.setCommandCallback(Network::Command::EntityPosition, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto it = m_entityMap.find(entityID); @@ -287,11 +289,11 @@ void ClientCommandHandler::setupCallbacks() { packet >> position.x >> position.y >> position.z; } else - gkError() << "EntityPosition: Entity ID" << entityID << "is invalid"; + gkError() << "EntityPosition: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; }); m_client.setCommandCallback(Network::Command::EntityRotation, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto it = m_entityMap.find(entityID); @@ -303,11 +305,11 @@ void ClientCommandHandler::setupCallbacks() { rotation.quat = glm::quat(w, x, y, z); } else - gkError() << "EntityRotation: Entity ID" << entityID << "is invalid"; + gkError() << "EntityRotation: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; }); m_client.setCommandCallback(Network::Command::EntityAnimation, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto it = m_entityMap.find(entityID); @@ -316,11 +318,11 @@ void ClientCommandHandler::setupCallbacks() { animation.deserialize(packet); } else - gkError() << "EntityAnimation: Entity ID" << entityID << "is invalid"; + gkError() << "EntityAnimation: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; }); m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) { - u32 entityID; + entt::entity entityID; packet >> entityID; auto it = m_entityMap.find(entityID); @@ -328,7 +330,7 @@ void ClientCommandHandler::setupCallbacks() { packet >> m_world.scene().registry().get_or_emplace(it->second); } else - gkError() << "EntityDrawableDef: Entity ID" << entityID << "is invalid"; + gkError() << "EntityDrawableDef: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; }); } diff --git a/source/client/network/ClientCommandHandler.hpp b/source/client/network/ClientCommandHandler.hpp index 3f5a75d60..d8c59e4a1 100644 --- a/source/client/network/ClientCommandHandler.hpp +++ b/source/client/network/ClientCommandHandler.hpp @@ -72,7 +72,7 @@ class ClientCommandHandler { std::unordered_map &m_playerBoxes; - std::unordered_map m_entityMap; + std::unordered_map m_entityMap; bool m_isRegistryInitialized = false; diff --git a/source/common/network/NetworkUtils.cpp b/source/common/network/NetworkUtils.cpp index 0c9e9da4b..1d1ae7eea 100644 --- a/source/common/network/NetworkUtils.cpp +++ b/source/common/network/NetworkUtils.cpp @@ -36,3 +36,15 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Color &color) { return packet; } +sf::Packet &operator<<(sf::Packet &packet, const entt::entity &entity) { + packet << std::underlying_type_t(entity); + return packet; +} + +sf::Packet &operator>>(sf::Packet &packet, entt::entity &entity) { + std::underlying_type_t id; + packet >> id; + entity = static_cast(id); + return packet; +} + diff --git a/source/common/network/NetworkUtils.hpp b/source/common/network/NetworkUtils.hpp index 1e8333865..c79787a58 100644 --- a/source/common/network/NetworkUtils.hpp +++ b/source/common/network/NetworkUtils.hpp @@ -143,4 +143,12 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Vector3 &vec) { sf::Packet &operator<<(sf::Packet &packet, const gk::Color &color); sf::Packet &operator>>(sf::Packet &packet, gk::Color &color); +//====================================================================================== +// entt::entity +//====================================================================================== +#include + +sf::Packet &operator<<(sf::Packet &packet, const entt::entity &entity); +sf::Packet &operator>>(sf::Packet &packet, entt::entity &entity); + #endif // NETWORKUTILS_HPP_ diff --git a/source/common/scene/NetworkComponent.hpp b/source/common/scene/NetworkComponent.hpp index df70ba398..61be9b3f8 100644 --- a/source/common/scene/NetworkComponent.hpp +++ b/source/common/scene/NetworkComponent.hpp @@ -27,10 +27,10 @@ #ifndef NETWORKCOMPONENT_HPP_ #define NETWORKCOMPONENT_HPP_ -#include +#include struct NetworkComponent { - u32 entityID = 0; + entt::entity entityID; bool hasSpawned = false; }; diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 25c7e1706..305ea40a9 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -112,7 +112,7 @@ void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &mess client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntitySpawn(u32 entityID, const ClientInfo *client) const { +void ServerCommandHandler::sendEntitySpawn(entt::entity entityID, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntitySpawn << entityID; @@ -122,7 +122,7 @@ void ServerCommandHandler::sendEntitySpawn(u32 entityID, const ClientInfo *clien client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityDespawn(u32 entityID, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityDespawn(entt::entity entityID, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityDespawn << entityID; @@ -132,7 +132,7 @@ void ServerCommandHandler::sendEntityDespawn(u32 entityID, const ClientInfo *cli client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityPosition(entt::entity entityID, double x, double y, double z, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityPosition << entityID << x << y << z; @@ -142,7 +142,7 @@ void ServerCommandHandler::sendEntityPosition(u32 entityID, double x, double y, client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityRotation(entt::entity entityID, float w, float x, float y, float z, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityRotation << entityID << w << x << y << z; @@ -152,7 +152,7 @@ void ServerCommandHandler::sendEntityRotation(u32 entityID, float w, float x, fl client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityAnimation(u32 entityID, const AnimationComponent &animation, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityAnimation(entt::entity entityID, const AnimationComponent &animation, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityAnimation << entityID << animation; @@ -162,7 +162,7 @@ void ServerCommandHandler::sendEntityAnimation(u32 entityID, const AnimationComp client->tcpSocket->send(packet); } -void ServerCommandHandler::sendEntityDrawableDef(u32 entityID, const DrawableDef &drawableDef, const ClientInfo *client) const { +void ServerCommandHandler::sendEntityDrawableDef(entt::entity entityID, const DrawableDef &drawableDef, const ClientInfo *client) const { sf::Packet packet; packet << Network::Command::EntityDrawableDef << entityID << drawableDef; diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index 9143e2228..f0ef794fd 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -31,6 +31,8 @@ #include +#include + #include "ChatCommandHandler.hpp" struct BlockData; @@ -58,12 +60,12 @@ class ServerCommandHandler { void sendPlayerInvUpdate(u16 clientID, const ClientInfo *client = nullptr) const; void sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client = nullptr) const; void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; - void sendEntitySpawn(u32 entityID, const ClientInfo *client = nullptr) const; - void sendEntityDespawn(u32 entityID, const ClientInfo *client = nullptr) const; - void sendEntityPosition(u32 entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; - void sendEntityRotation(u32 entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; - void sendEntityAnimation(u32 entityID, const AnimationComponent &animation, const ClientInfo *client = nullptr) const; - void sendEntityDrawableDef(u32 entityID, const DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; + void sendEntitySpawn(entt::entity entityID, const ClientInfo *client = nullptr) const; + void sendEntityDespawn(entt::entity entityID, const ClientInfo *client = nullptr) const; + void sendEntityPosition(entt::entity entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; + void sendEntityRotation(entt::entity entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; + void sendEntityAnimation(entt::entity entityID, const AnimationComponent &animation, const ClientInfo *client = nullptr) const; + void sendEntityDrawableDef(entt::entity entityID, const DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/ItemDropFactory.cpp index 7dbc2e277..ebb4343e9 100644 --- a/source/server/scene/ItemDropFactory.cpp +++ b/source/server/scene/ItemDropFactory.cpp @@ -34,13 +34,11 @@ #include "Registry.hpp" #include "RotationComponent.hpp" -static u32 counter = 0; // FIXME: TEMPORARY - void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { auto entity = registry.create(); registry.emplace(entity, x, y, z); registry.emplace(entity); - registry.emplace(entity, counter++); + registry.emplace(entity, entity); auto &drawableDef = registry.emplace(entity); auto &cube = drawableDef.addInventoryCube(); From 2cb2017313d13eba9ef2875eba71a5ea241c98af Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 18:07:48 +0200 Subject: [PATCH 33/38] [cmake/FindGameKit.cmake] Removed. --- .travis.yml | 12 ------------ CMakeLists.txt | 1 + cmake/FindGameKit.cmake | 16 ---------------- source/server/core/ServerApplication.hpp | 2 +- 4 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 cmake/FindGameKit.cmake diff --git a/.travis.yml b/.travis.yml index e26227d1d..396088dde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,18 +32,6 @@ addons: before_install: - sudo cp /usr/include/lua5.2/* /usr/include/ - - git clone git://github.com/SFML/SFML.git - - cd SFML - - cmake . - - make -j8 - - sudo make install - - cd .. - - git clone git://github.com/Quent42340/GameKit.git - - cd GameKit - - cmake . - - make -j8 - - sudo make install - - cd .. script: - mkdir build diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a893d60c..520578000 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,7 @@ include_directories(external/entt/single_include) # - gamekit #------------------------------------------------------------------------------ add_subdirectory(external/gamekit) +include_directories(external/gamekit/include) #------------------------------------------------------------------------------ # - SFML network diff --git a/cmake/FindGameKit.cmake b/cmake/FindGameKit.cmake deleted file mode 100644 index d0eff9317..000000000 --- a/cmake/FindGameKit.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2019 Quentin Bazin -# This file is licensed under LGPL 2.1 -# See LICENSE file - -# GAMEKIT_FOUND -# GAMEKIT_INCLUDE_DIR -# GAMEKIT_LIBARIES - -find_path(GAMEKIT_INCLUDE_DIR NAMES gk) -find_library(GAMEKIT_LIBRARIES NAMES gamekit) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(gamekit DEFAULT_MSG GAMEKIT_LIBRARIES GAMEKIT_INCLUDE_DIR) - -mark_as_advanced(GAMEKIT_INCLUDE_DIR GAMEKIT_LIBRARIES) - diff --git a/source/server/core/ServerApplication.hpp b/source/server/core/ServerApplication.hpp index 4fd895cf2..4be063efc 100644 --- a/source/server/core/ServerApplication.hpp +++ b/source/server/core/ServerApplication.hpp @@ -42,7 +42,7 @@ struct ServerOnlineEvent { bool isOnline; - u16 port; + int port; }; class ServerApplication { From 6d76a9face8a588ef08c172b8d976b0195950d9e Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 18:52:58 +0200 Subject: [PATCH 34/38] [scene] Components, controllers and factories now have their own subfolders. --- source/client/scene/{ => controller}/AnimationController.cpp | 0 source/client/scene/{ => controller}/AnimationController.hpp | 0 source/client/scene/{ => controller}/RenderingController.cpp | 0 source/client/scene/{ => controller}/RenderingController.hpp | 0 source/common/scene/{ => component}/AnimationComponent.hpp | 0 source/common/scene/{ => component}/DrawableComponent.hpp | 0 source/common/scene/{ => component}/DrawableDef.cpp | 0 source/common/scene/{ => component}/DrawableDef.hpp | 0 source/common/scene/{ => component}/InventoryCubeDef.hpp | 0 source/common/scene/{ => component}/NetworkComponent.hpp | 0 source/common/scene/{ => component}/PositionComponent.hpp | 0 source/common/scene/{ => component}/RotationComponent.hpp | 0 source/common/scene/{ => controller}/AbstractController.hpp | 0 source/server/scene/{ => controller}/CollisionController.cpp | 0 source/server/scene/{ => controller}/CollisionController.hpp | 0 source/server/scene/{ => controller}/NetworkController.cpp | 0 source/server/scene/{ => controller}/NetworkController.hpp | 0 source/server/scene/{ => factory}/ItemDropFactory.cpp | 0 source/server/scene/{ => factory}/ItemDropFactory.hpp | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename source/client/scene/{ => controller}/AnimationController.cpp (100%) rename source/client/scene/{ => controller}/AnimationController.hpp (100%) rename source/client/scene/{ => controller}/RenderingController.cpp (100%) rename source/client/scene/{ => controller}/RenderingController.hpp (100%) rename source/common/scene/{ => component}/AnimationComponent.hpp (100%) rename source/common/scene/{ => component}/DrawableComponent.hpp (100%) rename source/common/scene/{ => component}/DrawableDef.cpp (100%) rename source/common/scene/{ => component}/DrawableDef.hpp (100%) rename source/common/scene/{ => component}/InventoryCubeDef.hpp (100%) rename source/common/scene/{ => component}/NetworkComponent.hpp (100%) rename source/common/scene/{ => component}/PositionComponent.hpp (100%) rename source/common/scene/{ => component}/RotationComponent.hpp (100%) rename source/common/scene/{ => controller}/AbstractController.hpp (100%) rename source/server/scene/{ => controller}/CollisionController.cpp (100%) rename source/server/scene/{ => controller}/CollisionController.hpp (100%) rename source/server/scene/{ => controller}/NetworkController.cpp (100%) rename source/server/scene/{ => controller}/NetworkController.hpp (100%) rename source/server/scene/{ => factory}/ItemDropFactory.cpp (100%) rename source/server/scene/{ => factory}/ItemDropFactory.hpp (100%) diff --git a/source/client/scene/AnimationController.cpp b/source/client/scene/controller/AnimationController.cpp similarity index 100% rename from source/client/scene/AnimationController.cpp rename to source/client/scene/controller/AnimationController.cpp diff --git a/source/client/scene/AnimationController.hpp b/source/client/scene/controller/AnimationController.hpp similarity index 100% rename from source/client/scene/AnimationController.hpp rename to source/client/scene/controller/AnimationController.hpp diff --git a/source/client/scene/RenderingController.cpp b/source/client/scene/controller/RenderingController.cpp similarity index 100% rename from source/client/scene/RenderingController.cpp rename to source/client/scene/controller/RenderingController.cpp diff --git a/source/client/scene/RenderingController.hpp b/source/client/scene/controller/RenderingController.hpp similarity index 100% rename from source/client/scene/RenderingController.hpp rename to source/client/scene/controller/RenderingController.hpp diff --git a/source/common/scene/AnimationComponent.hpp b/source/common/scene/component/AnimationComponent.hpp similarity index 100% rename from source/common/scene/AnimationComponent.hpp rename to source/common/scene/component/AnimationComponent.hpp diff --git a/source/common/scene/DrawableComponent.hpp b/source/common/scene/component/DrawableComponent.hpp similarity index 100% rename from source/common/scene/DrawableComponent.hpp rename to source/common/scene/component/DrawableComponent.hpp diff --git a/source/common/scene/DrawableDef.cpp b/source/common/scene/component/DrawableDef.cpp similarity index 100% rename from source/common/scene/DrawableDef.cpp rename to source/common/scene/component/DrawableDef.cpp diff --git a/source/common/scene/DrawableDef.hpp b/source/common/scene/component/DrawableDef.hpp similarity index 100% rename from source/common/scene/DrawableDef.hpp rename to source/common/scene/component/DrawableDef.hpp diff --git a/source/common/scene/InventoryCubeDef.hpp b/source/common/scene/component/InventoryCubeDef.hpp similarity index 100% rename from source/common/scene/InventoryCubeDef.hpp rename to source/common/scene/component/InventoryCubeDef.hpp diff --git a/source/common/scene/NetworkComponent.hpp b/source/common/scene/component/NetworkComponent.hpp similarity index 100% rename from source/common/scene/NetworkComponent.hpp rename to source/common/scene/component/NetworkComponent.hpp diff --git a/source/common/scene/PositionComponent.hpp b/source/common/scene/component/PositionComponent.hpp similarity index 100% rename from source/common/scene/PositionComponent.hpp rename to source/common/scene/component/PositionComponent.hpp diff --git a/source/common/scene/RotationComponent.hpp b/source/common/scene/component/RotationComponent.hpp similarity index 100% rename from source/common/scene/RotationComponent.hpp rename to source/common/scene/component/RotationComponent.hpp diff --git a/source/common/scene/AbstractController.hpp b/source/common/scene/controller/AbstractController.hpp similarity index 100% rename from source/common/scene/AbstractController.hpp rename to source/common/scene/controller/AbstractController.hpp diff --git a/source/server/scene/CollisionController.cpp b/source/server/scene/controller/CollisionController.cpp similarity index 100% rename from source/server/scene/CollisionController.cpp rename to source/server/scene/controller/CollisionController.cpp diff --git a/source/server/scene/CollisionController.hpp b/source/server/scene/controller/CollisionController.hpp similarity index 100% rename from source/server/scene/CollisionController.hpp rename to source/server/scene/controller/CollisionController.hpp diff --git a/source/server/scene/NetworkController.cpp b/source/server/scene/controller/NetworkController.cpp similarity index 100% rename from source/server/scene/NetworkController.cpp rename to source/server/scene/controller/NetworkController.cpp diff --git a/source/server/scene/NetworkController.hpp b/source/server/scene/controller/NetworkController.hpp similarity index 100% rename from source/server/scene/NetworkController.hpp rename to source/server/scene/controller/NetworkController.hpp diff --git a/source/server/scene/ItemDropFactory.cpp b/source/server/scene/factory/ItemDropFactory.cpp similarity index 100% rename from source/server/scene/ItemDropFactory.cpp rename to source/server/scene/factory/ItemDropFactory.cpp diff --git a/source/server/scene/ItemDropFactory.hpp b/source/server/scene/factory/ItemDropFactory.hpp similarity index 100% rename from source/server/scene/ItemDropFactory.hpp rename to source/server/scene/factory/ItemDropFactory.hpp From 96a0cf6312c3b44f23cf2f44682c314dadda30be Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 19:09:37 +0200 Subject: [PATCH 35/38] [CollisionController] Now checks the dimension too. --- source/client/network/ClientCommandHandler.cpp | 1 + source/client/world/ClientWorld.cpp | 1 + .../common/scene/component/PositionComponent.hpp | 4 ++++ source/server/network/ServerCommandHandler.cpp | 6 ++++++ .../scene/controller/CollisionController.cpp | 16 +++++++++------- source/server/scene/factory/ItemDropFactory.cpp | 4 ++-- source/server/scene/factory/ItemDropFactory.hpp | 2 +- source/server/world/ServerWorld.cpp | 3 +-- 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 663878857..1f4988972 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -209,6 +209,7 @@ void ClientCommandHandler::setupCallbacks() { m_player.setPosition(x, y, z); m_world.clear(); m_world.updateSky(dimension); + m_entityMap.clear(); } }); diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index 9cf88c0ba..853288d10 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -102,6 +102,7 @@ void ClientWorld::checkPlayerChunk(double playerX, double playerY, double player void ClientWorld::clear() { m_chunks.clear(); + m_scene.registry().clear(); } void ClientWorld::updateSky(u16 dimensionID) { diff --git a/source/common/scene/component/PositionComponent.hpp b/source/common/scene/component/PositionComponent.hpp index c6b7ab9ea..e85e2c90d 100644 --- a/source/common/scene/component/PositionComponent.hpp +++ b/source/common/scene/component/PositionComponent.hpp @@ -27,11 +27,15 @@ #ifndef POSITIONCOMPONENT_HPP_ #define POSITIONCOMPONENT_HPP_ +#include + struct PositionComponent { double x = 0; double y = 0; double z = 0; + u16 dimension = 0; + bool isUpdated = true; }; diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 305ea40a9..46ed261b3 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -100,6 +100,12 @@ void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, m_server.sendToAllClients(packet); else client->tcpSocket->send(packet); + + // FIXME: sendPlayerChangeDimension shouldn't be exposed to Lua + // Instead, there should be a world.changePlayerDimension function that sends + // the packet above + the entities (instead of doing that here) + if (client) + m_worldController.getWorld(dimension).scene().sendEntities(*client); } void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client) const { diff --git a/source/server/scene/controller/CollisionController.cpp b/source/server/scene/controller/CollisionController.cpp index 984c031a7..f5373f06d 100644 --- a/source/server/scene/controller/CollisionController.cpp +++ b/source/server/scene/controller/CollisionController.cpp @@ -35,13 +35,15 @@ void CollisionController::update(entt::registry ®istry) { // FIXME: This should be a callback in a CollisionComponent registry.view().each([&](auto entity, auto &position, auto &box, auto &itemStack, auto &network) { for (auto &it : m_players) { - gk::DoubleBox hitbox = box + gk::Vector3d{position.x, position.y, position.z}; - gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; - if (hitbox.intersects(playerHitbox)) { - it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); - m_server->sendPlayerInvUpdate(it.second.clientID(), &it.second.client()); - m_server->sendEntityDespawn(network.entityID); - registry.destroy(entity); + if (it.second.dimension() == position.dimension) { + gk::DoubleBox hitbox = box + gk::Vector3d{position.x, position.y, position.z}; + gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; + if (hitbox.intersects(playerHitbox)) { + it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + m_server->sendPlayerInvUpdate(it.second.clientID(), &it.second.client()); + m_server->sendEntityDespawn(network.entityID); + registry.destroy(entity); + } } } }); diff --git a/source/server/scene/factory/ItemDropFactory.cpp b/source/server/scene/factory/ItemDropFactory.cpp index ebb4343e9..a38d5c40b 100644 --- a/source/server/scene/factory/ItemDropFactory.cpp +++ b/source/server/scene/factory/ItemDropFactory.cpp @@ -34,9 +34,9 @@ #include "Registry.hpp" #include "RotationComponent.hpp" -void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount) { +void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, u16 dimension, const std::string &itemID, u16 amount) { auto entity = registry.create(); - registry.emplace(entity, x, y, z); + registry.emplace(entity, x, y, z, dimension); registry.emplace(entity); registry.emplace(entity, entity); diff --git a/source/server/scene/factory/ItemDropFactory.hpp b/source/server/scene/factory/ItemDropFactory.hpp index 9970c48da..8816f87b9 100644 --- a/source/server/scene/factory/ItemDropFactory.hpp +++ b/source/server/scene/factory/ItemDropFactory.hpp @@ -33,7 +33,7 @@ class ItemDropFactory { public: - static void create(entt::registry ®istry, double x, double y, double z, const std::string &itemID, u16 amount = 1); + static void create(entt::registry ®istry, double x, double y, double z, u16 dimension, const std::string &itemID, u16 amount = 1); }; #endif // ITEMDROPFACTORY_HPP_ diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index fa7408f94..850f27bbc 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -153,11 +153,10 @@ void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) void ServerWorld::onBlockDigged(int x, int y, int z, const Block &block, ServerPlayer &player) { if (ServerConfig::useItemDrops) { - ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, m_dimension.id(), block.getItemDrop().item().stringID(), block.getItemDrop().amount()); } else { player.inventory().addStack(block.getItemDrop().item().stringID(), block.getItemDrop().amount()); - // gkDebug() << player.inventory().getStack(2, 2).item().stringID() << player.inventory().getStack(2, 2).amount(); m_server->sendPlayerInvUpdate(player.clientID(), &player.client()); } } From 4e9e039dcb0d081726d882cad3a4e522f062ec2c Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 19:13:31 +0200 Subject: [PATCH 36/38] [InventoryCube] Small fix. --- source/client/gui/InventoryCube.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client/gui/InventoryCube.cpp b/source/client/gui/InventoryCube.cpp index 0c228d11b..ee0f88146 100644 --- a/source/client/gui/InventoryCube.cpp +++ b/source/client/gui/InventoryCube.cpp @@ -76,7 +76,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) { const gk::FloatBox &boundingBox = block.boundingBox(); - constexpr s8f faceValue[nFaces]{2, 2, 4, 4, -1, 3}; + constexpr s8f faceValue[nFaces]{2, 2, 4, 4, 3, 3}; for (u8 f = 0; f < nFaces; ++f) { // Calculate UV's From c151847ee4371459d6fdb88d197604666ab01599 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 20:50:39 +0200 Subject: [PATCH 37/38] Windows-related fixes. --- CMakeLists.txt | 3 ++- source/client/CMakeLists.txt | 2 +- source/server/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 520578000..718983b06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,8 +191,9 @@ include_directories(external/gamekit/include) set(SFML_BUILD_AUDIO FALSE) set(SFML_BUILD_GRAPHICS FALSE) set(SFML_BUILD_WINDOW FALSE) +set(SFML_STATIC TRUE) -set(BUILD_SHARED_LIBS ON) +set(BUILD_SHARED_LIBS OFF) add_subdirectory(external/SFML) diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index 578c9f29e..be66b4e7f 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -50,6 +50,7 @@ endif () target_link_libraries(${PROJECT_NAME} ${CMAKE_PROJECT_NAME}_server_lib ${CMAKE_PROJECT_NAME}_common + gamekit ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -60,6 +61,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - gamekit ${UNIX_LIBS}) diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index 2bcedf3ba..3798b3f6a 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -69,6 +69,7 @@ target_link_libraries(${PROJECT_NAME}_lib sfml-system sfml-network) target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib ${CMAKE_PROJECT_NAME}_common + gamekit ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -79,6 +80,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - gamekit ${UNIX_LIBS}) From 9048b536969fed5b8a26af59457640d2fe296d00 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 29 Apr 2020 21:17:06 +0200 Subject: [PATCH 38/38] [ServerConfig] 'useItemDrops' is now 'false' by default. --- server_config.example.lua | 2 +- source/server/core/ServerConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_config.example.lua b/server_config.example.lua index 6a0c7986c..428e93514 100644 --- a/server_config.example.lua +++ b/server_config.example.lua @@ -3,5 +3,5 @@ -- See source/server/core/ServerConfig.cpp for more details -- Gameplay -useItemDrops = true +useItemDrops = false diff --git a/source/server/core/ServerConfig.cpp b/source/server/core/ServerConfig.cpp index 3939fb12b..e8fd637c0 100644 --- a/source/server/core/ServerConfig.cpp +++ b/source/server/core/ServerConfig.cpp @@ -27,7 +27,7 @@ #include "ServerConfig.hpp" // Gameplay -bool ServerConfig::useItemDrops = true; +bool ServerConfig::useItemDrops = false; #include #include