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 where TTrigger : struct where TState : struct { private StateMachineConfiguration? m_sm_config; private StateMachine? m_sm; public delegate bool CB_Init(HFSMBase initHFSM); public delegate void CB_Enter(); public delegate void CB_Tick(HFSMBase initHFSM); public delegate void CB_Exit(); public delegate void CB_Transition(HFSMBase initHFSM); private CB_Init? m_cbInit; private CB_Tick? m_cb_update_nullable; private readonly ConcurrentDictionary m_update_handlers = new(); private readonly ConcurrentDictionary 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(rootState); m_sm = new StateMachine(m_sm_config); m_hfsm_id = Singleton.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 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? getStateMachineConfiguration() => m_sm_config; public StateMachine? 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 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 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 where HFSMType : notnull where TTrigger : struct where TState : struct { private readonly ConcurrentDictionary>> 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? m_priority_hfsm_updates_nullable; private readonly ConcurrentBag>> m_update_hfsms = new(); public CompoundHFSM() { } public async Task?> createHfsm( TState rootState , HFSMBase.CB_Init cbInitHFSM) where THFSM : HFSMBase, 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 stateMachine) { if (null == stateMachine) { return false; } var hfsm_list = findHFSMs(hfsmType); if (hfsm_list == null) { hfsm_list = new ConcurrentBag>(); 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 stateMachine) { if (null == stateMachine) { return false; } return removeHFSM(hfsmType, stateMachine.getHfsmId()); } public bool removeHFSMBy(HFSMType hfsmType, out ConcurrentBag>? removeHFSMs) { removeHFSMs = null; if (false == m_hfsm_groups.TryRemove(hfsmType, out var removed_hfsms)) { return false; } removeHFSMs = removed_hfsms; return true; } public HFSMBase? lookupHFSM(HFSMType hfsmType, int hfsmId) { var hfsms = findHFSMs(hfsmType); if (hfsms == null) { return null; } var found_hfsm = hfsms.First(delegate (HFSMBase hfsm) { return hfsm.getHfsmId() == hfsmId; }); if (null == found_hfsm) { return null; } return found_hfsm; } public void setPriorityUpdates(ConcurrentDictionary 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 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(); 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>? findHFSMs(HFSMType fsmType) { m_hfsm_groups.TryGetValue(fsmType, out var found_group); return found_group; } }//CompoundHFSM //ServerCore