135 lines
4.8 KiB
C#
135 lines
4.8 KiB
C#
|
|
using System.Collections.Generic;
|
|||
|
|
using Cysharp.Threading.Tasks;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
namespace SLSFramework.General
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 全局命令队列管理器。
|
|||
|
|
/// 内部按 <see cref="CommandLane"/> 优先级分桶,同桶内保持 FIFO 顺序。
|
|||
|
|
/// 使用 <see cref="AddCommand"/> 将命令加入队列;队列空闲时自动休眠,有新命令时唤醒。
|
|||
|
|
/// </summary>
|
|||
|
|
public class CommandQueueManager : Singleton<CommandQueueManager>
|
|||
|
|
{
|
|||
|
|
// 每个 Lane 独立维护一个 FIFO 队列,key 数值越小优先级越高
|
|||
|
|
private readonly SortedDictionary<CommandLane, Queue<CommandBase>> lanes =
|
|||
|
|
new SortedDictionary<CommandLane, Queue<CommandBase>>();
|
|||
|
|
|
|||
|
|
// 全局 outerContext,在整个战斗生命周期内共享
|
|||
|
|
private readonly CommandContext globalContext = new CommandContext();
|
|||
|
|
|
|||
|
|
private bool isLoopRunning = false;
|
|||
|
|
|
|||
|
|
/// <summary>当前是否有命令正在执行或等待执行。</summary>
|
|||
|
|
public bool IsBusy => HasPending || isProcessing;
|
|||
|
|
|
|||
|
|
private bool isProcessing = false;
|
|||
|
|
|
|||
|
|
private bool HasPending
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
foreach (var queue in lanes.Values)
|
|||
|
|
if (queue.Count > 0) return true;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override void Awake()
|
|||
|
|
{
|
|||
|
|
base.Awake();
|
|||
|
|
|
|||
|
|
// 预建所有 Lane 的队列,避免运行时动态创建
|
|||
|
|
foreach (CommandLane lane in System.Enum.GetValues(typeof(CommandLane)))
|
|||
|
|
lanes[lane] = new Queue<CommandBase>();
|
|||
|
|
|
|||
|
|
StartProcessLoop().Forget();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 公开 API ─────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将命令追加到指定 Lane 的队列末尾。
|
|||
|
|
/// 默认 Lane 为 <see cref="CommandLane.Main"/>,Mod 制作者通常不需要指定 Lane。
|
|||
|
|
/// </summary>
|
|||
|
|
public CommandBase AddCommand(CommandBase command, CommandLane lane = CommandLane.Main)
|
|||
|
|
{
|
|||
|
|
if (command == null) return null;
|
|||
|
|
lanes[lane].Enqueue(command);
|
|||
|
|
return command;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将多条命令批量追加到指定 Lane。
|
|||
|
|
/// </summary>
|
|||
|
|
public void AddCommands(IEnumerable<CommandBase> commands, CommandLane lane = CommandLane.Main)
|
|||
|
|
{
|
|||
|
|
if (commands == null) return;
|
|||
|
|
foreach (var cmd in commands)
|
|||
|
|
AddCommand(cmd, lane);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将命令插入到 <see cref="CommandLane.Interrupt"/> Lane 的队首(最高优先级)。
|
|||
|
|
/// 仅供框架内部使用(死亡判定、强制打断等)。
|
|||
|
|
/// </summary>
|
|||
|
|
public CommandBase AddCommandNext(CommandBase command)
|
|||
|
|
{
|
|||
|
|
if (command == null) return null;
|
|||
|
|
|
|||
|
|
// Interrupt Lane 的队列本身是 FIFO,但它的整体优先级最高,
|
|||
|
|
// 因此直接 Enqueue 到 Interrupt 即可保证在 Main 之前执行。
|
|||
|
|
lanes[CommandLane.Interrupt].Enqueue(command);
|
|||
|
|
return command;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>等待直到队列完全空闲(所有 Lane 均无待执行命令)。</summary>
|
|||
|
|
public async UniTask WaitUntilIdle()
|
|||
|
|
{
|
|||
|
|
await UniTask.WaitUntil(() => !IsBusy);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 内部处理循环 ─────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
private async UniTaskVoid StartProcessLoop()
|
|||
|
|
{
|
|||
|
|
if (isLoopRunning) return;
|
|||
|
|
isLoopRunning = true;
|
|||
|
|
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
// 队列为空时休眠,避免空转
|
|||
|
|
await UniTask.WaitUntil(() => HasPending);
|
|||
|
|
|
|||
|
|
CommandBase next = DequeueHighestPriority();
|
|||
|
|
if (next == null) continue;
|
|||
|
|
|
|||
|
|
isProcessing = true;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
await next.RunAsync(globalContext);
|
|||
|
|
}
|
|||
|
|
catch (System.Exception ex)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[CommandQueue] 命令 {next.GetType().Name} 执行出错:{ex}");
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
isProcessing = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>从优先级最高的非空 Lane 取出队首命令。</summary>
|
|||
|
|
private CommandBase DequeueHighestPriority()
|
|||
|
|
{
|
|||
|
|
foreach (var queue in lanes.Values)
|
|||
|
|
{
|
|||
|
|
if (queue.Count > 0)
|
|||
|
|
return queue.Dequeue();
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|