1 /* Copyright (C) 2024 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
22 #include <unordered_map>
25 constexpr unsigned int FSM_INVALID_STATE
{std::numeric_limits
<unsigned int>::max()};
28 * Represents a signal in the state machine that a change has occurred.
29 * The CFsmEvent objects are under the control of CFsm so
30 * they are created and deleted via CFsm.
35 CFsmEvent(unsigned int type
, void* pParam
) :
40 unsigned int GetType() const
51 unsigned int m_Type
; // Event type
52 void* m_Param
; // Event paramater
56 * Manages states, events, actions and transitions
57 * between states. It provides an interface for advertising
58 * events and track the current state. The implementation is
59 * a Mealy state machine, so the system respond to events
60 * and execute some action.
62 * A Mealy state machine has behaviour associated with state
63 * transitions; Mealy machines are event driven where an
64 * event triggers a state transition.
66 template <typename Context
>
69 using Action
= bool(Context
* pContext
, CFsmEvent
* pEvent
);
71 struct CallbackFunction
73 Action
* pFunction
{nullptr};
74 Context
* pContext
{nullptr};
76 bool operator()(CFsmEvent
& event
) const
78 return !pFunction
|| pFunction(pContext
, &event
);
83 * Adds a new transistion to the state machine.
85 void AddTransition(unsigned int state
, unsigned int eventType
, unsigned int nextState
,
86 Action
* pAction
= nullptr, Context
* pContext
= nullptr)
88 m_Transitions
.insert({TransitionKey
{state
, eventType
},
89 Transition
{{pAction
, pContext
}, nextState
}});
93 * Sets the initial state for FSM.
95 void SetFirstState(unsigned int firstState
)
97 m_FirstState
= firstState
;
101 * Sets the current state and update the last state to the current state.
103 void SetCurrState(unsigned int state
)
107 unsigned int GetCurrState() const
112 void SetNextState(unsigned int nextState
)
114 m_NextState
= nextState
;
117 unsigned int GetNextState() const
123 * Updates the FSM and retrieves next state.
124 * @return whether the state was changed.
126 bool Update(unsigned int eventType
, void* pEventData
)
129 m_CurrState
= m_FirstState
;
132 auto transitionIterator
= m_Transitions
.find({m_CurrState
, eventType
});
133 if (transitionIterator
== m_Transitions
.end())
136 CFsmEvent event
{eventType
, pEventData
};
138 // Save the default state transition (actions might call SetNextState
140 SetNextState(transitionIterator
->second
.nextState
);
142 if (!transitionIterator
->second
.action(event
))
145 SetCurrState(GetNextState());
147 // Reset the next state since it's no longer valid
148 SetNextState(FSM_INVALID_STATE
);
154 * Tests whether the state machine has finished its work.
164 using UnderlyingType
= unsigned int;
165 UnderlyingType state
;
166 UnderlyingType eventType
;
170 size_t operator()(const TransitionKey
& key
) const noexcept
172 constexpr size_t count
{std::numeric_limits
<size_t>::digits
/ 2};
173 const size_t wideState
{static_cast<size_t>(key
.state
)};
174 const size_t rotatedState
{(wideState
<< count
) | (wideState
>> count
)};
175 return static_cast<size_t>(key
.eventType
) ^ rotatedState
;
179 friend bool operator==(const TransitionKey
& lhs
, const TransitionKey
& rhs
) noexcept
181 return lhs
.state
== rhs
.state
&& lhs
.eventType
== rhs
.eventType
;
187 CallbackFunction action
;
188 unsigned int nextState
;
191 using TransitionMap
= std::unordered_map
<TransitionKey
, const Transition
,
192 typename
TransitionKey::Hash
>;
195 * Verifies whether state machine has already been updated.
197 bool IsFirstTime() const
199 return m_CurrState
== FSM_INVALID_STATE
;
203 unsigned int m_FirstState
{FSM_INVALID_STATE
};
204 unsigned int m_CurrState
{FSM_INVALID_STATE
};
205 unsigned int m_NextState
{FSM_INVALID_STATE
};
206 TransitionMap m_Transitions
;