Skip to content

Commit

Permalink
+ added serialization support
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew-gresyk committed May 16, 2020
1 parent 56410a0 commit ed1a9ef
Show file tree
Hide file tree
Showing 58 changed files with 3,395 additions and 1,322 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,36 @@ Header-only heriarchical FSM framework in C++14, with fully statically-defined s

- Visual Studio 14, 15, 16
- GCC 5, 6, 7, 8, 9
- Clang 3.7, 3.8, 3.9, 4, 5, 6, 7, 8
- Clang 3.7, 3.8, 3.9, 4, 5, 6, 7, 8, 9, 10

---

## Tutorial
## Documentation

Check [Wiki](https://github.com/andrew-gresyk/HFSM2/wiki/Tutorial) for basic usage and more info.
[Old Wiki](https://github.com/andrew-gresyk/HFSM2/wiki/Tutorial) docs are being upgraded and moved to the [New GitBook](https://doc.hfsm.dev/).
In-line comment-based docs is an on-going effort.

---

## Feature Highlights

- Permissive [MIT License](https://github.com/andrew-gresyk/HFSM2/blob/master/LICENSE)
- Written in widely-supported modern(ish) C++14
- Written in widely-supported modern(ish) C++11
- Header-only
- Convenient, minimal boilerplate
- Fully static, no dynamic allocations
- Uses inline-friendly compile-time pylymorphism, no virtual methods were harmed
- Type-safe transitions: `FSM.changeTo<TargetState>()`
- 100% NoUML-compliant
- [Hierarchical](https://github.com/andrew-gresyk/HFSM2/wiki/Transitions-within-Hierarchy), with a selection of composite (sub-machine) and orthogonal regions
- Gamedev-friendly, supports explicit `State::update()`
- Also supports traditional event-based workflow with `State::react()`
- [Dynamic planning](https://github.com/andrew-gresyk/HFSM2/wiki/Plans) support
- AI-friendly with [Dynamic planning](https://github.com/andrew-gresyk/HFSM2/wiki/Plans) support
- [Utility theory](https://github.com/andrew-gresyk/HFSM2/wiki/Utility-Theory) support (both max score and ranked weighted random)
- Scaleable, supports robust state re-use via state injections
- [Debug-assisted](https://gresyk.dev/features/2018/01/15/hfsm-magic.html), includes automatic structure and activity visualization API with `#define HFSM_ENABLE_STRUCTURE_REPORT`
- [Serializable](https://doc.hfsm.dev/user-guide/debugging-and-tools/serialization), with activity and transition history support
- Built-in logging support
- Convenient, minimal boilerplate

---

Expand All @@ -56,6 +58,15 @@ Check [Wiki](https://github.com/andrew-gresyk/HFSM2/wiki/Tutorial) for basic usa

---

## Get In Touch

Please share your comments and suggestions on:

- [Gitter](https://gitter.im/andrew-gresyk/HFSM2)
- [Twitter](https://www.twitter.com/andrew_gresyk)

---

## Special Thanks

- [Kevin Greene](https://github.com/kgreenek)
Expand Down
50 changes: 50 additions & 0 deletions examples/snippets/docs_serialization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#define HFSM_ENABLE_SERIALIZATION

//#include <hfsm2/machine.hpp>
#include <hfsm2/machine_dev.hpp>

#include <catch2/catch.hpp>
#undef assert
#define assert(x) REQUIRE(x)

using M = hfsm2::Machine;

using FSM = M::PeerRoot<
struct State1,
struct State2
>;

struct State1 : FSM::State { /* .. */ };
struct State2 : FSM::State { /* .. */ };

//------------------------------------------------------------------------------

TEST_CASE("Docs.Serialization", "[Docs]") {
// Buffer for serialization
// Members:
// bitSize - Number of payload bits used
// payload - Serialized data
FSM::Instance::SerialBuffer buffer;

{
FSM::Instance fsm; // Create a new FSM instance
fsm.changeTo<State2>(); // Request a transition to 'State2'
fsm.update(); // Process transitions
assert(fsm.isActive<State2>()); // Check if transition completed

fsm.save(buffer); // Serialize FSM configuration into 'buffer'
}

{
FSM::Instance fsm; // Create a fresh FSM instance
assert(fsm.isActive<State1>()); // Initial 'State1' is activated by default

fsm.load(buffer); // De-serialize FSM from 'buffer'
assert(fsm.isActive<State2>()); // Check its configuration is restored
}
}

////////////////////////////////////////////////////////////////////////////////
2 changes: 1 addition & 1 deletion examples/snippets/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// HFSM (hierarchical state machine for games and interactive applications)
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#define CATCH_CONFIG_MAIN
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/wiki_plans.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// HFSM (hierarchical state machine for games and interactive applications)
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#include <hfsm2/machine.hpp>
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/wiki_transitions-within-hierarchy.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// HFSM (hierarchical state machine for games and interactive applications)
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#include <hfsm2/machine.hpp>
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/wiki_tutorial.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// HFSM (hierarchical state machine for games and interactive applications)
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#include <hfsm2/machine.hpp>
Expand Down
2 changes: 1 addition & 1 deletion examples/snippets/wiki_utility-theory.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// HFSM (hierarchical state machine for games and interactive applications)
// HFSM2 (hierarchical state machine for games and interactive applications)
// Created by Andrew Gresyk

#define HFSM_ENABLE_LOG_INTERFACE
Expand Down
106 changes: 64 additions & 42 deletions include/hfsm2/detail/root.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class R_ {
using Logger = typename Config_::Logger;

using Apex = TApex;

using ApexInfo = WrapInfo<Apex>;

using Info = RF_<Config_, Apex>;
using StateList = typename Info::StateList;
using RegionList = typename Info::RegionList;
Expand All @@ -30,6 +30,9 @@ class R_ {
static constexpr ShortIndex ORTHO_REGIONS = ApexInfo::ORTHO_REGIONS;
static constexpr ShortIndex ORTHO_UNITS = ApexInfo::ORTHO_UNITS;

static constexpr LongIndex ACTIVE_BITS = ApexInfo::ACTIVE_BITS;
static constexpr LongIndex RESUMABLE_BITS = ApexInfo::RESUMABLE_BITS;

static constexpr LongIndex STATE_COUNT = ApexInfo::STATE_COUNT;
static constexpr LongIndex REGION_COUNT = ApexInfo::REGION_COUNT;

Expand All @@ -39,9 +42,9 @@ class R_ {
private:
using Args = typename Info::Args;

using StateRegistry = StateRegistryT<Args>;
using CompoForks = typename StateRegistry::CompoForks;
using AllForks = typename StateRegistry::AllForks;
using Registry = RegistryT<Args>;
using CompoForks = typename Registry::CompoForks;
using RegistryBackUp = typename Registry::BackUp;

using MaterialApex = Material<I_<0, 0, 0, 0>, Args, Apex>;

Expand All @@ -55,23 +58,26 @@ class R_ {

using GuardControl = GuardControlT<Args>;

public:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#ifdef HFSM_ENABLE_STRUCTURE_REPORT
using StructureStateInfos = typename Args::StructureStateInfos;

static constexpr LongIndex NAME_COUNT = MaterialApex::NAME_COUNT;

using Prefix = StaticArray<wchar_t, REVERSE_DEPTH * 2 + 2>;
using Prefixes = StaticArray<Prefix, STATE_COUNT>;

using Structure = Array<StructureEntry, NAME_COUNT>;
using ActivityHistory = Array<char, NAME_COUNT>;
using StructureStateInfos = typename Args::StructureStateInfos;
#endif

#ifdef HFSM_ENABLE_TRANSITION_HISTORY
using TransitionHistory = Array<Transition, COMPO_REGIONS * 4>;
#ifdef HFSM_ENABLE_SERIALIZATION
using WriteStream = typename Args::WriteStream;
using ReadStream = typename Args::ReadStream;
#endif

public:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#ifdef HFSM_ENABLE_STRUCTURE_REPORT
using Structure = Array<StructureEntry, NAME_COUNT>;
using ActivityHistory = Array<char, NAME_COUNT>;
#endif

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand All @@ -86,10 +92,10 @@ class R_ {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

template <typename T>
static constexpr StateID stateId() { return StateList ::template index<T>(); }
static constexpr StateID stateId() { return StateList ::template index<T>(); }

template <typename T>
static constexpr RegionID regionId() { return (RegionID) RegionList::template index<T>(); }
static constexpr RegionID regionId() { return (RegionID) RegionList::template index<T>(); }

//----------------------------------------------------------------------

Expand All @@ -109,8 +115,8 @@ class R_ {

template <typename T>
struct Accessor<T, true> {
HFSM_INLINE static T& get( MaterialApex& apex) { return apex.template access<T>(); }
HFSM_INLINE static const T& get(const MaterialApex& apex) { return apex.template access<T>(); }
HFSM_INLINE static T& get( MaterialApex& apex) { return apex.template access<T>(); }
HFSM_INLINE static const T& get(const MaterialApex& apex) { return apex.template access<T>(); }
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand All @@ -122,20 +128,20 @@ class R_ {
// .. you're trying to access() a type that is not present in the state machine hierarchy

template <typename T>
HFSM_INLINE T& access() { return Accessor<T>::get(_apex); }
HFSM_INLINE T& access() { return Accessor<T>::get(_apex); }

template <typename T>
HFSM_INLINE const T& access() const { return Accessor<T>::get(_apex); }
HFSM_INLINE const T& access() const { return Accessor<T>::get(_apex); }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#else

template <typename T>
HFSM_INLINE T& access() { return Accessor<T, MaterialApex>{_apex}.get(); }
HFSM_INLINE T& access() { return Accessor<T, MaterialApex>{_apex}.get(); }

template <typename T>
HFSM_INLINE const T& access() const { return Accessor<T, const MaterialApex>{_apex}.get(); }
HFSM_INLINE const T& access() const { return Accessor<T, const MaterialApex>{_apex}.get(); }

#endif

Expand All @@ -148,19 +154,19 @@ class R_ {

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

HFSM_INLINE bool isActive (const StateID stateId) const { return _stateRegistry.isActive (stateId); }
HFSM_INLINE bool isResumable(const StateID stateId) const { return _stateRegistry.isResumable(stateId); }
HFSM_INLINE bool isActive (const StateID stateId) const { return _registry.isActive (stateId); }
HFSM_INLINE bool isResumable(const StateID stateId) const { return _registry.isResumable(stateId); }

HFSM_INLINE bool isScheduled(const StateID stateId) const { return isResumable(stateId); }
HFSM_INLINE bool isScheduled(const StateID stateId) const { return isResumable(stateId); }

template <typename TState>
HFSM_INLINE bool isActive () const { return isActive (stateId<TState>()); }
HFSM_INLINE bool isActive () const { return isActive (stateId<TState>()); }

template <typename TState>
HFSM_INLINE bool isResumable() const { return isResumable(stateId<TState>()); }
HFSM_INLINE bool isResumable() const { return isResumable(stateId<TState>()); }

template <typename TState>
HFSM_INLINE bool isScheduled() const { return isResumable<TState>(); }
HFSM_INLINE bool isScheduled() const { return isResumable<TState>(); }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Expand All @@ -174,42 +180,58 @@ class R_ {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

template <typename TState>
HFSM_INLINE void changeTo () { changeTo (stateId<TState>()); }
HFSM_INLINE void changeTo () { changeTo (stateId<TState>()); }

template <typename TState>
HFSM_INLINE void restart () { restart (stateId<TState>()); }
HFSM_INLINE void restart () { restart (stateId<TState>()); }

template <typename TState>
HFSM_INLINE void resume () { resume (stateId<TState>()); }
HFSM_INLINE void resume () { resume (stateId<TState>()); }

template <typename TState>
HFSM_INLINE void utilize () { utilize (stateId<TState>()); }
HFSM_INLINE void utilize () { utilize (stateId<TState>()); }

template <typename TState>
HFSM_INLINE void randomize() { randomize(stateId<TState>()); }
HFSM_INLINE void randomize() { randomize(stateId<TState>()); }

template <typename TState>
HFSM_INLINE void schedule () { schedule (stateId<TState>()); }
HFSM_INLINE void schedule () { schedule (stateId<TState>()); }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

HFSM_INLINE void clearResumable() { _stateRegistry.resumable.clear(); }
HFSM_INLINE void clearRequests() { _stateRegistry.requested.clear(); }

#ifdef HFSM_ENABLE_STRUCTURE_REPORT
const Structure& structure() const { return _structure; }
const ActivityHistory& activityHistory() const { return _activityHistory; }
const Structure& structure() const { return _structure; }
const ActivityHistory& activityHistory() const { return _activityHistory; }
#endif

#ifdef HFSM_ENABLE_TRANSITION_HISTORY
const TransitionHistory& transitionHistory() const { return _transitionHistory; }
using TransitionHistory = Array<Transition, COMPO_REGIONS * 4>;

const TransitionHistory& transitionHistory() const { return _transitionHistory; }

void replayTransition (const Transition& transition) { replayTransitions(&transition, 1); }
void replayTransition (const Transition& transition) { replayTransitions(&transition, 1); }
void replayTransitions(const Transition* const transitions, const uint64_t count);
#endif

void reset();

#ifdef HFSM_ENABLE_SERIALIZATION
// Buffer for serialization
// Members:
// bitSize - Number of payload bits used
// payload - Serialized data
// See https://doc.hfsm.dev/user-guide/debugging-and-tools/serialization
using SerialBuffer = typename Args::SerialBuffer;

// Serialize the structural configuration into 'buffer'
void save( SerialBuffer& buffer) const;

// De-serialize the configuration and initialize the instance
void load(const SerialBuffer& buffer);
#endif

#if defined HFSM_ENABLE_LOG_INTERFACE || defined HFSM_ENABLE_VERBOSE_DEBUG_LOG
void attachLogger(Logger* const logger) { _logger = logger; }
void attachLogger(Logger* const logger) { _logger = logger; }
#endif

private:
Expand Down Expand Up @@ -239,7 +261,7 @@ class R_ {
Context& _context;
RNG& _rng;

StateRegistry _stateRegistry;
Registry _registry;
PlanData _planData;

Requests _requests;
Expand Down
Loading

0 comments on commit ed1a9ef

Please sign in to comment.