cloning an entity is no longer allowed
This commit is contained in:
12
README.md
12
README.md
@@ -396,25 +396,19 @@ Entities are represented by _entity identifiers_. An entity identifier is an
|
||||
opaque type that users should not inspect or modify in any way. It carries
|
||||
information about the entity itself and its version.
|
||||
|
||||
A registry can be used both to construct and destroy entities, as well as to
|
||||
clone already existing entities:
|
||||
A registry can be used both to construct and destroy entities:
|
||||
|
||||
```cpp
|
||||
// constructs a naked entity with no components and returns its identifier
|
||||
auto entity = registry.create();
|
||||
|
||||
// clones an entity and assigns all its components by copy to the newly created one
|
||||
auto other = registry.clone(entity);
|
||||
|
||||
// destroys an entity and all its components
|
||||
registry.destroy(entity);
|
||||
```
|
||||
|
||||
Be aware that cloning an entity can lead to unexpected results under certain
|
||||
conditions. Please refer to the inline documentation for more details.<br/>
|
||||
Once an entity is deleted, the registry can freely reuse it internally with a
|
||||
When an entity is destroyed, the registry can freely reuse it internally with a
|
||||
slightly different identifier. In particular, the version of an entity is
|
||||
increased each and every time it's destroyed.<br/>
|
||||
increased each and every time it's discarded.<br/>
|
||||
In case entity identifiers are stored around, the registry offers all the
|
||||
functionalities required to test them and get out of the them all the
|
||||
information they carry:
|
||||
|
||||
@@ -408,51 +408,6 @@ public:
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clones an entity and returns the newly created one.
|
||||
*
|
||||
* There are two kinds of entity identifiers:
|
||||
*
|
||||
* * Newly created ones in case no entities have been previously destroyed.
|
||||
* * Recycled ones with updated versions.
|
||||
*
|
||||
* Users should not care about the type of the returned entity identifier.
|
||||
* In case entity identifers are stored around, the `valid` member
|
||||
* function can be used to know if they are still valid or the entity has
|
||||
* been destroyed and potentially recycled.
|
||||
*
|
||||
* @warning
|
||||
* In case there are listeners that observe the construction of components
|
||||
* and assign other components to the entity in their bodies, the result of
|
||||
* invoking this function may not be as expected. In the worst case, it
|
||||
* could lead to undefined behavior. An assertion will abort the execution
|
||||
* at runtime in debug mode if a violation is detected.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to clone 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 entity A valid entity identifier
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
entity_type clone(const entity_type entity) ENTT_NOEXCEPT {
|
||||
assert(valid(entity));
|
||||
const auto other = create();
|
||||
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
auto &tup = pools[pos-1];
|
||||
auto &cpool = std::get<0>(tup);
|
||||
|
||||
if(cpool && cpool->has(entity)) {
|
||||
cpool->clone(other, entity);
|
||||
std::get<1>(tup).publish(*this, other);
|
||||
}
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys an entity and lets the registry recycle the identifier.
|
||||
*
|
||||
|
||||
@@ -377,28 +377,6 @@ public:
|
||||
direct.push_back(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a sparse set by cloning another entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to clone an entity that doesn't belong to the sparse set or to
|
||||
* assign an entity that already belongs to the sparse set results in
|
||||
* undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* sparse set doesn't contain the entity to clone or if it already contains
|
||||
* the given entity.
|
||||
*
|
||||
* @param entity A valid entity identifier.
|
||||
* @param source A valid entity identifier from which to clone.
|
||||
*/
|
||||
inline virtual void clone(const entity_type entity, const entity_type source) {
|
||||
assert(has(source));
|
||||
assert(!has(entity));
|
||||
construct(entity);
|
||||
// useful to suppress warnings when asserts are disabled
|
||||
(void)source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an entity from a sparse set.
|
||||
*
|
||||
@@ -842,24 +820,6 @@ public:
|
||||
return instances.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a sparse set by cloning another entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to clone an entity that doesn't belong to the sparse set or to
|
||||
* assign an entity that already belongs to the sparse set results in
|
||||
* undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* sparse set doesn't contain the entity to clone or if it already contains
|
||||
* the given entity.
|
||||
*
|
||||
* @param entity A valid entity identifier.
|
||||
* @param source A valid entity identifier from which to clone.
|
||||
*/
|
||||
inline void clone(const entity_type entity, const entity_type source) override {
|
||||
construct(entity, get(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an entity from a sparse set and destroies its object.
|
||||
*
|
||||
|
||||
@@ -354,23 +354,6 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
|
||||
ASSERT_EQ(registry.current(pre), registry.current(post));
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, CloneEntities) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity, 42);
|
||||
registry.assign<char>(entity, 'c');
|
||||
|
||||
const auto other = registry.clone(entity);
|
||||
|
||||
ASSERT_TRUE(registry.has<int>(other));
|
||||
ASSERT_TRUE(registry.has<char>(other));
|
||||
ASSERT_EQ(registry.get<int>(entity), registry.get<int>(other));
|
||||
ASSERT_EQ(registry.get<char>(entity), registry.get<char>(other));
|
||||
ASSERT_EQ(registry.get<int>(other), 42);
|
||||
ASSERT_EQ(registry.get<char>(other), 'c');
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, AttachSetRemoveTags) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto &cregistry = registry;
|
||||
@@ -719,11 +702,6 @@ TEST(DefaultRegistry, ComponentSignals) {
|
||||
|
||||
ASSERT_EQ(listener.counter, 1);
|
||||
ASSERT_EQ(listener.last, e1);
|
||||
|
||||
e1 = registry.clone(e0);
|
||||
|
||||
ASSERT_EQ(listener.counter, 2);
|
||||
ASSERT_EQ(listener.last, e1);
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, TagSignals) {
|
||||
|
||||
@@ -54,23 +54,6 @@ TEST(SparseSetNoType, Functionalities) {
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, Clone) {
|
||||
entt::SparseSet<unsigned int> set;
|
||||
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
set.construct(0);
|
||||
|
||||
ASSERT_TRUE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
set.clone(42, 0);
|
||||
|
||||
ASSERT_TRUE(set.has(0));
|
||||
ASSERT_TRUE(set.has(42));
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, DataBeginEnd) {
|
||||
entt::SparseSet<unsigned int> set;
|
||||
|
||||
@@ -324,26 +307,6 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, Clone) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
set.construct(0, 3);
|
||||
|
||||
ASSERT_TRUE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
ASSERT_EQ(set.get(0), 3);
|
||||
|
||||
set.clone(42, 0);
|
||||
|
||||
ASSERT_TRUE(set.has(0));
|
||||
ASSERT_TRUE(set.has(42));
|
||||
ASSERT_EQ(set.get(0), set.get(42));
|
||||
ASSERT_EQ(set.get(42), 3);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, AggregatesMustWork) {
|
||||
struct AggregateType { int value; };
|
||||
// the goal of this test is to enforce the requirements for aggregate types
|
||||
@@ -723,3 +686,18 @@ TEST(SparseSetWithType, ReferencesGuaranteed) {
|
||||
ASSERT_EQ(set.get(0).value, 3);
|
||||
ASSERT_EQ(set.get(1).value, 3);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, 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::SparseSet<unsigned int, MoveOnlyComponent> set;
|
||||
(void)set;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user