Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef FSM_H
19 : #define FSM_H
20 :
21 : #include <limits>
22 : #include <map>
23 : #include <set>
24 : #include <vector>
25 :
26 :
27 : constexpr unsigned int FSM_INVALID_STATE{std::numeric_limits<unsigned int>::max()};
28 :
29 : class CFsmEvent;
30 : class CFsmTransition;
31 : class CFsm;
32 :
33 : using Condition = bool(void* pContext);
34 : using Action = bool(void* pContext, const CFsmEvent* pEvent);
35 :
36 : struct CallbackFunction
37 : {
38 : void* pFunction;
39 : void* pContext;
40 : };
41 :
42 : using StateSet = std::set<unsigned int>;
43 : using EventMap = std::map<unsigned int, CFsmEvent*>;
44 : using TransitionList = std::vector<CFsmTransition*>;
45 : using CallbackList = std::vector<CallbackFunction>;
46 :
47 : /**
48 : * Represents a signal in the state machine that a change has occurred.
49 : * The CFsmEvent objects are under the control of CFsm so
50 : * they are created and deleted via CFsm.
51 : */
52 : class CFsmEvent
53 : {
54 : NONCOPYABLE(CFsmEvent);
55 : public:
56 :
57 : CFsmEvent(unsigned int type);
58 : ~CFsmEvent();
59 :
60 0 : unsigned int GetType() const
61 : {
62 0 : return m_Type;
63 : }
64 :
65 0 : void* GetParamRef()
66 : {
67 0 : return m_Param;
68 : }
69 :
70 : void SetParamRef(void* pParam);
71 :
72 : private:
73 : unsigned int m_Type; // Event type
74 : void* m_Param; // Event paramater
75 : };
76 :
77 :
78 : /**
79 : * An association of event, condition, action and next state.
80 : */
81 : class CFsmTransition
82 : {
83 : NONCOPYABLE(CFsmTransition);
84 : public:
85 :
86 : CFsmTransition(unsigned int state);
87 : ~CFsmTransition();
88 :
89 : /**
90 : * Registers an action that will be executed when the transition occurs.
91 : * @param pAction the function which will be executed.
92 : * @param pContext data passed to the function.
93 : */
94 : void RegisterAction(void* pAction, void* pContext);
95 :
96 : /**
97 : * Registers a condition which will be evaluated when the transition occurs.
98 : * @param pCondition the predicate which will be executed.
99 : * @param pContext data passed to the predicate.
100 : */
101 : void RegisterCondition(void* pCondition, void* pContext);
102 :
103 : /**
104 : * Set event for which transition will occur.
105 : */
106 : void SetEvent(CFsmEvent* pEvent);
107 0 : CFsmEvent* GetEvent() const
108 : {
109 0 : return m_Event;
110 : }
111 :
112 : /**
113 : * Set next state the transition will switch the system to.
114 : */
115 : void SetNextState(unsigned int nextState);
116 0 : unsigned int GetNextState() const
117 : {
118 0 : return m_NextState;
119 : }
120 :
121 0 : unsigned int GetCurrState() const
122 : {
123 0 : return m_CurrState;
124 : }
125 :
126 : const CallbackList& GetActions() const
127 : {
128 : return m_Actions;
129 : }
130 :
131 : const CallbackList& GetConditions() const
132 : {
133 : return m_Conditions;
134 : }
135 :
136 : /**
137 : * Evaluates conditions for the transition.
138 : * @return whether all the conditions are true.
139 : */
140 : bool ApplyConditions() const;
141 :
142 : /**
143 : * Executes actions for the transition.
144 : * @note If there are no actions, assume true.
145 : * @return whether all the actions returned true.
146 : */
147 : bool RunActions() const;
148 :
149 : private:
150 : unsigned int m_CurrState;
151 : unsigned int m_NextState;
152 : CFsmEvent* m_Event;
153 : CallbackList m_Actions;
154 : CallbackList m_Conditions;
155 : };
156 :
157 : /**
158 : * Manages states, events, actions and transitions
159 : * between states. It provides an interface for advertising
160 : * events and track the current state. The implementation is
161 : * a Mealy state machine, so the system respond to events
162 : * and execute some action.
163 : *
164 : * A Mealy state machine has behaviour associated with state
165 : * transitions; Mealy machines are event driven where an
166 : * event triggers a state transition.
167 : */
168 : class CFsm
169 : {
170 : NONCOPYABLE(CFsm);
171 : public:
172 :
173 : CFsm();
174 : virtual ~CFsm();
175 :
176 : /**
177 : * Constructs the state machine. This method must be overriden so that
178 : * connections are constructed for the particular state machine implemented.
179 : */
180 : virtual void Setup();
181 :
182 : /**
183 : * Clear event, action and condition lists and reset state machine.
184 : */
185 : void Shutdown();
186 :
187 : /**
188 : * Adds the specified state to the internal list of states.
189 : * @note If a state with the specified ID exists, the state is not added.
190 : */
191 : void AddState(unsigned int state);
192 :
193 : /**
194 : * Adds the specified event to the internal list of events.
195 : * @note If an eveny with the specified ID exists, the event is not added.
196 : * @return a pointer to the new event.
197 : */
198 : CFsmEvent* AddEvent(unsigned int eventType);
199 :
200 : /**
201 : * Adds a new transistion to the state machine.
202 : * @return a pointer to the new transition.
203 : */
204 : CFsmTransition* AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState );
205 :
206 : /**
207 : * Adds a new transition to the state machine.
208 : * @return a pointer to the new transition.
209 : */
210 : CFsmTransition* AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState,
211 : void* pAction, void* pContext);
212 :
213 : /**
214 : * Looks up the transition given the state, event and next state to transition to.
215 : */
216 : CFsmTransition* GetTransition(unsigned int state, unsigned int eventType) const;
217 : CFsmTransition* GetEventTransition (unsigned int eventType) const;
218 :
219 : /**
220 : * Sets the initial state for FSM.
221 : */
222 : void SetFirstState(unsigned int firstState);
223 :
224 : /**
225 : * Sets the current state and update the last state to the current state.
226 : */
227 : void SetCurrState(unsigned int state);
228 0 : unsigned int GetCurrState() const
229 : {
230 0 : return m_CurrState;
231 : }
232 :
233 0 : void SetNextState(unsigned int nextState)
234 : {
235 0 : m_NextState = nextState;
236 0 : }
237 :
238 0 : unsigned int GetNextState() const
239 : {
240 0 : return m_NextState;
241 : }
242 :
243 : const StateSet& GetStates() const
244 : {
245 : return m_States;
246 : }
247 :
248 : const EventMap& GetEvents() const
249 : {
250 : return m_Events;
251 : }
252 :
253 : const TransitionList& GetTransitions() const
254 : {
255 : return m_Transitions;
256 : }
257 :
258 : /**
259 : * Updates the FSM and retrieves next state.
260 : * @return whether the state was changed.
261 : */
262 : bool Update(unsigned int eventType, void* pEventData);
263 :
264 : /**
265 : * Verifies whether the specified state is managed by the FSM.
266 : */
267 : bool IsValidState(unsigned int state) const;
268 :
269 : /**
270 : * Verifies whether the specified event is managed by the FSM.
271 : */
272 : bool IsValidEvent(unsigned int eventType) const;
273 :
274 : /**
275 : * Tests whether the state machine has finished its work.
276 : * @note This is state machine specific.
277 : */
278 : virtual bool IsDone() const;
279 :
280 : private:
281 : /**
282 : * Verifies whether state machine has already been updated.
283 : */
284 : bool IsFirstTime() const;
285 :
286 : bool m_Done;
287 : unsigned int m_FirstState;
288 : unsigned int m_CurrState;
289 : unsigned int m_NextState;
290 : StateSet m_States;
291 : EventMap m_Events;
292 : TransitionList m_Transitions;
293 : };
294 :
295 : #endif // FSM_H
|