Skip to content

Commit

Permalink
feat(ecs): add repeat while and groups of systems
Browse files Browse the repository at this point in the history
  • Loading branch information
joaomanita committed Feb 12, 2024
1 parent a8e1ee6 commit 4c887c7
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 36 deletions.
18 changes: 18 additions & 0 deletions core/include/cubos/core/ecs/cubos.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ namespace cubos::core::ecs
/// @return Reference to this object, for chaining.
TagBuilder& after(const std::string& tag);

/// @brief Tags all systems on this tag with the given tag.
/// @param tag Tag to be inherited from.
/// @return Reference to this object, for chaining.
TagBuilder& tagged(const std::string& tag);

/// @brief Adds a condition to the current tag. If this condition returns false, systems
/// with this tag will not be executed. For the tagged systems to run, all conditions must
/// return true.
Expand All @@ -80,6 +85,12 @@ namespace cubos::core::ecs
template <typename F>
TagBuilder& runIf(F func);

/// @brief Makes all systems within the tag repeat as a group until the given condition evaluates to false.
/// @param func Condition function.
/// @return Reference to this object, for chaining.
template <typename F>
TagBuilder& repeatWhile(F func);

private:
World& mWorld;
core::ecs::Dispatcher& mDispatcher;
Expand Down Expand Up @@ -316,6 +327,13 @@ namespace cubos::core::ecs
return *this;
}

template <typename F>
TagBuilder& TagBuilder::repeatWhile(F func)
{
mDispatcher.tagRepeatWhile(System<bool>::make(mWorld, std::move(func), {}));
return *this;
}

template <typename R, typename... TArgs>
Cubos& Cubos::addResource(TArgs... args)
{
Expand Down
139 changes: 138 additions & 1 deletion core/include/cubos/core/ecs/system/dispatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ namespace cubos::core::ecs
/// @param condition Condition to add.
void tagAddCondition(ecs::System<bool> condition);

/// @brief Adds a repeat condition to the current tag.
/// @param func Condition to add.
void tagRepeatWhile(ecs::System<bool> condition);

/// @brief Adds a (sub)group to the current group.
void groupAddGroup();

/// @brief Adds a system, and sets it as the current system for further configuration.
/// @param system System to add.
void addSystem(ecs::System<void> system);
Expand All @@ -86,6 +93,10 @@ namespace cubos::core::ecs
/// @param tag Tag to run under.
void systemAddTag(const std::string& tag);

/// @brief Links a system to a certain group.
/// @param grouptag Tag of the group.
void systemAddGroup(const std::string& grouptag);

/// @brief Sets the current system to run after the tag.
/// @param tag Tag to run after.
void systemSetAfterTag(const std::string& tag);
Expand All @@ -108,7 +119,6 @@ namespace cubos::core::ecs
/// @param cmds Command buffer.
void callSystems(CommandBuffer& cmds);

private:
struct Dependency;
struct SystemSettings;
struct System;
Expand Down Expand Up @@ -137,8 +147,125 @@ namespace cubos::core::ecs
std::shared_ptr<SystemSettings> settings;
ecs::System<void> system;
std::unordered_set<std::string> tags;
std::string groupTag = "main";
};

class Step
{
public:
virtual ~Step() = default;
virtual void call(CommandBuffer& cmds, std::vector<ecs::System<bool>>& conditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> runConditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> retConditions) = 0;
};

class SystemStep : public Step
{
public:
SystemStep(System* system)
: mSystem(system)
{
}
void call(CommandBuffer& cmds, std::vector<ecs::System<bool>>& conditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> runConditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> retConditions) override;

System* getSystem()
{
return mSystem;
}

private:
System* mSystem;
};

