From 156af7c122c756ff2eb081f76c1bd3ba3dc142ad Mon Sep 17 00:00:00 2001 From: Andrew Gresyk Date: Sat, 1 Feb 2025 21:39:54 +0200 Subject: [PATCH] 2.6.0 No longer clear composite region's plan on exiting it to allow continuation of plan execution on resuming the region --- development/hfsm2/detail/root/control_3.inl | 8 - .../hfsm2/detail/structure/ancestors_2.hpp | 4 +- .../hfsm2/detail/structure/ancestors_2.inl | 23 +++ .../hfsm2/detail/structure/composite.inl | 5 - development/hfsm2/machine_dev.hpp | 6 +- include/hfsm2/machine.hpp | 42 +++--- projects/premake/hfsm2-lite.sln | 5 + projects/premake/test-14.vcxproj | 1 + projects/premake/test-14.vcxproj.filters | 3 + projects/premake/test-15.vcxproj | 1 + projects/premake/test-15.vcxproj.filters | 3 + projects/premake/test-16.vcxproj | 1 + projects/premake/test-16.vcxproj.filters | 3 + projects/premake/test-17.vcxproj | 1 + projects/premake/test-17.vcxproj.filters | 3 + projects/premake/test-clang.vcxproj | 1 + projects/premake/test-clang.vcxproj.filters | 3 + test/reported/issue_49.cpp | 1 + test/reported/resuming_plan.cpp | 141 ++++++++++++++++++ 19 files changed, 219 insertions(+), 36 deletions(-) create mode 100644 test/reported/resuming_plan.cpp diff --git a/development/hfsm2/detail/root/control_3.inl b/development/hfsm2/detail/root/control_3.inl index e72a2dd..fd369a3 100644 --- a/development/hfsm2/detail/root/control_3.inl +++ b/development/hfsm2/detail/root/control_3.inl @@ -232,8 +232,6 @@ FullControlT(); } else if (subStatus.result == TaskStatus::SUCCESS) { if (Plan p = plan(_regionId)) { @@ -268,8 +266,6 @@ FullControlT(); } } else @@ -422,8 +418,6 @@ FullControlT(); } else if (subStatus.result == TaskStatus::SUCCESS) { if (Plan p = plan(_regionId)) { @@ -455,8 +449,6 @@ FullControlT(); } } else diff --git a/development/hfsm2/detail/structure/ancestors_2.hpp b/development/hfsm2/detail/structure/ancestors_2.hpp index a74ead4..82f955d 100644 --- a/development/hfsm2/detail/structure/ancestors_2.hpp +++ b/development/hfsm2/detail/structure/ancestors_2.hpp @@ -77,8 +77,8 @@ struct HFSM2_EMPTY_BASES A_ HFSM2_CONSTEXPR(14) void exit ( PlanControl& ) noexcept {} #if HFSM2_PLANS_AVAILABLE() - HFSM2_CONSTEXPR(14) void planSucceeded ( FullControl& control) noexcept { control.succeed(); } - HFSM2_CONSTEXPR(14) void planFailed ( FullControl& control) noexcept { control.fail(); } + HFSM2_CONSTEXPR(14) void planSucceeded ( FullControl& control) noexcept; + HFSM2_CONSTEXPR(14) void planFailed ( FullControl& control) noexcept; #endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/development/hfsm2/detail/structure/ancestors_2.inl b/development/hfsm2/detail/structure/ancestors_2.inl index aa32fd5..2bf0324 100644 --- a/development/hfsm2/detail/structure/ancestors_2.inl +++ b/development/hfsm2/detail/structure/ancestors_2.inl @@ -3,6 +3,29 @@ namespace detail { //////////////////////////////////////////////////////////////////////////////// +#if HFSM2_PLANS_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +A_::planSucceeded(FullControl& control) noexcept { + control.succeed(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +HFSM2_CONSTEXPR(14) +void +A_::planFailed(FullControl& control) noexcept { + control.plan().clear(); + control.fail(); +} + +#endif + +//------------------------------------------------------------------------------ + template HFSM2_CONSTEXPR(14) void diff --git a/development/hfsm2/detail/structure/composite.inl b/development/hfsm2/detail/structure/composite.inl index 58af3b8..0adfacb 100644 --- a/development/hfsm2/detail/structure/composite.inl +++ b/development/hfsm2/detail/structure/composite.inl @@ -338,11 +338,6 @@ C_::deepExit(PlanControl& control) noexcept { resumable = active; active = INVALID_PRONG; - -#if HFSM2_PLANS_AVAILABLE() - Plan plan = control.plan(REGION_ID); - plan.clear(); -#endif } // COMMON diff --git a/development/hfsm2/machine_dev.hpp b/development/hfsm2/machine_dev.hpp index 91d0e63..c7ff794 100644 --- a/development/hfsm2/machine_dev.hpp +++ b/development/hfsm2/machine_dev.hpp @@ -1,5 +1,5 @@ // HFSM2 (hierarchical state machine for games and interactive applications) -// 2.5.2 (2025-01-10) +// 2.6.0 (2025-02-01) // // Created by Andrew Gresyk // @@ -32,8 +32,8 @@ #pragma once #define HFSM2_VERSION_MAJOR 2 -#define HFSM2_VERSION_MINOR 5 -#define HFSM2_VERSION_PATCH 2 +#define HFSM2_VERSION_MINOR 6 +#define HFSM2_VERSION_PATCH 0 #define HFSM2_VERSION (10000 * HFSM2_VERSION_MAJOR + 100 * HFSM2_VERSION_MINOR + HFSM2_VERSION_PATCH) diff --git a/include/hfsm2/machine.hpp b/include/hfsm2/machine.hpp index 92fab1f..5132e7c 100644 --- a/include/hfsm2/machine.hpp +++ b/include/hfsm2/machine.hpp @@ -1,5 +1,5 @@ // HFSM2 (hierarchical state machine for games and interactive applications) -// 2.5.2 (2025-01-10) +// 2.6.0 (2025-02-01) // // Created by Andrew Gresyk // @@ -32,8 +32,8 @@ #pragma once #define HFSM2_VERSION_MAJOR 2 -#define HFSM2_VERSION_MINOR 5 -#define HFSM2_VERSION_PATCH 2 +#define HFSM2_VERSION_MINOR 6 +#define HFSM2_VERSION_PATCH 0 #define HFSM2_VERSION (10000 * HFSM2_VERSION_MAJOR + 100 * HFSM2_VERSION_MINOR + HFSM2_VERSION_PATCH) @@ -7142,8 +7142,6 @@ FullControlT(); } else if (subStatus.result == TaskStatus::SUCCESS) { if (Plan p = plan(_regionId)) { @@ -7178,8 +7176,6 @@ FullControlT(); } } else @@ -7316,8 +7312,6 @@ FullControlT(); } else if (subStatus.result == TaskStatus::SUCCESS) { if (Plan p = plan(_regionId)) { @@ -7349,8 +7343,6 @@ FullControlT(); } } else @@ -7857,8 +7849,8 @@ struct HFSM2_EMPTY_BASES A_ HFSM2_CONSTEXPR(14) void exit ( PlanControl& ) noexcept {} #if HFSM2_PLANS_AVAILABLE() - HFSM2_CONSTEXPR(14) void planSucceeded ( FullControl& control) noexcept { control.succeed(); } - HFSM2_CONSTEXPR(14) void planFailed ( FullControl& control) noexcept { control.fail(); } + HFSM2_CONSTEXPR(14) void planSucceeded ( FullControl& control) noexcept; + HFSM2_CONSTEXPR(14) void planFailed ( FullControl& control) noexcept; #endif HFSM2_CONSTEXPR(14) void wideEntryGuard(GuardControl& control) noexcept; @@ -7898,6 +7890,25 @@ struct HFSM2_EMPTY_BASES A_ namespace hfsm2 { namespace detail { +#if HFSM2_PLANS_AVAILABLE() + +template +HFSM2_CONSTEXPR(14) +void +A_::planSucceeded(FullControl& control) noexcept { + control.succeed(); +} + +template +HFSM2_CONSTEXPR(14) +void +A_::planFailed(FullControl& control) noexcept { + control.plan().clear(); + control.fail(); +} + +#endif + template HFSM2_CONSTEXPR(14) void @@ -11998,11 +12009,6 @@ C_::deepExit(PlanControl& control) noexcept { resumable = active; active = INVALID_PRONG; - -#if HFSM2_PLANS_AVAILABLE() - Plan plan = control.plan(REGION_ID); - plan.clear(); -#endif } template diff --git a/projects/premake/hfsm2-lite.sln b/projects/premake/hfsm2-lite.sln index 0b3991b..fa1155e 100644 --- a/projects/premake/hfsm2-lite.sln +++ b/projects/premake/hfsm2-lite.sln @@ -21,6 +21,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-clang", "test-clang.vc EndProject Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "tools", "tools.pyproj", "{2347027E-E5DE-43C4-8EF9-5F1411ADE33B}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{CAF6FD08-F000-458A-9DC3-74685C05D7F2}" + ProjectSection(SolutionItems) = preProject + ..\..\hfsm2.natvis = ..\..\hfsm2.natvis + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/projects/premake/test-14.vcxproj b/projects/premake/test-14.vcxproj index 0fbd80b..4ef7e73 100644 --- a/projects/premake/test-14.vcxproj +++ b/projects/premake/test-14.vcxproj @@ -269,6 +269,7 @@ + diff --git a/projects/premake/test-14.vcxproj.filters b/projects/premake/test-14.vcxproj.filters index 55f3e1d..a8e0991 100644 --- a/projects/premake/test-14.vcxproj.filters +++ b/projects/premake/test-14.vcxproj.filters @@ -284,6 +284,9 @@ test\reported + + test\reported + test\shared diff --git a/projects/premake/test-15.vcxproj b/projects/premake/test-15.vcxproj index 7a7a612..67478fb 100644 --- a/projects/premake/test-15.vcxproj +++ b/projects/premake/test-15.vcxproj @@ -273,6 +273,7 @@ + diff --git a/projects/premake/test-15.vcxproj.filters b/projects/premake/test-15.vcxproj.filters index 55f3e1d..a8e0991 100644 --- a/projects/premake/test-15.vcxproj.filters +++ b/projects/premake/test-15.vcxproj.filters @@ -284,6 +284,9 @@ test\reported + + test\reported + test\shared diff --git a/projects/premake/test-16.vcxproj b/projects/premake/test-16.vcxproj index 25d5328..e89fef6 100644 --- a/projects/premake/test-16.vcxproj +++ b/projects/premake/test-16.vcxproj @@ -261,6 +261,7 @@ + diff --git a/projects/premake/test-16.vcxproj.filters b/projects/premake/test-16.vcxproj.filters index 55f3e1d..a8e0991 100644 --- a/projects/premake/test-16.vcxproj.filters +++ b/projects/premake/test-16.vcxproj.filters @@ -284,6 +284,9 @@ test\reported + + test\reported + test\shared diff --git a/projects/premake/test-17.vcxproj b/projects/premake/test-17.vcxproj index d8ccf30..806b834 100644 --- a/projects/premake/test-17.vcxproj +++ b/projects/premake/test-17.vcxproj @@ -265,6 +265,7 @@ + diff --git a/projects/premake/test-17.vcxproj.filters b/projects/premake/test-17.vcxproj.filters index 55f3e1d..a8e0991 100644 --- a/projects/premake/test-17.vcxproj.filters +++ b/projects/premake/test-17.vcxproj.filters @@ -284,6 +284,9 @@ test\reported + + test\reported + test\shared diff --git a/projects/premake/test-clang.vcxproj b/projects/premake/test-clang.vcxproj index 373c074..f91ab1d 100644 --- a/projects/premake/test-clang.vcxproj +++ b/projects/premake/test-clang.vcxproj @@ -269,6 +269,7 @@ + diff --git a/projects/premake/test-clang.vcxproj.filters b/projects/premake/test-clang.vcxproj.filters index 55f3e1d..a8e0991 100644 --- a/projects/premake/test-clang.vcxproj.filters +++ b/projects/premake/test-clang.vcxproj.filters @@ -284,6 +284,9 @@ test\reported + + test\reported + test\shared diff --git a/test/reported/issue_49.cpp b/test/reported/issue_49.cpp index efa3b7a..478792c 100644 --- a/test/reported/issue_49.cpp +++ b/test/reported/issue_49.cpp @@ -76,6 +76,7 @@ struct A : FSM::State void enter(PlanControl& control) { auto plan = control.plan(); + plan.clear(); plan.change(); plan.change(); diff --git a/test/reported/resuming_plan.cpp b/test/reported/resuming_plan.cpp new file mode 100644 index 0000000..02ca654 --- /dev/null +++ b/test/reported/resuming_plan.cpp @@ -0,0 +1,141 @@ +// HFSM2 (hierarchical state machine for games and interactive applications) +// Created by Andrew Gresyk + +// Issue reported in https://discord.com/channels/755015945269018695/759099438353350696/1334175930188300348 + +#define HFSM2_ENABLE_PLANS +#include + +#include + +using M = hfsm2::Machine; + +struct Manual; +struct Automatic; +struct Idling; +struct WaitInput; +struct WorkOnBox; +struct BoxA; +struct BoxB; +struct BoxC; + +enum class Input { retry, stop }; + +struct BoxPresent { }; + +// clang-format off +using FSM = M::PeerRoot< + Manual, + M::Composite + > + >; +// clang-format on + +struct Manual : FSM::State { }; + +struct Automatic : FSM::State { }; + +struct Idling : FSM::State { + using FSM::State::react; + + void react(const BoxPresent&, EventControl& c) + { + auto plan = c.plan(); + + plan.change(); + plan.change(); + + c.changeTo(); + } +}; + +struct WaitInput : FSM::State { + using FSM::State::react; + + void react(const Input& e, EventControl& c) + { + if (e == Input::retry) + c.resume(); + else + c.changeTo(); + } +}; + +struct WorkOnBox : FSM::State { + void planSucceeded(FullControl& c) + { + c.changeTo(); + } + + void planFailed(FullControl& c) + { + c.changeTo(); + } +}; + +struct BoxA : FSM::State { + void update(FullControl& c) + { + c.succeed(); + } +}; + +struct BoxB : FSM::State { + bool should_fail = true; + + void update(FullControl& c) + { + if (should_fail) + c.fail(); + else + c.succeed(); + + should_fail = !should_fail; + } +}; + +struct BoxC : FSM::State { + void update(FullControl& c) + { + c.succeed(); + } +}; + +TEST_CASE("FSM.Reported.Resuming Plan") { + FSM::Instance machine; + + machine.immediateChangeTo(); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + + machine.react(BoxPresent {}); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + + machine.update(); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + + machine.update(); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + + machine.react(Input::retry); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + + machine.update(); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); + REQUIRE(machine.isActive()); +}