mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-21 09:45:48 +08:00
jsonl1
This commit is contained in:
@@ -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)
|
||||
|
||||
239
BetterGenshinImpact/Core/Recorder/KeyMouseMacroPlayerJsonLine.cs
Normal file
239
BetterGenshinImpact/Core/Recorder/KeyMouseMacroPlayerJsonLine.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user