fixed #133
This commit is contained in:
230
src/entt/entity/attachee.hpp
Normal file
230
src/entt/entity/attachee.hpp
Normal 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
|
||||
@@ -23,7 +23,7 @@ struct Null {
|
||||
|
||||
template<typename Entity>
|
||||
constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
using traits_type = entt::entt_traits<Entity>;
|
||||
return traits_type::entity_mask | (traits_type::version_mask << traits_type::entity_shift);
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
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 accommodate<Component>(dst, wrapper.component);
|
||||
other.template assign<Component>(dst, wrapper.component);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/family.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "attachee.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "entt_traits.hpp"
|
||||
#include "snapshot.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
@@ -44,6 +46,78 @@ class Registry {
|
||||
using signal_type = SigH<void(Registry &, const Entity)>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename Component>
|
||||
struct Pool: SparseSet<Entity, Component> {
|
||||
Pool(Registry *registry) ENTT_NOEXCEPT
|
||||
: registry{registry}
|
||||
{}
|
||||
|
||||
template<typename... Args>
|
||||
Component & construct(const Entity entity, Args &&... args) {
|
||||
auto &component = SparseSet<Entity, Component>::construct(entity, std::forward<Args>(args)...);
|
||||
ctor.publish(*registry, entity);
|
||||
return component;
|
||||
}
|
||||
|
||||
void destroy(const Entity entity) override {
|
||||
dtor.publish(*registry, entity);
|
||||
SparseSet<Entity, Component>::destroy(entity);
|
||||
}
|
||||
|
||||
typename signal_type::sink_type construction() ENTT_NOEXCEPT {
|
||||
return ctor.sink();
|
||||
}
|
||||
|
||||
typename signal_type::sink_type destruction() ENTT_NOEXCEPT {
|
||||
return dtor.sink();
|
||||
}
|
||||
|
||||
private:
|
||||
Registry *registry;
|
||||
signal_type ctor;
|
||||
signal_type dtor;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct Attaching: Attachee<Entity, Tag> {
|
||||
Attaching(Registry *registry)
|
||||
: registry{registry}
|
||||
{}
|
||||
|
||||
template<typename... Args>
|
||||
Tag & construct(const Entity entity, Args &&... args) ENTT_NOEXCEPT {
|
||||
auto &tag = Attachee<Entity, Tag>::construct(entity, std::forward<Args>(args)...);
|
||||
ctor.publish(*registry, entity);
|
||||
return tag;
|
||||
}
|
||||
|
||||
void destroy() ENTT_NOEXCEPT override {
|
||||
dtor.publish(*registry, Attachee<Entity>::get());
|
||||
Attachee<Entity, Tag>::destroy();
|
||||
}
|
||||
|
||||
Entity move(const Entity entity) ENTT_NOEXCEPT {
|
||||
const auto owner = Attachee<Entity>::get();
|
||||
dtor.publish(*registry, owner);
|
||||
Attachee<Entity, Tag>::move(entity);
|
||||
ctor.publish(*registry, entity);
|
||||
return owner;
|
||||
}
|
||||
|
||||
typename signal_type::sink_type construction() ENTT_NOEXCEPT {
|
||||
return ctor.sink();
|
||||
}
|
||||
|
||||
typename signal_type::sink_type destruction() ENTT_NOEXCEPT {
|
||||
return dtor.sink();
|
||||
}
|
||||
|
||||
private:
|
||||
Registry *registry;
|
||||
signal_type ctor;
|
||||
signal_type dtor;
|
||||
};
|
||||
|
||||
template<typename handler_family::family_type(*Type)(), typename... Component>
|
||||
static void creating(Registry ®istry, const Entity entity) {
|
||||
if(registry.has<Component...>(entity)) {
|
||||
@@ -57,28 +131,44 @@ class Registry {
|
||||
return handler.has(entity) ? handler.destroy(entity) : void();
|
||||
}
|
||||
|
||||
struct Attachee {
|
||||
Attachee(const Entity entity) ENTT_NOEXCEPT: entity{entity} {}
|
||||
virtual ~Attachee() = default;
|
||||
Entity entity;
|
||||
};
|
||||
template<typename Tag>
|
||||
inline bool managed(tag_t) const ENTT_NOEXCEPT {
|
||||
const auto ttype = tag_family::type<Tag>();
|
||||
return ttype < tags.size() && tags[ttype];
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline bool managed() const ENTT_NOEXCEPT {
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return ctype < pools.size() && pools[ctype];
|
||||
}
|
||||
|
||||
template<typename Tag>
|
||||
struct Attaching: Attachee {
|
||||
// requirements for aggregates are relaxed only since C++17
|
||||
template<typename... Args>
|
||||
Attaching(const Entity entity, Args &&... args)
|
||||
: Attachee{entity}, tag{std::forward<Args>(args)...}
|
||||
{}
|
||||
inline const Attaching<Tag> & pool(tag_t) const ENTT_NOEXCEPT {
|
||||
assert(managed<Tag>(tag_t{}));
|
||||
return static_cast<const Attaching<Tag> &>(*tags[tag_family::type<Tag>()]);
|
||||
}
|
||||
|
||||
Tag tag;
|
||||
};
|
||||
template<typename Tag>
|
||||
inline Attaching<Tag> & pool(tag_t) ENTT_NOEXCEPT {
|
||||
return const_cast<Attaching<Tag> &>(const_cast<const Registry *>(this)->pool<Tag>(tag_t{}));
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline const Pool<Component> & pool() const ENTT_NOEXCEPT {
|
||||
assert(managed<Component>());
|
||||
return static_cast<const Pool<Component> &>(*pools[component_family::type<Component>()]);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline Pool<Component> & pool() ENTT_NOEXCEPT {
|
||||
return const_cast<Pool<Component> &>(const_cast<const Registry *>(this)->pool<Component>());
|
||||
}
|
||||
|
||||
template<typename Comp, std::size_t Pivot, typename... Component, std::size_t... Indexes>
|
||||
void connect(std::index_sequence<Indexes...>) {
|
||||
auto &cpool = pools[component_family::type<Comp>()];
|
||||
std::get<1>(cpool).sink().template connect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
std::get<2>(cpool).sink().template connect<&Registry::destroying<Component...>>();
|
||||
pool<Comp>().construction().template connect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
pool<Comp>().destruction().template connect<&Registry::destroying<Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
@@ -90,9 +180,8 @@ class Registry {
|
||||
|
||||
template<typename Comp, std::size_t Pivot, typename... Component, std::size_t... Indexes>
|
||||
void disconnect(std::index_sequence<Indexes...>) {
|
||||
auto &cpool = pools[component_family::type<Comp>()];
|
||||
std::get<1>(cpool).sink().template disconnect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
std::get<2>(cpool).sink().template disconnect<&Registry::destroying<Component...>>();
|
||||
pool<Comp>().construction().template disconnect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
pool<Comp>().destruction().template disconnect<&Registry::destroying<Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
@@ -103,24 +192,6 @@ class Registry {
|
||||
(void)accumulator;
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline bool managed() const ENTT_NOEXCEPT {
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return ctype < pools.size() && std::get<0>(pools[ctype]);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline const SparseSet<Entity, Component> & pool() const ENTT_NOEXCEPT {
|
||||
assert(managed<Component>());
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return static_cast<SparseSet<Entity, Component> &>(*std::get<0>(pools[ctype]));
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline SparseSet<Entity, Component> & pool() ENTT_NOEXCEPT {
|
||||
return const_cast<SparseSet<Entity, Component> &>(const_cast<const Registry *>(this)->pool<Component>());
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
void assure() {
|
||||
const auto ctype = component_family::type<Component>();
|
||||
@@ -129,10 +200,8 @@ class Registry {
|
||||
pools.resize(ctype + 1);
|
||||
}
|
||||
|
||||
auto &cpool = std::get<0>(pools[ctype]);
|
||||
|
||||
if(!cpool) {
|
||||
cpool = std::make_unique<SparseSet<Entity, Component>>();
|
||||
if(!pools[ctype]) {
|
||||
pools[ctype] = std::make_unique<Pool<Component>>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +212,10 @@ class Registry {
|
||||
if(!(ttype < tags.size())) {
|
||||
tags.resize(ttype + 1);
|
||||
}
|
||||
|
||||
if(!tags[ttype]) {
|
||||
tags[ttype] = std::make_unique<Attaching<Tag>>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -510,22 +583,18 @@ public:
|
||||
assert(valid(entity));
|
||||
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
auto &tup = pools[pos-1];
|
||||
auto &cpool = std::get<0>(tup);
|
||||
auto &cpool = pools[pos-1];
|
||||
|
||||
if(cpool && cpool->has(entity)) {
|
||||
std::get<2>(tup).publish(*this, entity);
|
||||
cpool->destroy(entity);
|
||||
}
|
||||
};
|
||||
|
||||
for(auto pos = tags.size(); pos; --pos) {
|
||||
auto &tup = tags[pos-1];
|
||||
auto &tag = std::get<0>(tup);
|
||||
auto &tag = tags[pos-1];
|
||||
|
||||
if(tag && tag->entity == entity) {
|
||||
std::get<2>(tup).publish(*this, entity);
|
||||
tag.reset();
|
||||
if(tag && tag->get() == entity) {
|
||||
tag->destroy();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -589,10 +658,7 @@ public:
|
||||
assert(valid(entity));
|
||||
assert(!has<Tag>());
|
||||
assure<Tag>(tag_t{});
|
||||
auto &tup = tags[tag_family::type<Tag>()];
|
||||
std::get<0>(tup).reset(new Attaching<Tag>{entity, std::forward<Args>(args)...});
|
||||
std::get<1>(tup).publish(*this, entity);
|
||||
return get<Tag>();
|
||||
return pool<Tag>(tag_t{}).construct(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,9 +685,7 @@ public:
|
||||
Component & assign(const entity_type entity, Args &&... args) {
|
||||
assert(valid(entity));
|
||||
assure<Component>();
|
||||
pool<Component>().construct(entity, std::forward<Args>(args)...);
|
||||
std::get<1>(pools[component_family::type<Component>()]).publish(*this, entity);
|
||||
return pool<Component>().get(entity);
|
||||
return pool<Component>().construct(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,12 +694,7 @@ public:
|
||||
*/
|
||||
template<typename Tag>
|
||||
void remove() {
|
||||
if(has<Tag>()) {
|
||||
auto &tup = tags[tag_family::type<Tag>()];
|
||||
auto &tag = std::get<0>(tup);
|
||||
std::get<2>(tup).publish(*this, tag->entity);
|
||||
tag.reset();
|
||||
}
|
||||
return has<Tag>() ? pool<Tag>(tag_t{}).destroy() : void();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,8 +714,6 @@ public:
|
||||
void remove(const entity_type entity) {
|
||||
assert(valid(entity));
|
||||
assert(managed<Component>());
|
||||
const auto ctype = component_family::type<Component>();
|
||||
std::get<2>(pools[ctype]).publish(*this, entity);
|
||||
pool<Component>().destroy(entity);
|
||||
}
|
||||
|
||||
@@ -667,16 +724,7 @@ public:
|
||||
*/
|
||||
template<typename Tag>
|
||||
bool has() const ENTT_NOEXCEPT {
|
||||
const auto ttype = tag_family::type<Tag>();
|
||||
bool found = false;
|
||||
|
||||
if(ttype < tags.size()) {
|
||||
auto &tag = std::get<0>(tags[ttype]);
|
||||
// it's a valid tag and the associated entity hasn't been destroyed in the meantime
|
||||
found = tag && (tag->entity == (entities[tag->entity & traits_type::entity_mask]));
|
||||
}
|
||||
|
||||
return found;
|
||||
return managed<Tag>(tag_t{}) && tags[tag_family::type<Tag>()]->get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,7 +782,7 @@ public:
|
||||
template<typename Tag>
|
||||
const Tag & get() const ENTT_NOEXCEPT {
|
||||
assert(has<Tag>());
|
||||
return static_cast<Attaching<Tag> *>(std::get<0>(tags[tag_family::type<Tag>()]).get())->tag;
|
||||
return pool<Tag>(tag_t{}).get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,28 +950,18 @@ public:
|
||||
entity_type move(const entity_type entity) ENTT_NOEXCEPT {
|
||||
assert(valid(entity));
|
||||
assert(has<Tag>());
|
||||
auto &tag = std::get<0>(tags[tag_family::type<Tag>()]);
|
||||
const auto owner = tag->entity;
|
||||
tag->entity = entity;
|
||||
return owner;
|
||||
return pool<Tag>(tag_t{}).move(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the owner of the given tag, if any.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to get the owner of a tag that hasn't been previously attached
|
||||
* to an entity results in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* tag hasn't an owner.
|
||||
*
|
||||
* @tparam Tag Type of tag of which to get the owner.
|
||||
* @return A valid entity identifier.
|
||||
* @return A valid entity identifier if an owner exists, the null entity
|
||||
* identifier otherwise.
|
||||
*/
|
||||
template<typename Tag>
|
||||
entity_type attachee() const ENTT_NOEXCEPT {
|
||||
assert(has<Tag>());
|
||||
return std::get<0>(tags[tag_family::type<Tag>()])->entity;
|
||||
return managed<Tag>(tag_t{}) ? tags[tag_family::type<Tag>()]->get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -957,9 +995,9 @@ public:
|
||||
assure<Component>();
|
||||
auto &cpool = pool<Component>();
|
||||
|
||||
return (cpool.has(entity)
|
||||
return cpool.has(entity)
|
||||
? cpool.get(entity) = Component{std::forward<Args>(args)...}
|
||||
: cpool.construct(entity, std::forward<Args>(args)...));
|
||||
: cpool.construct(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -988,7 +1026,7 @@ public:
|
||||
template<typename Tag>
|
||||
sink_type construction(tag_t) ENTT_NOEXCEPT {
|
||||
assure<Tag>(tag_t{});
|
||||
return std::get<1>(tags[tag_family::type<Tag>()]).sink();
|
||||
return pool<Tag>(tag_t{}).construction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,7 +1055,7 @@ public:
|
||||
template<typename Component>
|
||||
sink_type construction() ENTT_NOEXCEPT {
|
||||
assure<Component>();
|
||||
return std::get<1>(pools[component_family::type<Component>()]).sink();
|
||||
return pool<Component>().construction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1046,7 +1084,7 @@ public:
|
||||
template<typename Tag>
|
||||
sink_type destruction(tag_t) ENTT_NOEXCEPT {
|
||||
assure<Tag>(tag_t{});
|
||||
return std::get<2>(tags[tag_family::type<Tag>()]).sink();
|
||||
return pool<Tag>(tag_t{}).destruction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1075,7 +1113,7 @@ public:
|
||||
template<typename Component>
|
||||
sink_type destruction() ENTT_NOEXCEPT {
|
||||
assure<Component>();
|
||||
return std::get<2>(pools[component_family::type<Component>()]).sink();
|
||||
return pool<Component>().destruction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1180,11 +1218,9 @@ public:
|
||||
void reset(const entity_type entity) {
|
||||
assert(valid(entity));
|
||||
assure<Component>();
|
||||
const auto ctype = component_family::type<Component>();
|
||||
auto &cpool = *std::get<0>(pools[ctype]);
|
||||
auto &cpool = pool<Component>();
|
||||
|
||||
if(cpool.has(entity)) {
|
||||
std::get<2>(pools[ctype]).publish(*this, entity);
|
||||
cpool.destroy(entity);
|
||||
}
|
||||
}
|
||||
@@ -1200,12 +1236,9 @@ public:
|
||||
template<typename Component>
|
||||
void reset() {
|
||||
assure<Component>();
|
||||
const auto ctype = component_family::type<Component>();
|
||||
auto &cpool = *std::get<0>(pools[ctype]);
|
||||
auto &sig = std::get<2>(pools[ctype]);
|
||||
auto &cpool = pool<Component>();
|
||||
|
||||
for(const auto entity: cpool) {
|
||||
sig.publish(*this, entity);
|
||||
for(const auto entity: static_cast<SparseSet<Entity> &>(cpool)) {
|
||||
cpool.destroy(entity);
|
||||
}
|
||||
}
|
||||
@@ -1278,13 +1311,13 @@ public:
|
||||
bool orphan = true;
|
||||
|
||||
for(std::size_t i = 0; i < pools.size() && orphan; ++i) {
|
||||
const auto &pool = std::get<0>(pools[i]);
|
||||
orphan = !(pool && pool->has(entity));
|
||||
const auto &cpool = pools[i];
|
||||
orphan = !(cpool && cpool->has(entity));
|
||||
}
|
||||
|
||||
for(std::size_t i = 0; i < tags.size() && orphan; ++i) {
|
||||
const auto &tag = std::get<0>(tags[i]);
|
||||
orphan = !(tag && (tag->entity == entity));
|
||||
const auto &tag = tags[i];
|
||||
orphan = !(tag && (tag->get() == entity));
|
||||
}
|
||||
|
||||
return orphan;
|
||||
@@ -1532,7 +1565,7 @@ public:
|
||||
std::vector<const SparseSet<Entity> *> set(last - first);
|
||||
|
||||
std::transform(first, last, set.begin(), [this](const component_type ctype) {
|
||||
return ctype < pools.size() ? std::get<0>(pools[ctype]).get() : nullptr;
|
||||
return ctype < pools.size() ? pools[ctype].get() : nullptr;
|
||||
});
|
||||
|
||||
return RuntimeView<Entity>{std::move(set)};
|
||||
@@ -1606,8 +1639,8 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
|
||||
std::vector<std::tuple<std::unique_ptr<SparseSet<Entity>>, signal_type, signal_type>> pools;
|
||||
std::vector<std::tuple<std::unique_ptr<Attachee>, signal_type, signal_type>> tags;
|
||||
std::vector<std::unique_ptr<SparseSet<Entity>>> pools;
|
||||
std::vector<std::unique_ptr<Attachee<Entity>>> tags;
|
||||
std::vector<entity_type> entities;
|
||||
size_type available{};
|
||||
entity_type next{};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#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"
|
||||
|
||||
@@ -67,6 +67,7 @@ SETUP_AND_ADD_TEST(monostate entt/core/monostate.cpp)
|
||||
# Test entity
|
||||
|
||||
SETUP_AND_ADD_TEST(actor entt/entity/actor.cpp)
|
||||
SETUP_AND_ADD_TEST(attachee entt/entity/attachee.cpp)
|
||||
SETUP_AND_ADD_TEST(entity entt/entity/entity.cpp)
|
||||
SETUP_AND_ADD_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_AND_ADD_TEST(prototype entt/entity/prototype.cpp)
|
||||
|
||||
69
test/entt/entity/attachee.cpp
Normal file
69
test/entt/entity/attachee.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <unordered_set>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/attachee.hpp>
|
||||
|
||||
TEST(AttacheeNoType, Functionalities) {
|
||||
entt::Attachee<std::uint64_t> attachee;
|
||||
|
||||
attachee.construct(42u);
|
||||
|
||||
ASSERT_EQ(attachee.get(), 42u);
|
||||
|
||||
attachee.destroy();
|
||||
|
||||
ASSERT_NE(attachee.get(), 42u);
|
||||
|
||||
(void)entt::Attachee<std::uint64_t>{std::move(attachee)};
|
||||
entt::Attachee<std::uint64_t> other;
|
||||
other = std::move(attachee);
|
||||
}
|
||||
|
||||
TEST(AttacheeWithType, Functionalities) {
|
||||
entt::Attachee<std::uint64_t, int> attachee;
|
||||
const auto &cattachee = attachee;
|
||||
|
||||
attachee.construct(42u, 3);
|
||||
|
||||
ASSERT_EQ(attachee.get(), 3);
|
||||
ASSERT_EQ(cattachee.get(), 3);
|
||||
ASSERT_EQ(attachee.Attachee<std::uint64_t>::get(), 42u);
|
||||
|
||||
attachee.move(0u);
|
||||
|
||||
ASSERT_EQ(attachee.get(), 3);
|
||||
ASSERT_EQ(cattachee.get(), 3);
|
||||
ASSERT_EQ(attachee.Attachee<std::uint64_t>::get(), 0u);
|
||||
|
||||
attachee.destroy();
|
||||
|
||||
ASSERT_NE(attachee.Attachee<std::uint64_t>::get(), 0u);
|
||||
ASSERT_NE(attachee.Attachee<std::uint64_t>::get(), 42u);
|
||||
}
|
||||
|
||||
TEST(AttacheeWithType, AggregatesMustWork) {
|
||||
struct AggregateType { int value; };
|
||||
// the goal of this test is to enforce the requirements for aggregate types
|
||||
entt::Attachee<std::uint64_t, AggregateType>{}.construct(0, 42);
|
||||
}
|
||||
|
||||
TEST(AttacheeWithType, TypesFromStandardTemplateLibraryMustWork) {
|
||||
// see #37 - this test shouldn't crash, that's all
|
||||
entt::Attachee<std::uint64_t, std::unordered_set<int>> attachee;
|
||||
attachee.construct(0).insert(42);
|
||||
attachee.destroy();
|
||||
}
|
||||
|
||||
TEST(AttacheeWithType, MoveOnlyComponent) {
|
||||
struct MoveOnlyComponent {
|
||||
MoveOnlyComponent() = default;
|
||||
~MoveOnlyComponent() = default;
|
||||
MoveOnlyComponent(const MoveOnlyComponent &) = delete;
|
||||
MoveOnlyComponent(MoveOnlyComponent &&) = default;
|
||||
MoveOnlyComponent & operator=(const MoveOnlyComponent &) = delete;
|
||||
MoveOnlyComponent & operator=(MoveOnlyComponent &&) = default;
|
||||
};
|
||||
|
||||
// it's purpose is to ensure that move only components are always accepted
|
||||
entt::Attachee<std::uint64_t, MoveOnlyComponent> attachee;
|
||||
(void)attachee;
|
||||
}
|
||||
@@ -376,8 +376,10 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
|
||||
TEST(DefaultRegistry, AttachSetRemoveTags) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto &cregistry = registry;
|
||||
const typename decltype(registry)::entity_type null = entt::null;
|
||||
|
||||
ASSERT_FALSE(registry.has<int>());
|
||||
ASSERT_EQ(registry.attachee<int>(), null);
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entt::tag_t{}, entity, 42);
|
||||
@@ -411,6 +413,7 @@ TEST(DefaultRegistry, AttachSetRemoveTags) {
|
||||
ASSERT_FALSE(registry.has<int>());
|
||||
ASSERT_FALSE(registry.has<int>(entt::tag_t{}, entity));
|
||||
ASSERT_FALSE(registry.has<int>(entt::tag_t{}, other));
|
||||
ASSERT_EQ(registry.attachee<int>(), null);
|
||||
|
||||
registry.assign<int>(entt::tag_t{}, entity, 42);
|
||||
registry.destroy(entity);
|
||||
@@ -811,3 +814,14 @@ TEST(DefaultRegistry, DestroyByTagAndComponents) {
|
||||
registry.destroy<double>(entt::tag_t{});
|
||||
registry.destroy<float>(entt::tag_t{});
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, SignalsOnAccommodate) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto entity = registry.create();
|
||||
|
||||
registry.prepare<int, char>();
|
||||
registry.assign<int>(entity);
|
||||
registry.accommodate<char>(entity);
|
||||
|
||||
ASSERT_FALSE((registry.view<int, char>(entt::persistent_t{}).empty()));
|
||||
}
|
||||
|
||||
@@ -400,8 +400,8 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
(void)entt::SparseSet<std::uint64_t>{std::move(set)};
|
||||
entt::SparseSet<std::uint64_t> other;
|
||||
(void)entt::SparseSet<std::uint64_t, int>{std::move(set)};
|
||||
entt::SparseSet<std::uint64_t, int> other;
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user