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.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -30,26 +30,6 @@ CFsmEvent::~CFsmEvent()
|
||||
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::Setup()
|
||||
|
||||
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 @@ void CFsm::AddState(unsigned int 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*/)
|
||||
{
|
||||
// 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
|
||||
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 @@ bool CFsm::Update(unsigned int eventType, void* pEventParam)
|
||||
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());
|
||||
|
@ -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 <limits>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
constexpr unsigned int FSM_INVALID_STATE{std::numeric_limits<unsigned int>::max()};
|
||||
|
||||
class CFsmEvent;
|
||||
class CFsmTransition;
|
||||
class CFsm;
|
||||
|
||||
using Action = bool(void* pContext, CFsmEvent* pEvent);
|
||||
@ -35,10 +34,14 @@ struct CallbackFunction
|
||||
{
|
||||
Action* pFunction{nullptr};
|
||||
void* pContext{nullptr};
|
||||
|
||||
bool operator()(CFsmEvent& event) const
|
||||
{
|
||||
return !pFunction || pFunction(pContext, &event);
|
||||
}
|
||||
};
|
||||
|
||||
using StateSet = std::set<unsigned int>;
|
||||
using TransitionList = std::vector<CFsmTransition*>;
|
||||
|
||||
/**
|
||||
* Represents a signal in the state machine that a change has occurred.
|
||||
@ -68,61 +71,6 @@ private:
|
||||
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,17 +109,10 @@ public:
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -201,11 +142,6 @@ public:
|
||||
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 @@ public:
|
||||
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<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.
|
||||
*/
|
||||
@ -234,7 +201,7 @@ private:
|
||||
unsigned int m_CurrState;
|
||||
unsigned int m_NextState;
|
||||
StateSet m_States;
|
||||
TransitionList m_Transitions;
|
||||
TransitionMap m_Transitions;
|
||||
};
|
||||
|
||||
#endif // FSM_H
|
||||
|
Loading…
Reference in New Issue
Block a user