This commit is contained in:
辉鸭蛋
2024-12-23 22:29:10 +08:00
parent da8fc4a89e
commit 5ff8b30dc1
6 changed files with 265 additions and 20 deletions

View File

@@ -25,7 +25,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
{
private readonly ILogger<GlobalKeyMouseRecord> _logger = App.GetLogger<GlobalKeyMouseRecord>();
private KeyMouseRecorder? _recorder;
private KeyMouseRecorderJsonLine? _recorder;
// private SharpAviRecorder _sharpAviRecorder;
@@ -100,21 +100,21 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
_ffmpegRecorder.Start();
_directInputMonitor.Start();
_recorder = new KeyMouseRecorder();
_recorder = new KeyMouseRecorderJsonLine(fileName);
Status = KeyMouseRecorderStatus.Recording;
_logger.LogInformation("录制:{Text}", "已启动");
}
public string StopRecord()
public void StopRecord()
{
if (Status != KeyMouseRecorderStatus.Recording)
{
throw new InvalidOperationException("未处于录制中状态,无法停止");
}
var macro = _recorder?.ToJsonMacro() ?? string.Empty;
// var macro = _recorder?.ToJsonMacro() ?? string.Empty;
_recorder = null;
_directInputMonitor?.Stop();
_directInputMonitor?.Dispose();
@@ -133,7 +133,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
Status = KeyMouseRecorderStatus.Stop;
return macro;
// return macro;
}
public void Tick(object? sender, EventArgs e)

View File

