Skip to main content

StateManager.ixx File

Generic state manager and transition orchestrator. More...

Included Headers

Namespaces Index

namespacehelios
namespaceengine

Main engine module aggregating core infrastructure and game systems. More...

namespacestate

Generic, template-based state management system. More...

Classes Index

classStateManager<StateType>

Manages state transitions using a rule-based system. More...

Description

Generic state manager and transition orchestrator.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file StateManager.ixx
3 * @brief Generic state manager and transition orchestrator.
4 */
5module;
6
7#include <memory>
8#include <span>
9#include <vector>
10
11
12export module helios.engine.state.StateManager;
13
14
15import helios.engine.state.TypedStateCommandHandler;
16import helios.engine.state.StateTransitionListener;
17import helios.engine.state.types.StateTransitionRule;
18
19import helios.engine.state.components;
20import helios.engine.state.commands;
21import helios.engine.state.types;
22
23import helios.engine.ecs.GameObject;
24
25import helios.engine.runtime.messaging.command;
26
27import helios.engine.runtime.world.UpdateContext;
28
29import helios.engine.runtime.world.GameWorld;
30import helios.engine.runtime.world.Session;
31
32import helios.engine.runtime.world.Session;
33
34import helios.core.types;
35import helios.util.Guid;
36import helios.engine.common;
37
38using namespace helios::engine::state::types;
41
42export namespace helios::engine::state {
43
44
45 /**
46 * @brief Manages state transitions using a rule-based system.
47 *
48 * @details Implements both Manager and TypedStateCommandHandler interfaces.
49 * Processes state transition commands by matching against registered rules,
50 * executing guards, and notifying listeners.
51 *
52 * ## Transition Flow
53 *
54 * 1 Commands are submitted via submit() and queued
55 * 2 During flush(), the last pending command is processed
56 * 3 Rules are checked to find a matching transition
57 * 4 If a guard is present, it must return true
58 * 5 Listeners are notified: onStateExit -> onStateTransition
59 * 6 Session state is updated via StateComponent
60 * 7 Listeners are notified: onStateEnter (session already reflects new state)
61 *
62 * @tparam StateType The state enum type.
63 *
64 * @see StateTransitionRule
65 * @see StateTransitionListener
66 * @see StateCommand
67 */
68 template <typename StateType>
70
71 /**
72 * @brief Queue of pending state commands.
73 */
74 std::vector<StateCommand<StateType>> pending_;
75
76 /**
77 * @brief Registered transition listeners.
78 */
79 std::vector<std::unique_ptr<StateTransitionListener<StateType>>> listeners_;
80
81 /**
82 * @brief Transition rules defining valid state changes.
83 */
84 std::vector<StateTransitionRule<StateType>> rules_;
85
86 /**
87 * @brief Notifies listeners of state exit.
88 *
89 * @param from The state being exited.
90 * @param to The target state.
91 * @param transitionId The transition identifier.
92 * @param updateContext The current frame's update context.
93 */
94 void signalExit(
95 const StateType from,
96 const StateType to,
97 const StateTransitionIdType<StateType> transitionId,
99
100 for (auto& listener : listeners_) {
101 listener->onStateExit(updateContext, from);
102 }
103 }
104
105 /**
106 * @brief Notifies listeners of the transition.
107 *
108 * @param from The source state.
109 * @param to The target state.
110 * @param transitionId The transition identifier.
111 * @param updateContext The current frame's update context.
112 */
113 void signalTransition(
114 const StateType from,
115 const StateType to,
116 const StateTransitionIdType<StateType> transitionId,
118
119 for (auto& listener : listeners_) {
120 listener->onStateTransition(
121 updateContext,
122 StateTransitionContext<StateType>{from, to, transitionId}
123 );
124 }
125 }
126
127 /**
128 * @brief Notifies listeners of state entry.
129 *
130 * @param from The source state.
131 * @param to The state being entered.
132 * @param transitionId The transition identifier.
133 * @param updateContext The current frame's update context.
134 */
135 void signalEnter(
136 const StateType from,
137 const StateType to,
138 const StateTransitionIdType<StateType> transitionId,
140
141 for (auto& listener : listeners_) {
142 listener->onStateEnter(updateContext, to);
143
144 }
145 }
146
147
148 public:
150
151 /**
152 * @brief Constructs a state manager with transition rules.
153 *
154 * @param rules Span of valid transition rules.
155 */
156 explicit StateManager(std::span<const StateTransitionRule<StateType>> rules)
157 : rules_(rules.begin(), rules.end()) {}
158
159 /**
160 * @brief Registers a state transition listener.
161 *
162 * @param listener The listener to add.
163 *
164 * @return Reference to this manager for chaining.
165 */
167 listeners_.push_back(std::move(listener));
168 return *this;
169 }
170
171 /**
172 * @brief Processes pending state commands.
173 *
174 * @details Processes the last pending command, finds matching rules,
175 * executes guards, and triggers the transition if valid.
176 *
177 * @param updateContext The current frame's update context.
178 */
179 void flush(
181 ) noexcept {
182
183 if (pending_.empty()) {
184 return;
185 }
186
187 auto command = pending_.back();
188
189 auto transitionRequest = command.transitionRequest();
190
191 auto& session = updateContext.session();
192 auto currentFrom = session.state<StateType>();
193 auto from = transitionRequest.from();
194 auto transitionId = transitionRequest.transitionId();
195
196
197 if (currentFrom != from) {
198 pending_.clear();
199 return;
200 }
201
202
203 for (auto& rule : rules_) {
204 if (rule.from() == from && rule.transitionId() == transitionId) {
205
206 if (rule.guard()) {
207 if (!rule.guard()(updateContext, transitionRequest)) {
208 break;
209 }
210 }
211
212 signalExit(from, rule.to(), transitionId, updateContext);
213 signalTransition(from, rule.to(), transitionId, updateContext);
214 session.setStateFrom<StateType>(StateTransitionContext<StateType>{rule.from(), rule.to(), transitionId});
215 signalEnter(from, rule.to(), transitionId, updateContext);
216 }
217 }
218
219 pending_.clear();
220 }
221
222 /**
223 * @brief Submits a state command for processing.
224 *
225 * @param stateCommand The command to queue.
226 *
227 * @return True (always accepts commands).
228 */
229 bool submit(
230 const StateCommand<StateType> stateCommand
231 ) noexcept {
232 pending_.push_back(stateCommand);
233 return true;
234 };
235
236 /**
237 * @brief Submits a delayed state command for processing.
238 *
239 * @details Extracts the transition request from the delayed command
240 * and queues it as a regular StateCommand. The timer ID is not
241 * retained by the manager.
242 * The delayed command is guaranteed to be ready for processing when
243 * submitted here.
244 *
245 * @param stateCommand The delayed command to queue.
246 *
247 * @return True (always accepts commands).
248 */
249 bool submit(
250 const DelayedStateCommand<StateType> stateCommand
251 ) noexcept {
252 pending_.push_back(StateCommand<StateType>(stateCommand.transitionRequest()));
253 return true;
254 };
255
256 /**
257 * @brief Initializes the manager and registers with GameWorld.
258 *
259 * @param gameWorld The game world to register with.
260 */
264 }
265
266 /**
267 * @brief Clears all pending commands.
268 */
269 void reset() {
270 // intentionally left empty. Clearing the pending queue would also mean
271 // that any pending state transisions **required by the reset** are nuked.
272 }
273
274 };
275
276}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.