forked from 0ad/0ad
Split the FSM-transitions and put them in an unordered_map
Comments By: @vladislavbelov Differential Revision: https://code.wildfiregames.com/D5225 This was SVN commit r28020.
This commit is contained in:
parent
15dca2c0c9
commit
6faf704731
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2023 Wildfire Games.
|
/* Copyright (C) 2024 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -30,26 +30,6 @@ CFsmEvent::~CFsmEvent()
|
|||||||
m_Param = nullptr;
|
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()
|
CFsm::CFsm()
|
||||||
{
|
{
|
||||||
m_Done = false;
|
m_Done = false;
|
||||||
@ -70,11 +50,6 @@ void CFsm::Setup()
|
|||||||
|
|
||||||
void CFsm::Shutdown()
|
void CFsm::Shutdown()
|
||||||
{
|
{
|
||||||
// Release transitions
|
|
||||||
TransitionList::iterator itTransition = m_Transitions.begin();
|
|
||||||
for (; itTransition < m_Transitions.end(); ++itTransition)
|
|
||||||
delete *itTransition;
|
|
||||||
|
|
||||||
m_States.clear();
|
m_States.clear();
|
||||||
m_Transitions.clear();
|
m_Transitions.clear();
|
||||||
|
|
||||||
@ -89,7 +64,7 @@ void CFsm::AddState(unsigned int state)
|
|||||||
m_States.insert(state);
|
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*/)
|
Action* pAction /* = nullptr */, void* pContext /* = nullptr*/)
|
||||||
{
|
{
|
||||||
// Make sure we store the current state
|
// Make sure we store the current state
|
||||||
@ -98,38 +73,7 @@ CFsmTransition* CFsm::AddTransition(unsigned int state, unsigned int eventType,
|
|||||||
// Make sure we store the next state
|
// Make sure we store the next state
|
||||||
AddState(nextState);
|
AddState(nextState);
|
||||||
|
|
||||||
// Create new transition
|
m_Transitions.insert({TransitionKey{state, eventType}, Transition{{pAction, pContext}, nextState}});
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFsm::SetFirstState(unsigned int firstState)
|
void CFsm::SetFirstState(unsigned int firstState)
|
||||||
@ -152,18 +96,21 @@ bool CFsm::Update(unsigned int eventType, void* pEventParam)
|
|||||||
if (IsFirstTime())
|
if (IsFirstTime())
|
||||||
m_CurrState = m_FirstState;
|
m_CurrState = m_FirstState;
|
||||||
|
|
||||||
|
if (!IsValidState(m_CurrState))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Lookup transition
|
// Lookup transition
|
||||||
CFsmTransition* pTransition = GetTransition(m_CurrState, eventType);
|
auto transitionIterator = m_Transitions.find({m_CurrState, eventType});
|
||||||
if (!pTransition)
|
if (transitionIterator == m_Transitions.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CFsmEvent event{eventType, pEventParam};
|
CFsmEvent event{eventType, pEventParam};
|
||||||
|
|
||||||
// Save the default state transition (actions might call SetNextState
|
// Save the default state transition (actions might call SetNextState
|
||||||
// to override this)
|
// to override this)
|
||||||
SetNextState(pTransition->GetNextState());
|
SetNextState(transitionIterator->second.nextState);
|
||||||
|
|
||||||
if (!pTransition->RunAction(event))
|
if (!transitionIterator->second.action(event))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetCurrState(GetNextState());
|
SetCurrState(GetNextState());
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2023 Wildfire Games.
|
/* Copyright (C) 2024 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -20,13 +20,12 @@
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
constexpr unsigned int FSM_INVALID_STATE{std::numeric_limits<unsigned int>::max()};
|
constexpr unsigned int FSM_INVALID_STATE{std::numeric_limits<unsigned int>::max()};
|
||||||
|
|
||||||
class CFsmEvent;
|
class CFsmEvent;
|
||||||
class CFsmTransition;
|
|
||||||
class CFsm;
|
class CFsm;
|
||||||
|
|
||||||
using Action = bool(void* pContext, CFsmEvent* pEvent);
|
using Action = bool(void* pContext, CFsmEvent* pEvent);
|
||||||
@ -35,10 +34,14 @@ struct CallbackFunction
|
|||||||
{
|
{
|
||||||
Action* pFunction{nullptr};
|
Action* pFunction{nullptr};
|
||||||
void* pContext{nullptr};
|
void* pContext{nullptr};
|
||||||
|
|
||||||
|
bool operator()(CFsmEvent& event) const
|
||||||
|
{
|
||||||
|
return !pFunction || pFunction(pContext, &event);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using StateSet = std::set<unsigned int>;
|
using StateSet = std::set<unsigned int>;
|
||||||
using TransitionList = std::vector<CFsmTransition*>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a signal in the state machine that a change has occurred.
|
* Represents a signal in the state machine that a change has occurred.
|
||||||
@ -68,61 +71,6 @@ private:
|
|||||||
void* m_Param; // Event paramater
|
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
|
* Manages states, events, actions and transitions
|
||||||
* between states. It provides an interface for advertising
|
* between states. It provides an interface for advertising
|
||||||
@ -161,17 +109,10 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new transistion to the state machine.
|
* 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);
|
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.
|
* Sets the initial state for FSM.
|
||||||
*/
|
*/
|
||||||
@ -201,11 +142,6 @@ public:
|
|||||||
return m_States;
|
return m_States;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransitionList& GetTransitions() const
|
|
||||||
{
|
|
||||||
return m_Transitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the FSM and retrieves next state.
|
* Updates the FSM and retrieves next state.
|
||||||
* @return whether the state was changed.
|
* @return whether the state was changed.
|
||||||
@ -224,6 +160,37 @@ public:
|
|||||||
virtual bool IsDone() const;
|
virtual bool IsDone() const;
|
||||||
|
|
||||||
private:
|
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<size_t>(key.state) <<
|
||||||
|
((sizeof(size_t) / 2) * std::numeric_limits<unsigned char>::digits)) +
|
||||||
|
static_cast<size_t>(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<TransitionKey, const Transition, TransitionKey::Hash>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies whether state machine has already been updated.
|
* Verifies whether state machine has already been updated.
|
||||||
*/
|
*/
|
||||||
@ -234,7 +201,7 @@ private:
|
|||||||
unsigned int m_CurrState;
|
unsigned int m_CurrState;
|
||||||
unsigned int m_NextState;
|
unsigned int m_NextState;
|
||||||
StateSet m_States;
|
StateSet m_States;
|
||||||
TransitionList m_Transitions;
|
TransitionMap m_Transitions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FSM_H
|
#endif // FSM_H
|
||||||
|
Loading…
Reference in New Issue
Block a user