@@ -0,0 +1,239 @@
using BetterGenshinImpact.Core.Recorder.Model;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.Helpers;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Fischless.WindowsInput;
using Vanara.PInvoke;
using Wpf.Ui.Violeta.Controls;
namespace BetterGenshinImpact.Core.Recorder;
public class KeyMouseMacroPlayerJsonLine
{
public static async Task PlayMacro(string path, CancellationToken ct, bool withDelay = true)
{
if (!TaskContext.Instance().IsInitialized)
{
Toast.Warning("请先在启动页,启动截图器再使用本功能");
return;
}
var file = new FileInfo(path);
var infoJson = await File.ReadAllTextAsync(file.FullName, ct);
var info = JsonSerializer.Deserialize<KeyMouseScriptInfo>(infoJson, KeyMouseRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize macro info");
var macroEventsLines = await File.ReadAllTextAsync(Path.Combine(file.Directory!.FullName, "macroEvents.jsonl"), ct);
List<MacroEvent> macroEvents = new();
foreach (var line in macroEventsLines.Split("\n"))
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
macroEvents.Add(JsonSerializer.Deserialize<MacroEvent>(line, KeyMouseRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize macro event"));
}
// MacroEvents 需要以实际时间进行排序
macroEvents.Sort((a, b) => a.Time.CompareTo(b.Time));
// 删除为负数的时间
macroEvents.RemoveAll(m => m.Time < 0);
var script = new KeyMouseScript
{
Info = info,
MacroEvents = macroEvents
};
script.Adapt(TaskContext.Instance().SystemInfo.CaptureAreaRect, TaskContext.Instance().DpiScale);
script.Merge();
SystemControl.ActivateWindow();
if (withDelay)
{
for (var i = 2; i >= 1; i--)
{
TaskControl.Logger.LogInformation("{Sec}秒后进行重放...", i);
await Task.Delay(1000, ct);
}
TaskControl.Logger.LogInformation("开始重放");
}
await PlayMacro(script.MacroEvents, ct);
}
public static async Task PlayMacro(List<MacroEvent> macroEvents, CancellationToken ct)
{
WorkingArea = PrimaryScreen.WorkingArea;
var startTime = DateTime.UtcNow;
foreach (var e in macroEvents)
{
if (ct.IsCancellationRequested)
{
return;
}
var timeToWait = (int)(e.Time - (DateTime.UtcNow - startTime).TotalMilliseconds);
if (timeToWait < 0)
{
TaskControl.Logger.LogWarning("无法原速重放事件{Event},落后{TimeToWait}ms", e.Type.ToString(), -timeToWait);
}
else
{
await Task.Delay(timeToWait, ct);
}
switch (e.Type)
{
case MacroEventType.KeyDown:
var vkDown = (User32.VK)e.KeyCode!;
if (InputBuilder.IsExtendedKey(vkDown))
{
Simulation.SendInput.Keyboard.KeyDown(false, vkDown);
}
else
{
Simulation.SendInput.Keyboard.KeyDown(vkDown);
}
break;
case MacroEventType.KeyUp:
var vkUp = (User32.VK)e.KeyCode!;
if (InputBuilder.IsExtendedKey(vkUp))
{
Simulation.SendInput.Keyboard.KeyUp(false, vkUp);
}
else
{
Simulation.SendInput.Keyboard.KeyUp(vkUp);
}
break;
case MacroEventType.MouseDown:
var buttonMouseDown = Enum.Parse<MouseButtons>(e.MouseButton!);
var xMouseDown = ToVirtualDesktopX(e.MouseX);
var yMouseDown = ToVirtualDesktopY(e.MouseY);
switch (buttonMouseDown)
{
case MouseButtons.Left:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseDown, yMouseDown).LeftButtonDown();
break;
case MouseButtons.Right:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseDown, yMouseDown).RightButtonDown();
break;
case MouseButtons.Middle:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseDown, yMouseDown).MiddleButtonDown();
break;
case MouseButtons.None:
break;
case MouseButtons.XButton1:
break;
case MouseButtons.XButton2:
break;
default:
throw new ArgumentOutOfRangeException();
}
break;
case MacroEventType.MouseUp:
var buttonMouseUp = Enum.Parse<MouseButtons>(e.MouseButton!);
var xMouseUp = ToVirtualDesktopX(e.MouseX);
var yMouseUp = ToVirtualDesktopY(e.MouseY);
switch (buttonMouseUp)
{
case MouseButtons.Left:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseUp, yMouseUp).LeftButtonUp();
break;
case MouseButtons.Right:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseUp, yMouseUp).RightButtonUp();
break;
case MouseButtons.Middle:
Simulation.SendInput.Mouse.MoveMouseTo(xMouseUp, yMouseUp).MiddleButtonUp();
break;
case MouseButtons.None:
break;
case MouseButtons.XButton1:
break;
case MouseButtons.XButton2:
break;
default:
throw new ArgumentOutOfRangeException();
}
break;
case MacroEventType.MouseMoveTo:
Simulation.SendInput.Mouse.MoveMouseTo(ToVirtualDesktopX(e.MouseX), ToVirtualDesktopY(e.MouseY));
break;
case MacroEventType.MouseWheel:
var num = (int)(e.MouseY / 120.0);
if (num != 0)
{
// 不支持多次的场景,但是不会出现这种情况
Simulation.SendInput.Mouse.VerticalScroll(num);
}
break;
case MacroEventType.MouseMoveBy:
if (e.CameraOrientation != null)
{
var cao = CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcMat);
var diff = ((int)Math.Round(cao) - (int)e.CameraOrientation + 180) % 360 - 180;
diff += diff < -180 ? 360 : 0;
//过滤一下特别大的角度偏差
if (diff != 0 && diff < 8 && diff > -8)
{
TaskControl.Logger.LogWarning("视角重放偏差{diff}°,尝试修正", diff);
e.MouseX -= diff;
}
}
Simulation.SendInput.Mouse.MoveMouseBy(e.MouseX, e.MouseY);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public static Size WorkingArea;
public static double ToVirtualDesktopX(int x)
{
return x * 65535 * 1d / WorkingArea.Width;
}
public static double ToVirtualDesktopY(int y)
{
return y * 65535 * 1d / WorkingArea.Height;
}
}

View File

