mirror of
https://github.com/babalae/better-genshin-impact
synced 2025-01-08 11:57:53 +08:00
Add HutaoInterop Pipe
This commit is contained in:
parent
790d42cee0
commit
1f2d3a40d9
@ -1,6 +1,7 @@
|
|||||||
using BetterGenshinImpact.GameTask;
|
using BetterGenshinImpact.GameTask;
|
||||||
using BetterGenshinImpact.Helpers;
|
using BetterGenshinImpact.Helpers;
|
||||||
using BetterGenshinImpact.Helpers.Extensions;
|
using BetterGenshinImpact.Helpers.Extensions;
|
||||||
|
using BetterGenshinImpact.Hutao;
|
||||||
using BetterGenshinImpact.Service;
|
using BetterGenshinImpact.Service;
|
||||||
using BetterGenshinImpact.Service.Interface;
|
using BetterGenshinImpact.Service.Interface;
|
||||||
using BetterGenshinImpact.Service.Notification;
|
using BetterGenshinImpact.Service.Notification;
|
||||||
@ -10,6 +11,7 @@ using BetterGenshinImpact.View.Pages;
|
|||||||
using BetterGenshinImpact.ViewModel;
|
using BetterGenshinImpact.ViewModel;
|
||||||
using BetterGenshinImpact.ViewModel.Pages;
|
using BetterGenshinImpact.ViewModel.Pages;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -36,6 +38,10 @@ public partial class App : Application
|
|||||||
.CheckIntegration()
|
.CheckIntegration()
|
||||||
.UseElevated()
|
.UseElevated()
|
||||||
.UseSingleInstance("BetterGI")
|
.UseSingleInstance("BetterGI")
|
||||||
|
.ConfigureLogging(builder =>
|
||||||
|
{
|
||||||
|
builder.ClearProviders();
|
||||||
|
})
|
||||||
.ConfigureServices(
|
.ConfigureServices(
|
||||||
(context, services) =>
|
(context, services) =>
|
||||||
{
|
{
|
||||||
@ -66,7 +72,6 @@ public partial class App : Application
|
|||||||
|
|
||||||
// App Host
|
// App Host
|
||||||
services.AddHostedService<ApplicationHostService>();
|
services.AddHostedService<ApplicationHostService>();
|
||||||
|
|
||||||
// Page resolver service
|
// Page resolver service
|
||||||
services.AddSingleton<IPageService, PageService>();
|
services.AddSingleton<IPageService, PageService>();
|
||||||
|
|
||||||
@ -98,6 +103,7 @@ public partial class App : Application
|
|||||||
services.AddHostedService(sp => sp.GetRequiredService<NotificationService>());
|
services.AddHostedService(sp => sp.GetRequiredService<NotificationService>());
|
||||||
services.AddSingleton<NotifierManager>();
|
services.AddSingleton<NotifierManager>();
|
||||||
services.AddSingleton<IScriptService, ScriptService>();
|
services.AddSingleton<IScriptService, ScriptService>();
|
||||||
|
services.AddSingleton<HutaoNamedPipe>();
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
//services.Configure<AppConfig>(context.Configuration.GetSection(nameof(AppConfig)));
|
//services.Configure<AppConfig>(context.Configuration.GetSection(nameof(AppConfig)));
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.RichTextBoxEx.Wpf" Version="1.1.0.1" />
|
<PackageReference Include="Serilog.Sinks.RichTextBoxEx.Wpf" Version="1.1.0.1" />
|
||||||
|
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageReference Include="Vanara.PInvoke.NtDll" Version="4.0.2" />
|
<PackageReference Include="Vanara.PInvoke.NtDll" Version="4.0.2" />
|
||||||
<PackageReference Include="Vanara.PInvoke.SHCore" Version="4.0.2" />
|
<PackageReference Include="Vanara.PInvoke.SHCore" Version="4.0.2" />
|
||||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.0.2" />
|
<PackageReference Include="Vanara.PInvoke.User32" Version="4.0.2" />
|
||||||
|
43
BetterGenshinImpact/Hutao/HutaoNamedPipe.cs
Normal file
43
BetterGenshinImpact/Hutao/HutaoNamedPipe.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
internal sealed class HutaoNamedPipe : IDisposable
|
||||||
|
{
|
||||||
|
private readonly NamedPipeClientStream clientStream = new(".", "Snap.Hutao.PrivateNamedPipe", PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
|
||||||
|
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
private Lazy<bool> isSupported;
|
||||||
|
|
||||||
|
public HutaoNamedPipe(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
isSupported = new(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
clientStream.Connect(TimeSpan.Zero);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (TimeoutException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSupported
|
||||||
|
{
|
||||||
|
get => isSupported.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
clientStream.Dispose();
|
||||||
|
}
|
||||||
|
}
|
6
BetterGenshinImpact/Hutao/PipePacketCommand.cs
Normal file
6
BetterGenshinImpact/Hutao/PipePacketCommand.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
internal enum PipePacketCommand : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
}
|
7
BetterGenshinImpact/Hutao/PipePacketContentType.cs
Normal file
7
BetterGenshinImpact/Hutao/PipePacketContentType.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
internal enum PipePacketContentType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Json = 1,
|
||||||
|
}
|
26
BetterGenshinImpact/Hutao/PipePacketHeader.cs
Normal file
26
BetterGenshinImpact/Hutao/PipePacketHeader.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
// Layout:
|
||||||
|
// 0 1 2 3 4 Bytes
|
||||||
|
// ┌─────────┬──────┬─────────┬─────────────┐
|
||||||
|
// │ Version │ Type │ Command │ ContentType │
|
||||||
|
// ├─────────┴──────┴─────────┴─────────────┤ 4 Bytes
|
||||||
|
// │ ContentLength │
|
||||||
|
// ├────────────────────────────────────────┤ 8 Bytes
|
||||||
|
// │ │
|
||||||
|
// │─────────────── Checksum ───────────────│
|
||||||
|
// │ │
|
||||||
|
// └────────────────────────────────────────┘ 16 Bytes
|
||||||
|
// Any content will be placed after the header.
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
internal struct PipePacketHeader
|
||||||
|
{
|
||||||
|
public byte Version;
|
||||||
|
public PipePacketType Type;
|
||||||
|
public PipePacketCommand Command;
|
||||||
|
public PipePacketContentType ContentType;
|
||||||
|
public int ContentLength;
|
||||||
|
public ulong Checksum;
|
||||||
|
}
|
9
BetterGenshinImpact/Hutao/PipePacketType.cs
Normal file
9
BetterGenshinImpact/Hutao/PipePacketType.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
internal enum PipePacketType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Request = 1,
|
||||||
|
Response = 2,
|
||||||
|
SessionTermination = 3,
|
||||||
|
}
|
83
BetterGenshinImpact/Hutao/PipeStreamExtension.cs
Normal file
83
BetterGenshinImpact/Hutao/PipeStreamExtension.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO.Hashing;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace BetterGenshinImpact.Hutao;
|
||||||
|
|
||||||
|
internal static class PipeStreamExtension
|
||||||
|
{
|
||||||
|
public static TData? ReadJsonContent<TData>(this PipeStream stream, ref readonly PipePacketHeader header)
|
||||||
|
{
|
||||||
|
using (IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent(header.ContentLength))
|
||||||
|
{
|
||||||
|
Span<byte> content = memoryOwner.Memory.Span[..header.ContentLength];
|
||||||
|
stream.ReadExactly(content);
|
||||||
|
|
||||||
|
if (XxHash64.HashToUInt64(content) != header.Checksum)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("PipePacket Content Hash incorrect");
|
||||||
|
}
|
||||||
|
return JsonSerializer.Deserialize<TData>(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReadPacket<TData>(this PipeStream stream, out PipePacketHeader header, out TData? data)
|
||||||
|
where TData : class
|
||||||
|
{
|
||||||
|
data = default;
|
||||||
|
|
||||||
|
stream.ReadPacket(out header);
|
||||||
|
if (header.ContentType is PipePacketContentType.Json)
|
||||||
|
{
|
||||||
|
data = stream.ReadJsonContent<TData>(in header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe void ReadPacket(this PipeStream stream, out PipePacketHeader header)
|
||||||
|
{
|
||||||
|
fixed (PipePacketHeader* pHeader = &header)
|
||||||
|
{
|
||||||
|
stream.ReadExactly(new(pHeader, sizeof(PipePacketHeader)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WritePacketWithJsonContent<TData>(this PipeStream stream, byte version, PipePacketType type, PipePacketCommand command, TData data)
|
||||||
|
{
|
||||||
|
PipePacketHeader header = default;
|
||||||
|
header.Version = version;
|
||||||
|
header.Type = type;
|
||||||
|
header.Command = command;
|
||||||
|
header.ContentType = PipePacketContentType.Json;
|
||||||
|
|
||||||
|
stream.WritePacket(ref header, JsonSerializer.SerializeToUtf8Bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WritePacket(this PipeStream stream, ref PipePacketHeader header, byte[] content)
|
||||||
|
{
|
||||||
|
header.ContentLength = content.Length;
|
||||||
|
header.Checksum = XxHash64.HashToUInt64(content);
|
||||||
|
|
||||||
|
stream.WritePacket(in header);
|
||||||
|
stream.Write(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WritePacket(this PipeStream stream, byte version, PipePacketType type, PipePacketCommand command)
|
||||||
|
{
|
||||||
|
PipePacketHeader header = default;
|
||||||
|
header.Version = version;
|
||||||
|
header.Type = type;
|
||||||
|
header.Command = command;
|
||||||
|
|
||||||
|
stream.WritePacket(in header);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe void WritePacket(this PipeStream stream, ref readonly PipePacketHeader header)
|
||||||
|
{
|
||||||
|
fixed (PipePacketHeader* pHeader = &header)
|
||||||
|
{
|
||||||
|
stream.Write(new(pHeader, sizeof(PipePacketHeader)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user