Files
2025-05-01 07:20:41 +09:00

543 lines
13 KiB
C#

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