using System.Reflection; using GFramework.Core.Abstractions.Coroutine; using GFramework.Core.Coroutine; using GFramework.Core.Coroutine.Instructions; using GFramework.Godot.Extensions; namespace GFramework.Godot.Coroutine; /// /// Godot 协程管理器,负责在不同的引擎更新阶段驱动 Core 协程调度器。 /// /// /// 该类型为 Godot 层协程功能的统一入口。 /// 它不仅提供静态运行 API,也负责把 Godot 的 Process、Physics 与 Deferred 生命周期映射为 /// ,以保证阶段型等待指令的语义真实有效。 /// public partial class Timing : Node { private const string NotInitializedMessage = "Timing not yet initialized (_Ready not executed)"; private static readonly Timing?[] ActiveInstances = new Timing?[16]; private static Timing? _instance; private Dictionary _ownedCoroutineRegistrations = new(); private Dictionary> _ownedCoroutinesByNode = new(); private GodotTimeSource? _deferredRealtimeTimeSource; private CoroutineScheduler? _deferredScheduler; private GodotTimeSource? _deferredTimeSource; private ushort _frameCounter; private byte _instanceId = 1; private GodotTimeSource? _physicsRealtimeTimeSource; private CoroutineScheduler? _physicsScheduler; private GodotTimeSource? _physicsTimeSource; private GodotTimeSource? _processIgnorePauseRealtimeTimeSource; private CoroutineScheduler? _processIgnorePauseScheduler; private GodotTimeSource? _processIgnorePauseTimeSource; private GodotTimeSource? _processRealtimeTimeSource; private CoroutineScheduler? _processScheduler; private GodotTimeSource? _processTimeSource; /// /// 获取 Process 调度器。 /// private CoroutineScheduler ProcessScheduler => _processScheduler ?? throw new InvalidOperationException(NotInitializedMessage); /// /// 获取忽略暂停的 Process 调度器。 /// private CoroutineScheduler ProcessIgnorePauseScheduler => _processIgnorePauseScheduler ?? throw new InvalidOperationException(NotInitializedMessage); /// /// 获取 Physics 调度器。 /// private CoroutineScheduler PhysicsScheduler => _physicsScheduler ?? throw new InvalidOperationException(NotInitializedMessage); /// /// 获取 Deferred 调度器。 /// private CoroutineScheduler DeferredScheduler => _deferredScheduler ?? throw new InvalidOperationException(NotInitializedMessage); #region 单例 /// /// 获取 Timing 单例实例。 /// 如果实例不存在,则会自动创建并挂载到场景树根节点。 /// public static Timing Instance { get { if (_instance != null) { return _instance; } var tree = (SceneTree)Engine.GetMainLoop(); _instance = tree.Root.GetNodeOrNull(nameof(Timing)); if (_instance != null) { return _instance; } _instance = new Timing { Name = nameof(Timing) }; tree.Root.WaitUntilReady(() => tree.Root.AddChild(_instance)); return _instance; } } #endregion private sealed class OwnedCoroutineRegistration { /// /// 创建一个节点归属协程注册记录。 /// /// 归属节点。 /// 归属节点的 Godot 实例 ID。 /// 被管理的协程句柄。 /// 节点退出场景树时触发的终止逻辑。 public OwnedCoroutineRegistration(Node owner, ulong ownerId, CoroutineHandle handle, Action killCallback) { Handle = handle; Owner = new WeakReference(owner); OwnerId = ownerId; OnOwnerTreeExiting = () => killCallback(handle); } /// /// 获取协程句柄。 /// public CoroutineHandle Handle { get; } /// /// 获取节点弱引用。 /// public WeakReference Owner { get; } /// /// 获取归属节点 ID。 /// public ulong OwnerId { get; } /// /// 获取节点退出场景树时使用的清理回调。 /// public Action OnOwnerTreeExiting { get; } } #region Debug 信息 /// /// 获取 Process 段活跃协程数量。 /// public int ProcessCoroutines => _processScheduler?.ActiveCoroutineCount ?? 0; /// /// 获取忽略暂停的 Process 段活跃协程数量。 /// public int ProcessIgnorePauseCoroutines => _processIgnorePauseScheduler?.ActiveCoroutineCount ?? 0; /// /// 获取 Physics 段活跃协程数量。 /// public int PhysicsCoroutines => _physicsScheduler?.ActiveCoroutineCount ?? 0; /// /// 获取 Deferred 段活跃协程数量。 /// public int DeferredCoroutines => _deferredScheduler?.ActiveCoroutineCount ?? 0; #endregion #region 生命周期 /// /// 节点就绪时初始化所有调度器与生命周期桥接。 /// public override void _Ready() { ProcessPriority = -1; ProcessMode = ProcessModeEnum.Always; RegisterInstance(); TrySetPhysicsPriority(-1); InitializeSchedulers(); } /// /// 节点退出场景树时清理实例与归属关系。 /// public override void _ExitTree() { DetachAllOwnedRegistrations(); ClearOnInstance(); if (_instanceId < ActiveInstances.Length) { ActiveInstances[_instanceId] = null; } CleanupInstanceIfNecessary(this); } /// /// 仅在当前实例仍持有共享单例引用时清理它,避免多宿主场景误清其他实例。 /// /// 正在退出生命周期的实例。 private static void CleanupInstanceIfNecessary(Timing instance) { if (ReferenceEquals(_instance, instance)) { _instance = null; } } /// /// Godot 每帧更新逻辑。 /// /// 本帧 Process 增量。 public override void _Process(double delta) { var paused = GetTree().Paused; if (!paused) { _processScheduler?.Update(); } _processIgnorePauseScheduler?.Update(); _frameCounter++; CallDeferred(nameof(ProcessDeferred)); } /// /// Godot 物理帧更新逻辑。 /// /// 本帧 Physics 增量。 public override void _PhysicsProcess(double delta) { _physicsScheduler?.Update(); } /// /// 当前帧尾的延迟更新逻辑。 /// private void ProcessDeferred() { if (GetTree().Paused) { return; } _deferredScheduler?.Update(); } #endregion #region 初始化 /// /// 预热 Timing 单例,以便在业务逻辑首次使用前完成挂载。 /// public static void Prewarm() { _ = Instance; } /// /// 初始化所有调度器和时间源。 /// private void InitializeSchedulers() { _processTimeSource = new GodotTimeSource(GetProcessDeltaTime); _processRealtimeTimeSource = GodotTimeSource.CreateRealtime(); _processIgnorePauseTimeSource = new GodotTimeSource(GetProcessDeltaTime); _processIgnorePauseRealtimeTimeSource = GodotTimeSource.CreateRealtime(); _physicsTimeSource = new GodotTimeSource(GetPhysicsProcessDeltaTime); _physicsRealtimeTimeSource = GodotTimeSource.CreateRealtime(); _deferredTimeSource = new GodotTimeSource(GetProcessDeltaTime); _deferredRealtimeTimeSource = GodotTimeSource.CreateRealtime(); _processScheduler = new CoroutineScheduler( _processTimeSource, _instanceId, 256, false, _processRealtimeTimeSource, CoroutineExecutionStage.Update); _processIgnorePauseScheduler = new CoroutineScheduler( _processIgnorePauseTimeSource, _instanceId, 256, false, _processIgnorePauseRealtimeTimeSource, CoroutineExecutionStage.Update); _physicsScheduler = new CoroutineScheduler( _physicsTimeSource, _instanceId, 128, false, _physicsRealtimeTimeSource, CoroutineExecutionStage.FixedUpdate); _deferredScheduler = new CoroutineScheduler( _deferredTimeSource, _instanceId, 64, false, _deferredRealtimeTimeSource, CoroutineExecutionStage.EndOfFrame); AttachSchedulerLifecycleHandlers(ProcessScheduler); AttachSchedulerLifecycleHandlers(ProcessIgnorePauseScheduler); AttachSchedulerLifecycleHandlers(PhysicsScheduler); AttachSchedulerLifecycleHandlers(DeferredScheduler); } /// /// 把调度器的完成通知接入 Timing 的节点归属清理逻辑。 /// /// 待桥接的调度器。 private void AttachSchedulerLifecycleHandlers(CoroutineScheduler scheduler) { scheduler.OnCoroutineFinished += HandleCoroutineFinished; } /// /// 注册当前 Timing 实例到实例槽位表中。 /// private void RegisterInstance() { if (ActiveInstances[_instanceId] == null) { ActiveInstances[_instanceId] = this; return; } for (byte i = 1; i < ActiveInstances.Length; i++) { if (ActiveInstances[i] == null) { _instanceId = i; ActiveInstances[i] = this; return; } } throw new OverflowException("最多只能存在 15 个 Timing 实例"); } /// /// 通过反射设置 Physics 处理优先级,兼容不同 Godot 版本的 API 表面。 /// /// 要设置的优先级。 private void TrySetPhysicsPriority(int priority) { try { typeof(Node) .GetProperty( "ProcessPhysicsPriority", BindingFlags.Instance | BindingFlags.Public) ?.SetValue(this, priority); } catch { // ignored } } #endregion #region 协程启动 API /// /// 运行受场景暂停影响的游戏级协程。 /// /// 要运行的协程枚举器。 /// 协程标签。 /// 新创建的协程句柄。 public static CoroutineHandle RunGameCoroutine(IEnumerator coroutine, string? tag = null) { return RunCoroutine(coroutine, Segment.Process, tag); } /// /// 运行忽略场景暂停的 UI 级协程。 /// /// 要运行的协程枚举器。 /// 协程标签。 /// 新创建的协程句柄。 public static CoroutineHandle RunUiCoroutine(IEnumerator coroutine, string? tag = null) { return RunCoroutine(coroutine, Segment.ProcessIgnorePause, tag); } /// /// 在指定段运行协程。 /// /// 要运行的协程枚举器。 /// 协程执行段。 /// 协程标签。 /// 可选取消令牌。 /// 新创建的协程句柄。 public static CoroutineHandle RunCoroutine( IEnumerator coroutine, Segment segment = Segment.Process, string? tag = null, CancellationToken cancellationToken = default) { return Instance.RunCoroutineOnInstance(coroutine, segment, tag, cancellationToken); } /// /// 运行一个显式归属于指定节点的协程。 /// /// 拥有该协程生命周期的节点。 /// 要运行的协程枚举器。 /// 协程执行段。 /// 协程标签。 /// 可选取消令牌。 /// 新创建的协程句柄。 public static CoroutineHandle RunOwnedCoroutine( Node owner, IEnumerator coroutine, Segment segment = Segment.Process, string? tag = null, CancellationToken cancellationToken = default) { return Instance.RunOwnedCoroutineOnInstance(owner, coroutine, segment, tag, cancellationToken); } /// /// 在当前实例上运行协程。 /// /// 要运行的协程枚举器。 /// 协程执行段。 /// 协程标签。 /// 可选取消令牌。 /// 新创建的协程句柄。 public CoroutineHandle RunCoroutineOnInstance( IEnumerator? coroutine, Segment segment = Segment.Process, string? tag = null, CancellationToken cancellationToken = default) { if (coroutine == null) { return default; } return GetScheduler(segment).Run(coroutine, tag, group: null, cancellationToken: cancellationToken); } /// /// 在当前实例上运行归属于指定节点的协程。 /// /// 拥有该协程的节点。 /// 要运行的协程枚举器。 /// 协程执行段。 /// 协程标签。 /// 可选取消令牌。 /// 新创建的协程句柄。 public CoroutineHandle RunOwnedCoroutineOnInstance( Node? owner, IEnumerator? coroutine, Segment segment = Segment.Process, string? tag = null, CancellationToken cancellationToken = default) { if (owner == null || coroutine == null || !IsNodeAlive(owner)) { return default; } var handle = RunCoroutineOnInstance( coroutine.CancelWith(owner), segment, tag, cancellationToken); if (!handle.IsValid) { return handle; } if (!GetScheduler(segment).IsCoroutineAlive(handle)) { return handle; } RegisterOwnedCoroutine(owner, handle); return handle; } #endregion #region 协程控制 API /// /// 暂停指定的协程。 /// /// 协程句柄。 /// 如果成功暂停则返回 public static bool PauseCoroutine(CoroutineHandle handle) { return GetInstance(handle.Key)?.PauseOnInstance(handle) == true; } /// /// 恢复指定的协程。 /// /// 协程句柄。 /// 如果成功恢复则返回 public static bool ResumeCoroutine(CoroutineHandle handle) { return GetInstance(handle.Key)?.ResumeOnInstance(handle) == true; } /// /// 终止指定的协程。 /// /// 协程句柄。 /// 如果成功终止则返回 public static bool KillCoroutine(CoroutineHandle handle) { return GetInstance(handle.Key)?.KillOnInstance(handle) == true; } /// /// 终止某个节点归属的所有协程。 /// /// 协程归属节点。 /// 被终止的协程数量。 public static int KillCoroutines(Node owner) { var count = 0; foreach (var timing in EnumerateActiveInstances()) { count += timing.KillOwnedCoroutinesOnInstance(owner); } return count; } /// /// 终止所有具有指定标签的协程。 /// /// 协程标签。 /// 被终止的协程数量。 public static int KillCoroutines(string tag) { return Instance.KillByTagOnInstance(tag); } /// /// 终止所有协程。 /// /// 被终止的协程总数。 public static int KillAllCoroutines() { return Instance.ClearOnInstance(); } /// /// 根据协程句柄查询其当前快照。 /// /// 要查询的协程句柄。 /// 查询成功时返回快照。 /// 如果找到活跃协程则返回 public static bool TryGetCoroutineSnapshot(CoroutineHandle handle, out CoroutineSnapshot snapshot) { var instance = GetInstance(handle.Key); if (instance == null) { snapshot = default; return false; } return instance.TryGetSnapshotOnInstance(handle, out snapshot); } /// /// 获取某个节点当前归属的活跃协程数量。 /// /// 要查询的节点。 /// 该节点当前归属的活跃协程数量。 public static int GetOwnedCoroutineCount(Node owner) { var count = 0; foreach (var timing in EnumerateActiveInstances()) { count += timing.GetOwnedCoroutineCountOnInstance(owner); } return count; } /// /// 在当前实例上暂停协程。 /// /// 协程句柄。 /// 如果成功暂停则返回 private bool PauseOnInstance(CoroutineHandle handle) { return ProcessScheduler.Pause(handle) || ProcessIgnorePauseScheduler.Pause(handle) || PhysicsScheduler.Pause(handle) || DeferredScheduler.Pause(handle); } /// /// 在当前实例上恢复协程。 /// /// 协程句柄。 /// 如果成功恢复则返回 private bool ResumeOnInstance(CoroutineHandle handle) { return ProcessScheduler.Resume(handle) || ProcessIgnorePauseScheduler.Resume(handle) || PhysicsScheduler.Resume(handle) || DeferredScheduler.Resume(handle); } /// /// 在当前实例上终止协程。 /// /// 协程句柄。 /// 如果成功终止则返回 private bool KillOnInstance(CoroutineHandle handle) { return ProcessScheduler.Kill(handle) || ProcessIgnorePauseScheduler.Kill(handle) || PhysicsScheduler.Kill(handle) || DeferredScheduler.Kill(handle); } /// /// 在当前实例上根据标签终止协程。 /// /// 协程标签。 /// 被终止的协程数量。 private int KillByTagOnInstance(string tag) { var count = 0; count += ProcessScheduler.KillByTag(tag); count += ProcessIgnorePauseScheduler.KillByTag(tag); count += PhysicsScheduler.KillByTag(tag); count += DeferredScheduler.KillByTag(tag); return count; } /// /// 在当前实例上终止某个节点归属的所有协程。 /// /// 协程归属节点。 /// 被终止的协程数量。 private int KillOwnedCoroutinesOnInstance(Node owner) { var ownerId = owner.GetInstanceId(); if (!_ownedCoroutinesByNode.TryGetValue(ownerId, out var handles)) { return 0; } var count = 0; foreach (var handle in handles.ToArray()) { if (KillOnInstance(handle)) { count++; } } return count; } /// /// 获取某个节点当前归属的活跃协程数量。 /// /// 要查询的节点。 /// 活跃归属协程数量。 private int GetOwnedCoroutineCountOnInstance(Node owner) { var ownerId = owner.GetInstanceId(); return _ownedCoroutinesByNode.TryGetValue(ownerId, out var handles) ? handles.Count : 0; } /// /// 清空当前实例上的所有协程。 /// /// 被清除的协程总数。 private int ClearOnInstance() { var count = 0; count += ProcessScheduler.Clear(); count += ProcessIgnorePauseScheduler.Clear(); count += PhysicsScheduler.Clear(); count += DeferredScheduler.Clear(); return count; } #endregion #region 工具方法 /// /// 根据 ID 获取 Timing 实例。 /// /// 实例 ID。 /// 对应的 Timing 实例;如果不存在则返回 public static Timing? GetInstance(byte id) { return id < ActiveInstances.Length ? ActiveInstances[id] : null; } /// /// 枚举所有当前已注册的 Timing 实例。 /// /// 活跃 Timing 实例序列。 private static IEnumerable EnumerateActiveInstances() { return ActiveInstances.Where(static timing => timing is not null).Select(static timing => timing!); } /// /// 检查节点是否处于有效状态。 /// /// 要检查的节点。 /// 如果节点存在、实例有效、未进入删除队列且仍在场景树中,则返回 public static bool IsNodeAlive(Node? node) { return node != null && IsInstanceValid(node) && !node.IsQueuedForDeletion() && node.IsInsideTree(); } /// /// 根据分段选择具体调度器。 /// /// 目标执行段。 /// 与分段对应的协程调度器。 private CoroutineScheduler GetScheduler(Segment segment) { return segment switch { Segment.Process => ProcessScheduler, Segment.ProcessIgnorePause => ProcessIgnorePauseScheduler, Segment.PhysicsProcess => PhysicsScheduler, Segment.DeferredProcess => DeferredScheduler, _ => throw new ArgumentOutOfRangeException(nameof(segment), segment, "Unsupported coroutine segment.") }; } /// /// 在当前实例上查询指定句柄的快照。 /// /// 协程句柄。 /// 查询成功时返回快照。 /// 如果找到活跃协程则返回 private bool TryGetSnapshotOnInstance(CoroutineHandle handle, out CoroutineSnapshot snapshot) { return ProcessScheduler.TryGetSnapshot(handle, out snapshot) || ProcessIgnorePauseScheduler.TryGetSnapshot(handle, out snapshot) || PhysicsScheduler.TryGetSnapshot(handle, out snapshot) || DeferredScheduler.TryGetSnapshot(handle, out snapshot); } /// /// 注册节点归属协程,并在节点退树时强制终止该协程。 /// /// 协程归属节点。 /// 要登记的协程句柄。 private void RegisterOwnedCoroutine(Node owner, CoroutineHandle handle) { var ownerId = owner.GetInstanceId(); var registration = new OwnedCoroutineRegistration(owner, ownerId, handle, ownedHandle => _ = KillOnInstance(ownedHandle)); _ownedCoroutineRegistrations[handle] = registration; if (!_ownedCoroutinesByNode.TryGetValue(ownerId, out var handles)) { handles = new HashSet(); _ownedCoroutinesByNode[ownerId] = handles; } handles.Add(handle); owner.TreeExiting += registration.OnOwnerTreeExiting; } /// /// 在协程结束时解除节点归属回调并清理索引。 /// /// 触发事件的协程调度器。 /// 协程结束事件数据。 private void HandleCoroutineFinished( object? sender, CoroutineFinishedEventArgs eventArgs) { CleanupOwnedCoroutineRegistration(eventArgs.Handle); } /// /// 清理单个协程对应的节点归属注册。 /// /// 要清理的协程句柄。 private void CleanupOwnedCoroutineRegistration(CoroutineHandle handle) { if (!_ownedCoroutineRegistrations.TryGetValue(handle, out var registration)) { return; } if (registration.Owner.TryGetTarget(out var owner) && IsInstanceValid(owner)) { owner.TreeExiting -= registration.OnOwnerTreeExiting; } if (_ownedCoroutinesByNode.TryGetValue(registration.OwnerId, out var handles)) { handles.Remove(handle); if (handles.Count == 0) { _ownedCoroutinesByNode.Remove(registration.OwnerId); } } _ownedCoroutineRegistrations.Remove(handle); } /// /// 清理所有已登记的节点归属回调。 /// private void DetachAllOwnedRegistrations() { foreach (var handle in _ownedCoroutineRegistrations.Keys.ToArray()) { CleanupOwnedCoroutineRegistration(handle); } } #endregion #region 延迟调用 /// /// 延迟执行指定动作。 /// /// 延迟时间,单位秒。 /// 到期时执行的动作。 /// 执行所在的协程段。 /// 新创建的协程句柄。 public static CoroutineHandle CallDelayed(double delay, Action? action, Segment segment = Segment.Process) { if (action == null) { return default; } return RunCoroutine(DelayedCallCoroutine(delay, action), segment); } /// /// 延迟执行指定动作,并在节点失活时自动放弃执行。 /// /// 延迟时间,单位秒。 /// 到期时执行的动作。 /// 用于控制生命周期的节点。 /// 执行所在的协程段。 /// 新创建的协程句柄。 public static CoroutineHandle CallDelayed( double delay, Action? action, Node cancelWith, Segment segment = Segment.Process) { if (action == null) { return default; } return RunOwnedCoroutine(cancelWith, DelayedCallWithCancelCoroutine(delay, action, cancelWith), segment); } /// /// 延迟调用协程实现。 /// /// 延迟时间。 /// 要执行的动作。 /// 可直接交给调度器运行的协程枚举器。 private static IEnumerator DelayedCallCoroutine(double delay, Action action) { yield return new Delay(delay); action(); } /// /// 带节点生命周期判断的延迟调用协程实现。 /// /// 延迟时间。 /// 要执行的动作。 /// 生命周期检查节点。 /// 可直接交给调度器运行的协程枚举器。 private static IEnumerator DelayedCallWithCancelCoroutine(double delay, Action action, Node cancelWith) { yield return new Delay(delay); if (IsNodeAlive(cancelWith)) { action(); } } #endregion }