초기커밋
This commit is contained in:
542
ServerCore/State/HFSMBase.cs
Normal file
542
ServerCore/State/HFSMBase.cs
Normal file
@@ -0,0 +1,542 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using AsyncStateMachine;
|
||||
using AsyncStateMachine.Behaviours;
|
||||
using AsyncStateMachine.Callbacks;
|
||||
using AsyncStateMachine.Contracts;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// Hierachical Finite State Machines
|
||||
//=============================================================================================
|
||||
|
||||
public abstract class HFSMBase<TTrigger, TState>
|
||||
where TTrigger : struct
|
||||
where TState : struct
|
||||
{
|
||||
private StateMachineConfiguration<TTrigger, TState>? m_sm_config;
|
||||
private StateMachine<TTrigger, TState>? m_sm;
|
||||
|
||||
public delegate bool CB_Init(HFSMBase<TTrigger, TState> initHFSM);
|
||||
public delegate void CB_Enter();
|
||||
public delegate void CB_Tick(HFSMBase<TTrigger, TState> initHFSM);
|
||||
public delegate void CB_Exit();
|
||||
public delegate void CB_Transition(HFSMBase<TTrigger, TState> initHFSM);
|
||||
|
||||
private CB_Init? m_cbInit;
|
||||
private CB_Tick? m_cb_update_nullable;
|
||||
private readonly ConcurrentDictionary<TState, Action> m_update_handlers = new();
|
||||
private readonly ConcurrentDictionary<TState, CB_Transition> m_transition_handlers = new();
|
||||
private readonly SimpleTimer m_state_timer = new();
|
||||
|
||||
private uint m_hfsm_id = 0;
|
||||
|
||||
private bool m_is_disable = false;
|
||||
|
||||
private bool m_is_paused = false;
|
||||
|
||||
internal class HFSMInstantId : InstantIdGeneratorWith32Bit { }
|
||||
|
||||
public HFSMBase()
|
||||
{ }
|
||||
|
||||
private bool createStateMachine(TState rootState)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_sm_config = new StateMachineConfiguration<TTrigger, TState>(rootState);
|
||||
m_sm = new StateMachine<TTrigger, TState>(m_sm_config);
|
||||
m_hfsm_id = Singleton<HFSMInstantId>.It.nextId();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var err_msg = $"Failed to create StateMachine !!! : Exception:{e}";
|
||||
Log.getLogger().fatal(err_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> initHfsm(TState root, CB_Init cb_init)
|
||||
{
|
||||
var is_success = createStateMachine(root);
|
||||
if (false == is_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cbInit = cb_init;
|
||||
|
||||
is_success = m_cbInit(this);
|
||||
if (false == is_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
onSubscribeStateChangeNotify();
|
||||
|
||||
if (m_sm == null)
|
||||
return false;
|
||||
|
||||
await m_sm.InitializeAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void onSubscribeStateChangeNotify() { }
|
||||
|
||||
public StateMachineConfiguration<TTrigger, TState>? getStateMachineConfiguration() => m_sm_config;
|
||||
|
||||
public StateMachine<TTrigger, TState>? getStateMachine() => m_sm;
|
||||
|
||||
public uint getHfsmId() => m_hfsm_id;
|
||||
|
||||
public SimpleTimer getStateTimer() => m_state_timer;
|
||||
|
||||
public TState? getState() { return m_sm?.CurrentState; }
|
||||
|
||||
public bool isState(TState stateType) { return m_sm?.CurrentState.Equals(stateType) ?? false; }
|
||||
|
||||
public async Task<bool> isInStateAsync(TState stateType)
|
||||
{
|
||||
if (m_sm == null)
|
||||
return false;
|
||||
|
||||
return await m_sm.InStateAsync(stateType);
|
||||
}
|
||||
|
||||
public void setDisable(bool isDisable)
|
||||
{
|
||||
m_is_disable = isDisable;
|
||||
}
|
||||
|
||||
public bool isDisable() => m_is_disable;
|
||||
|
||||
public void bindUpdate(CB_Tick cb)
|
||||
{
|
||||
m_cb_update_nullable = cb;
|
||||
}
|
||||
|
||||
private void doUpdate()
|
||||
{
|
||||
m_cb_update_nullable?.Invoke(this);
|
||||
}
|
||||
|
||||
public bool onUpdateHFSM(StateMachine<TTrigger, TState> stateManchine, Action fnUpdate)
|
||||
{
|
||||
var curr_state = stateManchine.CurrentState;
|
||||
if (curr_state == null)
|
||||
return false;
|
||||
|
||||
if (true == m_update_handlers.ContainsKey((TState)curr_state))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false == m_update_handlers.TryAdd((TState)curr_state, fnUpdate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doCurrentUpdate()
|
||||
{
|
||||
var curr_state = getState();
|
||||
if (curr_state == null)
|
||||
return;
|
||||
|
||||
if (true != m_update_handlers.TryGetValue((TState)curr_state, out var update_handler))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
update_handler?.Invoke();
|
||||
}
|
||||
|
||||
public async Task fireAsyncTo(TTrigger trigger)
|
||||
{
|
||||
if (true == isDisable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sm == null)
|
||||
return;
|
||||
|
||||
await m_sm.FireAsync(trigger);
|
||||
}
|
||||
|
||||
public void startStateTimer(int msec)
|
||||
{
|
||||
m_state_timer.activate(msec);
|
||||
}
|
||||
|
||||
public void stopStateTimer()
|
||||
{
|
||||
m_state_timer.deactivate();
|
||||
}
|
||||
|
||||
public void resetStateTimer()
|
||||
{
|
||||
m_state_timer.reset();
|
||||
}
|
||||
|
||||
public void pauseStateTimer()
|
||||
{
|
||||
m_state_timer.pause();
|
||||
}
|
||||
|
||||
public void unpauseStateTimer()
|
||||
{
|
||||
m_state_timer.unpause();
|
||||
}
|
||||
|
||||
public int getReaminTimeMSec()
|
||||
{
|
||||
return (int)m_state_timer.getRemainTimeMSec();
|
||||
}
|
||||
|
||||
public bool isExpiredStateTimer()
|
||||
{
|
||||
if (m_state_timer.expired())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void onPause()
|
||||
{
|
||||
m_is_paused = true;
|
||||
|
||||
pauseStateTimer();
|
||||
}
|
||||
|
||||
public virtual void onUnpause()
|
||||
{
|
||||
m_is_paused = false;
|
||||
|
||||
unpauseStateTimer();
|
||||
}
|
||||
|
||||
public bool isPausing()
|
||||
{
|
||||
return m_is_paused;
|
||||
}
|
||||
|
||||
public virtual void onTick()
|
||||
{
|
||||
if (true == isDisable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (true == isPausing())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
doCurrentUpdate();
|
||||
doUpdate();
|
||||
}
|
||||
|
||||
}//HFSMBase
|
||||
|
||||
//=============================================================================================
|
||||
/// Compound HFSM
|
||||
//=============================================================================================
|
||||
public class CompoundHFSM<HFSMType, TTrigger, TState>
|
||||
where HFSMType : notnull
|
||||
where TTrigger : struct
|
||||
where TState : struct
|
||||
{
|
||||
private readonly ConcurrentDictionary<HFSMType, ConcurrentBag<HFSMBase<TTrigger, TState>>> m_hfsm_groups = new();
|
||||
|
||||
private class PriorityUpdate
|
||||
{
|
||||
public HFSMType HFSMTypeValue { get; set; }
|
||||
public int SeqNo { get; set; }
|
||||
|
||||
public PriorityUpdate(HFSMType hFSMTypeValue, int seqNo)
|
||||
{
|
||||
HFSMTypeValue = hFSMTypeValue;
|
||||
SeqNo = seqNo;
|
||||
}
|
||||
}
|
||||
|
||||
protected ConcurrentDictionary<HFSMType, int>? m_priority_hfsm_updates_nullable;
|
||||
private readonly ConcurrentBag<ConcurrentBag<HFSMBase<TTrigger, TState>>> m_update_hfsms = new();
|
||||
|
||||
|
||||
public CompoundHFSM()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<HFSMBase<TTrigger, TState>?> createHfsm<THFSM>( TState rootState
|
||||
, HFSMBase<TTrigger, TState>.CB_Init cbInitHFSM)
|
||||
where THFSM : HFSMBase<TTrigger, TState>, new()
|
||||
{
|
||||
var new_hfsm = new THFSM();
|
||||
if (true != await new_hfsm.initHfsm(rootState, cbInitHFSM))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new_hfsm;
|
||||
}
|
||||
|
||||
public bool addHFSM(HFSMType hfsmType, HFSMBase<TTrigger, TState> stateMachine)
|
||||
{
|
||||
if (null == stateMachine)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hfsm_list = findHFSMs(hfsmType);
|
||||
if (hfsm_list == null)
|
||||
{
|
||||
hfsm_list = new ConcurrentBag<HFSMBase<TTrigger, TState>>();
|
||||
m_hfsm_groups.TryAdd(hfsmType, hfsm_list);
|
||||
}
|
||||
|
||||
hfsm_list.Add(stateMachine);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool removeHFSM(HFSMType hfsmType, uint hfsmId)
|
||||
{
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var hfsm in hfsms)
|
||||
{
|
||||
if (hfsm.getHfsmId() == hfsmId)
|
||||
{
|
||||
hfsms.TryTake(out _);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool removeHFSM(HFSMType hfsmType, HFSMBase<TTrigger, TState> stateMachine)
|
||||
{
|
||||
if (null == stateMachine)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeHFSM(hfsmType, stateMachine.getHfsmId());
|
||||
}
|
||||
|
||||
public bool removeHFSMBy(HFSMType hfsmType, out ConcurrentBag<HFSMBase<TTrigger, TState>>? removeHFSMs)
|
||||
{
|
||||
removeHFSMs = null;
|
||||
if (false == m_hfsm_groups.TryRemove(hfsmType, out var removed_hfsms))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
removeHFSMs = removed_hfsms;
|
||||
return true;
|
||||
}
|
||||
|
||||
public HFSMBase<TTrigger, TState>? lookupHFSM(HFSMType hfsmType, int hfsmId)
|
||||
{
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var found_hfsm = hfsms.First(delegate (HFSMBase<TTrigger, TState> hfsm)
|
||||
{
|
||||
return hfsm.getHfsmId() == hfsmId;
|
||||
});
|
||||
if (null == found_hfsm)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return found_hfsm;
|
||||
}
|
||||
|
||||
public void setPriorityUpdates(ConcurrentDictionary<HFSMType, int> prioritys)
|
||||
{
|
||||
m_priority_hfsm_updates_nullable = prioritys;
|
||||
}
|
||||
|
||||
public void resetUpdateHFSMs()
|
||||
{
|
||||
m_update_hfsms.Clear();
|
||||
}
|
||||
|
||||
public bool getCurrentState(HFSMType hfsmType, int hfsmId, out TState? state)
|
||||
{
|
||||
state = default;
|
||||
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var hfsm in hfsms)
|
||||
{
|
||||
if (hfsmId == hfsm.getHfsmId())
|
||||
{
|
||||
state = hfsm.getState();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> isCurrentState(HFSMType hfsmType, TState state)
|
||||
{
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var hfsm in hfsms)
|
||||
{
|
||||
if (true == await hfsm.isInStateAsync(state))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task fireAsyncTo(HFSMType hfsmType, int hfsmId, TTrigger trigger)
|
||||
{
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var hfsm in hfsms)
|
||||
{
|
||||
if (hfsmId == hfsm.getHfsmId())
|
||||
{
|
||||
await hfsm.fireAsyncTo(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task fireToGroup(HFSMType hfsmType, TTrigger trigger)
|
||||
{
|
||||
var hfsms = findHFSMs(hfsmType);
|
||||
if (hfsms == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var hfsm in hfsms)
|
||||
{
|
||||
await hfsm.fireAsyncTo(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task fireToAll(TTrigger trigger)
|
||||
{
|
||||
foreach (var pair_hfsm_group in m_hfsm_groups)
|
||||
{
|
||||
foreach (var fsm in pair_hfsm_group.Value)
|
||||
{
|
||||
await fsm.fireAsyncTo(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onTick()
|
||||
{
|
||||
if (0 == m_update_hfsms.Count)
|
||||
{
|
||||
if (null != m_priority_hfsm_updates_nullable)
|
||||
{
|
||||
//우선 순위가 설정된 경우, 우선 순위값을 list 로 만든다.
|
||||
var toSortList = new List<PriorityUpdate>();
|
||||
foreach (var pair_priority_info in m_priority_hfsm_updates_nullable)
|
||||
{
|
||||
var priorityUpdate = new PriorityUpdate(pair_priority_info.Key, pair_priority_info.Value);
|
||||
toSortList.Add(priorityUpdate);
|
||||
}
|
||||
|
||||
//우선 순위값을 비교하여 정렬 한다.
|
||||
toSortList.Sort(delegate (PriorityUpdate a, PriorityUpdate b)
|
||||
{
|
||||
if (a.SeqNo == b.SeqNo)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (a.SeqNo > b.SeqNo)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
//정렬된 순서대로 HFSM Group 을 추가 한다.
|
||||
foreach (var value in toSortList)
|
||||
{
|
||||
var hfsms = findHFSMs(value.HFSMTypeValue);
|
||||
if (hfsms != null)
|
||||
{
|
||||
m_update_hfsms.Add(hfsms);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//우선 순위가 설정되지 않은 경우
|
||||
foreach (var hfsm_group in m_hfsm_groups)
|
||||
{
|
||||
m_update_hfsms.Add(hfsm_group.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//등록된 순서대로 HFSM Group 을 갱신 한다.
|
||||
foreach (var hfsm_list in m_update_hfsms)
|
||||
{
|
||||
foreach (var hfsm in hfsm_list)
|
||||
{
|
||||
hfsm.onTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentBag<HFSMBase<TTrigger, TState>>? findHFSMs(HFSMType fsmType)
|
||||
{
|
||||
m_hfsm_groups.TryGetValue(fsmType, out var found_group);
|
||||
return found_group;
|
||||
}
|
||||
|
||||
}//CompoundHFSM
|
||||
//ServerCore
|
||||
Reference in New Issue
Block a user