using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
using GFramework.Game.Abstractions.Enums;
using GFramework.Game.Abstractions.UI;
namespace GFramework.Game.UI;
///
/// UI切换处理器管道,负责管理和执行UI切换扩展点
///
public class UiTransitionPipeline
{
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("UiTransitionPipeline");
private readonly List _aroundHandlers = [];
private readonly Dictionary _aroundOptions = new();
private readonly List _handlers = [];
private readonly Dictionary _options = new();
///
/// 注册UI切换处理器
///
/// 处理器实例
/// 执行选项
public void RegisterHandler(IUiTransitionHandler handler, UiTransitionHandlerOptions? options = null)
{
ArgumentNullException.ThrowIfNull(handler);
if (_handlers.Contains(handler))
{
Log.Debug("Handler already registered: {0}", handler.GetType().Name);
return;
}
_handlers.Add(handler);
_options[handler] = options ?? new UiTransitionHandlerOptions();
Log.Debug(
"Handler registered: {0}, Priority={1}, Phases={2}, TimeoutMs={3}",
handler.GetType().Name,
handler.Priority,
handler.Phases,
_options[handler].TimeoutMs
);
}
///
/// 注销UI切换处理器
///
/// 处理器实例
public void UnregisterHandler(IUiTransitionHandler handler)
{
ArgumentNullException.ThrowIfNull(handler);
if (!_handlers.Remove(handler)) return;
_options.Remove(handler);
Log.Debug("Handler unregistered: {0}", handler.GetType().Name);
}
///
/// 注册 Around 中间件处理器
///
/// 处理器实例
/// 执行选项
public void RegisterAroundHandler(IUiAroundTransitionHandler handler, UiTransitionHandlerOptions? options = null)
{
ArgumentNullException.ThrowIfNull(handler);
if (_aroundHandlers.Contains(handler))
{
Log.Debug("Around handler already registered: {0}", handler.GetType().Name);
return;
}
_aroundHandlers.Add(handler);
_aroundOptions[handler] = options ?? new UiTransitionHandlerOptions();
Log.Debug(
"Around handler registered: {0}, Priority={1}",
handler.GetType().Name,
handler.Priority
);
}
///
/// 注销 Around 中间件处理器
///
/// 处理器实例
public void UnregisterAroundHandler(IUiAroundTransitionHandler handler)
{
ArgumentNullException.ThrowIfNull(handler);
if (!_aroundHandlers.Remove(handler)) return;
_aroundOptions.Remove(handler);
Log.Debug("Around handler unregistered: {0}", handler.GetType().Name);
}
///
/// 执行指定阶段的所有Handler
///
/// UI切换事件
/// 执行阶段
/// 取消令牌
/// 异步任务
public async Task ExecuteAsync(
UiTransitionEvent @event,
UiTransitionPhases phases,
CancellationToken cancellationToken = default
)
{
@event.Set("Phases", phases.ToString());
Log.Debug(
"Execute pipeline: Phases={0}, From={1}, To={2}, Type={3}, HandlerCount={4}",
phases,
@event.FromUiKey ?? "None",
@event.ToUiKey ?? "None",
@event.TransitionType,
_handlers.Count
);
var sortedHandlers = FilterAndSortHandlers(@event, phases);
if (sortedHandlers.Count == 0)
{
Log.Debug("No handlers to execute for phases: {0}", phases);
return;
}
Log.Debug(
"Executing {0} handlers for phases {1}",
sortedHandlers.Count,
phases
);
foreach (var handler in sortedHandlers)
{
var options = _options[handler];
await ExecuteSingleHandlerAsync(handler, options, @event, cancellationToken);
}
Log.Debug("Pipeline execution completed for phases: {0}", phases);
}
///
/// 执行 Around 中间件处理器,包裹核心操作
///
/// UI切换事件
/// 核心操作委托
/// 取消令牌
/// 异步任务
public async Task ExecuteAroundAsync(
UiTransitionEvent @event,
Func coreAction,
CancellationToken cancellationToken = default)
{
var handlers = _aroundHandlers
.Where(h => h.ShouldHandle(@event))
.OrderBy(h => h.Priority)
.ToList();
if (handlers.Count == 0)
{
await coreAction();
return;
}
Log.Debug(
"Executing {0} around handlers for event: {1}",
handlers.Count,
@event.TransitionType
);
// 构建中间件链
Func pipeline = coreAction;
for (int i = handlers.Count - 1; i >= 0; i--)
{
var handler = handlers[i];
var options = _aroundOptions[handler];
var next = pipeline;
pipeline = async () => await ExecuteSingleAroundHandlerAsync(
handler, options, @event, next, cancellationToken);
}
await pipeline();
}
private List FilterAndSortHandlers(
UiTransitionEvent @event,
UiTransitionPhases phases)
{
return _handlers
.Where(h => h.Phases.HasFlag(phases) && h.ShouldHandle(@event, phases))
.OrderBy(h => h.Priority)
.ToList();
}
private static async Task ExecuteSingleHandlerAsync(
IUiTransitionHandler handler,
UiTransitionHandlerOptions options,
UiTransitionEvent @event,
CancellationToken cancellationToken)
{
Log.Debug(
"Executing handler: {0}, Priority={1}",
handler.GetType().Name,
handler.Priority
);
try
{
using var timeoutCts = options.TimeoutMs > 0
? new CancellationTokenSource(options.TimeoutMs)
: null;
using var linkedCts = timeoutCts != null && cancellationToken.CanBeCanceled
? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)
: null;
await handler.HandleAsync(
@event,
linkedCts?.Token ?? cancellationToken
).ConfigureAwait(false);
Log.Debug("Handler completed: {0}", handler.GetType().Name);
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
Log.Error(
"Handler timeout: {0}, TimeoutMs={1}",
handler.GetType().Name,
options.TimeoutMs
);
if (options.ContinueOnError) return;
Log.Error("Stopping pipeline due to timeout and ContinueOnError=false");
throw;
}
catch (OperationCanceledException)
{
Log.Debug("Handler cancelled: {0}", handler.GetType().Name);
throw;
}
catch (Exception ex)
{
Log.Error("Handler failed: {0}, Error: {1}", handler.GetType().Name, ex.Message);
if (options.ContinueOnError) return;
Log.Error("Stopping pipeline due to error and ContinueOnError=false");
throw;
}
}
private static async Task ExecuteSingleAroundHandlerAsync(
IUiAroundTransitionHandler handler,
UiTransitionHandlerOptions options,
UiTransitionEvent @event,
Func next,
CancellationToken cancellationToken)
{
Log.Debug("Executing around handler: {0}", handler.GetType().Name);
try
{
using var timeoutCts = options.TimeoutMs > 0
? new CancellationTokenSource(options.TimeoutMs)
: null;
using var linkedCts = timeoutCts != null && cancellationToken.CanBeCanceled
? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token)
: null;
await handler.HandleAsync(@event, next, linkedCts?.Token ?? cancellationToken);
Log.Debug("Around handler completed: {0}", handler.GetType().Name);
}
catch (Exception ex)
{
Log.Error("Around handler failed: {0}, Error: {1}",
handler.GetType().Name, ex.Message);
if (!options.ContinueOnError)
throw;
}
}
}