class GroupStep : public Step, public std::enable_shared_from_this<Step>
{
public:
GroupStep(std::string grouptag, std::shared_ptr<GroupStep> parentStep)
: groupTag(std::move(grouptag))
, mParentStep(std::move(parentStep))
{
}
void call(CommandBuffer& cmds, std::vector<ecs::System<bool>>& conditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> runConditions,
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> retConditions) override;

void addSystemStep(System* system)
{
mSteps.insert(mSteps.begin(), std::make_shared<SystemStep>(system));
}

std::shared_ptr<GroupStep> addGroupStep(const std::string& grouptag,
const std::shared_ptr<GroupStep>& parent)
{
std::shared_ptr<GroupStep> newStep = std::make_shared<GroupStep>(grouptag, parent);
mSteps.insert(mSteps.begin(), newStep);
return std::dynamic_pointer_cast<GroupStep>(mSteps[0]);
}

void moveToFront(const std::shared_ptr<Step>& step)
{
auto it = std::find(mSteps.begin(), mSteps.end(), step);
if (it != mSteps.end())
{
std::rotate(mSteps.begin(), it, it + 1);
}
}

std::shared_ptr<GroupStep> findGroup(const std::string& tag)
{
if (tag == groupTag)
{
return std::dynamic_pointer_cast<GroupStep>(shared_from_this());
}

for (const auto& step : this->mSteps)
{
if (auto groupStepPtr = std::dynamic_pointer_cast<GroupStep>(step))
{
return groupStepPtr->findGroup(tag);
}
}

return nullptr;
}

void queueSystem(const std::string& grouptag, System* system)
{
if (grouptag == this->groupTag)
{
addSystemStep(system);

if (this->groupTag != "main")
{
this->mParentStep->moveToFront(shared_from_this());
}
return;
}

for (const auto& step : this->mSteps)
{
if (auto groupStepPtr = std::dynamic_pointer_cast<GroupStep>(step))
{
groupStepPtr->queueSystem(grouptag, system);
}
}
}

const std::string& getGroupTag()
{
return groupTag;
}
std::bitset<CUBOS_CORE_DISPATCHER_MAX_CONDITIONS> conditions;

private:
const std::string groupTag;
std::shared_ptr<GroupStep> mParentStep;
std::vector<std::shared_ptr<Step>> mSteps;
};

private:
/// @brief Internal class used to implement a DFS algorithm for call chain compilation
struct DFSNode
{
Expand Down Expand Up @@ -183,6 +310,8 @@ namespace cubos::core::ecs
// Variables for holding information after call chain is compiled.

std::vector<System*> mSystems; ///< Compiled order of running systems.
std::shared_ptr<GroupStep> mMainStep = std::make_shared<GroupStep>("main", nullptr);
std::shared_ptr<GroupStep> mCurrGroup = mMainStep;
};

inline void Dispatcher::tagAddCondition(ecs::System<bool> condition)
Expand All @@ -192,6 +321,14 @@ namespace cubos::core::ecs
mTagSettings[mCurrTag]->conditions |= bit;
}

inline void Dispatcher::tagRepeatWhile(ecs::System<bool> condition)
{
ENSURE_CURR_TAG();
groupAddGroup();
auto bit = assignConditionBit(std::move(condition));
mCurrGroup->conditions |= bit;
}

inline void Dispatcher::addSystem(ecs::System<void> system)
{
// Wrap the system and put it in the pending queue
Expand Down
6 changes: 6 additions & 0 deletions core/src/ecs/cubos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ TagBuilder& TagBuilder::after(const std::string& tag)
return *this;
}

TagBuilder& TagBuilder::tagged(const std::string& tag)

Check warning on line 50 in core/src/ecs/cubos.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/ecs/cubos.cpp#L50

Added line #L50 was not covered by tests
{
mDispatcher.tagInheritTag(tag);
return *this;

Check warning on line 53 in core/src/ecs/cubos.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/ecs/cubos.cpp#L52-L53

Added lines #L52 - L53 were not covered by tests
}

Cubos& Cubos::addPlugin(void (*func)(Cubos&))
{
if (!mPlugins.contains(func))
Expand Down
Loading

0 comments on commit 4c887c7

Please sign in to comment.