using System; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace BITKit.StateMachine { /// /// 动态异步状态机 /// /// 异步状态 public class AsyncStateMachine:IStateMachine ,IDisposable where T : class, IStateAsync { private readonly IServiceProvider _serviceProvider; public AsyncStateMachine(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public AsyncStateMachine() { _serviceProvider = new ServiceCollection().BuildServiceProvider(); } public bool Enabled { get; set; } = true; public T CurrentState { get;private set; } public T NextOrCurrentState => _nextTargetState.IfNotAllow(CurrentState); public event Action OnStateChanging; public event Func OnStateChangeAsync; public event Action OnStateChanged; public event Action OnStateRegistered; public IReadOnlyDictionary Dictionary => _dictionary; public event Action OnStateUnRegistered; public IDictionary StateDictionary { get; } = new Dictionary(); public readonly ValidHandle IsBusy=new(); private readonly CancellationTokenSource _cancellationTokenSource=new(); private readonly Dictionary _dictionary = new(); private readonly Optional _nextTargetState = new(); public async void Initialize() { await IsBusy; if(_cancellationTokenSource.IsCancellationRequested)return; using var _ = IsBusy.GetHandle(); foreach (var (_,value) in StateDictionary) { await value.InitializeAsync(); value.Initialize(); } } public async void UpdateState(float deltaTime) { if (CurrentState is null) return; using var _ = IsBusy.GetHandle(); CurrentState.OnStateUpdate(deltaTime); await CurrentState.OnStateUpdateAsync(deltaTime); } public async void DisposeState() { await IsBusy; if (_cancellationTokenSource.IsCancellationRequested) return; if (CurrentState is null) return; using var _ = IsBusy.GetHandle(); CurrentState.Enabled = false; await CurrentState.OnStateExitAsync(CurrentState, null); CurrentState.OnStateExit(CurrentState, null); CurrentState = null; } public T TransitionState() where TState : T { T nextState; foreach (var (type, value) in Dictionary) { if ((typeof(TState) == value.GetType()) is false) continue; nextState = value; TransitionState(nextState); return nextState; } nextState = _serviceProvider.GetRequiredService(); return TransitionState(nextState);; } public async UniTask TransitionStateAsync(T nextState) { if (nextState is not null) { if (nextState.Identifier == 0) { nextState.Identifier = nextState.GetHashCode(); } } if (Equals(nextState, CurrentState)) return; if(_nextTargetState.Allow && Equals(_nextTargetState.Value,nextState))return; if (_nextTargetState.Allow) { _nextTargetState.Value = nextState; return; } _nextTargetState.SetValueThenAllow(nextState); await IsBusy; if(CurrentState==nextState)return; if(_cancellationTokenSource.IsCancellationRequested)return; using var _ = IsBusy.GetHandle(); OnStateChanging?.Invoke(CurrentState,nextState); await OnStateChangeAsync.UniTaskFunc(CurrentState,nextState); if (nextState is not null && _dictionary.TryAdd(nextState.Identifier, nextState)) { await nextState.InitializeAsync(); if(_cancellationTokenSource.IsCancellationRequested)return; nextState.Initialize(); } if (CurrentState is not null) { CurrentState.Enabled = false; await CurrentState.OnStateExitAsync(CurrentState, nextState); if(_cancellationTokenSource.IsCancellationRequested)return; CurrentState.OnStateExit(CurrentState,nextState); } if (_nextTargetState.Allow && _nextTargetState.Value != nextState) { return; } var tempState = CurrentState; CurrentState = _nextTargetState.Value; _nextTargetState.Clear(); nextState.Enabled = true; await nextState.OnStateEntryAsync(tempState); if(_cancellationTokenSource.IsCancellationRequested)return; nextState.OnStateEntry(tempState); OnStateChanged?.Invoke(tempState,nextState); } public T TransitionState(T nextState) { TransitionStateAsync(nextState).Forget(); return nextState; } public async void UnRegister(T newState) { if (newState is null) return; if (Dictionary.ContainsKey(newState.Identifier) is false) return; _dictionary.Remove(newState.Identifier); if (Equals(CurrentState, newState)) { await CurrentState.OnStateExitAsync(CurrentState, null); CurrentState.OnStateExit(CurrentState, null); if (CurrentState is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync(); } if (CurrentState is IDisposable disposable) { disposable.Dispose(); } CurrentState = null; } OnStateUnRegistered?.Invoke(newState); } public void Dispose() { if(_isDisposed)return; _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); _isDisposed = true; } private bool _isDisposed; } }