commit 7a305babb4f8a8c3d9110ce825039577042fe6d6 Author: Erris Date: Fri Apr 18 21:31:17 2025 +0200 The whole project diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/include/state-machine_pool.hpp b/include/state-machine_pool.hpp new file mode 100644 index 0000000..9e33e11 --- /dev/null +++ b/include/state-machine_pool.hpp @@ -0,0 +1,110 @@ +#ifndef STATEMACHINE_POOL_HPP +#define STATEMACHINE_POOL_HPP + +#include + +template +class StateMachinePool { + public: + void RegisterEntity(const std::string &id, StateOwnerType &entity) { + machines[id] = std::make_unique>(entity, factory); + } + + void UnregisterEntity(const std::string &id) { + machines.erase(id); + } + + void UpdateAll() { + for (const auto &[id, machine] : machines) + machine->Update(); + } + + void ChangeState(const std::string &id, const std::string &stateId) { + auto it = machines.find(id); + + if (it != machines.end()) + it->second->ChangeState(stateId); + } + + void RevertState(const std::string &id) { + auto it = machines.find(id); + + if (it != machines.end()) + it->second->RevertState(); + } + + void RegisterState(const std::string &id, std::shared_ptr> state) { + factory.RegisterState(id, state); + } + + StateMachine *GetMachine(const std::string &id) { + auto it = machines.find(id); + + return it != machines.end() ? it->second.get() : nullptr; + } + + json Serialize() const { + json j; + + for (const auto &[id, machine] : machines) { + j["machines"][id] = { + {"current", machine->GetCurrentStateId()}, + {"history", machine->GetHistory()} + }; + } + + j["settings"] = { + {"auto_transitions", auto_transition ? "true" : "false"}, + {"strict_transitions", strict_transition ? "true" : "false"} + }; + + return j; + } + + void Deserialize(const json &j, std::function getEntity) { + SetStrictTransitions(j.value("strict_transitions", false)); + SetAutoTransitions(j.value("auto_transitions", false)); + + const json &machines_json = j["machines"]; + + for (auto it = machines_json.begin(); it != machines_json.end(); ++it) { + std::string id = it.key(); + const json &entry = it.value(); + + std::string current = entry.value("current", "null"); + std::vector history = entry.value("history", std::vector{}); + + StateOwnerType& entity = getEntity(id); + RegisterEntity(id, entity); + + auto *machine = GetMachine(id); + + if (machine) { + machine->SetHistory(history); + machine->SetState(current); + } + } + } + + void SetStrictTransitions(bool enabled) { + strict_transition = enabled; + + for (auto &[_, machine] : machines) + machine->SetStrictTransitions(enabled); + } + + void SetAutoTransitions(bool enabled) { + auto_transition = enabled; + + for (const auto &[_, machine] : machines) + machine->SetAutoTransitions(enabled); + } + + private: + StateFactory factory; + std::unordered_map>> machines; + bool auto_transition = false; + bool strict_transition = false; +}; + +#endif // STATEMACHINE_POOL_HPP diff --git a/include/state.hpp b/include/state.hpp new file mode 100644 index 0000000..7852363 --- /dev/null +++ b/include/state.hpp @@ -0,0 +1,52 @@ +#ifndef STATE_HPP +#define STATE_HPP + +#include +#include +#include + +template +struct Transition { + const std::string target_state; + const std::function condition; +}; + +template +class State { + public: + virtual ~State() = default; + + virtual void OnEnter(OwnerType &owner) = 0; + virtual void OnUpdate(OwnerType &owner) = 0; + virtual void OnExit(OwnerType &owner) = 0; + + virtual void AddTransition(const std::string &to, + std::function condition) = 0; + virtual std::vector> GetTransitions() = 0; + + virtual std::string Id() const = 0; +}; + +template +class AState : public State { + public: + AState(const std::string &id) : entity_id(id) {}; + + void AddTransition(const std::string &to, + std::function condition) override { + transitions.push_back({ to, condition }); + } + std::vector> GetTransitions() override { + return transitions; + } + + std::string Id() const override { + return entity_id; + } + + private: + std::vector> transitions; + std::string entity_id; +}; + +#endif // STATE_HPP diff --git a/include/state_factory.hpp b/include/state_factory.hpp new file mode 100644 index 0000000..b6359d0 --- /dev/null +++ b/include/state_factory.hpp @@ -0,0 +1,33 @@ +#ifndef STATE_FACTORY_HPP +#define STATE_FACTORY_HPP + +#include +#include + + +template +class StateFactory { + using SharedState = std::shared_ptr>; + + public: + void RegisterState(const std::string &id, SharedState state) { + if (!state) + return; + + states[id] = std::move(state); + } + + SharedState Get(const std::string &id) const { + auto it = states.find(id); + return it != states.end() ? it->second : nullptr; + } + + const std::unordered_map All() const { + return states; + } + + private: + std::unordered_map states; +}; + +#endif // STATE_FACTORY_HPP diff --git a/include/state_machine.hpp b/include/state_machine.hpp new file mode 100644 index 0000000..8bfe412 --- /dev/null +++ b/include/state_machine.hpp @@ -0,0 +1,123 @@ +#ifndef STATE_MACHINE_HPP +#define STATE_MACHINE_HPP + +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +template +class StateMachine { + using SharedState = std::shared_ptr>; + + public: + StateMachine(T &owner, const StateFactory &factory) : owner(owner), factory(factory) {}; + + void ChangeState(const std::string &id) { + auto new_state = factory.Get(id); + if (!new_state) + return; + + if (strict_transition && current_state) { + bool allowed = false; + for (const auto &transition : current_state->GetTransitions()) { + if (transition.target_state == id && + (!transition.condition || transition.condition(owner))) { + allowed = true; + break; + } + } + if (!allowed) + return; + } + + if (current_state) { + current_state->OnExit(owner); + history.push_back(current_state->Id()); + } + + current_state = std::move(new_state); + current_state->OnEnter(owner); + }; + + void SetState(const std::string &id) { + auto new_state = factory.Get(id); + if (!new_state) + return; + + if (current_state) + current_state->OnExit(owner); + + current_state = std::move(new_state); + current_state->OnEnter(owner); + } + + void RevertState() { + if (history.empty()) + return; + + std::string last = history.back(); + history.pop_back(); + ChangeState(last); + } + + void Update() { + if (!current_state) + return; + + for (const auto &transition : current_state->GetTransitions()) + if (auto_transition && transition.condition(owner)) { + ChangeState(transition.target_state); + return; + } + + current_state->OnUpdate(owner); + } + + const std::vector &GetHistory() const { + return history; + } + + void SetHistory(const std::vector history) { + this->history = history; + } + + void ReplayHistory() { + for (const std::string &stateId : history) + ChangeState(stateId); + } + + const std::string GetCurrentStateId() const { + static const std::string nullState = "null"; + return current_state ? current_state->Id() : nullState; + } + + void SetStrictTransitions(bool enabled) { + this->strict_transition = enabled; + } + + bool GetStrictTransitions() const { + return strict_transition; + } + + void SetAutoTransitions(bool enabled) { + this->auto_transition = enabled; + } + + bool GetAutoTransitions() const { + return auto_transition; + } + + private: + T &owner; + const StateFactory &factory; + std::shared_ptr> current_state; + bool strict_transition = false; + bool auto_transition = false; + std::vector history; +}; + +#endif // STATE_MACHINE_HPP diff --git a/include/state_system.hpp b/include/state_system.hpp new file mode 100644 index 0000000..417375d --- /dev/null +++ b/include/state_system.hpp @@ -0,0 +1,70 @@ +#ifndef STATE_SYSTEM_HPP +#define STATE_SYSTEM_HPP + +#include +#include + +class StateSystem { + public: + template + void RegisterEntity(const std::string &id, T &object) { + GetPool().RegisterEntity(id, object); + } + + template + void UnregisterEntity(const std::string &id) { + GetPool().UnregisterEntity(id); + } + + template + void UpdateAll() { + GetPool().UpdateAll(); + } + + template + void ChangeState(const std::string &id, const std::string &stateId) { + GetPool().ChangeState(id, stateId); + } + + template + void RevertState(const std::string &id) { + GetPool().RevertState(id); + } + + template + void RegisterState(const std::string &id, std::shared_ptr> state) { + if (state) + GetPool().RegisterState(id, std::move(state)); + } + + template + json Serialize() const { + StateMachinePool &pool = GetPool(); + return pool.Serialize(); + } + + template + void Deserialize(const json& j, std::function getEntity) { + GetPool().Deserialize(j, getEntity); + } + + template + void SetStrictTransitions(bool enabled) { + GetPool().SetStrictTransitions(enabled); + } + + template + void SetAutoTransitions(bool enabled) { + GetPool().SetAutoTransitions(enabled); + } + + private: + template + StateMachinePool &GetPool() const { + static StateMachinePool pool; + return pool; + } +}; + + +#endif // STATE_SYSTEM_HPP