using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace SLSUtilities.General
{
///
/// 全局命令队列管理器。
/// 内部按 优先级分桶,同桶内保持 FIFO 顺序。
/// 使用 将命令加入队列;队列空闲时自动休眠,有新命令时唤醒。
///
public class CommandQueueManager : Singleton
{
// 每个 Lane 独立维护一个 FIFO 队列,key 数值越小优先级越高
private readonly SortedDictionary> lanes =
new SortedDictionary>();
// 全局 outerContext,在整个战斗生命周期内共享
private readonly CommandContext globalContext = new CommandContext();
private bool isLoopRunning = false;
/// 当前是否有命令正在执行或等待执行。
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();
StartProcessLoop().Forget();
}
// ── 公开 API ─────────────────────────────────────────────────────
///
/// 将命令追加到指定 Lane 的队列末尾。
/// 默认 Lane 为 ,Mod 制作者通常不需要指定 Lane。
///
public CommandBase AddCommand(CommandBase command, CommandLane lane = CommandLane.Main)
{
if (command == null) return null;
lanes[lane].Enqueue(command);
return command;
}
///
/// 将多条命令批量追加到指定 Lane。
///
public void AddCommands(IEnumerable commands, CommandLane lane = CommandLane.Main)
{
if (commands == null) return;
foreach (var cmd in commands)
AddCommand(cmd, lane);
}
///
/// 将命令插入到 Lane 的队首(最高优先级)。
/// 仅供框架内部使用(死亡判定、强制打断等)。
///
public CommandBase AddCommandNext(CommandBase command)
{
if (command == null) return null;
// Interrupt Lane 的队列本身是 FIFO,但它的整体优先级最高,
// 因此直接 Enqueue 到 Interrupt 即可保证在 Main 之前执行。
lanes[CommandLane.Interrupt].Enqueue(command);
return command;
}
/// 等待直到队列完全空闲(所有 Lane 均无待执行命令)。
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;
}
}
}
/// 从优先级最高的非空 Lane 取出队首命令。
private CommandBase DequeueHighestPriority()
{
foreach (var queue in lanes.Values)
{
if (queue.Count > 0)
return queue.Dequeue();
}
return null;
}
}
}