Index: ps/trunk/source/network/FSM.h =================================================================== --- ps/trunk/source/network/FSM.h +++ ps/trunk/source/network/FSM.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,13 +20,12 @@ #include #include -#include +#include constexpr unsigned int FSM_INVALID_STATE{std::numeric_limits::max()}; class CFsmEvent; -class CFsmTransition; class CFsm; using Action = bool(void* pContext, CFsmEvent* pEvent); @@ -35,10 +34,14 @@ { Action* pFunction{nullptr}; void* pContext{nullptr}; + + bool operator()(CFsmEvent& event) const + { + return !pFunction || pFunction(pContext, &event); + } }; using StateSet = std::set; -using TransitionList = std::vector; /** * Represents a signal in the state machine that a change has occurred. @@ -68,61 +71,6 @@ void* m_Param; // Event paramater }; - -/** - * An association of event, action and next state. - */ -class CFsmTransition -{ - NONCOPYABLE(CFsmTransition); -public: - /** - * @param action Object executed upon transition. - */ - CFsmTransition(const unsigned int state, const CallbackFunction action); - - /** - * Set event for which transition will occur. - */ - void SetEventType(const unsigned int eventType); - unsigned int GetEventType() const - { - return m_EventType; - } - - /** - * Set next state the transition will switch the system to. - */ - void SetNextState(unsigned int nextState); - unsigned int GetNextState() const - { - return m_NextState; - } - - unsigned int GetCurrState() const - { - return m_CurrState; - } - - CallbackFunction GetAction() const - { - return m_Action; - } - - /** - * Executes action for the transition. - * @note If there are no action, assume true. - * @return whether the action returned true. - */ - bool RunAction(CFsmEvent& event) const; - -private: - unsigned int m_CurrState; - unsigned int m_NextState; - unsigned int m_EventType; - CallbackFunction m_Action; -}; - /** * Manages states, events, actions and transitions * between states. It provides an interface for advertising @@ -161,18 +109,11 @@ /** * Adds a new transistion to the state machine. - * @return a pointer to the new transition. */ - CFsmTransition* AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState, + void AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState, Action* pAction = nullptr, void* pContext = nullptr); /** - * Looks up the transition given the state, event and next state to transition to. - */ - CFsmTransition* GetTransition(unsigned int state, unsigned int eventType) const; - CFsmTransition* GetEventTransition (unsigned int eventType) const; - - /** * Sets the initial state for FSM. */ void SetFirstState(unsigned int firstState); @@ -201,11 +142,6 @@ return m_States; } - const TransitionList& GetTransitions() const - { - return m_Transitions; - } - /** * Updates the FSM and retrieves next state. * @return whether the state was changed. @@ -224,6 +160,37 @@ virtual bool IsDone() const; private: + struct TransitionKey + { + using UnderlyingType = unsigned int; + UnderlyingType state; + UnderlyingType eventType; + + struct Hash + { + size_t operator()(const TransitionKey& key) const noexcept + { + static_assert(sizeof(UnderlyingType) <= sizeof(size_t) / 2); + return (static_cast(key.state) << + ((sizeof(size_t) / 2) * std::numeric_limits::digits)) + + static_cast(key.eventType); + } + }; + + friend bool operator==(const TransitionKey& lhs, const TransitionKey& rhs) noexcept + { + return lhs.state == rhs.state && lhs.eventType == rhs.eventType; + } + }; + + struct Transition + { + CallbackFunction action; + unsigned int nextState; + }; + + using TransitionMap = std::unordered_map; + /** * Verifies whether state machine has already been updated. */ @@ -234,7 +201,7 @@ unsigned int m_CurrState; unsigned int m_NextState; StateSet m_States; - TransitionList m_Transitions; + TransitionMap m_Transitions; }; #endif // FSM_H Index: ps/trunk/source/network/FSM.cpp =================================================================== --- ps/trunk/source/network/FSM.cpp +++ ps/trunk/source/network/FSM.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -30,26 +30,6 @@ m_Param = nullptr; } -CFsmTransition::CFsmTransition(const unsigned int state, const CallbackFunction action) - : m_CurrState{state}, - m_Action{action} -{} - -void CFsmTransition::SetEventType(const unsigned int eventType) -{ - m_EventType = eventType; -} - -void CFsmTransition::SetNextState(unsigned int nextState) -{ - m_NextState = nextState; -} - -bool CFsmTransition::RunAction(CFsmEvent& event) const -{ - return !m_Action.pFunction || m_Action.pFunction(m_Action.pContext, &event); -} - CFsm::CFsm() { m_Done = false; @@ -70,11 +50,6 @@ void CFsm::Shutdown() { - // Release transitions - TransitionList::iterator itTransition = m_Transitions.begin(); - for (; itTransition < m_Transitions.end(); ++itTransition) - delete *itTransition; - m_States.clear(); m_Transitions.clear(); @@ -89,7 +64,7 @@ m_States.insert(state); } -CFsmTransition* CFsm::AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState, +void CFsm::AddTransition(unsigned int state, unsigned int eventType, unsigned int nextState, Action* pAction /* = nullptr */, void* pContext /* = nullptr*/) { // Make sure we store the current state @@ -98,38 +73,7 @@ // Make sure we store the next state AddState(nextState); - // Create new transition - CFsmTransition* pNewTransition = new CFsmTransition(state, {pAction, pContext}); - - // Setup new transition - pNewTransition->SetEventType(eventType); - pNewTransition->SetNextState(nextState); - - // Store new transition - m_Transitions.push_back(pNewTransition); - - return pNewTransition; -} - -CFsmTransition* CFsm::GetTransition(unsigned int state, unsigned int eventType) const -{ - if (!IsValidState(state)) - return nullptr; - - TransitionList::const_iterator it = m_Transitions.begin(); - for (; it != m_Transitions.end(); ++it) - { - CFsmTransition* pCurrTransition = *it; - if (!pCurrTransition) - continue; - - // Is it our transition? - if (pCurrTransition->GetCurrState() == state && pCurrTransition->GetEventType() == eventType) - return pCurrTransition; - } - - // No transition found - return nullptr; + m_Transitions.insert({TransitionKey{state, eventType}, Transition{{pAction, pContext}, nextState}}); } void CFsm::SetFirstState(unsigned int firstState) @@ -152,18 +96,21 @@ if (IsFirstTime()) m_CurrState = m_FirstState; + if (!IsValidState(m_CurrState)) + return false; + // Lookup transition - CFsmTransition* pTransition = GetTransition(m_CurrState, eventType); - if (!pTransition) + auto transitionIterator = m_Transitions.find({m_CurrState, eventType}); + if (transitionIterator == m_Transitions.end()) return false; CFsmEvent event{eventType, pEventParam}; // Save the default state transition (actions might call SetNextState // to override this) - SetNextState(pTransition->GetNextState()); + SetNextState(transitionIterator->second.nextState); - if (!pTransition->RunAction(event)) + if (!transitionIterator->second.action(event)) return false; SetCurrState(GetNextState());