using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; namespace SLSFramework.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; } } }