[Scene] Added. [external/entt] Added.

This commit is contained in:
Quentin Bazin 2020-04-04 20:26:02 +02:00
parent 6f888e826d
commit 5815549f7d
36 changed files with 9568 additions and 21 deletions

16
external/entt/config/config.h vendored Normal file
View File

@ -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

111
external/entt/core/algorithm.hpp vendored Normal file
View File

@ -0,0 +1,111 @@
#ifndef ENTT_CORE_ALGORITHM_HPP
#define ENTT_CORE_ALGORITHM_HPP
#include <functional>
#include <algorithm>
#include <utility>
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.<br/>
* 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 It, typename Compare = std::less<>, typename... Args>
void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const {
std::sort(std::forward<Args>(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<typename It, typename Compare = std::less<>>
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<typename It, typename Compare = std::less<>>
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

53
external/entt/core/family.hpp vendored Normal file
View File

@ -0,0 +1,53 @@
#ifndef ENTT_CORE_FAMILY_HPP
#define ENTT_CORE_FAMILY_HPP
#include <type_traits>
#include <cstddef>
#include <atomic>
#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<typename...>
class Family {
static std::atomic<std::size_t> identifier;
template<typename...>
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<typename... Type>
inline static family_type type() ENTT_NOEXCEPT {
return family<std::decay_t<Type>...>();
}
};
template<typename... Types>
std::atomic<std::size_t> Family<Types...>::identifier{};
}
#endif // ENTT_CORE_FAMILY_HPP

121
external/entt/core/hashed_string.hpp vendored Normal file
View File

@ -0,0 +1,121 @@
#ifndef ENTT_CORE_HASHED_STRING_HPP
#define ENTT_CORE_HASHED_STRING_HPP
#include <cstddef>
#include <cstdint>
#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.<br/>
* 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;
// FowlerNollVo 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.<br/>
* Example of use:
* @code{.cpp}
* HashedString sh{"my.png"};
* @endcode
*
* @tparam N Number of characters of the identifier.
* @param str Human-readable identifer.
*/
template<std::size_t N>
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

104
external/entt/core/ident.hpp vendored Normal file
View File

@ -0,0 +1,104 @@
#ifndef ENTT_CORE_IDENT_HPP
#define ENTT_CORE_IDENT_HPP
#include <type_traits>
#include <cstddef>
#include <utility>
#include <tuple>
#include "../config/config.h"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename...>
struct IsPartOf;
template<typename Type, typename Current, typename... Other>
struct IsPartOf<Type, Current, Other...>: std::conditional_t<std::is_same<Type, Current>::value, std::true_type, IsPartOf<Type, Other...>> {};
template<typename Type>
struct IsPartOf<Type>: 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<AType, AnotherType>;
*
* switch(aTypeIdentifier) {
* case ID::get<AType>():
* // ...
* break;
* case ID::get<AnotherType>():
* // ...
* break;
* default:
* // ...
* }
* @endcode
*
* @tparam Types List of types for which to generate identifiers.
*/
template<typename... Types>
class Identifier final {
using tuple_type = std::tuple<std::decay_t<Types>...>;
template<typename Type, std::size_t... Indexes>
static constexpr std::size_t get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
static_assert(internal::IsPartOf<Type, Types...>::value, "!");
std::size_t max{};
using accumulator_type = std::size_t[];
accumulator_type accumulator = { (max = std::is_same<Type, std::tuple_element_t<Indexes, tuple_type>>::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<typename Type>
static constexpr identifier_type get() ENTT_NOEXCEPT {
return get<std::decay_t<Type>>(std::make_index_sequence<sizeof...(Types)>{});
}
};
}
#endif // ENTT_CORE_IDENT_HPP

61
external/entt/core/monostate.hpp vendored Normal file
View File

@ -0,0 +1,61 @@
#ifndef ENTT_CORE_MONOSTATE_HPP
#define ENTT_CORE_MONOSTATE_HPP
#include <atomic>
#include <cassert>
#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.<br/>
* 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<HashedString::hash_type>
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<typename Type>
void operator=(Type val) const ENTT_NOEXCEPT {
Monostate::value<Type> = 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<typename Type>
operator Type() const ENTT_NOEXCEPT {
return Monostate::value<Type>;
}
private:
template<typename Type>
static std::atomic<Type> value;
};
template<HashedString::hash_type ID>
template<typename Type>
std::atomic<Type> Monostate<ID>::value{};
}
#endif // ENTT_CORE_MONOSTATE_HPP

243
external/entt/entity/actor.hpp vendored Normal file
View File

@ -0,0 +1,243 @@
#ifndef ENTT_ENTITY_ACTOR_HPP
#define ENTT_ENTITY_ACTOR_HPP
#include <cassert>
#include <utility>
#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<typename Entity>
struct Actor {
/*! @brief Type of registry used internally. */
using registry_type = Registry<Entity>;
/*! @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<Entity> &reg)
: reg{&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<typename Tag, typename... Args>
Tag & assign(tag_t, Args &&... args) {
return (reg->template remove<Tag>(), reg->template assign<Tag>(tag_t{}, entt, std::forward<Args>(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.<br/>
* 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<typename Component, typename... Args>
Component & assign(Args &&... args) {
return reg->template accommodate<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given tag from an actor.
* @tparam Tag Type of the tag to remove.
*/
template<typename Tag>
void remove(tag_t) {
assert(has<Tag>(tag_t{}));
reg->template remove<Tag>();
}
/**
* @brief Removes the given component from an actor.
* @tparam Component Type of the component to remove.
*/
template<typename Component>
void remove() {
reg->template remove<Component>(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<typename Tag>
bool has(tag_t) const ENTT_NOEXCEPT {
return (reg->template has<Tag>() && (reg->template attachee<Tag>() == 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<typename Component>
bool has() const ENTT_NOEXCEPT {
return reg->template has<Component>(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<typename Tag>
const Tag & get(tag_t) const ENTT_NOEXCEPT {
assert(has<Tag>(tag_t{}));
return reg->template get<Tag>();
}
/**
* @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<typename Tag>
inline Tag & get(tag_t) ENTT_NOEXCEPT {
return const_cast<Tag &>(const_cast<const Actor *>(this)->get<Tag>(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<typename Component>
const Component & get() const ENTT_NOEXCEPT {
return reg->template get<Component>(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<typename Component>
inline Component & get() ENTT_NOEXCEPT {
return const_cast<Component &>(const_cast<const Actor *>(this)->get<Component>());
}
/**
* @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<registry_type &>(const_cast<const Actor *>(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.<br/>
* Users should have a really good reason to choose something different.
*/
using DefaultActor = Actor<DefaultRegistry::entity_type>;
}
#endif // ENTT_ENTITY_ACTOR_HPP

230
external/entt/entity/attachee.hpp vendored Normal file
View File

@ -0,0 +1,230 @@
#ifndef ENTT_ENTITY_ATTACHEE_HPP
#define ENTT_ENTITY_ATTACHEE_HPP
#include <cassert>
#include <utility>
#include <type_traits>
#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<typename...>
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<typename Entity>
class Attachee<Entity> {
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.<br/>
* 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.<br/>
* 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<Entity>
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Type Type of object assigned to the entity.
*/
template<typename Entity, typename Type>
class Attachee<Entity, Type>: public Attachee<Entity> {
using underlying_type = Attachee<Entity>;
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<Type *>(&storage)->~Type();
}
}
/**
* @brief Returns the object associated to an attachee.
*
* @warning
* Attempting to query an empty attachee results in undefined behavior.<br/>
* 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<const Type *>(&storage);
}
/**
* @brief Returns the object associated to an attachee.
*
* @warning
* Attempting to query an empty attachee results in undefined behavior.<br/>
* 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<Type &>(const_cast<const Attachee *>(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.<br/>
* 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<typename... Args>
Type & construct(entity_type entity, Args &&... args) ENTT_NOEXCEPT {
underlying_type::construct(entity);
new (&storage) Type{std::forward<Args>(args)...};
return *reinterpret_cast<Type *>(&storage);
}
/**
* @brief Removes an entity from an attachee and destroies its object.
*
* @warning
* Attempting to free an empty attachee results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* attachee is already empty.
*/
void destroy() ENTT_NOEXCEPT override {
reinterpret_cast<Type *>(&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.<br/>
* 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<sizeof(Type), alignof(Type)> storage;
};
}
#endif // ENTT_ENTITY_ATTACHEE_HPP

84
external/entt/entity/entity.hpp vendored Normal file
View File

@ -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<typename Entity>
constexpr operator Entity() const ENTT_NOEXCEPT {
using traits_type = entt::entt_traits<Entity>;
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<typename Entity>
constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
return entity == static_cast<Entity>(*this);
}
template<typename Entity>
constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
return entity != static_cast<Entity>(*this);
}
};
template<typename Entity>
constexpr bool operator==(const Entity entity, Null null) ENTT_NOEXCEPT {
return null == entity;
}
template<typename Entity>
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

102
external/entt/entity/entt_traits.hpp vendored Normal file
View File

@ -0,0 +1,102 @@
#ifndef ENTT_ENTITY_ENTT_TRAITS_HPP
#define ENTT_ENTITY_ENTT_TRAITS_HPP
#include <cstdint>
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<typename>
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<std::uint16_t> {
/*! @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<std::uint32_t> {
/*! @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<std::uint64_t> {
/*! @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

105
external/entt/entity/helper.hpp vendored Normal file
View File

@ -0,0 +1,105 @@
#ifndef ENTT_ENTITY_HELPER_HPP
#define ENTT_ENTITY_HELPER_HPP
#include <type_traits>
#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.<br/>
* 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<typename Entity, typename... Component>
void dependency(Registry<Entity> &registry, const Entity entity) {
using accumulator_type = int[];
accumulator_type accumulator = { ((registry.template has<Component>(entity) ? void() : (registry.template assign<Component>(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<AType, AnotherType>(registry.construction<MyType>());
* @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<typename... Dependency, typename Entity>
inline void connect(Sink<void(Registry<Entity> &, const Entity)> sink) {
sink.template connect<dependency<Entity, Dependency...>>();
}
/**
* @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<AType, AnotherType>(registry.construction<MyType>());
* @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<typename... Dependency, typename Entity>
inline void disconnect(Sink<void(Registry<Entity> &, const Entity)> sink) {
sink.template disconnect<dependency<Entity, Dependency...>>();
}
/**
* @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.<br/>
* As an example and where the user defined literal for hashed strings hasn't
* been changed:
* @code{.cpp}
* entt::DefaultRegistry registry;
* registry.assign<entt::label<"enemy"_hs>>(entity);
* @endcode
*
* @tparam Value The numeric representation of an instance of hashed string.
*/
template<typename HashedString::hash_type Value>
using label = std::integral_constant<typename HashedString::hash_type, Value>;
}
#endif // ENTT_ENTITY_HELPER_HPP

497
external/entt/entity/prototype.hpp vendored Normal file
View File

@ -0,0 +1,497 @@
#ifndef ENTT_ENTITY_PROTOTYPE_HPP
#define ENTT_ENTITY_PROTOTYPE_HPP
#include <tuple>
#include <utility>
#include <cstddef>
#include <type_traits>
#include <unordered_map>
#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.<br/>
* 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<typename Entity>
class Prototype final {
using basic_fn_type = void(const Prototype &, Registry<Entity> &, const Entity);
using component_type = typename Registry<Entity>::component_type;
template<typename Component>
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<Entity>;
/*! @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<Entity> &registry)
: registry{&registry},
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<typename Component, typename... Args>
Component & set(Args &&... args) {
basic_fn_type *accommodate = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
const auto &wrapper = prototype.registry->template get<Wrapper<Component>>(prototype.entity);
other.template accommodate<Component>(dst, wrapper.component);
};
basic_fn_type *assign = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
if(!other.template has<Component>(dst)) {
const auto &wrapper = prototype.registry->template get<Wrapper<Component>>(prototype.entity);
other.template assign<Component>(dst, wrapper.component);
}
};
handlers[registry->template type<Component>()] = Handler{accommodate, assign};
auto &wrapper = registry->template accommodate<Wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
return wrapper.component;
}
/**
* @brief Removes the given component from a prototype.
* @tparam Component Type of component to remove.
*/
template<typename Component>
void unset() ENTT_NOEXCEPT {
registry->template reset<Wrapper<Component>>(entity);
handlers.erase(registry->template type<Component>());
}
/**
* @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<typename... Component>
bool has() const ENTT_NOEXCEPT {
return registry->template has<Wrapper<Component>...>(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.<br/>
* 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<typename Component>
const Component & get() const ENTT_NOEXCEPT {
return registry->template get<Wrapper<Component>>(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.<br/>
* 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<typename Component>
inline Component & get() ENTT_NOEXCEPT {
return const_cast<Component &>(const_cast<const Prototype *>(this)->get<Component>());
}
/**
* @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.<br/>
* 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<typename... Component>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
get() const ENTT_NOEXCEPT {
return std::tuple<const Component &...>{get<Component>()...};
}
/**
* @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.<br/>
* 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<typename... Component>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
get() ENTT_NOEXCEPT {
return std::tuple<Component &...>{get<Component>()...};
}
/**
* @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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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<component_type, Handler> handlers;
Registry<Entity> *registry;
entity_type entity;
};
/**
* @brief Default prototype
*
* The default prototype is the best choice for almost all the
* applications.<br/>
* Users should have a really good reason to choose something different.
*/
using DefaultPrototype = Prototype<DefaultRegistry::entity_type>;
}
#endif // ENTT_ENTITY_PROTOTYPE_HPP

1662
external/entt/entity/registry.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

724
external/entt/entity/snapshot.hpp vendored Normal file
View File

@ -0,0 +1,724 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <array>
#include <cstddef>
#include <utility>
#include <cassert>
#include <iterator>
#include <type_traits>
#include <unordered_map>
#include "../config/config.h"
#include "entt_traits.hpp"
#include "utility.hpp"
namespace entt {
/**
* @brief Forward declaration of the registry class.
*/
template<typename>
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.<br/>
* 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<typename Entity>
class Snapshot final {
/*! @brief A registry is allowed to create snapshots. */
friend class Registry<Entity>;
using follow_fn_type = Entity(const Registry<Entity> &, const Entity);
Snapshot(const Registry<Entity> &registry, Entity seed, follow_fn_type *follow) ENTT_NOEXCEPT
: registry{registry},
seed{seed},
follow{follow}
{}
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
archive(static_cast<Entity>(sz));
while(first != last) {
const auto entity = *(first++);
if(registry.template has<Component>(entity)) {
archive(entity, registry.template get<Component>(entity));
}
}
}
template<typename... Component, typename Archive, typename It, std::size_t... Indexes>
void component(Archive &archive, It first, It last, std::index_sequence<Indexes...>) const {
std::array<std::size_t, sizeof...(Indexes)> size{};
auto begin = first;
while(begin != last) {
const auto entity = *(begin++);
using accumulator_type = std::size_t[];
accumulator_type accumulator = { (registry.template has<Component>(entity) ? ++size[Indexes] : size[Indexes])... };
(void)accumulator;
}
using accumulator_type = int[];
accumulator_type accumulator = { (get<Component>(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<typename Archive>
const Snapshot & entities(Archive &archive) const {
archive(static_cast<Entity>(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<typename Archive>
const Snapshot & destroyed(Archive &archive) const {
auto size = registry.size() - registry.alive();
archive(static_cast<Entity>(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<typename Component, typename Archive>
const Snapshot & component(Archive &archive) const {
const auto sz = registry.template size<Component>();
const auto *entities = registry.template data<Component>();
archive(static_cast<Entity>(sz));
for(std::remove_const_t<decltype(sz)> i{}; i < sz; ++i) {
const auto entity = entities[i];
archive(entity, registry.template get<Component>(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<typename... Component, typename Archive>
std::enable_if_t<(sizeof...(Component) > 1), const Snapshot &>
component(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (component<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<typename... Component, typename Archive, typename It>
const Snapshot & component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::make_index_sequence<sizeof...(Component)>{});
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<typename Tag, typename Archive>
const Snapshot & tag(Archive &archive) const {
const bool has = registry.template has<Tag>();
// numerical length is forced for tags to facilitate loading
archive(has ? Entity(1): Entity{});
if(has) {
archive(registry.template attachee<Tag>(), registry.template get<Tag>());
}
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<typename... Tag, typename Archive>
std::enable_if_t<(sizeof...(Tag) > 1), const Snapshot &>
tag(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (tag<Tag>(archive), 0)... };
(void)accumulator;
return *this;
}
private:
const Registry<Entity> &registry;
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.<br/>
* 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<typename Entity>
class SnapshotLoader final {
/*! @brief A registry is allowed to create snapshot loaders. */
friend class Registry<Entity>;
using assure_fn_type = void(Registry<Entity> &, const Entity, const bool);
SnapshotLoader(Registry<Entity> &registry, 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<typename Archive>
void assure(Archive &archive, bool destroyed) const {
Entity length{};
archive(length);
while(length--) {
Entity entity{};
archive(entity);
assure_fn(registry, entity, destroyed);
}
}
template<typename Type, typename Archive, typename... Args>
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<Type>(args..., entity, static_cast<const Type &>(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<typename Archive>
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<typename Archive>
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<typename... Component, typename Archive>
const SnapshotLoader & component(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (assign<Component>(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<typename... Tag, typename Archive>
const SnapshotLoader & tag(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (assign<Tag>(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.<br/>
* 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<Entity> &registry;
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.<br/>
* Identifiers that entities originally had are not transferred to the target.
* Instead, the loader maps remote identifiers to local ones while restoring a
* snapshot.<br/>
* 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<typename Entity>
class ContinuousLoader final {
using traits_type = entt_traits<Entity>;
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<typename Type, typename Member>
std::enable_if_t<std::is_same<Member, Entity>::value>
update(Type &instance, Member Type:: *member) {
instance.*member = map(instance.*member);
}
template<typename Type, typename Member>
std::enable_if_t<std::is_same<typename std::iterator_traits<typename Member::iterator>::value_type, Entity>::value>
update(Type &instance, Member Type:: *member) {
for(auto &entity: instance.*member) {
entity = map(entity);
}
}
template<typename Other, typename Type, typename Member>
std::enable_if_t<!std::is_same<Other, Type>::value>
update(Other &, Member Type:: *) {}
template<typename Archive>
void assure(Archive &archive, void(ContinuousLoader:: *member)(Entity)) {
Entity length{};
archive(length);
while(length--) {
Entity entity{};
archive(entity);
(this->*member)(entity);
}
}
template<typename Component>
void reset() {
for(auto &&ref: remloc) {
const auto local = ref.second.first;
if(registry.valid(local)) {
registry.template reset<Component>(local);
}
}
}
template<typename Other, typename Archive, typename Func, typename... Type, typename... Member>
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<entity_type> &registry) 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<typename Archive>
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<typename Archive>
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.<br/>
* 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<typename... Component, typename Archive, typename... Type, typename... Member>
ContinuousLoader & component(Archive &archive, Member Type:: *... member) {
auto apply = [this](const auto entity, const auto &component) {
registry.template accommodate<std::decay_t<decltype(component)>>(entity, component);
};
using accumulator_type = int[];
accumulator_type accumulator = { 0, (reset<Component>(), assign<Component>(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.<br/>
* 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<typename... Tag, typename Archive, typename... Type, typename... Member>
ContinuousLoader & tag(Archive &archive, Member Type:: *... member) {
auto apply = [this](const auto entity, const auto &tag) {
registry.template assign<std::decay_t<decltype(tag)>>(tag_t{}, entity, tag);
};
using accumulator_type = int[];
accumulator_type accumulator = { 0, (registry.template remove<Tag>(), assign<Tag>(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.<br/>
* 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.<br/>
* 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<Entity, std::pair<Entity, bool>> remloc;
Registry<Entity> &registry;
};
}
#endif // ENTT_ENTITY_SNAPSHOT_HPP

1120
external/entt/entity/sparse_set.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

23
external/entt/entity/utility.hpp vendored Normal file
View File

@ -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

1889
external/entt/entity/view.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

26
external/entt/entt.hpp vendored Normal file
View File

@ -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"

116
external/entt/locator/locator.hpp vendored Normal file
View File

@ -0,0 +1,116 @@
#ifndef ENTT_LOCATOR_LOCATOR_HPP
#define ENTT_LOCATOR_LOCATOR_HPP
#include <memory>
#include <utility>
#include <cassert>
#include "../config/config.h"
namespace entt {
/**
* @brief Service locator, nothing more.
*
* A service locator can be used to do what it promises: locate services.<br/>
* 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<typename Service>
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<bool>(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<Service> 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<typename Impl = Service, typename... Args>
inline static void set(Args &&... args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
}
/**
* @brief Sets or replaces a service.
* @param ptr Service to use to replace the current one.
*/
inline static void set(std::shared_ptr<Service> ptr) {
assert(static_cast<bool>(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> service;
};
template<typename Service>
std::shared_ptr<Service> ServiceLocator<Service>::service{};
}
#endif // ENTT_LOCATOR_LOCATOR_HPP

339
external/entt/process/process.hpp vendored Normal file
View File

@ -0,0 +1,339 @@
#ifndef ENTT_PROCESS_PROCESS_HPP
#define ENTT_PROCESS_PROCESS_HPP
#include <type_traits>
#include <functional>
#include <utility>
#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.<br/>
* 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<typename Derived, typename Delta>
class Process {
enum class State: unsigned int {
UNINITIALIZED = 0,
RUNNING,
PAUSED,
SUCCEEDED,
FAILED,
ABORTED,
FINISHED
};
template<State state>
using tag = std::integral_constant<State, state>;
template<typename Target = Derived>
auto tick(int, tag<State::UNINITIALIZED>, void *data)
-> decltype(std::declval<Target>().init(data)) {
static_cast<Target *>(this)->init(data);
}
template<typename Target = Derived>
auto tick(int, tag<State::RUNNING>, Delta delta, void *data)
-> decltype(std::declval<Target>().update(delta, data)) {
static_cast<Target *>(this)->update(delta, data);
}
template<typename Target = Derived>
auto tick(int, tag<State::SUCCEEDED>)
-> decltype(std::declval<Target>().succeeded()) {
static_cast<Target *>(this)->succeeded();
}
template<typename Target = Derived>
auto tick(int, tag<State::FAILED>)
-> decltype(std::declval<Target>().failed()) {
static_cast<Target *>(this)->failed();
}
template<typename Target = Derived>
auto tick(int, tag<State::ABORTED>)
-> decltype(std::declval<Target>().aborted()) {
static_cast<Target *>(this)->aborted();
}
template<State S, typename... Args>
void tick(char, tag<S>, 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<Process, Derived>::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<State::UNINITIALIZED>{}, data);
current = State::RUNNING;
// no break on purpose, tasks are executed immediately
case State::RUNNING:
tick(0, tag<State::RUNNING>{}, 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<State::SUCCEEDED>{});
current = State::FINISHED;
break;
case State::FAILED:
tick(0, tag<State::FAILED>{});
current = State::FINISHED;
stopped = true;
break;
case State::ABORTED:
tick(0, tag<State::ABORTED>{});
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.<br/>
* 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<typename Func, typename Delta>
struct ProcessAdaptor: Process<ProcessAdaptor<Func, Delta>, 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<typename... Args>
ProcessAdaptor(Args &&... args)
: Func{std::forward<Args>(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

311
external/entt/process/scheduler.hpp vendored Normal file
View File

@ -0,0 +1,311 @@
#ifndef ENTT_PROCESS_SCHEDULER_HPP
#define ENTT_PROCESS_SCHEDULER_HPP
#include <vector>
#include <memory>
#include <utility>
#include <algorithm>
#include <type_traits>
#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.<br/>
* 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<MyProcess>(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<typename Delta>
class Scheduler final {
struct ProcessHandler final {
using instance_type = std::unique_ptr<void, void(*)(void *)>;
using update_fn_type = bool(ProcessHandler &, Delta, void *);
using abort_fn_type = void(ProcessHandler &, bool);
using next_type = std::unique_ptr<ProcessHandler>;
instance_type instance;
update_fn_type *update;
abort_fn_type *abort;
next_type next;
};
struct Then final {
Then(ProcessHandler *handler)
: handler{handler}
{}
template<typename Proc, typename... Args>
decltype(auto) then(Args &&... args) && {
static_assert(std::is_base_of<Process<Proc, Delta>, Proc>::value, "!");
handler = Scheduler::then<Proc>(handler, std::forward<Args>(args)...);
return std::move(*this);
}
template<typename Func>
decltype(auto) then(Func &&func) && {
using Proc = ProcessAdaptor<std::decay_t<Func>, Delta>;
return std::move(*this).template then<Proc>(std::forward<Func>(func));
}
private:
ProcessHandler *handler;
};
template<typename Proc>
static bool update(ProcessHandler &handler, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(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<typename Proc>
static void abort(ProcessHandler &handler, const bool immediately) {
static_cast<Proc *>(handler.instance.get())->abort(immediately);
}
template<typename Proc>
static void deleter(void *proc) {
delete static_cast<Proc *>(proc);
}
template<typename Proc, typename... Args>
static auto then(ProcessHandler *handler, Args &&... args) {
if(handler) {
auto proc = typename ProcessHandler::instance_type{new Proc{std::forward<Args>(args)...}, &Scheduler::deleter<Proc>};
handler->next.reset(new ProcessHandler{std::move(proc), &Scheduler::update<Proc>, &Scheduler::abort<Proc>, nullptr});
handler = handler->next.get();
}
return handler;
}
public:
/*! @brief Unsigned integer type. */
using size_type = typename std::vector<ProcessHandler>::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<MyProcess>(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<MyOtherProcess>();
* @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<typename Proc, typename... Args>
auto attach(Args &&... args) {
static_assert(std::is_base_of<Process<Proc, Delta>, Proc>::value, "!");
auto proc = typename ProcessHandler::instance_type{new Proc{std::forward<Args>(args)...}, &Scheduler::deleter<Proc>};
ProcessHandler handler{std::move(proc), &Scheduler::update<Proc>, &Scheduler::abort<Proc>, 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.<br/>
* 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<MyProcess>(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<typename Func>
auto attach(Func &&func) {
using Proc = ProcessAdaptor<std::decay_t<Func>, Delta>;
return attach<Proc>(std::forward<Func>(func));
}
/**
* @brief Updates all scheduled processes.
*
* All scheduled processes are executed in no specific order.<br/>
* 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.<br/>
* 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<ProcessHandler> handlers{};
};
}
#endif // ENTT_PROCESS_SCHEDULER_HPP

201
external/entt/resource/cache.hpp vendored Normal file
View File

@ -0,0 +1,201 @@
#ifndef ENTT_RESOURCE_CACHE_HPP
#define ENTT_RESOURCE_CACHE_HPP
#include <memory>
#include <utility>
#include <type_traits>
#include <unordered_map>
#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<typename Resource>
class ResourceCache {
using container_type = std::unordered_map<HashedString::hash_type, std::shared_ptr<Resource>>;
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<typename Loader, typename... Args>
bool load(const resource_type id, Args &&... args) {
static_assert(std::is_base_of<ResourceLoader<Loader, Resource>, Loader>::value, "!");
bool loaded = true;
if(resources.find(id) == resources.cend()) {
std::shared_ptr<Resource> resource = Loader{}.get(std::forward<Args>(args)...);
loaded = (static_cast<bool>(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<typename Loader, typename... Args>
bool reload(const resource_type id, Args &&... args) {
return (discard(id), load<Loader>(id, std::forward<Args>(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<typename Loader, typename... Args>
ResourceHandle<Resource> temp(Args &&... args) const {
return { Loader{}.get(std::forward<Args>(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<Resource> 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

116
external/entt/resource/handle.hpp vendored Normal file
View File

@ -0,0 +1,116 @@
#ifndef ENTT_RESOURCE_HANDLE_HPP
#define ENTT_RESOURCE_HANDLE_HPP
#include <memory>
#include <utility>
#include <cassert>
#include "../config/config.h"
namespace entt {
template<typename Resource>
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.<br/>
* 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<typename Resource>
class ResourceHandle final {
/*! @brief Resource handles are friends of their caches. */
friend class ResourceCache<Resource>;
ResourceHandle(std::shared_ptr<Resource> 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.<br/>
* 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<bool>(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.<br/>
* 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.<br/>
* 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.<br/>
* 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<bool>(resource));
return resource.get();
}
/**
* @brief Returns true if the handle contains a resource, false otherwise.
*/
explicit operator bool() const { return static_cast<bool>(resource); }
private:
std::shared_ptr<Resource> resource;
};
}
#endif // ENTT_RESOURCE_HANDLE_HPP

62
external/entt/resource/loader.hpp vendored Normal file
View File

@ -0,0 +1,62 @@
#ifndef ENTT_RESOURCE_LOADER_HPP
#define ENTT_RESOURCE_LOADER_HPP
#include <memory>
namespace entt {
template<typename Resource>
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.<br/>
* As an example:
*
* @code{.cpp}
* struct MyResource {};
*
* struct MyLoader: entt::ResourceLoader<MyLoader, MyResource> {
* std::shared_ptr<MyResource> load(int) const {
* // use the integer value somehow
* return std::make_shared<MyResource>();
* }
* };
* @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<typename Loader, typename Resource>
class ResourceLoader {
/*! @brief Resource loaders are friends of their caches. */
friend class ResourceCache<Resource>;
template<typename... Args>
std::shared_ptr<Resource> get(Args &&... args) const {
return static_cast<const Loader *>(this)->load(std::forward<Args>(args)...);
}
};
}
#endif // ENTT_RESOURCE_LOADER_HPP

166
external/entt/signal/delegate.hpp vendored Normal file
View File

@ -0,0 +1,166 @@
#ifndef ENTT_SIGNAL_DELEGATE_HPP
#define ENTT_SIGNAL_DELEGATE_HPP
#include <utility>
#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<typename>
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<typename Ret, typename... Args>
class Delegate<Ret(Args...)> final {
using proto_fn_type = Ret(void *, Args...);
using stub_type = std::pair<void *, proto_fn_type *>;
template<Ret(*Function)(Args...)>
static Ret proto(void *, Args... args) {
return (Function)(args...);
}
template<typename Class, Ret(Class:: *Member)(Args...) const>
static Ret proto(void *instance, Args... args) {
return (static_cast<const Class *>(instance)->*Member)(args...);
}
template<typename Class, Ret(Class:: *Member)(Args...)>
static Ret proto(void *instance, Args... args) {
return (static_cast<Class *>(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<Ret(*Function)(Args...)>
void connect() ENTT_NOEXCEPT {
stub = std::make_pair(nullptr, &proto<Function>);
}
/**
* @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<typename Class, Ret(Class:: *Member)(Args...) const>
void connect(Class *instance) ENTT_NOEXCEPT {
stub = std::make_pair(instance, &proto<Class, Member>);
}
/**
* @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<typename Class, Ret(Class:: *Member)(Args...)>
void connect(Class *instance) ENTT_NOEXCEPT {
stub = std::make_pair(instance, &proto<Class, Member>);
}
/**
* @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<Ret(Args...)> &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<typename Ret, typename... Args>
bool operator!=(const Delegate<Ret(Args...)> &lhs, const Delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
}
#endif // ENTT_SIGNAL_DELEGATE_HPP

188
external/entt/signal/dispatcher.hpp vendored Normal file
View File

@ -0,0 +1,188 @@
#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP
#include <vector>
#include <memory>
#include <utility>
#include <cstdint>
#include <algorithm>
#include <type_traits>
#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.<br/>
* 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<struct InternalDispatcherEventFamily>;
template<typename Class, typename Event>
using instance_type = typename SigH<void(const Event &)>::template instance_type<Class>;
struct BaseSignalWrapper {
virtual ~BaseSignalWrapper() = default;
virtual void publish() = 0;
};
template<typename Event>
struct SignalWrapper final: BaseSignalWrapper {
using sink_type = typename SigH<void(const Event &)>::sink_type;
void publish() override {
const auto &curr = current++;
current %= std::extent<decltype(events)>::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<typename... Args>
inline void trigger(Args &&... args) {
signal.publish({ std::forward<Args>(args)... });
}
template<typename... Args>
inline void enqueue(Args &&... args) {
events[current].push_back({ std::forward<Args>(args)... });
}
private:
SigH<void(const Event &)> signal{};
std::vector<Event> events[2];
int current{};
};
template<typename Event>
SignalWrapper<Event> & wrapper() {
const auto type = event_family::type<Event>();
if(!(type < wrappers.size())) {
wrappers.resize(type + 1);
}
if(!wrappers[type]) {
wrappers[type] = std::make_unique<SignalWrapper<Event>>();
}
return static_cast<SignalWrapper<Event> &>(*wrappers[type]);
}
public:
/*! @brief Type of sink for the given event. */
template<typename Event>
using sink_type = typename SignalWrapper<Event>::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<typename Event>
inline sink_type<Event> sink() ENTT_NOEXCEPT {
return wrapper<Event>().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<typename Event, typename... Args>
inline void trigger(Args &&... args) {
wrapper<Event>().trigger(std::forward<Args>(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<typename Event, typename... Args>
inline void enqueue(Args &&... args) {
wrapper<Event>().enqueue(std::forward<Args>(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<typename Event>
inline void update() {
wrapper<Event>().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<std::unique_ptr<BaseSignalWrapper>> wrappers;
};
}
#endif // ENTT_SIGNAL_DISPATCHER_HPP

336
external/entt/signal/emitter.hpp vendored Normal file
View File

@ -0,0 +1,336 @@
#ifndef ENTT_SIGNAL_EMITTER_HPP
#define ENTT_SIGNAL_EMITTER_HPP
#include <type_traits>
#include <functional>
#include <algorithm>
#include <utility>
#include <cstdint>
#include <memory>
#include <vector>
#include <list>
#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<MyEmitter> {
* // ...
* }
* ```
*
* 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.<br/>
* 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<typename Derived>
class Emitter {
using handler_family = Family<struct InternalEmitterHandlerFamily>;
struct BaseHandler {
virtual ~BaseHandler() = default;
virtual bool empty() const ENTT_NOEXCEPT = 0;
virtual void clear() ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct Handler final: BaseHandler {
using listener_type = std::function<void(const Event &, Derived &)>;
using element_type = std::pair<bool, listener_type>;
using container_type = std::list<element_type>;
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<typename Event>
Handler<Event> & handler() ENTT_NOEXCEPT {
const std::size_t family = handler_family::type<Event>();
if(!(family < handlers.size())) {
handlers.resize(family+1);
}
if(!handlers[family]) {
handlers[family] = std::make_unique<Handler<Event>>();
}
return static_cast<Handler<Event> &>(*handlers[family]);
}
public:
/** @brief Type of listeners accepted for the given event. */
template<typename Event>
using Listener = typename Handler<Event>::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.<br/>
* It can be used to break connections still in use.
*
* @tparam Event Type of event for which the connection is created.
*/
template<typename Event>
struct Connection final: private Handler<Event>::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<Event>::connection_type conn)
: Handler<Event>::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<Emitter<Derived>, 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<typename Event, typename... Args>
void publish(Args &&... args) {
handler<Event>().publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(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.<br/>
* 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<typename Event>
Connection<Event> on(Listener<Event> listener) {
return handler<Event>().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.<br/>
* 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<typename Event>
Connection<Event> once(Listener<Event> listener) {
return handler<Event>().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<typename Event>
void erase(Connection<Event> conn) ENTT_NOEXCEPT {
handler<Event>().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<typename Event>
void clear() ENTT_NOEXCEPT {
handler<Event>().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<typename Event>
bool empty() const ENTT_NOEXCEPT {
const std::size_t family = handler_family::type<Event>();
return (!(family < handlers.size()) ||
!handlers[family] ||
static_cast<Handler<Event> &>(*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<std::unique_ptr<BaseHandler>> handlers{};
};
}
#endif // ENTT_SIGNAL_EMITTER_HPP

426
external/entt/signal/sigh.hpp vendored Normal file
View File

@ -0,0 +1,426 @@
#ifndef ENTT_SIGNAL_SIGH_HPP
#define ENTT_SIGNAL_SIGH_HPP
#include <algorithm>
#include <utility>
#include <vector>
#include "../config/config.h"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
struct sigh_traits;
template<typename Ret, typename... Args>
struct sigh_traits<Ret(Args...)> {
using proto_fn_type = Ret(void *, Args...);
using call_type = std::pair<void *, proto_fn_type *>;
};
template<typename, typename>
struct Invoker;
template<typename Ret, typename... Args, typename Collector>
struct Invoker<Ret(Args...), Collector> {
using proto_fn_type = typename sigh_traits<Ret(Args...)>::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<typename... Args, typename Collector>
struct Invoker<void(Args...), Collector> {
using proto_fn_type = typename sigh_traits<void(Args...)>::proto_fn_type;
virtual ~Invoker() = default;
bool invoke(Collector &, proto_fn_type *proto, void *instance, Args... args) const {
return (proto(instance, args...), true);
}
};
template<typename Ret>
struct NullCollector final {
using result_type = Ret;
bool operator()(result_type) const ENTT_NOEXCEPT { return true; }
};
template<>
struct NullCollector<void> final {
using result_type = void;
bool operator()() const ENTT_NOEXCEPT { return true; }
};
template<typename>
struct DefaultCollector;
template<typename Ret, typename... Args>
struct DefaultCollector<Ret(Args...)> final {
using collector_type = NullCollector<Ret>;
};
template<typename Function>
using DefaultCollectorType = typename DefaultCollector<Function>::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<typename Function>
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<typename Function, typename Collector = internal::DefaultCollectorType<Function>>
class SigH;
/**
* @brief Sink implementation.
*
* A sink is an opaque object used to connect listeners to signals.<br/>
* 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<typename Ret, typename... Args>
class Sink<Ret(Args...)> final {
/*! @brief A signal is allowed to create sinks. */
template<typename, typename>
friend class SigH;
using call_type = typename internal::sigh_traits<Ret(Args...)>::call_type;
template<Ret(*Function)(Args...)>
static Ret proto(void *, Args... args) {
return (Function)(args...);
}
template<typename Class, Ret(Class:: *Member)(Args... args) const>
static Ret proto(void *instance, Args... args) {
return (static_cast<const Class *>(instance)->*Member)(args...);
}
template<typename Class, Ret(Class:: *Member)(Args... args)>
static Ret proto(void *instance, Args... args) {
return (static_cast<Class *>(instance)->*Member)(args...);
}
Sink(std::vector<call_type> *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<Ret(*Function)(Args...)>
void connect() {
disconnect<Function>();
calls->emplace_back(nullptr, &proto<Function>);
}
/**
* @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<typename Class, Ret(Class:: *Member)(Args...) const = &Class::receive>
void connect(Class *instance) {
disconnect<Class, Member>(instance);
calls->emplace_back(instance, &proto<Class, Member>);
}
/**
* @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<typename Class, Ret(Class:: *Member)(Args...) = &Class::receive>
void connect(Class *instance) {
disconnect<Class, Member>(instance);
calls->emplace_back(instance, &proto<Class, Member>);
}
/**
* @brief Disconnects a free function from a signal.
* @tparam Function A valid free function pointer.
*/
template<Ret(*Function)(Args...)>
void disconnect() {
call_type target{nullptr, &proto<Function>};
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<typename Class, Ret(Class:: *Member)(Args...) const>
void disconnect(Class *instance) {
call_type target{instance, &proto<Class, Member>};
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<typename Class, Ret(Class:: *Member)(Args...)>
void disconnect(Class *instance) {
call_type target{instance, &proto<Class, Member>};
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<typename Class>
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<call_type> *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<typename Ret, typename... Args, typename Collector>
class SigH<Ret(Args...), Collector> final: private internal::Invoker<Ret(Args...), Collector> {
using call_type = typename internal::sigh_traits<Ret(Args...)>::call_type;
public:
/*! @brief Unsigned integer type. */
using size_type = typename std::vector<call_type>::size_type;
/*! @brief Collector type. */
using collector_type = Collector;
/*! @brief Sink type. */
using sink_type = Sink<Ret(Args...)>;
/**
* @brief Instance type when it comes to connecting member functions.
* @tparam Class Type of class to which the member function belongs.
*/
template<typename Class>
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.<br/>
* 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<call_type> 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<typename Ret, typename... Args>
bool operator!=(const SigH<Ret(Args...)> &lhs, const SigH<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
}
#endif // ENTT_SIGNAL_SIGH_HPP

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -0,0 +1,64 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* 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<gk::BoxShape>(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<RotationAnimation>(testEntity, 0.f, 0.f, 1.f, 1.f);
}
void Scene::update() {
m_registry.view<gk::BoxShape, RotationAnimation>().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<gk::BoxShape>().each([&](auto, auto &boxShape) {
target.draw(boxShape, states);
});
}

View File

@ -0,0 +1,54 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* 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 <gk/gl/Camera.hpp>
#include <gk/gl/Drawable.hpp>
#include <gk/graphics/BoxShape.hpp>
#include <entt/entt.hpp>
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_

View File

@ -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");

View File

@ -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);
}

View File

@ -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_