From 8fa071722eede631d5c913cc0bf387f1dd5ffbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Sun, 11 Aug 2024 17:58:22 +0800 Subject: [PATCH] js : dispatcher.addTimer --- .../Core/Script/Dependence/Dispatcher.cs | 22 ++++++++ .../Script/Dependence/Model/RealTimeTimer.cs | 23 ++++++++ .../TimerConfig/AutoPickExternalConfig.cs | 13 +++++ .../Core/Script/EngineExtend.cs | 1 + .../Core/Script/Project/ScriptProject.cs | 2 +- .../GameTask/AutoPick/AutoPickTrigger.cs | 11 +++- .../GameTask/GameTaskManager.cs | 56 ++++++++++++++++--- .../Model/Enum/DispatcherCaptureModeEnum.cs | 5 ++ .../Enum/DispatcherTimerOperationEnum.cs | 9 ++- BetterGenshinImpact/GameTask/TaskRunner.cs | 26 +++++++-- .../GameTask/TaskTriggerDispatcher.cs | 23 +++++++- .../Script/AutoCrystalfly/main.js | 4 ++ .../ViewModel/Pages/ScriptControlViewModel.cs | 34 ++++++++++- 13 files changed, 210 insertions(+), 19 deletions(-) create mode 100644 BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs create mode 100644 BetterGenshinImpact/Core/Script/Dependence/Model/RealTimeTimer.cs create mode 100644 BetterGenshinImpact/Core/Script/Dependence/Model/TimerConfig/AutoPickExternalConfig.cs diff --git a/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs new file mode 100644 index 00000000..5dd7551a --- /dev/null +++ b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs @@ -0,0 +1,22 @@ +using System; +using BetterGenshinImpact.Core.Script.Dependence.Model; +using BetterGenshinImpact.GameTask; + +namespace BetterGenshinImpact.Core.Script.Dependence; + +public class Dispatcher +{ + public void RunTask() + { + } + + public void AddTimer(RealTimeTimer timer) + { + if (string.IsNullOrEmpty(timer.Name)) + { + throw new ArgumentNullException(nameof(timer.Name), "实时任务名称不能为空"); + } + + TaskTriggerDispatcher.Instance().AddTrigger(timer.Name, timer.Config); + } +} diff --git a/BetterGenshinImpact/Core/Script/Dependence/Model/RealTimeTimer.cs b/BetterGenshinImpact/Core/Script/Dependence/Model/RealTimeTimer.cs new file mode 100644 index 00000000..d002f81a --- /dev/null +++ b/BetterGenshinImpact/Core/Script/Dependence/Model/RealTimeTimer.cs @@ -0,0 +1,23 @@ +namespace BetterGenshinImpact.Core.Script.Dependence.Model; + +/// +/// 实时任务计时器 +/// +public class RealTimeTimer +{ + /// + /// 实时任务触发器名称 + /// + public string? Name { get; set; } + + /// + /// 实时任务触发器时间间隔 + /// 默认50ms + /// + public int Interval { get; set; } = 50; + + /// + /// 实时任务配置 + /// + public object? Config; +} diff --git a/BetterGenshinImpact/Core/Script/Dependence/Model/TimerConfig/AutoPickExternalConfig.cs b/BetterGenshinImpact/Core/Script/Dependence/Model/TimerConfig/AutoPickExternalConfig.cs new file mode 100644 index 00000000..d3dbce8b --- /dev/null +++ b/BetterGenshinImpact/Core/Script/Dependence/Model/TimerConfig/AutoPickExternalConfig.cs @@ -0,0 +1,13 @@ +namespace BetterGenshinImpact.Core.Script.Dependence.Model.TimerConfig; + +public class AutoPickExternalConfig +{ + // 关闭黑白名单 + public bool DisabledBlackWhiteList { get; set; } = false; + + // 需要F的文本(对话、拾取) + public string[] TextList { get; set; } = []; + + // 无视文本和图标遇到F就点击 + public bool ForceInteraction { get; set; } = false; +} diff --git a/BetterGenshinImpact/Core/Script/EngineExtend.cs b/BetterGenshinImpact/Core/Script/EngineExtend.cs index 5edb6511..e9f7b774 100644 --- a/BetterGenshinImpact/Core/Script/EngineExtend.cs +++ b/BetterGenshinImpact/Core/Script/EngineExtend.cs @@ -15,6 +15,7 @@ public class EngineExtend engine.AddHostObject("genshin", new Dependence.Genshin()); engine.AddHostObject("log", new Log()); engine.AddHostObject("file", new LimitedFile(workDir)); // 限制文件访问 + engine.AddHostObject("dispatcher", new Dispatcher()); // 直接添加方法 #pragma warning disable CS8974 // Converting method group to non-delegate type diff --git a/BetterGenshinImpact/Core/Script/Project/ScriptProject.cs b/BetterGenshinImpact/Core/Script/Project/ScriptProject.cs index 18549e81..abe9392b 100644 --- a/BetterGenshinImpact/Core/Script/Project/ScriptProject.cs +++ b/BetterGenshinImpact/Core/Script/Project/ScriptProject.cs @@ -57,7 +57,7 @@ public class ScriptProject } } - private async Task LoadCode() + public async Task LoadCode() { var code = await File.ReadAllTextAsync(Path.Combine(ProjectPath, Manifest.Main)); if (string.IsNullOrEmpty(code)) diff --git a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs index dd0e7026..49238218 100644 --- a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs +++ b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs @@ -17,6 +17,7 @@ using System.Windows.Forms; using BetterGenshinImpact.Core.Recognition; using Vanara.PInvoke; using System.Windows.Input; +using BetterGenshinImpact.Core.Script.Dependence.Model.TimerConfig; namespace BetterGenshinImpact.GameTask.AutoPick; @@ -48,14 +49,22 @@ public class AutoPickTrigger : ITaskTrigger private User32.VK _pickVk = User32.VK.VK_F; private RecognitionObject _pickRo; + // 外部配置 + private AutoPickExternalConfig? _externalConfig; + public AutoPickTrigger() { _autoPickAssets = AutoPickAssets.Instance; + _pickRo = _autoPickAssets.FRo; + } + + public AutoPickTrigger(AutoPickExternalConfig? config) : this() + { + _externalConfig = config; } public void Init() { - _pickRo = _autoPickAssets.FRo; var keyName = TaskContext.Instance().Config.AutoPickConfig.PickKey; if (!string.IsNullOrEmpty(keyName)) { diff --git a/BetterGenshinImpact/GameTask/GameTaskManager.cs b/BetterGenshinImpact/GameTask/GameTaskManager.cs index 6713a13e..5e13c55a 100644 --- a/BetterGenshinImpact/GameTask/GameTaskManager.cs +++ b/BetterGenshinImpact/GameTask/GameTaskManager.cs @@ -6,6 +6,7 @@ using OpenCvSharp; using System.Collections.Generic; using System.IO; using System.Linq; +using BetterGenshinImpact.Core.Script.Dependence.Model.TimerConfig; using BetterGenshinImpact.GameTask.AutoFight.Assets; using BetterGenshinImpact.GameTask.AutoFishing.Assets; using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Assets; @@ -30,7 +31,7 @@ internal class GameTaskManager /// 一定要在任务上下文初始化完毕后使用 /// /// - public static List LoadTriggers() + public static List LoadInitialTriggers() { ReloadAssets(); TriggerDictionary = new Dictionary() @@ -40,10 +41,20 @@ internal class GameTaskManager { "AutoPick", new AutoPick.AutoPickTrigger() }, { "QuickTeleport", new QuickTeleport.QuickTeleportTrigger() }, { "AutoSkip", new AutoSkip.AutoSkipTrigger() }, - { "AutoFishing", new AutoFishing.AutoFishingTrigger() }, + { "AutoFish", new AutoFishing.AutoFishingTrigger() }, { "AutoCook", new AutoCook.AutoCookTrigger() } }; + return ConvertToTriggerList(); + } + + public static List ConvertToTriggerList() + { + if (TriggerDictionary is null) + { + return []; + } + var loadedTriggers = TriggerDictionary.Values.ToList(); loadedTriggers.ForEach(i => i.Init()); @@ -52,16 +63,45 @@ internal class GameTaskManager return loadedTriggers; } + public static void ClearTriggers() + { + TriggerDictionary?.Clear(); + } + + /// + /// 通过名称添加触发器 + /// + /// + /// + public static void AddTrigger(string name, object? externalConfig) + { + TriggerDictionary ??= new Dictionary(); + TriggerDictionary.Clear(); + + if (name == "AutoPick") + { + TriggerDictionary.Add("AutoPick", new AutoPick.AutoPickTrigger(externalConfig as AutoPickExternalConfig)); + } + // else if (name == "AutoSkip") + // { + // TriggerDictionary.Add("AutoSkip", new AutoSkip.AutoSkipTrigger()); + // } + // else if (name == "AutoFish") + // { + // TriggerDictionary.Add("AutoFish", new AutoFishing.AutoFishingTrigger()); + // } + } + public static void RefreshTriggerConfigs() { if (TriggerDictionary is { Count: > 0 }) { - TriggerDictionary["AutoPick"].Init(); - TriggerDictionary["AutoSkip"].Init(); - TriggerDictionary["AutoFishing"].Init(); - TriggerDictionary["QuickTeleport"].Init(); - TriggerDictionary["GameLoading"].Init(); - TriggerDictionary["AutoCook"].Init(); + TriggerDictionary.GetValueOrDefault("AutoPick")?.Init(); + TriggerDictionary.GetValueOrDefault("AutoSkip")?.Init(); + TriggerDictionary.GetValueOrDefault("AutoFish")?.Init(); + TriggerDictionary.GetValueOrDefault("QuickTeleport")?.Init(); + TriggerDictionary.GetValueOrDefault("GameLoading")?.Init(); + TriggerDictionary.GetValueOrDefault("AutoCook")?.Init(); // 清理画布 WeakReferenceMessenger.Default.Send(new PropertyChangedMessage(new object(), "RemoveAllButton", new object(), "")); VisionContext.Instance().DrawContent.ClearAll(); diff --git a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs index e7ffe189..ec64160d 100644 --- a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs +++ b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs @@ -1,5 +1,10 @@ namespace BetterGenshinImpact.GameTask.Model.Enum; +/// +/// 调度器捕获模式, 影响以下内容: +/// 1. 是否缓存图像 +/// 2. 是否执行触发器 +/// public enum DispatcherCaptureModeEnum { // 正常运行调度器 diff --git a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs index 6218aeb4..2a3033a9 100644 --- a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs +++ b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs @@ -2,18 +2,23 @@ /// /// 存在触发器运行的情况下,优先使用触发器的缓存图像 +/// 此枚举会影响调度器使用的 DispatcherCaptureModeEnum 模式 /// public enum DispatcherTimerOperationEnum { // 关闭实时触发器,自己主动获取图像 UseSelfCaptureImage, - // 使用实时触发器的缓存图模式,但是不执行触发器 + // 使用实时触发器的缓存图模式,但是不执行触发器 UseCacheImage, - // 使用实时触发器的缓存图模式 + // 使用实时触发器的缓存图模式,并执行触发器 UseCacheImageWithTrigger, + // 使用实时触发器的缓存图模式,并清空当前已有触发器,执行触发器 + // 清空触发器是为了让js脚本手动添加触发器 + UseCacheImageWithTriggerEmpty, + // 不做任何操作 None } diff --git a/BetterGenshinImpact/GameTask/TaskRunner.cs b/BetterGenshinImpact/GameTask/TaskRunner.cs index b55a718c..5893def2 100644 --- a/BetterGenshinImpact/GameTask/TaskRunner.cs +++ b/BetterGenshinImpact/GameTask/TaskRunner.cs @@ -108,11 +108,17 @@ public class TaskRunner TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.OnlyCacheCapture); Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像 } - else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImage) + else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTrigger) { TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger); Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像 } + else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty) + { + TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger); + TaskTriggerDispatcher.Instance().ClearTriggers(); + Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像 + } } public void End() @@ -122,10 +128,22 @@ public class TaskRunner { TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.Start); } - else if (_timerOperation is DispatcherTimerOperationEnum.UseCacheImage or DispatcherTimerOperationEnum.UseCacheImageWithTrigger) + else if (_timerOperation is DispatcherTimerOperationEnum.UseCacheImage or DispatcherTimerOperationEnum.UseCacheImageWithTrigger or DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty) { - TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.NormalTrigger); - Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像 + // 还原到原来的模式 + if (TaskContext.Instance().Config.CommonConfig.ScreenshotEnabled || TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled) + { + TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger); + } + else + { + TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.NormalTrigger); + } + + if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty) + { + TaskTriggerDispatcher.Instance().SetTriggers(GameTaskManager.LoadInitialTriggers()); + } } } diff --git a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs index 7a4bfecb..5ac6c977 100644 --- a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs +++ b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs @@ -56,7 +56,9 @@ namespace BetterGenshinImpact.GameTask private Bitmap _bitmap = new(10, 10); /// - /// 仅捕获模式 + /// 调度器捕获模式, 影响以下内容: + /// 1. 是否缓存图像 + /// 2. 是否执行触发器 /// private DispatcherCaptureModeEnum _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.NormalTrigger; @@ -98,6 +100,23 @@ namespace BetterGenshinImpact.GameTask } } + public void ClearTriggers() + { + GameTaskManager.ClearTriggers(); + _triggers?.Clear(); + } + + public void SetTriggers(List list) + { + _triggers = list; + } + + public void AddTrigger(string name, object? externalConfig) + { + GameTaskManager.AddTrigger(name, externalConfig); + SetTriggers(GameTaskManager.ConvertToTriggerList()); + } + public void Start(IntPtr hWnd, CaptureModes mode, int interval = 50) { // 初始化截图器 @@ -109,7 +128,7 @@ namespace BetterGenshinImpact.GameTask TaskContext.Instance().Init(hWnd); // 初始化触发器(一定要在任务上下文初始化完毕后使用) - _triggers = GameTaskManager.LoadTriggers(); + _triggers = GameTaskManager.LoadInitialTriggers(); // 启动截图 GameCapture.Start(hWnd, diff --git a/BetterGenshinImpact/Script/AutoCrystalfly/main.js b/BetterGenshinImpact/Script/AutoCrystalfly/main.js index 2a14aa50..b7882b5c 100644 --- a/BetterGenshinImpact/Script/AutoCrystalfly/main.js +++ b/BetterGenshinImpact/Script/AutoCrystalfly/main.js @@ -1,4 +1,8 @@ (async function () { + + // 启用自动拾取的实时任务 + dispatcher.addTimer('AutoPick'); + log.info('开始捕捉晶蝶,请在队伍中务必携带{zy},使用成男/成女角色', '早柚'); log.info('前往 {name}', '枫丹-塔拉塔海谷'); diff --git a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs index bbab6347..e23c08ad 100644 --- a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs @@ -20,6 +20,7 @@ using BetterGenshinImpact.GameTask; using BetterGenshinImpact.GameTask.Model.Enum; using Wpf.Ui; using Wpf.Ui.Controls; +using System.Text.RegularExpressions; namespace BetterGenshinImpact.ViewModel.Pages; @@ -346,16 +347,29 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware // 重新加载脚本项目 var projects = SelectedScriptGroup.Projects.Select(project => new ScriptProject(project.FolderName)).ToList(); + var codeList = await ReadCodeList(projects); + var hasTimer = HasTimerOperation(codeList); + if (hasTimer) + { + _logger.LogInformation("脚本组 {Name} 包含实时任务操作调用", SelectedScriptGroup.Name); + } + _logger.LogInformation("脚本组 {Name} 加载完成,共{Cnt}个脚本,开始执行", SelectedScriptGroup.Name, projects.Count); // 循环执行所有脚本 - await new TaskRunner(DispatcherTimerOperationEnum.UseCacheImageWithTrigger) + var timerOperation = hasTimer ? DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty : DispatcherTimerOperationEnum.UseSelfCaptureImage; + await new TaskRunner(timerOperation) .RunAsync(async () => { foreach (var project in projects) { try { + if (hasTimer) + { + TaskTriggerDispatcher.Instance().ClearTriggers(); + } + _logger.LogInformation("------------------------------"); _logger.LogInformation("→ 开始执行脚本: {Name}", project.Manifest.Name); await project.ExecuteAsync(); @@ -375,4 +389,22 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware }); _logger.LogInformation("脚本组 {Name} 执行结束", SelectedScriptGroup.Name); } + + private async Task> ReadCodeList(List list) + { + var codeList = new List(); + foreach (var project in list) + { + var code = await project.LoadCode(); + codeList.Add(code); + } + + return codeList; + } + + private bool HasTimerOperation(IEnumerable codeList) + { + var regex = new Regex(@"^(?!\s*\/\/)\s*dispatcher\.\s*addTimer"); + return codeList.Any(code => regex.IsMatch(code)); + } }