Skip to content

Commit

Permalink
Fixed: Trying to access EntityDesc of pair (no such thing exists)
Browse files Browse the repository at this point in the history
Fixed: Wildcard relationship deletion
Added: Preparation for entity aliasing
  • Loading branch information
richardbiely committed Dec 10, 2023
1 parent 28128a7 commit 30af94d
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 56 deletions.
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
* [Basics](#basics)
* [Entity dependencies](#entity-dependencies)
* [Entity constraints](#entity-constraints)
* [Entity aliasing](#entity-aliasing)
* [Targets](#targets)
* [Relations](#relations)
* [Cleanup rules](#cleanup-rules)
Expand Down Expand Up @@ -771,10 +772,10 @@ When adding an entity with a dependency to some source it is guaranteed the depe

```cpp
ecs::World w;
auto rabbit = w.add();
auto animal = w.add();
auto herbivore = w.add();
auto carrot = w.add();
ecs::Entity rabbit = w.add();
ecs::Entity animal = w.add();
ecs::Entity herbivore = w.add();
ecs::Entity carrot = w.add();
w.add(carrot, ecs::Pair(ecs::DependsOn, herbivore));
w.add(herbivore, ecs::Pair(ecs::DependsOn, animal));

Expand All @@ -800,16 +801,36 @@ Entity constrains are used to define what entities can not be combined with othe

```cpp
ecs::World w;
auto weak = w.add();
auto strong = w.add();
ecs::Entity weak = w.add();
ecs::Entity strong = w.add();
w.add(weak, ecs::Pair(ecs::CantCombine, strong));

auto e = w.add();
ecs::Entity e = w.add();
w.add(e, strong);
// Following line is an invalid operation.
w.add(e, weak);
```

### Entity aliasing
Entities can alias another entities by using the (AliasOf, target) relationship. This is a powerful feature that helps you identify an entire group of entities using a single entity.

```cpp
ecs::World w;
ecs::Entity animal = w.add();
ecs::Entity herbivore = w.add();
ecs::Entity rabbit = w.add();
ecs::Entity hare = w.add();

w.add(herbivore, ecs::Pair(ecs::AliasOf, animal)); // w.as(herbivore, animal)
w.add(rabbit, ecs::Pair(ecs::AliasOf, herbivore)); // w.as(rabbit, herbivore)
w.add(hare, ecs::Pair(ecs::AliasOf, herbivore)); // w.as(hare, herbivore)

ecs::Query q = w.query().add(animal);
q.each([](Entity entity) {
// runs for herbivore, rabbit and hare
});
```

### Cleanup rules
When deleting an entity we might want to define how the deletion is going to happen. Do we simply want to remove the entity or does everything connected to it need to get deleted as well? This behavior can be customized via relationships called cleanup rules.

Expand Down
4 changes: 3 additions & 1 deletion include/gaia/ecs/entity_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ namespace gaia {
OnDeleteTarget_Remove = 1 << 3,
OnDeleteTarget_Delete = 1 << 4,
OnDeleteTarget_Error = 1 << 5,
HasCantCombine = 1 << 6,
HasAcyclic = 1 << 6,
HasCantCombine = 1 << 7,
HasAliasOf = 1 << 8,
};

struct EntityContainer: cnt::ilist_item_base {
Expand Down
11 changes: 7 additions & 4 deletions include/gaia/ecs/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ namespace gaia {
struct Acyclic_ {};
struct All_ {};
struct ChildOf_ {};
struct AliasOf_ {};

//! Core component. The entity it is attached to is ignored by queries
inline Entity Core = Entity(0, 0, false, false, EntityKind::EK_Gen);
Expand All @@ -312,15 +313,17 @@ namespace gaia {
// Entity dependencies
inline Entity DependsOn = Entity(8, false, false, false, EntityKind::EK_Gen);
inline Entity CantCombine = Entity(9, false, false, false, EntityKind::EK_Gen);
// Graph restrictions
//! Graph restrictions
inline Entity Acyclic = Entity(10, false, false, false, EntityKind::EK_Gen);
// Wildcard query entity
//! Wildcard query entity
inline Entity All = Entity(11, 0, false, false, EntityKind::EK_Gen);
// Entity representing a physical hierarchy
//! Entity representing a physical hierarchy
inline Entity ChildOf = Entity(12, 0, false, false, EntityKind::EK_Gen);
//! Alias for a base entity
inline Entity AliasOf = Entity(13, 0, false, false, EntityKind::EK_Gen);

// Always has to match the last internal entity
inline Entity GAIA_ID(LastCoreComponent) = ChildOf;
inline Entity GAIA_ID(LastCoreComponent) = AliasOf;

inline bool is_wildcard(Entity entity) {
return entity.pair() && (entity.id() == All.id() || entity.gen() == All.id());
Expand Down
4 changes: 2 additions & 2 deletions include/gaia/ecs/query_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ namespace gaia {
GAIA_NODISCARD bool match_one(const Chunk::EntityArray& archetypeIds, EntitySpan queryIds) const {
return match_inter(archetypeIds, queryIds, [&](Entity idArchetype, Entity idQuery) {
// TODO: Comparison inside match_inter is slow. Do something about it.
// Ideally we want to do "idQuery == idArchetype" here.
// Ideally we want to do only "idQuery == idArchetype" here.
if (idQuery.pair()) {
// all(Pair<All, All>) aka "any pair"
if (idQuery == Pair(All, All))
Expand Down Expand Up @@ -137,7 +137,7 @@ namespace gaia {
uint32_t matches = 0;
return match_inter(archetypeIds, queryIds, [&](Entity idArchetype, Entity idQuery) {
// TODO: Comparison inside match_inter is slow. Do something about it.
// Ideally we want to do "idQuery == idArchetype" here.
// Ideally we want to do only "idQuery == idArchetype" here.
if (idQuery.pair()) {
// all(Pair<All, All>) aka "any pair"
if (idQuery == Pair(All, All))
Expand Down
105 changes: 88 additions & 17 deletions include/gaia/ecs/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <type_traits>

#include "../cnt/darray.h"
#include "../cnt/darray_ext.h"
#include "../cnt/ilist.h"
#include "../cnt/map.h"
#include "../cnt/sarray.h"
Expand Down Expand Up @@ -160,6 +161,16 @@ namespace gaia {
return *this;
}

//! Shortcut for add(Pair(AliasOf, baseEntity))
EntityBuilder& alias(Entity baseEntity) {
return add(Pair(AliasOf, baseEntity));
}

//! Shortcut for add(Pair(ChildOf, parent))
EntityBuilder& child(Entity parent) {
return add(Pair(ChildOf, parent));
}

template <typename... T>
EntityBuilder& add() {
(verify_comp<T>(), ...);
Expand Down Expand Up @@ -249,6 +260,20 @@ namespace gaia {
ec.flags &= ~flag;
};

void try_set_flags(Entity entity, bool enable) {
try_set_AliasOf(entity, enable);
try_set_CantCombine(entity, enable);
try_set_OnDeleteTarget(entity, enable);
try_set_OnDelete(entity, enable);
}

void try_set_AliasOf(Entity entity, bool enable) {
if (!entity.pair() || entity.id() != AliasOf.id())
return;

updateFlag(m_entity, EntityContainerFlags::HasAliasOf, enable);
}

void try_set_CantCombine(Entity entity, bool enable) {
if (!entity.pair() || entity.id() != CantCombine.id())
return;
Expand Down Expand Up @@ -291,9 +316,7 @@ namespace gaia {
if (m_pArchetype->has(entity))
return;

try_set_CantCombine(entity, true);
try_set_OnDeleteTarget(entity, true);
try_set_OnDelete(entity, true);
try_set_flags(entity, true);

m_pArchetype = m_world.foc_archetype_add(m_pArchetype, entity);
}
Expand All @@ -303,9 +326,7 @@ namespace gaia {
World::verify_del(m_world, *m_pArchetype, m_entity, entity);
#endif

try_set_CantCombine(entity, false);
try_set_OnDeleteTarget(entity, false);
try_set_OnDelete(entity, false);
try_set_flags(entity, false);

m_pArchetype = m_world.foc_archetype_del(m_pArchetype, entity);
}
Expand Down Expand Up @@ -734,6 +755,9 @@ namespace gaia {
//! Removes any name associated with the entity
//! \param entity Entity the name of which we want to delete
void del_name(Entity entity) {
if (entity.pair())
return;

auto& ec = fetch(entity);
auto& entityDesc = ec.pChunk->sview_mut<EntityDesc>()[ec.row];
if (entityDesc.name == nullptr)
Expand All @@ -757,8 +781,7 @@ namespace gaia {
//! Deletes an entity along with all data associated with it.
//! \param entity Entity to delete
void del_entity(Entity entity) {
// Wildcard entites are a virtual concept, there is nothing to delete
if (is_wildcard(entity))
if (entity.pair())
return;

const auto& ec = fetch(entity);
Expand Down Expand Up @@ -919,18 +942,16 @@ namespace gaia {
GAIA_ASSERT(is_wildcard(entity));

Archetype* pDstArchetype = pArchetype;
bool found = false;

const auto& ids = pArchetype->ids();
for (auto id: ids) {
if (!id.pair() || id.gen() != entity.gen())
continue;

pDstArchetype = foc_archetype_del(pDstArchetype, id);
found = true;
}

return found ? pDstArchetype : nullptr;
return pArchetype != pDstArchetype ? pDstArchetype : nullptr;
}

//! Find the destination archetype \param pArchetype as if removing all entities
Expand All @@ -940,18 +961,16 @@ namespace gaia {
GAIA_ASSERT(is_wildcard(entity));

Archetype* pDstArchetype = pArchetype;
bool found = false;

const auto& ids = pArchetype->ids();
for (auto id: ids) {
if (!id.pair() || id.id() != entity.id())
continue;

pDstArchetype = foc_archetype_del(pDstArchetype, id);
found = true;
}

return found ? pDstArchetype : nullptr;
return pArchetype != pDstArchetype ? pDstArchetype : nullptr;
}

//! Find the destination archetype \param pArchetype as if removing all entities
Expand Down Expand Up @@ -1068,7 +1087,7 @@ namespace gaia {
del_entities_with(Pair(All, tgt), Pair(OnDeleteTarget, Delete));
} else {
// Remove from all entities referencing this one as a relationship pair's target
rem_from_entities(Pair(All, tgt), Pair(OnDeleteTarget, Delete));
rem_from_entities(Pair(All, tgt));
}

if ((ec.flags & EntityContainerFlags::OnDelete_Delete) != 0) {
Expand Down Expand Up @@ -1450,6 +1469,16 @@ namespace gaia {
(void)desc;
}

// Base entity alias.
{
const auto& id = AliasOf;
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<AliasOf_>(id);
GAIA_ASSERT(desc.entity == id);
(void)comp;
(void)desc;
}

// Special properites for core components
EntityBuilder(*this, Core) //
.add(Core)
Expand Down Expand Up @@ -1489,8 +1518,13 @@ namespace gaia {
.add(Pair(OnDelete, Error));
EntityBuilder(*this, ChildOf) //
.add(Core)
.add(Acyclic)
.add(Pair(OnDelete, Error))
.add(Pair(OnDeleteTarget, Delete));
EntityBuilder(*this, AliasOf) //
.add(Core)
.add(Acyclic)
.add(Pair(OnDelete, Error));
}

void done() {
Expand Down Expand Up @@ -1817,15 +1851,26 @@ namespace gaia {
// (*,X)
else if (rel == All) {
if (const auto* pTargets = relations(tgt)) {
// handle_del might invalide the targets map so we need to make a copy
// TODO: this is suboptimal at best, needs to be optimized
cnt::darray_ext<Entity, 64> tmp;
for (auto key: *pTargets)
handle_del(Pair(key.entity(), tgt));
tmp.push_back(key.entity());

for (auto e: tmp)
handle_del(Pair(e, tgt));
}
}
// (X,*)
else if (tgt == All) {
if (const auto* pRelations = targets(rel)) {
// handle_del might invalide the targets map so we need to make a copy
// TODO: this is suboptimal at best, needs to be optimized
cnt::darray_ext<Entity, 64> tmp;
for (auto key: *pRelations)
handle_del(Pair(rel, key.entity()));
tmp.push_back(key.entity());
for (auto e: tmp)
handle_del(Pair(rel, e));
}
}
} else {
Expand Down Expand Up @@ -1876,6 +1921,32 @@ namespace gaia {

//----------------------------------------------------------------------

//! Shortcut for add(entity, Pair(AliasOf, baseEntity)
void alias(Entity entity, Entity baseEntity) {
add(entity, Pair(AliasOf, baseEntity));
}

//! Checks if \param entity is an alias of \param baseEntity
//! True if entity is an alias for baseEntity. False otherwise.
GAIA_NODISCARD bool alias(Entity entity, Entity baseEntity) const {
return has(entity, Pair(AliasOf, baseEntity));
}

//----------------------------------------------------------------------

//! Shortcut for add(entity, Pair(ChildOf, parent)
void child(Entity entity, Entity parent) {
add(entity, Pair(ChildOf, parent));
}

//! Checks if \param entity is a child of \param parent
//! True if entity is a child of parent. False otherwise.
GAIA_NODISCARD bool child(Entity entity, Entity parent) const {
return has(entity, Pair(ChildOf, parent));
}

//----------------------------------------------------------------------

//! Starts a bulk set operation on \param entity.
//! \param entity Entity
//! \return ComponentSetter
Expand Down
Loading

0 comments on commit 30af94d

Please sign in to comment.