1
0
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:
phosit 2024-01-29 18:44:14 +00:00
parent 15dca2c0c9
commit 6faf704731
2 changed files with 50 additions and 136 deletions

View File

@ -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());

View File

@ -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