using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using BetterGenshinImpact.Core.Recognition; using BetterGenshinImpact.GameTask.Model.Area; using static BetterGenshinImpact.GameTask.Common.TaskControl; namespace BetterGenshinImpact.GameTask.Common; /// /// https://stackoverflow.com/questions/1563191/cleanest-way-to-write-retry-logic /// public static class NewRetry { /// /// 重试指定操作,若抛出 RetryException 则在指定间隔后重试,最多尝试 maxAttemptCount 次。 /// /// 要执行的操作 /// 重试间隔 /// 最大尝试次数 public static void Do(Action action, TimeSpan retryInterval, int maxAttemptCount = 3) { _ = Do(() => { action(); return null; }, retryInterval, maxAttemptCount); } /// /// 重试指定操作,若抛出 RetryException 则在指定间隔后重试,最多尝试 maxAttemptCount 次,返回操作结果。 /// /// 返回值类型 /// 要执行的操作 /// 重试间隔 /// 最大尝试次数 /// 操作结果 public static T Do(Func action, TimeSpan retryInterval, int maxAttemptCount = 3) { List exceptions = []; for (int attempted = 0; attempted < maxAttemptCount; attempted++) { try { if (attempted > 0) { Thread.Sleep(retryInterval); } return action(); } catch (RetryException ex) { exceptions.Add(ex); } } if (exceptions.Count > 0) { throw exceptions.Last(); } throw new AggregateException(exceptions); } /// /// 重试执行 action,直到返回 true 或达到最大重试次数。 /// /// 判断条件 /// 取消令牌 /// 最大重试次数 /// 每次重试间隔(毫秒) /// 是否成功 public static async Task WaitForAction(Func action, CancellationToken ct, int retryTimes = 10, int delayMs = 1000) { for (var i = 0; i < retryTimes; i++) { await TaskControl.Delay(delayMs, ct); if (action()) { return true; } } return false; } /// /// 重试直到某个元素出现,可执行键盘或鼠标操作。 /// /// 要识别的目标对象 /// 每次重试时执行的操作 /// 取消令牌 /// 最大尝试次数 /// 重试间隔(毫秒) /// 是否成功找到元素 public static async Task WaitForElementAppear( RecognitionObject recognitionObject, Action? retryAction, CancellationToken ct, int maxAttemptCount = 10, int retryInterval = 1000 ) { for (int i = 0; i < maxAttemptCount; i++) { if (ct.IsCancellationRequested) return false; // 执行重试操作(如按键) retryAction?.Invoke(); // 等待指定时间 await TaskControl.Delay(retryInterval, ct); // 截图并查找元素 using var screen = CaptureToRectArea(); using var result = screen.Find(recognitionObject); // 元素已出现 if (!result.IsEmpty()) { return true; } } return false; } /// /// 重试直到某个元素消失,可执行键盘或鼠标操作。 /// /// 要识别的目标对象 /// 每次重试时执行的操作 /// 取消令牌 /// 最大尝试次数 /// 重试间隔(毫秒) /// 是否成功等待元素消失 public static async Task WaitForElementDisappear( RecognitionObject recognitionObject, Action? retryAction, CancellationToken ct, int maxAttemptCount = 10, int retryInterval = 1000) { for (int i = 0; i < maxAttemptCount; i++) { if (ct.IsCancellationRequested) return false; // 执行重试操作(如按键) retryAction?.Invoke(); // 等待指定时间 await TaskControl.Delay(retryInterval, ct); // 截图并查找元素 using var screen = CaptureToRectArea(); using var result = screen.Find(recognitionObject); // 元素已消失 if (result.IsEmpty()) { return true; } } return false; } public static async Task WaitForElementDisappear( RecognitionObject recognitionObject, Action retryAction, // 接收截图的回调 CancellationToken ct, int maxAttemptCount = 10, int retryInterval = 1000) { for (int i = 0; i < maxAttemptCount; i++) { if (ct.IsCancellationRequested) return false; // 截图并查找元素 using var screen = CaptureToRectArea(); using var result = screen.Find(recognitionObject); // 元素已消失 if (result.IsEmpty()) return true; // 执行重试操作(传入当前截图) retryAction?.Invoke(screen); // 等待指定时间 await Delay(retryInterval, ct); } return false; } }