fix
This commit is contained in:
parent
37d4f290e3
commit
93016ca4d2
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "Lib/winipc-ua"]
|
||||
path = Lib/winipc-ua
|
||||
url = https://dev.lirent.ru/lirent/winipc-ua.git
|
||||
@ -1,12 +1,15 @@
|
||||
/* This software is licensed by the MIT License, see LICENSE file */
|
||||
/* Copyright © 2024-2025 Gregory Lirent */
|
||||
|
||||
using WinIPC.Config;
|
||||
|
||||
namespace WebmrAPI.Configuration
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public MonitoringSettings Monitoring { get; set; } = new MonitoringSettings();
|
||||
public WebServerSettings WebServer { get; set; } = new WebServerSettings();
|
||||
public IPCServiceOptions IPCServiceOptions { get; set; } = new IPCServiceOptions();
|
||||
}
|
||||
|
||||
public class MonitoringSettings
|
||||
|
||||
1
Lib/winipc-ua
Submodule
1
Lib/winipc-ua
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 838ab6852f7c3cdc6a4dab5c9fefecaa9feadc4b
|
||||
231
Services/IPCClientService.cs
Normal file
231
Services/IPCClientService.cs
Normal file
@ -0,0 +1,231 @@
|
||||
using Google.Protobuf;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WebmrAPI.Configuration;
|
||||
using WebmrAPI.Utils;
|
||||
using WebmrAPI.Utils.Sequences;
|
||||
using WinIPC.Models;
|
||||
using WinIPC.Services;
|
||||
using WinIPC.Utils;
|
||||
|
||||
namespace WebmrAPI.Services
|
||||
{
|
||||
public sealed class IPCClientService : IPCBaseClientService
|
||||
{
|
||||
public IPCClientService(ILogger<IPCClientService> logger, IOptions<AppSettings> appConfigOptions)
|
||||
: base(logger, appConfigOptions?.Value?.IPCServiceOptions.BasePipeName ?? throw new ArgumentNullException(nameof(appConfigOptions)))
|
||||
{ }
|
||||
|
||||
public async Task<IEnumerable<WindowInfo>> GetWindowInfos(int sessionId, IEnumerable<IntPtr>? hwnds = null)
|
||||
{
|
||||
var req = new WindowsRequest();
|
||||
|
||||
if (hwnds != null)
|
||||
{
|
||||
foreach (var hwnd in hwnds)
|
||||
{
|
||||
req.Hwnds.Add((int)hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
var resp = await SendRequest(sessionId, CommandType.GetWindowsInfo, req).GetResponseAsync();
|
||||
|
||||
if (resp != null && resp.Success)
|
||||
{
|
||||
return PayloadHandler.ExtractPayload<WindowsResponse>(resp).Data;
|
||||
}
|
||||
|
||||
// TODO log Error
|
||||
|
||||
return new List<WindowInfo>();
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetScreenshot(int sessionId, IntPtr hwnd = 0, int x = 0, int y = 0, int width = 0, int height = 0)
|
||||
{
|
||||
var req = new ScreenshotRequest()
|
||||
{
|
||||
Hwnd = (int)hwnd,
|
||||
CropPosition = new() { X = x, Y = y },
|
||||
CropSize = new() { Width = width, Height = height }
|
||||
};
|
||||
|
||||
var resp = await SendRequest(sessionId, CommandType.GetScreenshot, req).GetResponseAsync();
|
||||
|
||||
if (resp != null && resp.Success)
|
||||
{
|
||||
return PayloadHandler.ExtractPayload<ScreenshotResponse>(resp).Data.ToByteArray();
|
||||
}
|
||||
|
||||
// TODO log Error
|
||||
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public async Task<int> GetPixelColor(int sessionId, IntPtr hwnd = 0, int x = 0, int y = 0)
|
||||
{
|
||||
var req = new PixelRequest()
|
||||
{
|
||||
Hwnd = (int)hwnd,
|
||||
PixelPosition = new() { X = x, Y = y }
|
||||
};
|
||||
|
||||
var resp = await SendRequest(sessionId, CommandType.GetPixelColor, req).GetResponseAsync();
|
||||
|
||||
if (resp != null && resp.Success)
|
||||
{
|
||||
return (int)PayloadHandler.ExtractPayload<PixelResponse>(resp).RgbColor;
|
||||
}
|
||||
|
||||
// TODO log Error
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private IEnumerable<InputAction> GetMouseMovingPath(ISequenceGenerator<Point> pGen, DelayGenerator dGen, ref int nActions)
|
||||
{
|
||||
List<InputAction> actions = new();
|
||||
|
||||
foreach (var p in pGen)
|
||||
{
|
||||
var input = new MouseMoveInput() { Position = new() { X = p.X, Y = p.Y } };
|
||||
|
||||
actions.Add(new InputAction()
|
||||
{
|
||||
Type = InputType.MouseMoveTo,
|
||||
DelayMs = (uint)dGen.Next().Value,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(input))
|
||||
});
|
||||
++nActions;
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private IEnumerable<InputAction> GetMouseScrollSequence(ScrollAmountGenerator sGen, DelayGenerator dGen, ref int nActions)
|
||||
{
|
||||
List<InputAction> actions = new();
|
||||
|
||||
foreach (var s in sGen)
|
||||
{
|
||||
var input = new ScrollInput() { Offset = s };
|
||||
|
||||
actions.Add(new InputAction()
|
||||
{
|
||||
Type = InputType.MouseScroll,
|
||||
DelayMs = (uint)dGen.Next().Value,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(input))
|
||||
});
|
||||
++nActions;
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private async Task<int> SendInputActions(int sessionId, IntPtr hwnd, IEnumerable<InputAction> actions, int nActions)
|
||||
{
|
||||
var resp = await SendRequest(sessionId, CommandType.InputAction, new InputRequest() { Hwnd = (int)hwnd, Actions = { actions } }).GetResponseAsync();
|
||||
|
||||
if (resp != null && resp.Success)
|
||||
{
|
||||
return nActions - PayloadHandler.ExtractPayload<InputResponse>(resp).Count;
|
||||
}
|
||||
|
||||
// TODO log Error
|
||||
|
||||
return -nActions;
|
||||
}
|
||||
|
||||
public Task<int> MouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, IntPtr hwnd = 0)
|
||||
{
|
||||
int nActions = 0;
|
||||
return SendInputActions(sessionId, hwnd, GetMouseMovingPath(pGen, dGen, ref nActions), nActions);
|
||||
}
|
||||
|
||||
public Task<int> HoldDownAndMouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return HoldDownAndMouseMove(sessionId, pGen, dGen, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> HoldDownAndMouseMove(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions(), holdUp = sc.GetHoldUpActions();
|
||||
int nActions = holdDown.Count() + holdUp.Count();
|
||||
|
||||
return SendInputActions(sessionId, hwnd, holdDown.Concat(GetMouseMovingPath(pGen, dGen, ref nActions)).Concat(holdUp), nActions);
|
||||
}
|
||||
|
||||
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return MouseMoveAndPress(sessionId, pGen, dGen, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
return MouseMoveAndPress(sessionId, pGen, dGen, new KeyShortcutSequence(sc), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> MouseMoveAndPress(int sessionId, ISequenceGenerator<Point> pGen, DelayGenerator dGen, KeyShortcutSequence seq, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> press = seq.GetInputActions();
|
||||
int nActions = press.Count();
|
||||
|
||||
return SendInputActions(sessionId, hwnd, GetMouseMovingPath(pGen, dGen, ref nActions).Concat(press), nActions);
|
||||
}
|
||||
|
||||
public Task<int> MouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, IntPtr hwnd = 0)
|
||||
{
|
||||
int nActions = 0;
|
||||
return SendInputActions(sessionId, hwnd, GetMouseScrollSequence(sGen, dGen, ref nActions), nActions);
|
||||
}
|
||||
|
||||
public Task<int> HoldDownAndMouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return HoldDownAndMouseScroll(sessionId, sGen, dGen, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> HoldDownAndMouseScroll(int sessionId, ScrollAmountGenerator sGen, DelayGenerator dGen, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions(), holdUp = sc.GetHoldUpActions();
|
||||
int nActions = holdDown.Count() + holdUp.Count();
|
||||
|
||||
return SendInputActions(sessionId, hwnd, holdDown.Concat(GetMouseScrollSequence(sGen, dGen, ref nActions)).Concat(holdUp), nActions);
|
||||
}
|
||||
|
||||
public Task<int> HoldDown(int sessionId, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return HoldDown(sessionId, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> HoldUp(int sessionId, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return HoldUp(sessionId, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> HoldDown(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> holdDown = sc.GetHoldDownActions();
|
||||
return SendInputActions(sessionId, hwnd, holdDown, holdDown.Count());
|
||||
}
|
||||
|
||||
public Task<int> HoldUp(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> holdUp = sc.GetHoldUpActions();
|
||||
return SendInputActions(sessionId, hwnd, holdUp, holdUp.Count());
|
||||
}
|
||||
|
||||
public Task<int> Press(int sessionId, Key key, IntPtr hwnd = 0)
|
||||
{
|
||||
return Press(sessionId, new KeyShortcut(key), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> Press(int sessionId, KeyShortcut sc, IntPtr hwnd = 0)
|
||||
{
|
||||
return Press(sessionId, new KeyShortcutSequence(sc), hwnd);
|
||||
}
|
||||
|
||||
public Task<int> Press(int sessionId, KeyShortcutSequence seq, IntPtr hwnd = 0)
|
||||
{
|
||||
IEnumerable<InputAction> press = seq.GetInputActions();
|
||||
return SendInputActions(sessionId, hwnd, press, press.Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Services/SessionService.cs
Normal file
6
Services/SessionService.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace WebmrAPI.Services
|
||||
{
|
||||
public class SessionService
|
||||
{
|
||||
}
|
||||
}
|
||||
94
Utils/BaseRandom.cs
Normal file
94
Utils/BaseRandom.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class BaseRandom
|
||||
{
|
||||
private readonly static Random _random = new Random();
|
||||
private static readonly BaseRandom _self = new BaseRandom();
|
||||
|
||||
protected Random? _pRandom = null;
|
||||
|
||||
private Random Rand { get => _pRandom == null ? _random : _pRandom; }
|
||||
|
||||
public static byte[] NextGenerate(byte[] data)
|
||||
{
|
||||
return _self.Generate(data);
|
||||
}
|
||||
public static T NextGenerate<T>(T min, T max)
|
||||
where T : struct, INumber<T>
|
||||
{
|
||||
return _self.Generate(min, max);
|
||||
}
|
||||
public static T NextSpread<T>(T value, T offset)
|
||||
where T : struct, INumber<T>
|
||||
{
|
||||
return _self.Spread(value, offset);
|
||||
}
|
||||
|
||||
public byte[] Generate(byte[] data)
|
||||
{
|
||||
Rand.NextBytes(data);
|
||||
return data;
|
||||
}
|
||||
public double Generate(double min, double max)
|
||||
{
|
||||
if (min > max)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(min), "minValue cannot be greater than maxValue.");
|
||||
}
|
||||
return min + (max - min) * Rand.NextDouble();
|
||||
}
|
||||
public long Generate(long min, long max)
|
||||
{
|
||||
return Rand.NextInt64(min, max + 1);
|
||||
}
|
||||
public ulong Generate(ulong min, ulong max)
|
||||
{
|
||||
ulong res;
|
||||
long lmin = (long)min;
|
||||
long lmax = (long)max;
|
||||
|
||||
if (lmin > lmax) res = (ulong)Rand.NextInt64(lmax, lmin + 1);
|
||||
else res = (ulong)Rand.NextInt64(lmin, lmax + 1);
|
||||
return UInt64.Clamp(res, min, max);
|
||||
}
|
||||
public uint Generate(uint min, uint max)
|
||||
{
|
||||
return (uint)Generate((long)min, (long)max);
|
||||
}
|
||||
public T Generate<T>(T min, T max) where T : struct, INumber<T>
|
||||
{
|
||||
var min32 = Int32.CreateChecked(min);
|
||||
var max32 = Int32.CreateChecked(max);
|
||||
|
||||
return T.CreateChecked(Rand.Next(min32, max32));
|
||||
}
|
||||
public T Spread<T>(T value, T offset)
|
||||
where T : struct, INumber<T>
|
||||
{
|
||||
T min, max, cur;
|
||||
|
||||
cur = T.CreateChecked(Math.Abs(Double.CreateChecked(offset)));
|
||||
min = value - cur;
|
||||
max = value + cur;
|
||||
|
||||
if (min > max)
|
||||
{
|
||||
var tmp = min;
|
||||
min = max;
|
||||
max = tmp;
|
||||
}
|
||||
|
||||
value = Generate(min, max);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public BaseRandom() { }
|
||||
public BaseRandom(int seed)
|
||||
{
|
||||
_pRandom = new Random(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
171
Utils/Delay.cs
Normal file
171
Utils/Delay.cs
Normal file
@ -0,0 +1,171 @@
|
||||
using System.Collections;
|
||||
using WebmrAPI.Utils.Sequences;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class Delay : BaseRandom, IComparable<Delay>
|
||||
{
|
||||
private int _min = 0;
|
||||
private int _max = 0;
|
||||
private bool _mutable = true;
|
||||
|
||||
public readonly static Delay None = new Delay { _mutable = false };
|
||||
public readonly static Delay Random = new Delay { _mutable = false, Min = 50, Max = 250 };
|
||||
|
||||
public int Value
|
||||
{
|
||||
set => _max = _min = value;
|
||||
get => (_max > _min) ? Generate(_min, _max) : _min;
|
||||
}
|
||||
public int Min
|
||||
{
|
||||
get => _min > _max ? _max : _min;
|
||||
set
|
||||
{
|
||||
if (!_mutable)
|
||||
{
|
||||
throw new InvalidOperationException("Object is not mutable");
|
||||
}
|
||||
_min = value;
|
||||
}
|
||||
}
|
||||
public int Max
|
||||
{
|
||||
get => _max < _min ? _min : _max;
|
||||
set
|
||||
{
|
||||
if (!_mutable)
|
||||
{
|
||||
throw new InvalidOperationException("Object is not mutable");
|
||||
}
|
||||
_max = value;
|
||||
}
|
||||
}
|
||||
|
||||
async public Task WaitAsync()
|
||||
{
|
||||
await Task.Delay(Value);
|
||||
}
|
||||
public void Wait()
|
||||
{
|
||||
Thread.Sleep(Value);
|
||||
}
|
||||
public static bool operator ==(Delay? left, Delay? right)
|
||||
{
|
||||
if (left == null && right == null) return true;
|
||||
if (left == null || right == null) return false;
|
||||
|
||||
return left._min == right._min && left._max == right._max;
|
||||
}
|
||||
|
||||
public static bool operator !=(Delay? left, Delay? right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Delay other = (Delay)obj;
|
||||
return this == other;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_min, _max);
|
||||
}
|
||||
public static bool operator <(Delay left, Delay right)
|
||||
{
|
||||
if (left._min < right._min)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (left._min == right._min && left._max < right._max)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static bool operator >(Delay left, Delay right)
|
||||
{
|
||||
if (left._min > right._min)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (left._min == right._min && left._max > right._max)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator <=(Delay left, Delay right)
|
||||
{
|
||||
return left < right || left == right;
|
||||
}
|
||||
|
||||
public static bool operator >=(Delay left, Delay right)
|
||||
{
|
||||
return left > right || left == right;
|
||||
}
|
||||
public int CompareTo(Delay? other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = _min.CompareTo(other._min);
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return _max.CompareTo(other._max);
|
||||
}
|
||||
}
|
||||
|
||||
public class DelayGenerator : ISequenceGenerator<Delay>
|
||||
{
|
||||
private ISequenceGenerator<int> _generator;
|
||||
|
||||
public Delay Min { get => new Delay { Value = _generator.Min }; }
|
||||
public Delay Max { get => new Delay { Value = _generator.Max }; }
|
||||
public double Progress { get => _generator.Progress; }
|
||||
public double Step { get => _generator.Step; }
|
||||
public bool HasValues { get => _generator.HasValues; }
|
||||
|
||||
public Delay Next()
|
||||
{
|
||||
if (!HasValues)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
return new Delay { Value = _generator.Next() };
|
||||
}
|
||||
|
||||
public ISequenceGenerator<Delay> Reset()
|
||||
{
|
||||
_generator.Reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
public DelayGenerator(ISequenceGenerator<int> generator)
|
||||
{
|
||||
_generator = generator;
|
||||
}
|
||||
public IEnumerator<Delay> GetEnumerator()
|
||||
{
|
||||
while (HasValues)
|
||||
{
|
||||
yield return Next();
|
||||
}
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Utils/Perlin2D.cs
Normal file
92
Utils/Perlin2D.cs
Normal file
@ -0,0 +1,92 @@
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class Perlin2D : BaseRandom
|
||||
{
|
||||
byte[] _mTable = new byte[1024];
|
||||
|
||||
public Perlin2D(int seed = 0)
|
||||
: base(seed)
|
||||
{
|
||||
Generate(_mTable);
|
||||
}
|
||||
|
||||
private double[] GetPseudoRandomGradientVector(int x, int y)
|
||||
{
|
||||
int v = (int)(((x * 1836311903) ^ (y * 2971215073) + 4807526976) & 1023);
|
||||
v = _mTable[v] & 3;
|
||||
|
||||
switch (v)
|
||||
{
|
||||
case 0: return new double[] { 1, 0 };
|
||||
case 1: return new double[] { -1, 0 };
|
||||
case 2: return new double[] { 0, 1 };
|
||||
default: return new double[] { 0, -1 };
|
||||
}
|
||||
}
|
||||
|
||||
static double QunticCurve(double t)
|
||||
{
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
static double Lerp(double a, double b, double t)
|
||||
{
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
static double Dot(double[] a, double[] b)
|
||||
{
|
||||
return a[0] * b[0] + a[1] * b[1];
|
||||
}
|
||||
|
||||
public double Noise(double fx, double fy)
|
||||
{
|
||||
int left = (int)System.Math.Floor(fx);
|
||||
int top = (int)System.Math.Floor(fy);
|
||||
double pointInQuadX = fx - left;
|
||||
double pointInQuadY = fy - top;
|
||||
|
||||
double[] topLeftGradient = GetPseudoRandomGradientVector(left, top);
|
||||
double[] topRightGradient = GetPseudoRandomGradientVector(left + 1, top);
|
||||
double[] bottomLeftGradient = GetPseudoRandomGradientVector(left, top + 1);
|
||||
double[] bottomRightGradient = GetPseudoRandomGradientVector(left + 1, top + 1);
|
||||
|
||||
double[] distanceToTopLeft = { pointInQuadX, pointInQuadY };
|
||||
double[] distanceToTopRight = { pointInQuadX - 1, pointInQuadY };
|
||||
double[] distanceToBottomLeft = { pointInQuadX, pointInQuadY - 1 };
|
||||
double[] distanceToBottomRight = { pointInQuadX - 1, pointInQuadY - 1 };
|
||||
|
||||
double tx1 = Dot(distanceToTopLeft, topLeftGradient);
|
||||
double tx2 = Dot(distanceToTopRight, topRightGradient);
|
||||
double bx1 = Dot(distanceToBottomLeft, bottomLeftGradient);
|
||||
double bx2 = Dot(distanceToBottomRight, bottomRightGradient);
|
||||
|
||||
pointInQuadX = QunticCurve(pointInQuadX);
|
||||
pointInQuadY = QunticCurve(pointInQuadY);
|
||||
|
||||
double tx = Lerp(tx1, tx2, pointInQuadX);
|
||||
double bx = Lerp(bx1, bx2, pointInQuadX);
|
||||
double tb = Lerp(tx, bx, pointInQuadY);
|
||||
|
||||
return tb;
|
||||
}
|
||||
|
||||
public double Noise(double fx, double fy, int octaves, double persistence = 0.5)
|
||||
{
|
||||
double amplitude = 1;
|
||||
double max = 0;
|
||||
double result = 0;
|
||||
|
||||
while (octaves-- > 0)
|
||||
{
|
||||
max += amplitude;
|
||||
result += Noise(fx, fy) * amplitude;
|
||||
amplitude *= persistence;
|
||||
fx *= 2;
|
||||
fy *= 2;
|
||||
}
|
||||
|
||||
return result / max;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
Utils/Scroll.cs
Normal file
116
Utils/Scroll.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using WebmrAPI.Utils.Sequences;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public sealed class ScrollAmountGenerator : BaseGenerator<int>
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
Up,
|
||||
Down
|
||||
}
|
||||
|
||||
public class CorrectionData
|
||||
{
|
||||
public Direction Dir { get; set; }
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
private double _fJitter = 0.12;
|
||||
private double _fAccuracy = 0.83;
|
||||
private int _nCorrections = 0;
|
||||
private int _bAmount = 120;
|
||||
public int TotalAmount { get; private set; }
|
||||
public int CurrentAmount { get; private set; }
|
||||
public int BaseAmount { get => _bAmount; private set => UpdateSpread(value, FJitter); }
|
||||
public double FJitter { get => _fJitter; private set => UpdateSpread(BaseAmount, Math.Clamp(Math.Abs(value), 0.0, 1.0)); }
|
||||
public int CorrectionLimit { get; private set; } = 1;
|
||||
public override double Progress { get => (double)CurrentAmount / TotalAmount; protected set => base.Progress = value; }
|
||||
public double FAccuracy { get => _fAccuracy; private set => _fAccuracy = Math.Clamp(Math.Abs(value), 0.0, 1.0); }
|
||||
public Direction Dir { get; private set; }
|
||||
public override double Step { get; protected set; }
|
||||
|
||||
public delegate void CorrectionHandler(object sender, CorrectionData data);
|
||||
|
||||
public event CorrectionHandler? Correction;
|
||||
|
||||
public ScrollAmountGenerator SetBaseAmount(int baseAmount) { BaseAmount = baseAmount; return this; }
|
||||
public ScrollAmountGenerator SetJitterFactor(double jitterFactor) { FJitter = jitterFactor; return this; }
|
||||
public ScrollAmountGenerator SetCorrectionLimit(int limit) { CorrectionLimit = limit; return this; }
|
||||
public ScrollAmountGenerator SetAccuracyFactor(double accuracyFactor) { FAccuracy = accuracyFactor; return this; }
|
||||
|
||||
private void ToggleDirection()
|
||||
{
|
||||
Dir = Dir == Direction.Up ? Direction.Down : Direction.Up;
|
||||
}
|
||||
|
||||
private void UpdateSpread(int baseAmount, double jitterFactor)
|
||||
{
|
||||
var offset = baseAmount * jitterFactor;
|
||||
var amount = Math.Abs(Double.CreateChecked(baseAmount));
|
||||
|
||||
_bAmount = baseAmount;
|
||||
_fJitter = jitterFactor;
|
||||
Min = (int)(amount - offset);
|
||||
Max = (int)(amount + offset);
|
||||
}
|
||||
|
||||
protected override int Calc()
|
||||
{
|
||||
int value;
|
||||
if (Generate(0, 100000) > (int)(FAccuracy * 100000))
|
||||
value = Generate(Min, Max);
|
||||
else value = BaseAmount;
|
||||
|
||||
if (Dir == Direction.Down)
|
||||
{
|
||||
value = -value;
|
||||
}
|
||||
|
||||
CurrentAmount += value;
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Next()
|
||||
{
|
||||
if (Progress < 0)
|
||||
{
|
||||
Progress = 0;
|
||||
return First();
|
||||
}
|
||||
|
||||
if (!HasValues || IsZero(1.0 - Progress))
|
||||
{
|
||||
HasValues = false;
|
||||
Progress = 1.0;
|
||||
return Last();
|
||||
}
|
||||
else if (Progress > 1.0 && _nCorrections < CorrectionLimit)
|
||||
{
|
||||
ToggleDirection();
|
||||
Correction?.Invoke(this, new CorrectionData { Dir = Dir, Order = ++_nCorrections });
|
||||
}
|
||||
|
||||
return Calc();
|
||||
}
|
||||
|
||||
public override ISequenceGenerator<int> Reset()
|
||||
{
|
||||
HasValues = true;
|
||||
if ((_nCorrections & 1) != 0)
|
||||
{
|
||||
ToggleDirection();
|
||||
}
|
||||
_nCorrections = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScrollAmountGenerator(int totalAmount, Direction dir)
|
||||
: base(default, default)
|
||||
{
|
||||
TotalAmount = (dir == Direction.Up) ? totalAmount : -totalAmount;
|
||||
Dir = dir;
|
||||
UpdateSpread(BaseAmount, FJitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
180
Utils/Sequences/Base.cs
Normal file
180
Utils/Sequences/Base.cs
Normal file
@ -0,0 +1,180 @@
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils.Sequences
|
||||
{
|
||||
public interface ISequenceGenerator<T> : IEnumerable<T>
|
||||
{
|
||||
public static bool IsZero(double val, double epsilon = Double.Epsilon)
|
||||
{
|
||||
return Math.Abs(val) < epsilon;
|
||||
}
|
||||
public T Min { get; }
|
||||
public T Max { get; }
|
||||
public double Progress { get; }
|
||||
public double Step { get; }
|
||||
public bool HasValues { get; }
|
||||
public T Next();
|
||||
public ISequenceGenerator<T> Reset();
|
||||
}
|
||||
|
||||
public interface INumberGenerator<T> : ISequenceGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get; }
|
||||
public virtual double DMin { get => Double.CreateChecked(Min); }
|
||||
public virtual double DMax { get => Double.CreateChecked(Max); }
|
||||
public virtual double Mean { get => (DMin + DMax) / 2.0; }
|
||||
}
|
||||
|
||||
public interface IPointGenerator : ISequenceGenerator<Point>
|
||||
{
|
||||
public static double CalcDistance(Point pt1, Point pt2)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(pt1.X - pt2.X, 2) + Math.Pow(pt1.Y - pt2.Y, 2));
|
||||
}
|
||||
public static double CalcAngle(Point pt1, Point pt2)
|
||||
{
|
||||
return Math.Atan2(pt2.Y - pt1.Y, pt2.X - pt1.X);
|
||||
}
|
||||
public static Point CalcPoint(double x, double y, double offset, double pAngle)
|
||||
{
|
||||
return new Point((int)Math.Round(x + offset * Math.Cos(pAngle)), (int)Math.Round(y + offset * Math.Sin(pAngle)));
|
||||
}
|
||||
public static Point CalcMiddle(Point start, Point end)
|
||||
{
|
||||
return new Point(
|
||||
(int)Math.Round(start.X + (end.X - start.X) / 2.0),
|
||||
(int)Math.Round(start.Y + (end.Y - start.Y) / 2.0)
|
||||
);
|
||||
}
|
||||
|
||||
public static (double distance, double angle) CalcDistanceAngle(Point pt1, Point pt2)
|
||||
{
|
||||
double dx = pt2.X - pt1.X;
|
||||
double dy = pt2.Y - pt1.Y;
|
||||
|
||||
return (Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2)), Math.Atan2(dy, dx));
|
||||
}
|
||||
|
||||
public static double DegreesToRadians(double degrees)
|
||||
{
|
||||
return degrees * Math.PI / 180.0;
|
||||
}
|
||||
|
||||
public static double RadiansToDegrees(double radians)
|
||||
{
|
||||
return radians * (180.0 / Math.PI);
|
||||
}
|
||||
|
||||
public static Point NewPointAngleDistance(Point refPt, double distance, double aRadians, double epsilon = Double.Epsilon)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
if (IsZero(distance, epsilon)) return new Point(refPt.X, refPt.Y);
|
||||
|
||||
x = (int)Math.Round(refPt.X + distance * Math.Cos(aRadians));
|
||||
y = (int)Math.Round(refPt.Y + distance * Math.Sin(aRadians));
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
public static Point NewPointDistance(Point refPt, Point targetPt, double distance, double epsilon = Double.Epsilon)
|
||||
{
|
||||
return NewPointAngleDistance(refPt, distance, CalcAngle(refPt, targetPt), epsilon);
|
||||
}
|
||||
|
||||
public static Point NewPointAngle(Point refPt, Point targetPt, double aRadians, double epsilon = Double.Epsilon)
|
||||
{
|
||||
return NewPointAngleDistance(refPt, CalcDistance(refPt, targetPt), aRadians, epsilon);
|
||||
}
|
||||
|
||||
public static Point NewPointDistanceFactor(Point refPt, Point targetPt, double factor, double epsilon = Double.Epsilon)
|
||||
{
|
||||
return NewPointAngleDistance(refPt, CalcDistance(refPt, targetPt) * factor, CalcAngle(refPt, targetPt), epsilon);
|
||||
}
|
||||
|
||||
public static Point GetRandomPointInRect(Rectangle rect)
|
||||
{
|
||||
double x = BaseRandom.NextGenerate(rect.X, rect.Width);
|
||||
double y = BaseRandom.NextGenerate(rect.Y, rect.Height);
|
||||
|
||||
return new Point((int)Math.Round(x), (int)Math.Round(y));
|
||||
}
|
||||
|
||||
public IPointGenerator Self { get; }
|
||||
public virtual Point Start { get => Min; }
|
||||
public virtual Point End { get => Max; }
|
||||
public virtual double LinearX { get => Start.X + (End.X - Start.X) * Progress; }
|
||||
public virtual double LinearY { get => Start.Y + (End.Y - Start.Y) * Progress; }
|
||||
}
|
||||
|
||||
public abstract class BaseGenerator<T> : BaseRandom, ISequenceGenerator<T>
|
||||
{
|
||||
protected virtual double Epsilon { get; set; } = 0.01;
|
||||
protected virtual bool IsZero(double val)
|
||||
{
|
||||
return ISequenceGenerator<T>.IsZero(val, Epsilon);
|
||||
}
|
||||
private double _fShape = 1.0;
|
||||
private double _fFade = 0.5;
|
||||
|
||||
public T Min { get; protected set; }
|
||||
public T Max { get; protected set; }
|
||||
public virtual double Progress { get; protected set; } = -1.0;
|
||||
public double ProgressRemainder { get => 1.0 - Progress; }
|
||||
public virtual bool HasValues { get; protected set; } = true;
|
||||
public double FShape { get => _fShape; protected set => _fShape = Math.Clamp(value, 0.0, 1.0); }
|
||||
public double FFade { get => _fFade; protected set => _fFade = Math.Clamp(value, Epsilon, 1.0); }
|
||||
protected double Scale { get => Math.Pow(Progress * ProgressRemainder * 4.0, FFade) * FShape; }
|
||||
public abstract double Step { get; protected set; }
|
||||
public BaseGenerator(T min, T max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
protected static double CalcStepByCount(int count)
|
||||
{
|
||||
return 1.0 / count;
|
||||
}
|
||||
|
||||
protected abstract T Calc();
|
||||
protected virtual T First() => Calc();
|
||||
protected virtual T Last() => Calc();
|
||||
|
||||
public virtual T Next()
|
||||
{
|
||||
if (Progress < 0)
|
||||
{
|
||||
Progress = 0;
|
||||
return First();
|
||||
}
|
||||
|
||||
if ((Progress += Step) >= 1.0)
|
||||
{
|
||||
HasValues = false;
|
||||
Progress = 1.0;
|
||||
return Last();
|
||||
}
|
||||
|
||||
return Calc();
|
||||
}
|
||||
public virtual ISequenceGenerator<T> Reset()
|
||||
{
|
||||
HasValues = true;
|
||||
Progress = -1.0;
|
||||
return this;
|
||||
}
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
while (HasValues)
|
||||
{
|
||||
yield return Next();
|
||||
}
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Utils/Sequences/Bezier.cs
Normal file
161
Utils/Sequences/Bezier.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils.Sequences
|
||||
{
|
||||
public abstract class BezierGenerator<T, TControl> : BaseGenerator<T> where TControl : struct
|
||||
{
|
||||
public bool IsCubic { get => Control2 != null; }
|
||||
protected TControl Control1 { get; set; }
|
||||
protected TControl? Control2 { get; set; }
|
||||
public override double Step { get; protected set; }
|
||||
protected double CalcBezier(double start, double end, double ctrl1, double? ctrl2)
|
||||
{
|
||||
double t = Progress;
|
||||
double rt = ProgressRemainder;
|
||||
|
||||
if (IsCubic && ctrl2 != null)
|
||||
{
|
||||
double rt3 = rt * rt * rt;
|
||||
double t3 = t * t * t;
|
||||
double rt2_t = rt * rt * t;
|
||||
double rt_t2 = rt * t * t;
|
||||
|
||||
return rt3 * start +
|
||||
3 * rt2_t * ctrl1 +
|
||||
3 * rt_t2 * ctrl2.Value +
|
||||
t3 * end;
|
||||
}
|
||||
|
||||
double rt2 = rt * rt;
|
||||
double t2 = t * t;
|
||||
double rt_t = rt * t;
|
||||
|
||||
return rt2 * start +
|
||||
2 * rt_t * ctrl1 +
|
||||
t2 * end;
|
||||
}
|
||||
|
||||
public BezierGenerator(T min, T max, TControl ctrl1, TControl? ctrl2)
|
||||
: base(min, max)
|
||||
{
|
||||
Control1 = ctrl1;
|
||||
Control2 = ctrl2;
|
||||
}
|
||||
}
|
||||
public class BezierNumberGenerator<T> : BezierGenerator<T, double>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
private double Bezier { get => CalcBezier(Self.DMin, Self.DMax, Control1, Control2); }
|
||||
public BezierNumberGenerator<T> SetShapeFactor(double shape) { FShape = shape; return this; }
|
||||
public BezierNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
protected override T Calc()
|
||||
{
|
||||
return T.CreateChecked(Math.Clamp(Self.Mean + (Bezier - Self.Mean) * Scale, Self.DMin, Self.DMax));
|
||||
}
|
||||
|
||||
public BezierNumberGenerator(T start, T end, double ctrl1, double? ctrl2 = null, int count = 10)
|
||||
: base(start, end, ctrl1, ctrl2) { Step = CalcStepByCount(count); }
|
||||
}
|
||||
|
||||
public class ArcNumberGenerator<T> : BezierNumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public ArcNumberGenerator(T start, T end, int count = 10, bool bidirectional = true, bool reverse = false)
|
||||
: base(start, end, Double.NaN, null, count)
|
||||
{
|
||||
var hMean = (Self.DMax - Self.Mean);
|
||||
if (bidirectional)
|
||||
{
|
||||
Control1 = (reverse) ? Self.DMin : Self.DMax;
|
||||
Control2 = (reverse) ? Self.DMax : Self.DMin;
|
||||
}
|
||||
else
|
||||
{
|
||||
Control1 = (reverse) ? Self.DMin : Self.DMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BezierPointGenerator : BezierGenerator<Point, Point>, IPointGenerator
|
||||
{
|
||||
protected readonly double _pAngle;
|
||||
public IPointGenerator Self { get => this; }
|
||||
public BezierPointGenerator SetShapeFactor(double shape) { FShape = shape; return this; }
|
||||
public BezierPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
private double BezierX { get => CalcBezier(Min.X, Max.X, Control1.X, Control2?.X); }
|
||||
private double BezierY { get => CalcBezier(Min.Y, Max.Y, Control1.Y, Control2?.Y); }
|
||||
|
||||
protected override Point Calc()
|
||||
{
|
||||
double dx = BezierX - Self.LinearX;
|
||||
double dy = BezierY - Self.LinearY;
|
||||
|
||||
return IPointGenerator.CalcPoint(Self.LinearX + dx * Scale, Self.LinearY + dy * Scale, 0, 0);
|
||||
}
|
||||
|
||||
public static BezierPointGenerator CreateByCount(Point start, Point end, Point ctrl1, Point? ctrl2 = null, int count = 10)
|
||||
{
|
||||
return new BezierPointGenerator(start, end, ctrl1, ctrl2)
|
||||
{
|
||||
Step = CalcStepByCount(count)
|
||||
};
|
||||
}
|
||||
|
||||
public static BezierPointGenerator CreateByStepSize(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5)
|
||||
{
|
||||
return new BezierPointGenerator(start, end, ctrl1, ctrl2, pxPerStep);
|
||||
}
|
||||
|
||||
public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5)
|
||||
: this(start, end, ctrl1, ctrl2)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null)
|
||||
: base(start, end, ctrl1, ctrl2)
|
||||
{
|
||||
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
||||
}
|
||||
}
|
||||
public sealed class ArcPointGenerator : BezierPointGenerator
|
||||
{
|
||||
public new ArcPointGenerator SetShapeFactor(double shape) { base.SetShapeFactor(shape); return this; }
|
||||
public new ArcPointGenerator SetFadeFactor(double fade) { base.SetFadeFactor(fade); return this; }
|
||||
|
||||
public static ArcPointGenerator CreateByCount(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, int count = 10)
|
||||
{
|
||||
return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight)
|
||||
{
|
||||
Step = CalcStepByCount(count)
|
||||
};
|
||||
}
|
||||
|
||||
public static ArcPointGenerator CreateByStepSize(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5)
|
||||
{
|
||||
return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight, pxPerStep);
|
||||
}
|
||||
|
||||
public ArcPointGenerator(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5)
|
||||
: this(start, end, bidirectional, reverse, arcHeight)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
private ArcPointGenerator(Point start, Point end, bool bidirectional, bool reverse, double arcHeight)
|
||||
: base(start, end, Point.Empty, null)
|
||||
{
|
||||
var height = (reverse) ? -arcHeight : arcHeight;
|
||||
|
||||
if (bidirectional)
|
||||
{
|
||||
Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 1.0 / 3.0), height, _pAngle);
|
||||
Control2 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 2.0 / 3.0), -height, _pAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.CalcMiddle(start, end), height, _pAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Utils/Sequences/Consistent.cs
Normal file
97
Utils/Sequences/Consistent.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils.Sequences
|
||||
{
|
||||
public abstract class ConsistentGenerator<T> : ISequenceGenerator<T>
|
||||
{
|
||||
private Queue<ISequenceGenerator<T>> _seq = new();
|
||||
private Queue<ISequenceGenerator<T>> _mem = new();
|
||||
|
||||
private int _count = 0;
|
||||
private int _index = 0;
|
||||
|
||||
public T Min { get; protected set; }
|
||||
public T Max { get; protected set; }
|
||||
public virtual double Progress { get => (double)_index / _count; }
|
||||
public double Step { get => 1.0 / _count; }
|
||||
public bool HasValues { get => _seq.Count > 0; }
|
||||
|
||||
protected void AddGenerator(ISequenceGenerator<T> generator)
|
||||
{
|
||||
Max = generator.Max;
|
||||
_count += (int)(1.0 / generator.Step);
|
||||
}
|
||||
|
||||
public T Next()
|
||||
{
|
||||
if (HasValues)
|
||||
{
|
||||
if (_seq.Peek().HasValues)
|
||||
{
|
||||
_index++;
|
||||
return _seq.Peek().Next();
|
||||
}
|
||||
_mem.Enqueue(_seq.Dequeue().Reset());
|
||||
return Next();
|
||||
}
|
||||
|
||||
return Max;
|
||||
}
|
||||
public ISequenceGenerator<T> Reset()
|
||||
{
|
||||
while (_seq.Count > 0)
|
||||
{
|
||||
_mem.Enqueue(_seq.Dequeue().Reset());
|
||||
}
|
||||
_seq = _mem;
|
||||
_mem = new();
|
||||
_index = 0;
|
||||
return this;
|
||||
}
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
while (HasValues)
|
||||
{
|
||||
yield return Next();
|
||||
}
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
public ConsistentGenerator(ISequenceGenerator<T> generator)
|
||||
{
|
||||
Min = generator.Min;
|
||||
Max = generator.Max;
|
||||
_count = (int)(1.0 / generator.Step);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ConsistentNumberGenerator<T> : ConsistentGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
public ConsistentNumberGenerator<T> Add(ISequenceGenerator<T> generator)
|
||||
{
|
||||
AddGenerator(generator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsistentNumberGenerator(ISequenceGenerator<T> generator)
|
||||
: base(generator) { }
|
||||
}
|
||||
|
||||
public sealed class ConsistentPointGenerator : ConsistentGenerator<Point>, IPointGenerator
|
||||
{
|
||||
public IPointGenerator Self { get => this; }
|
||||
public ConsistentPointGenerator Add(ISequenceGenerator<Point> generator)
|
||||
{
|
||||
AddGenerator(generator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConsistentPointGenerator(ISequenceGenerator<Point> generator)
|
||||
: base(generator) { }
|
||||
}
|
||||
}
|
||||
206
Utils/Sequences/Oscillation.cs
Normal file
206
Utils/Sequences/Oscillation.cs
Normal file
@ -0,0 +1,206 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils.Sequences
|
||||
{
|
||||
public abstract class OscillationGenerator<T> : BaseGenerator<T>
|
||||
{
|
||||
private int _segments = 1;
|
||||
private double _amplitude = 100.0;
|
||||
public double Amplitude { get => _amplitude; protected set => _amplitude = Math.Abs(value); }
|
||||
public int Segments { get => _segments; protected set => _segments = value < 1 ? 1 : value; }
|
||||
public override double Step { get; protected set; }
|
||||
protected abstract double FOffset { get; }
|
||||
public OscillationGenerator(T min, T max)
|
||||
: base(min, max) { }
|
||||
}
|
||||
|
||||
public abstract class SawtoothGenerator<T> : OscillationGenerator<T>
|
||||
{
|
||||
protected override double FOffset { get { double s = (Progress * Segments) % 1.0; return (s < 0.5 ? s : (1.0 - s)) * 4.0 - 1.0; } }
|
||||
|
||||
public SawtoothGenerator(T min, T max)
|
||||
: base(min, max) { }
|
||||
}
|
||||
|
||||
public abstract class SineWaveGenerator<T> : OscillationGenerator<T>
|
||||
{
|
||||
protected override double FOffset { get => Math.Sin(Progress * (Segments * 2.0) * Math.PI); }
|
||||
|
||||
public SineWaveGenerator(T min, T max)
|
||||
: base(min, max) { }
|
||||
}
|
||||
|
||||
public abstract class SquareWaveGenerator<T> : OscillationGenerator<T>
|
||||
{
|
||||
protected override double FOffset { get => (Math.Floor(Progress * Segments) % 2 == 0) ? 1 : -1; }
|
||||
|
||||
public SquareWaveGenerator(T min, T max)
|
||||
: base(min, max) { }
|
||||
}
|
||||
public sealed class SawtoothNumberGenerator<T> : SawtoothGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
public SawtoothNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SawtoothNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SawtoothNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
|
||||
public SawtoothNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override T Calc()
|
||||
{
|
||||
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
|
||||
|
||||
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
|
||||
}
|
||||
public SawtoothNumberGenerator(T min, T max, int count = 10) : base(min, max)
|
||||
{
|
||||
Step = CalcStepByCount(count);
|
||||
Amplitude = 1.0; // Set default
|
||||
}
|
||||
}
|
||||
public sealed class SineWaveNumberGenerator<T> : SineWaveGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
public SineWaveNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SineWaveNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SineWaveNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
|
||||
public SineWaveNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override T Calc()
|
||||
{
|
||||
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
|
||||
|
||||
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
|
||||
}
|
||||
public SineWaveNumberGenerator(T min, T max, int count = 10) : base(min, max)
|
||||
{
|
||||
Step = CalcStepByCount(count);
|
||||
Amplitude = 1.0; // Set default
|
||||
}
|
||||
}
|
||||
public sealed class SquareWaveNumberGenerator<T> : SquareWaveGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
public SquareWaveNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SquareWaveNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SquareWaveNumberGenerator<T> SetSegments(int count) { Segments = count; return this; }
|
||||
public SquareWaveNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override T Calc()
|
||||
{
|
||||
var offs = Amplitude * FOffset * Scale * ((Self.DMax - Self.DMin) / 2.0);
|
||||
|
||||
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
|
||||
}
|
||||
public SquareWaveNumberGenerator(T min, T max, int count = 10) : base(min, max)
|
||||
{
|
||||
Step = CalcStepByCount(count);
|
||||
Amplitude = 1.0; // Set default
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SawtoothPointGenerator : SawtoothGenerator<Point>, IPointGenerator
|
||||
{
|
||||
private readonly double _pAngle;
|
||||
public IPointGenerator Self { get => this; }
|
||||
public SawtoothPointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SawtoothPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SawtoothPointGenerator SetSegments(int count) { Segments = count; return this; }
|
||||
public SawtoothPointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override Point Calc()
|
||||
{
|
||||
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
|
||||
}
|
||||
|
||||
public static SawtoothPointGenerator CreateByCount(Point start, Point end, int count)
|
||||
{
|
||||
return new SawtoothPointGenerator(start, end) { Step = CalcStepByCount(count) };
|
||||
}
|
||||
|
||||
public static SawtoothPointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
|
||||
{
|
||||
return new SawtoothPointGenerator(start, end, pxPerStep);
|
||||
}
|
||||
|
||||
public SawtoothPointGenerator(Point start, Point end, double pxPerStep = 5)
|
||||
: this(start, end)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
private SawtoothPointGenerator(Point start, Point end)
|
||||
: base(start, end)
|
||||
{
|
||||
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SineWavePointGenerator : SineWaveGenerator<Point>, IPointGenerator
|
||||
{
|
||||
private readonly double _pAngle;
|
||||
public IPointGenerator Self { get => this; }
|
||||
public SineWavePointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SineWavePointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SineWavePointGenerator SetSegments(int count) { Segments = count; return this; }
|
||||
public SineWavePointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override Point Calc()
|
||||
{
|
||||
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
|
||||
}
|
||||
|
||||
public static SineWavePointGenerator CreateByCount(Point start, Point end, int count)
|
||||
{
|
||||
return new SineWavePointGenerator(start, end) { Step = CalcStepByCount(count) };
|
||||
}
|
||||
|
||||
public static SineWavePointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
|
||||
{
|
||||
return new SineWavePointGenerator(start, end, pxPerStep);
|
||||
}
|
||||
|
||||
public SineWavePointGenerator(Point start, Point end, double pxPerStep = 5)
|
||||
: this(start, end)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
private SineWavePointGenerator(Point start, Point end)
|
||||
: base(start, end)
|
||||
{
|
||||
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SquareWavePointGenerator : SquareWaveGenerator<Point>, IPointGenerator
|
||||
{
|
||||
private readonly double _pAngle;
|
||||
public IPointGenerator Self { get => this; }
|
||||
public SquareWavePointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public SquareWavePointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public SquareWavePointGenerator SetSegments(int count) { Segments = count; return this; }
|
||||
public SquareWavePointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override Point Calc()
|
||||
{
|
||||
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Amplitude * FOffset * Scale, _pAngle);
|
||||
}
|
||||
|
||||
public static SquareWavePointGenerator CreateByCount(Point start, Point end, int count)
|
||||
{
|
||||
return new SquareWavePointGenerator(start, end) { Step = CalcStepByCount(count) };
|
||||
}
|
||||
|
||||
public static SquareWavePointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5)
|
||||
{
|
||||
return new SquareWavePointGenerator(start, end, pxPerStep);
|
||||
}
|
||||
|
||||
public SquareWavePointGenerator(Point start, Point end, double pxPerStep = 5)
|
||||
: this(start, end)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
private SquareWavePointGenerator(Point start, Point end)
|
||||
: base(start, end)
|
||||
{
|
||||
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Utils/Sequences/Perlin.cs
Normal file
97
Utils/Sequences/Perlin.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace WebmrAPI.Utils.Sequences
|
||||
{
|
||||
public abstract class PerlinGenerator<T> : BaseGenerator<T>
|
||||
{
|
||||
private Perlin2D _perlin;
|
||||
private double _amplitude = 100.0;
|
||||
public double Amplitude { get => _amplitude; protected set => _amplitude = Math.Abs(value); }
|
||||
public double Persistance { get; protected set; } = 0.5;
|
||||
public double Frequency { get; protected set; } = 5.0;
|
||||
public int Octaves { get; protected set; } = 4;
|
||||
public double Seed { get; private set; }
|
||||
public override double Step { get; protected set; }
|
||||
protected double Noise { get => _perlin.Noise(Progress * Frequency, Seed, Octaves, Persistance); }
|
||||
public PerlinGenerator(T min, T max, double seed)
|
||||
: base(min, max)
|
||||
{
|
||||
Seed = seed;
|
||||
_perlin = new Perlin2D((int)(Seed * 0x000fffff));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PerlinNumberGenerator<T> : PerlinGenerator<T>, INumberGenerator<T> where T : struct, INumber<T>
|
||||
{
|
||||
public INumberGenerator<T> Self { get => this; }
|
||||
public PerlinNumberGenerator<T> SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public PerlinNumberGenerator<T> SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public PerlinNumberGenerator<T> SetOctaves(int octaves) { Octaves = octaves; return this; }
|
||||
public PerlinNumberGenerator<T> SetPersistence(double peristance) { Persistance = peristance; return this; }
|
||||
public PerlinNumberGenerator<T> SetFrequency(double frequency) { Frequency = frequency; return this; }
|
||||
public PerlinNumberGenerator<T> SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override T Calc()
|
||||
{
|
||||
var offs = Noise * Scale * ((Self.DMax - Self.DMin) / 2.0);
|
||||
|
||||
return T.CreateChecked(Math.Clamp(Self.Mean + offs, Self.DMin, Self.DMax));
|
||||
}
|
||||
private static double GetSeed(T min, T max, double seed)
|
||||
{
|
||||
if (Double.IsNormal(seed)) return seed;
|
||||
return (Double.CreateChecked(min) + Double.CreateChecked(max)) / 1000.0;
|
||||
}
|
||||
public PerlinNumberGenerator(T min, T max, int count = 10, double seed = Double.NaN) : base(min, max, GetSeed(min, max, seed))
|
||||
{
|
||||
Step = CalcStepByCount(count);
|
||||
Amplitude = 1.0; // Set default
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PerlinPointGenerator : PerlinGenerator<Point>, IPointGenerator
|
||||
{
|
||||
private readonly double _pAngle;
|
||||
public IPointGenerator Self { get => this; }
|
||||
public PerlinPointGenerator SetShapeFactor(double fShape) { FShape = fShape; return this; }
|
||||
public PerlinPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; }
|
||||
public PerlinPointGenerator SetOctaves(int octaves) { Octaves = octaves; return this; }
|
||||
public PerlinPointGenerator SetPersistence(double peristance) { Persistance = peristance; return this; }
|
||||
public PerlinPointGenerator SetFrequency(double frequency) { Frequency = frequency; return this; }
|
||||
public PerlinPointGenerator SetAmplitude(double amplitude) { Amplitude = amplitude; return this; }
|
||||
protected override Point Calc()
|
||||
{
|
||||
return IPointGenerator.CalcPoint(Self.LinearX, Self.LinearY, Noise * Amplitude * Scale, _pAngle);
|
||||
}
|
||||
|
||||
private static double GetSeed(Point start, Point end, double seed)
|
||||
{
|
||||
if (Double.IsNormal(seed)) return seed;
|
||||
return (start.X + end.Y) / 1000.0;
|
||||
}
|
||||
|
||||
public static PerlinPointGenerator CreateByCount(Point start, Point end, int count, double seed = Double.NaN)
|
||||
{
|
||||
return new PerlinPointGenerator(start, end, seed)
|
||||
{
|
||||
Step = CalcStepByCount(count)
|
||||
};
|
||||
}
|
||||
public static PerlinPointGenerator CreateByStepSize(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
|
||||
{
|
||||
return new PerlinPointGenerator(start, end, pxPerStep, seed);
|
||||
}
|
||||
|
||||
public PerlinPointGenerator(Point start, Point end, double pxPerStep = 5, double seed = Double.NaN)
|
||||
: this(start, end, seed)
|
||||
{
|
||||
Step = pxPerStep / IPointGenerator.CalcDistance(start, end);
|
||||
}
|
||||
|
||||
private PerlinPointGenerator(Point start, Point end, double seed)
|
||||
: base(start, end, GetSeed(start, end, seed))
|
||||
{
|
||||
_pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
185
Utils/Shortcuts.cs
Normal file
185
Utils/Shortcuts.cs
Normal file
@ -0,0 +1,185 @@
|
||||
using Google.Protobuf;
|
||||
using WebmrAPI.Utils.Sequences;
|
||||
using WinIPC.Models;
|
||||
using WinIPC.Utils;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class Key
|
||||
{
|
||||
public Button Code { get; private set; }
|
||||
|
||||
public Key(Button code) { Code = code; }
|
||||
|
||||
public Key(string key)
|
||||
{
|
||||
Button code;
|
||||
|
||||
switch (key.ToLowerInvariant())
|
||||
{
|
||||
case "-": case "_": Code = Button.Sub; break;
|
||||
case "+": case "=": Code = Button.Add; break;
|
||||
case "~": case "`": Code = Button.Tilde; break;
|
||||
case "<": case ",": Code = Button.Comma; break;
|
||||
case ">": case ".": Code = Button.Period; break;
|
||||
case ":": case ";": Code = Button.Colon; break;
|
||||
case "{": case "[": Code = Button.LBracket; break;
|
||||
case "}": case "]": Code = Button.RBracket; break;
|
||||
case "\\": case "|": Code = Button.BSol; break;
|
||||
case "/": case "?": Code = Button.Sol; break;
|
||||
case "'": case "\"": Code = Button.Quote; break;
|
||||
case "num+": Code = Button.NumAdd; break;
|
||||
case "num-": Code = Button.NumSub; break;
|
||||
case "num*": Code = Button.NumMul; break;
|
||||
case "num/": Code = Button.NumDiv; break;
|
||||
case "num.": Code = Button.NumDec; break;
|
||||
|
||||
case "a": case "b": case "c": case "d": case "e": case "f":
|
||||
case "g": case "h": case "i": case "j": case "k": case "l":
|
||||
case "m": case "n": case "o": case "p": case "q": case "r":
|
||||
case "s": case "t": case "u": case "v": case "w": case "x":
|
||||
case "y": case "z": case "0": case "1": case "2": case "3":
|
||||
case "4": case "5": case "6": case "7": case "8": case "9":
|
||||
if (Enum.TryParse($"Key{key}", out code))
|
||||
{
|
||||
Code = code;
|
||||
break;
|
||||
}
|
||||
else goto default;
|
||||
case "shift": case "ctrl": case "alt": case "win":
|
||||
if (Enum.TryParse($"L{key}", out code))
|
||||
{
|
||||
Code = code;
|
||||
break;
|
||||
}
|
||||
else goto default;
|
||||
default:
|
||||
if (Enum.TryParse(key, out code))
|
||||
{
|
||||
Code = code;
|
||||
break;
|
||||
}
|
||||
else throw new ArgumentException($"Unexpected key \"{key}\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class KeyShortcutSequence
|
||||
{
|
||||
private Queue<KeyValuePair<KeyShortcut, uint>> _queue = new();
|
||||
|
||||
public KeyShortcutSequence(KeyShortcut key) { Next(key); }
|
||||
|
||||
public KeyShortcutSequence Next(KeyShortcut shortcut, int delay = 0)
|
||||
{
|
||||
_queue.Enqueue(new KeyValuePair<KeyShortcut, uint>(shortcut, (uint)delay));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEnumerable<InputAction> GetInputActions()
|
||||
{
|
||||
var actions = new InputAction[0];
|
||||
|
||||
do
|
||||
{
|
||||
var kvpair = _queue.Dequeue();
|
||||
|
||||
if (actions.Length > 0)
|
||||
{
|
||||
actions[actions.Length - 1].DelayMs += kvpair.Value;
|
||||
}
|
||||
|
||||
actions = actions.Concat(kvpair.Key.GetInputActions()).ToArray();
|
||||
} while (_queue.Count > 0);
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
public class KeyShortcut
|
||||
{
|
||||
private readonly static DelayGenerator _dGen = new(new PerlinNumberGenerator<int>(75, 200, Int32.MaxValue));
|
||||
private Queue<Key> _queue = new();
|
||||
|
||||
public KeyShortcut(Key key) { And(key); }
|
||||
public KeyShortcut(string key) { And(key); }
|
||||
|
||||
public KeyShortcut And(Key key)
|
||||
{
|
||||
_queue.Enqueue(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeyShortcut And(string key)
|
||||
{
|
||||
return And(new Key(key));
|
||||
}
|
||||
|
||||
public IEnumerable<InputAction> GetHoldDownActions()
|
||||
{
|
||||
Key key;
|
||||
var actions = new InputAction[_queue.Count];
|
||||
|
||||
for (int i = 0, n = _queue.Count; i < n;)
|
||||
{
|
||||
_queue.Enqueue(key = _queue.Dequeue());
|
||||
actions[i++] = new InputAction()
|
||||
{
|
||||
DelayMs = (uint)_dGen.Next().Value,
|
||||
Type = InputType.KeyDown,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
|
||||
};
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public IEnumerable<InputAction> GetHoldUpActions()
|
||||
{
|
||||
Key key;
|
||||
var actions = new InputAction[_queue.Count];
|
||||
|
||||
for (int n = _queue.Count; n > 0;)
|
||||
{
|
||||
_queue.Enqueue(key = _queue.Dequeue());
|
||||
actions[--n] = new InputAction()
|
||||
{
|
||||
DelayMs = (uint)_dGen.Next().Value,
|
||||
Type = InputType.KeyUp,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
|
||||
};
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public IEnumerable<InputAction> GetInputActions()
|
||||
{
|
||||
Key key;
|
||||
int n = _queue.Count * 2;
|
||||
var actions = new InputAction[n];
|
||||
|
||||
for (int i = 0; i < n;)
|
||||
{
|
||||
_queue.Enqueue(key = _queue.Dequeue());
|
||||
|
||||
actions[i++] = new InputAction()
|
||||
{
|
||||
DelayMs = (uint)_dGen.Next().Value,
|
||||
Type = InputType.KeyDown,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
|
||||
};
|
||||
|
||||
actions[--n] = new InputAction()
|
||||
{
|
||||
DelayMs = (uint)_dGen.Next().Value,
|
||||
Type = InputType.KeyUp,
|
||||
Payload = ByteString.CopyFrom(PayloadHandler.Serialize(new ButtonInput { Button = key.Code }))
|
||||
};
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -27,5 +27,14 @@
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.63.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Lib\winipc-ua\Models\ipc.proto" GrpcServices="None" ProtoRoot="." />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user