using Godot; using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; /// /// Provides defer-like behavior that's easier to work with in C# /// public partial class defer_manager : Node { // Called when the node enters the scene tree for the first time. public override void _Ready() { } private readonly ConcurrentQueue ActionQueue = new(); // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { while (ActionQueue.TryDequeue(out Action a)) { a?.Invoke(); } } public void DeferForget(Action action) { ActionQueue.Enqueue(action); } public async Task DeferAsync(Action action) { using SemaphoreSlim slim = new(0); ActionQueue.Enqueue(() => { action?.Invoke(); slim.Release(); } ); await slim.WaitAsync(); } public async Task DeferAsync(Func func) { if (func is null) throw new Exception("Cannot defer null function"); using SemaphoreSlim slim = new(0); T[] box = { default }; ActionQueue.Enqueue(() => { box[0] = func.Invoke(); slim.Release(); } ); await slim.WaitAsync(); return box[0]; } public void Defer(Action action) { using SemaphoreSlim slim = new(0); ActionQueue.Enqueue(() => { action?.Invoke(); slim.Release(); } ); slim.WaitAsync(); } public T Defer(Func func) { if (func is null) throw new Exception("Cannot defer null function"); using SemaphoreSlim slim = new(0); T[] box = { default }; ActionQueue.Enqueue(() => { box[0] = func.Invoke(); slim.Release(); } ); slim.WaitAsync(); return box[0]; } }