-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#include <cubos/core/ecs/observer/id.hpp> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |