GFramework/GFramework.Game/UI/UiTransitionPipeline.cs
GeWuYou fb14d7122c docs(style): 更新文档中的命名空间导入格式
- 将所有小写的命名空间导入更正为首字母大写格式
- 统一 GFramework 框架的命名空间引用规范
- 修复 core、ecs、godot 等模块的命名空间导入错误
- 标准化文档示例代码中的 using 语句格式
- 确保所有文档中的命名空间引用保持一致性
- 更新 global using 语句以匹配正确的命名空间格式
2026-03-10 07:18:49 +08:00

284 lines
9.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
using GFramework.Game.Abstractions.Enums;
using GFramework.Game.Abstractions.UI;
namespace GFramework.Game.UI;
/// <summary>
/// UI切换处理器管道负责管理和执行UI切换扩展点
/// </summary>
public class UiTransitionPipeline
{
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("UiTransitionPipeline");
private readonly List<IUiAroundTransitionHandler> _aroundHandlers = [];
private readonly Dictionary<IUiAroundTransitionHandler, UiTransitionHandlerOptions> _aroundOptions = new();
private readonly List<IUiTransitionHandler> _handlers = [];
private readonly Dictionary<IUiTransitionHandler, UiTransitionHandlerOptions> _options = new();
/// <summary>
/// 注册UI切换处理器
/// </summary>
/// <param name="handler">处理器实例</param>
/// <param name="options">执行选项</param>
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
);
}
/// <summary>
/// 注销UI切换处理器
/// </summary>
/// <param name="handler">处理器实例</param>
public void UnregisterHandler(IUiTransitionHandler handler)
{
ArgumentNullException.ThrowIfNull(handler);
if (!_handlers.Remove(handler)) return;
_options.Remove(handler);
Log.Debug("Handler unregistered: {0}", handler.GetType().Name);
}
/// <summary>
/// 注册 Around 中间件处理器
/// </summary>
/// <param name="handler">处理器实例</param>
/// <param name="options">执行选项</param>
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
);
}
/// <summary>
/// 注销 Around 中间件处理器
/// </summary>
/// <param name="handler">处理器实例</param>
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);
}
/// <summary>
/// 执行指定阶段的所有Handler
/// </summary>
/// <param name="event">UI切换事件</param>
/// <param name="phases">执行阶段</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
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);
}
/// <summary>
/// 执行 Around 中间件处理器,包裹核心操作
/// </summary>
/// <param name="event">UI切换事件</param>
/// <param name="coreAction">核心操作委托</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
public async Task ExecuteAroundAsync(
UiTransitionEvent @event,
Func<Task> 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<Task> 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<IUiTransitionHandler> 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<Task> 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;
}
}
}