The whole project
This commit is contained in:
commit
7a305babb4
110
include/state-machine_pool.hpp
Normal file
110
include/state-machine_pool.hpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#ifndef STATEMACHINE_POOL_HPP
|
||||||
|
#define STATEMACHINE_POOL_HPP
|
||||||
|
|
||||||
|
#include <state_machine.hpp>
|
||||||
|
|
||||||
|
template<typename StateOwnerType>
|
||||||
|
class StateMachinePool {
|
||||||
|
public:
|
||||||
|
void RegisterEntity(const std::string &id, StateOwnerType &entity) {
|
||||||
|
machines[id] = std::make_unique<StateMachine<StateOwnerType>>(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<StateOwnerType>> state) {
|
||||||
|
factory.RegisterState(id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMachine<StateOwnerType> *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<StateOwnerType &(const std::string&)> 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<std::string> history = entry.value("history", std::vector<std::string>{});
|
||||||
|
|
||||||
|
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<StateOwnerType> factory;
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<StateMachine<StateOwnerType>>> machines;
|
||||||
|
bool auto_transition = false;
|
||||||
|
bool strict_transition = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATEMACHINE_POOL_HPP
|
||||||
52
include/state.hpp
Normal file
52
include/state.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef STATE_HPP
|
||||||
|
#define STATE_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
struct Transition {
|
||||||
|
const std::string target_state;
|
||||||
|
const std::function<bool (const Type&)> condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OwnerType>
|
||||||
|
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<bool (const OwnerType&)> condition) = 0;
|
||||||
|
virtual std::vector<Transition<OwnerType>> GetTransitions() = 0;
|
||||||
|
|
||||||
|
virtual std::string Id() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OwnerType>
|
||||||
|
class AState : public State<OwnerType> {
|
||||||
|
public:
|
||||||
|
AState(const std::string &id) : entity_id(id) {};
|
||||||
|
|
||||||
|
void AddTransition(const std::string &to,
|
||||||
|
std::function<bool (const OwnerType&)> condition) override {
|
||||||
|
transitions.push_back({ to, condition });
|
||||||
|
}
|
||||||
|
std::vector<Transition<OwnerType>> GetTransitions() override {
|
||||||
|
return transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Id() const override {
|
||||||
|
return entity_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Transition<OwnerType>> transitions;
|
||||||
|
std::string entity_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATE_HPP
|
||||||
33
include/state_factory.hpp
Normal file
33
include/state_factory.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef STATE_FACTORY_HPP
|
||||||
|
#define STATE_FACTORY_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <state.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
template <typename OwnerType>
|
||||||
|
class StateFactory {
|
||||||
|
using SharedState = std::shared_ptr<State<OwnerType>>;
|
||||||
|
|
||||||
|
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<std::string, SharedState> All() const {
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, SharedState> states;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATE_FACTORY_HPP
|
||||||
123
include/state_machine.hpp
Normal file
123
include/state_machine.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#ifndef STATE_MACHINE_HPP
|
||||||
|
#define STATE_MACHINE_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <state_factory.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class StateMachine {
|
||||||
|
using SharedState = std::shared_ptr<State<T>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateMachine(T &owner, const StateFactory<T> &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<std::string> &GetHistory() const {
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetHistory(const std::vector<std::string> 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<T> &factory;
|
||||||
|
std::shared_ptr<State<T>> current_state;
|
||||||
|
bool strict_transition = false;
|
||||||
|
bool auto_transition = false;
|
||||||
|
std::vector<std::string> history;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATE_MACHINE_HPP
|
||||||
70
include/state_system.hpp
Normal file
70
include/state_system.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef STATE_SYSTEM_HPP
|
||||||
|
#define STATE_SYSTEM_HPP
|
||||||
|
|
||||||
|
#include <state_machine.hpp>
|
||||||
|
#include <state-machine_pool.hpp>
|
||||||
|
|
||||||
|
class StateSystem {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
void RegisterEntity(const std::string &id, T &object) {
|
||||||
|
GetPool<T>().RegisterEntity(id, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void UnregisterEntity(const std::string &id) {
|
||||||
|
GetPool<T>().UnregisterEntity(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void UpdateAll() {
|
||||||
|
GetPool<T>().UpdateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ChangeState(const std::string &id, const std::string &stateId) {
|
||||||
|
GetPool<T>().ChangeState(id, stateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RevertState(const std::string &id) {
|
||||||
|
GetPool<T>().RevertState(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RegisterState(const std::string &id, std::shared_ptr<State<T>> state) {
|
||||||
|
if (state)
|
||||||
|
GetPool<T>().RegisterState(id, std::move(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
json Serialize() const {
|
||||||
|
StateMachinePool<T> &pool = GetPool<T>();
|
||||||
|
return pool.Serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Deserialize(const json& j, std::function<T &(const std::string&)> getEntity) {
|
||||||
|
GetPool<T>().Deserialize(j, getEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SetStrictTransitions(bool enabled) {
|
||||||
|
GetPool<T>().SetStrictTransitions(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SetAutoTransitions(bool enabled) {
|
||||||
|
GetPool<T>().SetAutoTransitions(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename T>
|
||||||
|
StateMachinePool<T> &GetPool() const {
|
||||||
|
static StateMachinePool<T> pool;
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // STATE_SYSTEM_HPP
|
||||||
Loading…
x
Reference in New Issue
Block a user