Skip to content

Commit

Permalink
Added: (OnDelete[Target], Error) to prevent entities from getting del…
Browse files Browse the repository at this point in the history
…eted

Changed: Core components can't be deleted
Changed: Special core components won't be matched by user queries
  • Loading branch information
richardbiely committed Dec 4, 2023
1 parent 7d99342 commit 04475c1
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 38 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -760,9 +760,11 @@ Condition is one of the following:
Reaction is one of the following:
* ***Remove*** - removes the entity/pair from anything referencing it
* ***Delete*** - delete everything referencing the entity
* ***Error*** - error out when deleted

The default behavior of deleting an entity is to simply remove it from the parent entity. This is an equivalent of Pair(OnDelete, Remove) relationship pair attached to the entity getting deleted.
Additionally, a behavior which can not be changed, all relationship pairs formed by this entity need to be deleted as well. This is needed because entity ids are recycled internally and we could not guarantee that the relationship entity would be be used for something unrelated later.
All core entities are defined with (OnDelete,Error). This means that instead of deleting the entity an error is thrown when an attempt to delete the entity is made.

```cpp
ecs::Entity rabbit = w.add();
Expand Down Expand Up @@ -791,7 +793,7 @@ w.add(rabbit, bomb_exploding_on_del);
w.del(bomb_exploding_on_del);
```

A native ***ChildOf*** entity is defined that can be used to express a physical hierarchy. It defines a (OnDelete, Delete) relationship so if the parent is deleted, all the children all deleted as well.
A core ***ChildOf*** entity is defined that can be used to express a physical hierarchy. It defines a (OnDelete, Delete) relationship so if the parent is deleted, all the children all deleted as well.

## Unique components
Unique component is a special kind of data that exists at most once per chunk. In other words, you attach data to one chunk specifically. It survives entity removals and unlike generic components, they do not transfer to a new chunk along with their entity.
Expand Down
12 changes: 8 additions & 4 deletions include/gaia/ecs/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,25 +279,29 @@ namespace gaia {
}
};

struct Core {};
//! Core component. The entity it is attached to is ignored by queries
struct Core_ {};
struct OnDelete_ {};
struct OnDeleteTarget_ {};
struct Remove_ {};
struct Delete_ {};
struct Error_ {};
struct All_ {};
struct ChildOf_ {};

inline Entity GAIA_ID(Core) = Entity(0, 0, false, false, EntityKind::EK_Gen);
//! Core component. The entity it is attached to is ignored by queries
inline Entity Core = Entity(0, 0, false, false, EntityKind::EK_Gen);
inline Entity GAIA_ID(EntityDesc) = Entity(1, 0, false, false, EntityKind::EK_Gen);
inline Entity GAIA_ID(Component) = Entity(2, 0, false, false, EntityKind::EK_Gen);
inline Entity OnDelete = Entity(3, false, false, false, EntityKind::EK_Gen);
inline Entity OnDeleteTarget = Entity(4, false, false, false, EntityKind::EK_Gen);
inline Entity Remove = Entity(5, false, false, false, EntityKind::EK_Gen);
inline Entity Delete = Entity(6, false, false, false, EntityKind::EK_Gen);
inline Entity Error = Entity(7, false, false, false, EntityKind::EK_Gen);
// Wildcard query entity
inline Entity All = Entity(7, 0, false, false, EntityKind::EK_Gen);
inline Entity All = Entity(8, 0, false, false, EntityKind::EK_Gen);
// Entity representing a physical hierarchy
inline Entity ChildOf = Entity(8, 0, false, false, EntityKind::EK_Gen);
inline Entity ChildOf = Entity(9, 0, false, false, EntityKind::EK_Gen);

