Skip to content

Commit

Permalink
feat(ecs): add Observers class
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Feb 28, 2024
1 parent 4c54036 commit 10fdba7
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 0 deletions.
3 changes: 3 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ set(CUBOS_CORE_SOURCE
"src/ecs/system/arguments/resources.cpp"
"src/ecs/system/arguments/world.cpp"

"src/ecs/observer/id.cpp"
"src/ecs/observer/observers.cpp"

"src/ecs/query/term.cpp"
"src/ecs/query/data.cpp"
"src/ecs/query/filter.cpp"
Expand Down
22 changes: 22 additions & 0 deletions core/include/cubos/core/ecs/observer/id.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// @file
/// @brief Struct @ref cubos::core::ecs::ObserverId.
/// @ingroup core-ecs-observer

#pragma once

#include <cstddef>

namespace cubos::core::ecs
{
/// @brief Identifies an observer.
/// @ingroup core-ecs-observer
struct ObserverId
{
std::size_t inner; ///< Observer identifier.

/// @brief Compares two observer identifiers for equality.
/// @param other Other observer identifier.
/// @return Whether the two observer identifiers are equal.
bool operator==(const ObserverId& other) const = default;
};
} // namespace cubos::core::ecs
9 changes: 9 additions & 0 deletions core/include/cubos/core/ecs/observer/module.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// @dir
/// @brief @ref core-ecs-observer directory.

namespace cubos::core::ecs
{
/// @defgroup core-ecs-observer Observer
/// @ingroup core-ecs
/// @brief Observer part of the ECS.
}
59 changes: 59 additions & 0 deletions core/include/cubos/core/ecs/observer/observers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// @file
/// @brief Class @ref cubos::core::ecs::Observers.
/// @ingroup core-ecs-observer

#pragma once

#include <string>
#include <unordered_map>

#include <cubos/core/ecs/entity/entity.hpp>
#include <cubos/core/ecs/observer/id.hpp>
#include <cubos/core/ecs/system/system.hpp>
#include <cubos/core/ecs/table/column.hpp>

namespace cubos::core::ecs
{
/// @brief Stores and manages all of the observers associated with a world.
/// @ingroup core-ecs-observer
class Observers
{
public:
~Observers();

/// @brief Notifies that the given entity has a new column.
/// @param commandBuffer Command buffer to record the any commands emitted by the observer.
/// @param entity Entity.
/// @param column Column.
/// @return Whether an observer was triggered.
bool notifyAdd(CommandBuffer& commandBuffer, Entity entity, ColumnId column);

/// @brief Notifies that the given entity has lost a column.
/// @param commandBuffer Command buffer to record the any commands emitted by the observer.
/// @param entity Entity.
/// @param column Column.
/// @return Whether an observer was triggered.
bool notifyRemove(CommandBuffer& commandBuffer, Entity entity, ColumnId column);

/// @brief Hooks an observer to the addition of a column.
/// @param column Column.
/// @param observer Observer system.
/// @return Observer identifier.
ObserverId hookOnAdd(ColumnId column, System<void> observer);

/// @brief Hooks an observer to the removal of a column.
/// @param column Column.
/// @param observer Observer system.
/// @return Observer identifier.
ObserverId hookOnRemove(ColumnId column, System<void> observer);

/// @brief Unhooks an observer.
/// @param id Observer identifier.
void unhook(ObserverId id);

private:
std::vector<System<void>*> mObservers; /// Indexed by observer identifier.
std::unordered_multimap<ColumnId, ObserverId, ColumnIdHash> mOnAdd;
std::unordered_multimap<ColumnId, ObserverId, ColumnIdHash> mOnRemove;
};
} // namespace cubos::core::ecs
1 change: 1 addition & 0 deletions core/src/ecs/observer/id.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <cubos/core/ecs/observer/id.hpp>
71 changes: 71 additions & 0 deletions core/src/ecs/observer/observers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <cubos/core/ecs/observer/observers.hpp>

using cubos::core::ecs::ObserverId;
using cubos::core::ecs::Observers;

Observers::~Observers()
{
for (auto* observer : mObservers)
{
delete observer;
}
}

bool Observers::notifyAdd(CommandBuffer& commandBuffer, Entity entity, ColumnId column)
{
bool triggered = false;

auto range = mOnAdd.equal_range(column);
for (auto it = range.first; it != range.second; ++it)
{
auto* observer = mObservers[it->second.inner];
if (observer != nullptr)
{
observer->run({commandBuffer, entity});
triggered = true;
}
}

return triggered;
}

bool Observers::notifyRemove(CommandBuffer& commandBuffer, Entity entity, ColumnId column)
{
bool triggered = false;

auto range = mOnRemove.equal_range(column);
for (auto it = range.first; it != range.second; ++it)
{
auto* observer = mObservers[it->second.inner];
if (observer != nullptr)
{
observer->run({commandBuffer, entity});
triggered = true;
}
}

return triggered;
}

ObserverId Observers::hookOnAdd(ColumnId column, System<void> observer)
{
ObserverId id{.inner = mObservers.size()};
mObservers.push_back(new System<void>(std::move(observer)));
mOnAdd.emplace(column, id);
return id;
}

ObserverId Observers::hookOnRemove(ColumnId column, System<void> observer)
{
ObserverId id{.inner = mObservers.size()};
mObservers.push_back(new System<void>(std::move(observer)));
mOnRemove.emplace(column, id);
return id;
}

void Observers::unhook(ObserverId id)
{
CUBOS_ASSERT(mObservers[id.inner] != nullptr);
delete mObservers[id.inner];
mObservers[id.inner] = nullptr;
}
1 change: 1 addition & 0 deletions core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ add_executable(
ecs/query/term.cpp
ecs/query/filter.cpp
ecs/system/access.cpp
ecs/observer/observers.cpp
ecs/stress.cpp

geom/box.cpp
Expand Down
36 changes: 36 additions & 0 deletions core/tests/ecs/observer/observers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <doctest/doctest.h>

#include <cubos/core/ecs/command_buffer.hpp>
#include <cubos/core/ecs/observer/observers.hpp>

#include "../utils.hpp"

using namespace cubos::core::ecs;

TEST_CASE("ecs::Observers")
{
World world{};
CommandBuffer cmdBuffer{world};
Observers obs{};

static int acc = 0;
auto hook1 = obs.hookOnAdd(ColumnId{.inner = 1}, System<void>::make(world, []() { acc += 1; }, {}));
auto hook2 = obs.hookOnRemove(ColumnId{.inner = 1}, System<void>::make(world, []() { acc -= 1; }, {}));
CHECK(acc == 0);
obs.notifyAdd(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 0});
CHECK(acc == 0);
obs.notifyRemove(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 2});
CHECK(acc == 0);
obs.notifyAdd(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 1});
CHECK(acc == 1);
obs.notifyRemove(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 1});
CHECK(acc == 0);
obs.unhook(hook2);
obs.notifyAdd(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 1});
CHECK(acc == 1);
obs.notifyRemove(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 1});
CHECK(acc == 1);
obs.unhook(hook1);
obs.notifyAdd(cmdBuffer, Entity{0, 0}, ColumnId{.inner = 1});
CHECK(acc == 1);
}

0 comments on commit 10fdba7

Please sign in to comment.