diff --git a/Craftimizer/Utils/BackgroundTask.cs b/Craftimizer/Utils/BackgroundTask.cs new file mode 100644 index 0000000..69c56d9 --- /dev/null +++ b/Craftimizer/Utils/BackgroundTask.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Craftimizer.Utils; + +public sealed class BackgroundTask(Func func) : IDisposable where T : struct +{ + public T? Result { get; private set; } + public Exception? Exception { get; private set; } + public bool Completed { get; private set; } + public bool Cancelling => !Completed && TokenSource.IsCancellationRequested; + + private CancellationTokenSource TokenSource { get; } = new(); + private Func Func { get; } = func; + + public void Start() + { + var token = TokenSource.Token; + var task = Task.Run(() => Result = Func(token), token); + _ = task.ContinueWith(t => Completed = true); + _ = task.ContinueWith(t => + { + if (token.IsCancellationRequested) + return; + + try + { + t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException); + } + catch (AggregateException e) + { + Exception = e; + Log.Error(e, "Background task failed"); + } + }, TaskContinuationOptions.OnlyOnFaulted); + } + + public void Cancel() => + TokenSource.Cancel(); + + public void Dispose() => + Cancel(); +} diff --git a/Craftimizer/Windows/MacroEditor.cs b/Craftimizer/Windows/MacroEditor.cs index 971296d..58af39d 100644 --- a/Craftimizer/Windows/MacroEditor.cs +++ b/Craftimizer/Windows/MacroEditor.cs @@ -83,11 +83,10 @@ public sealed class MacroEditor : Window, IDisposable private ActionType[] DefaultActions { get; } private Action>? MacroSetter { get; set; } - private CancellationTokenSource? SolverTokenSource { get; set; } - private Exception? SolverException { get; set; } - private int? SolverStartStepCount { get; set; } + private BackgroundTask? SolverTask { get; set; } + private bool SolverRunning => (!SolverTask?.Completed) ?? false; private Solver.Solver? SolverObject { get; set; } - private bool SolverRunning => SolverTokenSource != null; + private int? SolverStartStepCount { get; set; } private ILoadedTextureIcon ExpertBadge { get; } private ILoadedTextureIcon CollectibleBadge { get; } @@ -197,7 +196,8 @@ public sealed class MacroEditor : Window, IDisposable public override void OnClose() { - SolverTokenSource?.Cancel(); + base.OnClose(); + SolverTask?.Cancel(); } public override void Update() @@ -1270,7 +1270,7 @@ public sealed class MacroEditor : Window, IDisposable ImGui.SameLine(); if (SolverRunning) { - if (SolverTokenSource?.IsCancellationRequested ?? false) + if (SolverTask?.Cancelling ?? false) { using var _disabled = ImRaii.Disabled(); ImGui.Button("Stopping", new(halfWidth, height)); @@ -1278,7 +1278,7 @@ public sealed class MacroEditor : Window, IDisposable else { if (ImGui.Button("Stop", new(halfWidth, height))) - SolverTokenSource?.Cancel(); + SolverTask?.Cancel(); } } else @@ -1524,9 +1524,7 @@ public sealed class MacroEditor : Window, IDisposable private void CalculateBestMacro() { - SolverTokenSource?.Cancel(); - SolverTokenSource = new(); - SolverException = null; + SolverTask?.Cancel(); Macro.ClearQueue(); RevertPreviousMacro(); @@ -1540,49 +1538,27 @@ public sealed class MacroEditor : Window, IDisposable SolverStartStepCount = Macro.Count; - var token = SolverTokenSource.Token; var state = State; - var task = Task.Run(() => CalculateBestMacroTask(state, token), token); - _ = task.ContinueWith(t => - { - if (token == SolverTokenSource.Token) - { - SolverTokenSource = null; - SolverObject = null; - } - }); - _ = task.ContinueWith(t => - { - if (token.IsCancellationRequested) - return; - - try - { - t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException); - } - catch (AggregateException e) - { - SolverException = e; - Log.Error(e, "Calculating macro failed"); - } - }, TaskContinuationOptions.OnlyOnFaulted); + SolverTask = new(token => CalculateBestMacroTask(state, token)); + SolverTask.Start(); } - private void CalculateBestMacroTask(SimulationState state, CancellationToken token) + private int CalculateBestMacroTask(SimulationState state, CancellationToken token) { var config = Service.Configuration.EditorSolverConfig; token.ThrowIfCancellationRequested(); - using (SolverObject = new Solver.Solver(config, state) { Token = token }) - { - SolverObject.OnLog += Log.Debug; - SolverObject.OnNewAction += a => Macro.Enqueue(a); - SolverObject.Start(); - _ = SolverObject.GetTask().GetAwaiter().GetResult(); - } + var solver = new Solver.Solver(config, state) { Token = token }; + solver.OnLog += Log.Debug; + solver.OnNewAction += a => Macro.Enqueue(a); + SolverObject = solver; + solver.Start(); + _ = solver.GetTask().GetAwaiter().GetResult(); token.ThrowIfCancellationRequested(); + + return 0; } private void RevertPreviousMacro() diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index 5d9dc36..35f34b8 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -60,44 +60,6 @@ public sealed unsafe class RecipeNote : Window, IDisposable public CharacterStats? CharacterStats { get; private set; } public CraftableStatus CraftStatus { get; private set; } - public sealed class BackgroundTask(Func func) : IDisposable where T : struct - { - public T? Result { get; private set; } - public Exception? Exception { get; private set; } - public bool Completed { get; private set; } - - private CancellationTokenSource TokenSource { get; } = new(); - private Func Func { get; } = func; - - public void Start() - { - var token = TokenSource.Token; - var task = Task.Run(() => Result = Func(token), token); - _ = task.ContinueWith(t => Completed = true); - _ = task.ContinueWith(t => - { - if (token.IsCancellationRequested) - return; - - try - { - t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException); - } - catch (AggregateException e) - { - Exception = e; - Log.Error(e, "Calculating macros failed"); - } - }, TaskContinuationOptions.OnlyOnFaulted); - } - - public void Cancel() => - TokenSource.Cancel(); - - public void Dispose() => - Cancel(); - } - private BackgroundTask<(Macro?, SimulationState?)>? SavedMacroTask { get; set; } private BackgroundTask? SuggestedMacroTask { get; set; } private BackgroundTask<(CommunityMacros.CommunityMacro?, SimulationState?)>? CommunityMacroTask { get; set; } diff --git a/Craftimizer/Windows/SynthHelper.cs b/Craftimizer/Windows/SynthHelper.cs index 07082b3..0b6f8aa 100644 --- a/Craftimizer/Windows/SynthHelper.cs +++ b/Craftimizer/Windows/SynthHelper.cs @@ -529,7 +529,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable private void OnUseAction(ActionType action) { - if (!IsCrafting) + if (!IsCrafting || !ShouldOpen || IsCollapsed) return; (_, CurrentState) = new SimNoRandom().Execute(GetCurrentState(), action); @@ -539,7 +539,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable private void OnFinishedUsingAction() { - if (!IsCrafting) + if (!IsCrafting || !ShouldOpen || IsCollapsed) return; CurrentState = GetCurrentState(); @@ -596,7 +596,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable private void OnStateUpdated() { - if (!IsCrafting) + if (!IsCrafting || !ShouldOpen || IsCollapsed) return; Macro.Clear();