// Always has to match the last internal entity
inline Entity GAIA_ID(LastCoreComponent) = ChildOf;
Expand Down
82 changes: 71 additions & 11 deletions include/gaia/ecs/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,9 @@ namespace gaia {
void reg_archetype(Archetype* pArchetype) {
GAIA_ASSERT(pArchetype != nullptr);

// Make sure hashes were set already
GAIA_ASSERT(
(m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));
// // Make sure hashes were set already
// GAIA_ASSERT(
// (m_archetypesById.empty() || pArchetype == m_pRootArchetype) || (pArchetype->lookup_hash().hash != 0));

// Make sure the archetype is not registered yet
GAIA_ASSERT(!m_archetypesById.contains(pArchetype->id()));
Expand Down Expand Up @@ -800,13 +800,14 @@ namespace gaia {
}
}

//! Handles specific conditions that might arise from deleting the comp/tag/pair \param entity.
//! Handles specific conditions that might arise from deleting \param entity.
//! Conditions are:
//! OnDelete - deleting an entity/pair
//! OnDeleteTarget - deleting a pair's target
//! Reactions are:
//! Remove - removes the entity/pair from anything referencing it
//! Delete - delete everything referencing the entity
//! Error - error out when deleted
//! These rules can be set up as:
//! e.add(Pair(OnDelete, Remove));
template <typename EntityOrPair>
Expand All @@ -818,6 +819,14 @@ namespace gaia {
// TODO: Make it possible to check the conditions simply via some pre-calculated bit mask
// to speed the search up (aka no search necessary).
if constexpr (std::is_same_v<EntityOrPair, Entity>) {
if (has(entity, Pair(OnDelete, Error))) {
GAIA_ASSERT2(false, "Trying to delete entity that is forbidden to be deleted");
GAIA_LOG_E(
"Trying to delete entity [%u.%u] %s [%s] that is forbidden to be deleted", entity.id(), entity.gen(),
name(entity), EntityKindString[entity.kind()]);
return;
}

if (has(entity, Pair(OnDelete, Delete))) {
// Delete all references to the entity if explicitely wanted
del_entities_with(entity);
Expand All @@ -828,6 +837,14 @@ namespace gaia {
} else {
auto target = entity.second();

if (has(target, Pair(OnDeleteTarget, Error))) {
GAIA_ASSERT2(false, "Trying to delete a pair but the target entity is forbidden to be deleted");
GAIA_LOG_E(
"Trying to delete a pair but the target entity [%u.%u] %s [%s] is forbidden to be deleted", //
target.id(), target.gen(), name(target), EntityKindString[target.kind()]);
return;
}

if (has(target, Pair(OnDeleteTarget, Delete)))
del_entities_with(target);

Expand Down Expand Up @@ -1022,9 +1039,9 @@ namespace gaia {

// Register the core component
{
const auto& id = GAIA_ID(Core);
const auto& id = Core;
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<Core>(id);
const auto& desc = comp_cache_mut().add<Core_>(id);
GAIA_ASSERT(desc.entity == id);
(void)comp;
(void)desc;
Expand Down Expand Up @@ -1090,6 +1107,14 @@ namespace gaia {
(void)comp;
(void)desc;
}
{
const auto& id = Error;
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<Error_>(id);
GAIA_ASSERT(desc.entity == id);
(void)comp;
(void)desc;
}

// Register All component. Used with relationship queries.
{
Expand All @@ -1108,9 +1133,39 @@ namespace gaia {
auto comp = add(*m_pRootArchetype, id.entity(), id.pair(), id.kind());
const auto& desc = comp_cache_mut().add<ChildOf_>(id);
GAIA_ASSERT(desc.entity == id);
(void)comp;
(void)desc;
add(comp, ecs::Pair(OnDeleteTarget, Delete));
}

// Special properites for core components
EntityBuilder(*this, Core) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, GAIA_ID(EntityDesc)) //
.add(Pair(OnDelete, Error));
EntityBuilder(*this, GAIA_ID(Component)) //
.add(Pair(OnDelete, Error));
EntityBuilder(*this, OnDelete) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, OnDeleteTarget) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, Remove) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, Delete) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, Error) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, All) //
.add(Core)
.add(Pair(OnDelete, Error));
EntityBuilder(*this, ChildOf) //
.add(Core)
.add(Pair(OnDelete, Error));
}

void done() {
Expand Down Expand Up @@ -1703,16 +1758,21 @@ namespace gaia {
//! \return Valid query object
template <bool UseCache = true>
auto query() {
if constexpr (UseCache)
return Query(
if constexpr (UseCache) {
Query q(
*const_cast<World*>(this), m_queryCache,
//
m_nextArchetypeId, m_worldVersion, m_archetypesById, m_entityToArchetypeMap);
else
return QueryUncached(
q.none(Core);
return q;
} else {
QueryUncached q(
*const_cast<World*>(this),
//
m_nextArchetypeId, m_worldVersion, m_archetypesById, m_entityToArchetypeMap);
q.none(Core);
return q;
}
}

//----------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 04475c1

Please sign in to comment.