@@ -14,7 +14,9 @@ using System.Text.Json.Serialization;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows.Forms;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Helpers;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Core.Recorder;
@@ -40,15 +42,21 @@ public class KeyMouseRecorderJsonLine
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
public KeyMouseRecorderJsonLine(string path)
public KeyMouseRecorderJsonLine(string folderName)
{
var path = Global.Absolute($@"User\KeyMouseScript\{folderName}\");
DateTime startTime = DateTime.UtcNow;
var bootTime = EnvironmentUtil.LastBootUpTime();
if (bootTime != null)
if (bootTime == null)
{
throw new Exception("无法获取系统启动时间");
TaskControl.Logger.LogWarning("无法获取系统启动时间");
}
else
{
startTime = bootTime.Value + TimeSpan.FromMilliseconds(StartTick);
}
var startTime = bootTime! + TimeSpan.FromMilliseconds(StartTick);
var rect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
Info = new KeyMouseScriptInfo
{
@@ -58,7 +66,7 @@ public class KeyMouseRecorderJsonLine
Height = rect.Height,
RecordDpi = TaskContext.Instance().DpiScale,
StartTime = $"{startTime:yyyy-MM-dd HH:mm:ss:ffff}",
StartTimeUnixTimestamp = (startTime - new DateTime(1970, 1, 1)).Value.TotalNanoseconds.ToString("F0")
StartTimeUnixTimestamp = (startTime - new DateTime(1970, 1, 1)).TotalNanoseconds.ToString("F0")
};
var infoJson = JsonSerializer.Serialize(Info, JsonOptions);
File.WriteAllText(Path.Combine(path, "info.json"), infoJson);
@@ -174,13 +182,12 @@ public class KeyMouseRecorderJsonLine
public void MouseMoveBy(MouseState state, uint tick, bool save = false)
{
var mEvent = new MacroEvent
{
Type = MacroEventType.MouseMoveBy,
MouseX = state.X,
MouseY = state.Y,
Time = tick - 5
Time = tick - 5 - StartTick
};
AddEvent(_mouseMoveByMacroEventsChannel, mEvent);
if (save)

View File

@@ -79,7 +79,7 @@ public class KeyMouseScript
break;
case MacroEventType.MouseMoveBy:
if (macroEvent.Time - currentMerge.Time > mergedEventTimeMax)
if (macroEvent.Time - currentMerge.Time > 10)
{
mergedMacroEvents.Add(currentMerge);
currentMerge = macroEvent;

View File

@@ -52,7 +52,7 @@ public class FfmpegRecorder
// _process.OutputDataReceived += (sender, args) => { Debug.WriteLine(args.Data); };
_process.ErrorDataReceived += (sender, args) =>
{
Debug.WriteLine(args.Data);
TaskControl.Logger.LogDebug(args.Data);
if (string.IsNullOrEmpty(_startTime))
{
if (args.Data != null && args.Data.Contains("start"))

View File

@@ -107,9 +107,9 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
{
try
{
var macro = GlobalKeyMouseRecord.Instance.StopRecord();
GlobalKeyMouseRecord.Instance.StopRecord();
// Genshin Copilot Macro
File.WriteAllText(Path.Combine(scriptPath, $"{fileName}/{fileName}.json"), macro);
// File.WriteAllText(Path.Combine(scriptPath, $"{fileName}/{fileName}.json"), macro);
// 刷新ListView
InitScriptListViewData();
IsRecording = false;
@@ -125,14 +125,13 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
[RelayCommand]
public async Task OnStartPlay(string path)
{
var name = new FileInfo(path).Name;
var file = new FileInfo(path);
var name = file.Name;
_logger.LogInformation("重放开始:{Name}", name);
try
{
var s = await File.ReadAllTextAsync(path);
await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
.RunAsync(async () => await KeyMouseMacroPlayer.PlayMacro(s, CancellationContext.Instance.Cts.Token));
.RunAsync(async () => await KeyMouseMacroPlayerJsonLine.PlayMacro(path, CancellationContext.Instance.Cts.Token));
}
catch (Exception e)
{