mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-10 16:38:13 +08:00
implement appcenter and minor fix
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.21.1" />
|
<PackageReference Include="Google.Protobuf" Version="3.21.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
106
src/AppCenterSDK/AppCenter.cs
Normal file
106
src/AppCenterSDK/AppCenter.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Net;
|
||||||
|
using YaeAchievement.AppCenterSDK.Models;
|
||||||
|
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK;
|
||||||
|
|
||||||
|
#pragma warning disable CA1416, CA2211
|
||||||
|
public static class AppCenter {
|
||||||
|
|
||||||
|
private const string LogCache = "./cache/bf18159fb833715i.miko";
|
||||||
|
private const string AppSecret = "648b83bf-d439-49bd-97f4-e1e506bdfe39";
|
||||||
|
private const string ApiUrl = "https://in.appcenter.ms/logs?api-version=1.0.0";
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
public static Guid? SessionID;
|
||||||
|
public static readonly string DeviceID;
|
||||||
|
public static readonly Device DeviceInfo;
|
||||||
|
private static List<Log> Queue;
|
||||||
|
private static readonly Lazy<HttpClient> httpClient = new(() => new HttpClient(new HttpClientHandler {
|
||||||
|
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null
|
||||||
|
}) {
|
||||||
|
DefaultRequestHeaders = {
|
||||||
|
{ "Install-ID", DeviceID },
|
||||||
|
{ "App-Secret", AppSecret }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static AppCenter() {
|
||||||
|
Queue = new List<Log>();
|
||||||
|
DeviceID = DeviceHelper.GetDeviceID();
|
||||||
|
DeviceInfo = new Device();
|
||||||
|
var running = true;
|
||||||
|
Task.Run(() => {
|
||||||
|
// ReSharper disable once LoopVariableIsNeverChangedInsideLoop
|
||||||
|
while (running) {
|
||||||
|
Upload();
|
||||||
|
Thread.Sleep(5 * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||||
|
running = false;
|
||||||
|
if (Queue.Count > 0) {
|
||||||
|
Directory.CreateDirectory("cache");
|
||||||
|
File.WriteAllText(LogCache, Queue.ToJson());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LogSerializer.AddLogType(PageLog.JsonIdentifier, typeof(PageLog));
|
||||||
|
LogSerializer.AddLogType(EventLog.JsonIdentifier, typeof(EventLog));
|
||||||
|
LogSerializer.AddLogType(HandledErrorLog.JsonIdentifier, typeof(HandledErrorLog));
|
||||||
|
LogSerializer.AddLogType(ManagedErrorLog.JsonIdentifier, typeof(ManagedErrorLog));
|
||||||
|
LogSerializer.AddLogType(StartServiceLog.JsonIdentifier, typeof(StartServiceLog));
|
||||||
|
LogSerializer.AddLogType(StartSessionLog.JsonIdentifier, typeof(StartSessionLog));
|
||||||
|
if (Directory.Exists("./cache") && File.Exists(LogCache)) {
|
||||||
|
var list = File.ReadAllText(LogCache).FromJson()?.Logs;
|
||||||
|
if (list != null) {
|
||||||
|
Queue.AddRange(list);
|
||||||
|
}
|
||||||
|
File.Delete(LogCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper restore InconsistentNaming
|
||||||
|
public static void Upload() {
|
||||||
|
if (Queue.Count == 0) return;
|
||||||
|
var uploadStatus = "";
|
||||||
|
do {
|
||||||
|
Queue = Queue.Select(log => {
|
||||||
|
log.Status = LogStatus.Uploading;
|
||||||
|
return log;
|
||||||
|
}).ToList();
|
||||||
|
using var uploadContent = new StringContent(Queue.ToJson());
|
||||||
|
try {
|
||||||
|
using var response = httpClient.Value.PostAsync(ApiUrl, uploadContent).Result;
|
||||||
|
var result = response.Content.ReadAsStringAsync().Result;
|
||||||
|
uploadStatus = JsonConvert.DeserializeObject<LogUploadResult>(result)!.Status;
|
||||||
|
} catch (Exception) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
} while (uploadStatus != "Success");
|
||||||
|
Queue.RemoveAll(log => log.Status == LogStatus.Uploading);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init() {
|
||||||
|
new StartServiceLog("Analytics", "Crashes").Enqueue();
|
||||||
|
SessionID = Guid.NewGuid();
|
||||||
|
new StartSessionLog().Enqueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TrackCrash(Exception exception, bool fatal = true) {
|
||||||
|
new ManagedErrorLog(exception, fatal).Enqueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Enqueue(this Log log) {
|
||||||
|
Queue.Add(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToJson(this IEnumerable<Log> queue) {
|
||||||
|
return LogSerializer.Serialize(new LogContainer(queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogContainer? FromJson(this string text) {
|
||||||
|
return LogSerializer.DeserializeLogs(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore CA1416, CA2211
|
||||||
68
src/AppCenterSDK/DeviceHelper.cs
Normal file
68
src/AppCenterSDK/DeviceHelper.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using Microsoft.Win32;
|
||||||
|
using YaeAchievement.Win32;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK;
|
||||||
|
|
||||||
|
#pragma warning disable CA1416
|
||||||
|
public static class DeviceHelper {
|
||||||
|
|
||||||
|
public static string? GetOem() {
|
||||||
|
using var root = Registry.LocalMachine;
|
||||||
|
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
|
||||||
|
var oem = sub?.GetValue("SystemManufacturer") as string;
|
||||||
|
return oem == "System manufacturer" ? null : oem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetModel() {
|
||||||
|
using var root = Registry.LocalMachine;
|
||||||
|
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
|
||||||
|
var model = sub?.GetValue("SystemProductName") as string;
|
||||||
|
return model == "System Product Name" ? null : model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetScreenSize() {
|
||||||
|
var desktop = Native.GetDC(IntPtr.Zero);
|
||||||
|
var size = $"{Native.GetDeviceCaps(desktop, 118)}x{Native.GetDeviceCaps(desktop, 117)}";
|
||||||
|
Native.ReleaseDC(IntPtr.Zero, desktop);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetCountry() {
|
||||||
|
using var root = Registry.CurrentUser;
|
||||||
|
using var sub = root.OpenSubKey("Control Panel\\International\\Geo");
|
||||||
|
return sub?.GetValue("Name") as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetSystemVersion() {
|
||||||
|
using var root = Registry.LocalMachine;
|
||||||
|
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
|
||||||
|
var majorVersion = sub?.GetValue("CurrentMajorVersionNumber");
|
||||||
|
if (majorVersion != null) {
|
||||||
|
var minorVersion = sub?.GetValue("CurrentMinorVersionNumber", "0");
|
||||||
|
var buildNumber = sub?.GetValue("CurrentBuildNumber", "0");
|
||||||
|
return $"{majorVersion}.{minorVersion}.{buildNumber}";
|
||||||
|
} else {
|
||||||
|
var version = sub?.GetValue("CurrentVersion", "0.0");
|
||||||
|
var buildNumber = sub?.GetValue("CurrentBuild", "0");
|
||||||
|
return $"{version}.{buildNumber}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetSystemBuild() {
|
||||||
|
using var root = Registry.LocalMachine;
|
||||||
|
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
|
||||||
|
return (int) (sub?.GetValue("UBR") ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public static string GetDeviceID() {
|
||||||
|
using var sdk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\miHoYoSDK");
|
||||||
|
if (sdk?.GetValue("MIHOYOSDK_DEVICE_ID") is not string id) {
|
||||||
|
id = $"{Random.Shared.NextInt64().ToString().SHA1Hash().ToLower()}{DateTimeOffset.Now.ToUnixTimeMilliseconds()}";
|
||||||
|
sdk?.SetValue("MIHOYOSDK_DEVICE_ID", id);
|
||||||
|
}
|
||||||
|
id = id.MD5Hash().ToLower();
|
||||||
|
return $"{id[..8]}-{id[8..12]}-{id[12..16]}-{id[16..20]}-{id[20..]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore CA1416
|
||||||
25
src/AppCenterSDK/ErrorLogHelper.cs
Normal file
25
src/AppCenterSDK/ErrorLogHelper.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK;
|
||||||
|
|
||||||
|
public static class ErrorLogHelper {
|
||||||
|
public static MException CreateModelExceptionAndBinaries(Exception exception) {
|
||||||
|
var modelException = new MException {
|
||||||
|
Type = exception.GetType().ToString(),
|
||||||
|
Message = exception.Message,
|
||||||
|
StackTrace = exception.StackTrace
|
||||||
|
};
|
||||||
|
if (exception is AggregateException aggregateException) {
|
||||||
|
if (aggregateException.InnerExceptions.Count != 0) {
|
||||||
|
modelException.InnerExceptions = new List<MException>();
|
||||||
|
foreach (var innerException in aggregateException.InnerExceptions) {
|
||||||
|
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(innerException));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (exception.InnerException != null) {
|
||||||
|
modelException.InnerExceptions ??= new List<MException>();
|
||||||
|
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(exception.InnerException));
|
||||||
|
}
|
||||||
|
return modelException;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/AppCenterSDK/Models/Device.cs
Normal file
51
src/AppCenterSDK/Models/Device.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class Device {
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "sdkName")]
|
||||||
|
public string SdkName { get; set; } = "appcenter.wpf.netcore";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "sdkVersion")]
|
||||||
|
public string SdkVersion { get; set; } = "4.5.0";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "osName")]
|
||||||
|
public string OsName { get; set; } = "WINDOWS";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "osVersion")]
|
||||||
|
public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "osBuild")]
|
||||||
|
public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "model")]
|
||||||
|
public string? Model { get; set; } = DeviceHelper.GetModel();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "oemName")]
|
||||||
|
public string? OemName { get; set; } = DeviceHelper.GetOem();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "screenSize")]
|
||||||
|
public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "carrierCountry")]
|
||||||
|
public string Country { get; set; } = DeviceHelper.GetCountry() ?? "CN";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "locale")]
|
||||||
|
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "timeZoneOffset")]
|
||||||
|
public int TimeZoneOffset { get; set; } = (int) TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "appVersion")]
|
||||||
|
public string AppVersion { get; set; } = GlobalVars.AppVersionName;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "appBuild")]
|
||||||
|
public string AppBuild { get; set; } = GlobalVars.AppVersionCode.ToString();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "appNamespace")]
|
||||||
|
public string AppNamespace { get; set; } = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType?.Namespace ?? string.Empty;
|
||||||
|
|
||||||
|
}
|
||||||
20
src/AppCenterSDK/Models/EventLog.cs
Normal file
20
src/AppCenterSDK/Models/EventLog.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class EventLog : LogWithProperties {
|
||||||
|
|
||||||
|
public const string JsonIdentifier = "event";
|
||||||
|
|
||||||
|
public EventLog(string name) {
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "id")]
|
||||||
|
private Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "name")]
|
||||||
|
private string Name { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
21
src/AppCenterSDK/Models/HandledErrorLog.cs
Normal file
21
src/AppCenterSDK/Models/HandledErrorLog.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class HandledErrorLog : LogWithProperties {
|
||||||
|
|
||||||
|
public const string JsonIdentifier = "handledError";
|
||||||
|
|
||||||
|
public HandledErrorLog(MException exception) {
|
||||||
|
Id = Guid.NewGuid();
|
||||||
|
Exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "id")]
|
||||||
|
public Guid? Id { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "exception")]
|
||||||
|
public MException Exception { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
23
src/AppCenterSDK/Models/Log.cs
Normal file
23
src/AppCenterSDK/Models/Log.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public abstract class Log {
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "sid")]
|
||||||
|
private Guid? Session { get; set; } = AppCenter.SessionID;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "timestamp")]
|
||||||
|
private DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "device")]
|
||||||
|
private Device Device { get; set; } = AppCenter.DeviceInfo;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
internal LogStatus Status = LogStatus.Pending;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LogStatus {
|
||||||
|
Pending, Uploading, Uploaded
|
||||||
|
}
|
||||||
14
src/AppCenterSDK/Models/LogContainer.cs
Normal file
14
src/AppCenterSDK/Models/LogContainer.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class LogContainer {
|
||||||
|
|
||||||
|
public LogContainer(IEnumerable<Log> logs) {
|
||||||
|
Logs = logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "logs")]
|
||||||
|
public IEnumerable<Log> Logs { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
19
src/AppCenterSDK/Models/LogUploadResult.cs
Normal file
19
src/AppCenterSDK/Models/LogUploadResult.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class LogUploadResult {
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "status")]
|
||||||
|
public string Status { get; set; } = null!;
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "validDiagnosticsIds")]
|
||||||
|
public Guid[] ValidDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "throttledDiagnosticsIds")]
|
||||||
|
public Guid[] ThrottledDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "correlationId")]
|
||||||
|
public Guid CorrelationId { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
10
src/AppCenterSDK/Models/LogWithProperties.cs
Normal file
10
src/AppCenterSDK/Models/LogWithProperties.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class LogWithProperties : Log {
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "properties")]
|
||||||
|
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
}
|
||||||
22
src/AppCenterSDK/Models/MException.cs
Normal file
22
src/AppCenterSDK/Models/MException.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class MException {
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "type")]
|
||||||
|
public string Type { get; set; } = "UnknownType";
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "message")]
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "stackTrace")]
|
||||||
|
public string? StackTrace { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "frames")]
|
||||||
|
public IList<StackFrame>? Frames { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "innerExceptions")]
|
||||||
|
public IList<MException>? InnerExceptions { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
50
src/AppCenterSDK/Models/ManagedErrorLog.cs
Normal file
50
src/AppCenterSDK/Models/ManagedErrorLog.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class ManagedErrorLog : Log {
|
||||||
|
|
||||||
|
public const string JsonIdentifier = "managedError";
|
||||||
|
|
||||||
|
public ManagedErrorLog(
|
||||||
|
Exception exception,
|
||||||
|
bool fatal = true
|
||||||
|
) {
|
||||||
|
var p = Process.GetCurrentProcess();
|
||||||
|
Id = Guid.NewGuid();
|
||||||
|
Fatal = fatal;
|
||||||
|
UserId = AppCenter.DeviceID;
|
||||||
|
ProcessId = p.Id;
|
||||||
|
Exception = ErrorLogHelper.CreateModelExceptionAndBinaries(exception);
|
||||||
|
ProcessName = p.ProcessName;
|
||||||
|
Architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
|
||||||
|
AppLaunchTimestamp = p.StartTime.ToUniversalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "id")]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "userId")]
|
||||||
|
public string? UserId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "processId")]
|
||||||
|
public int ProcessId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "processName")]
|
||||||
|
public string ProcessName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "fatal")]
|
||||||
|
public bool Fatal { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "appLaunchTimestamp")]
|
||||||
|
public DateTime? AppLaunchTimestamp { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "architecture")]
|
||||||
|
public string? Architecture { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "exception")]
|
||||||
|
public MException Exception { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
17
src/AppCenterSDK/Models/PageLog.cs
Normal file
17
src/AppCenterSDK/Models/PageLog.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class PageLog : LogWithProperties {
|
||||||
|
|
||||||
|
public const string JsonIdentifier = "page";
|
||||||
|
|
||||||
|
public PageLog(string name) {
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
60
src/AppCenterSDK/Models/Serialization/LogJsonConverter.cs
Normal file
60
src/AppCenterSDK/Models/Serialization/LogJsonConverter.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||||
|
|
||||||
|
#pragma warning disable CS8604, CS8765
|
||||||
|
public class LogJsonConverter : JsonConverter {
|
||||||
|
|
||||||
|
private readonly Dictionary<string, Type> _logTypes = new ();
|
||||||
|
private readonly object _jsonConverterLock = new ();
|
||||||
|
private static readonly JsonSerializerSettings SerializationSettings;
|
||||||
|
|
||||||
|
static LogJsonConverter() {
|
||||||
|
SerializationSettings = new JsonSerializerSettings {
|
||||||
|
Formatting = Formatting.None,
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
DateFormatHandling = DateFormatHandling.IsoDateFormat,
|
||||||
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
|
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddLogType(string typeName, Type type) {
|
||||||
|
lock (_jsonConverterLock) {
|
||||||
|
_logTypes[typeName] = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType) {
|
||||||
|
return typeof(Log).IsAssignableFrom(objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object? ReadJson(JsonReader reader, Type t, object o, JsonSerializer s) {
|
||||||
|
Type logType;
|
||||||
|
var jsonObject = JObject.Load(reader);
|
||||||
|
var typeName = jsonObject.GetValue("type")?.ToString();
|
||||||
|
lock (_jsonConverterLock) {
|
||||||
|
if (typeName == null || !_logTypes.ContainsKey(typeName)) {
|
||||||
|
throw new JsonReaderException("Could not identify type of log");
|
||||||
|
}
|
||||||
|
logType = _logTypes[typeName];
|
||||||
|
jsonObject.Remove("type");
|
||||||
|
}
|
||||||
|
return jsonObject.ToObject(logType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||||
|
var info = value.GetType().GetTypeInfo();
|
||||||
|
if (info.GetCustomAttribute(typeof(JsonObjectAttribute)) is not JsonObjectAttribute attribute) {
|
||||||
|
throw new JsonWriterException("Log type is missing JsonObjectAttribute");
|
||||||
|
}
|
||||||
|
var jsonObject = JObject.FromObject(value, JsonSerializer.CreateDefault(SerializationSettings));
|
||||||
|
jsonObject.Add("type", JToken.FromObject(attribute.Id));
|
||||||
|
writer.WriteRawValue(jsonObject.ToString(Formatting.None));
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/AppCenterSDK/Models/Serialization/LogSerializer.cs
Normal file
40
src/AppCenterSDK/Models/Serialization/LogSerializer.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||||
|
|
||||||
|
#pragma warning disable CS8604, CS8765, CS8603
|
||||||
|
public static class LogSerializer {
|
||||||
|
|
||||||
|
private static readonly JsonSerializerSettings SerializationSettings;
|
||||||
|
private static readonly LogJsonConverter Converter = new ();
|
||||||
|
|
||||||
|
static LogSerializer() {
|
||||||
|
SerializationSettings = new JsonSerializerSettings {
|
||||||
|
Formatting = Formatting.None,
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
DateFormatHandling = DateFormatHandling.IsoDateFormat,
|
||||||
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
|
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
|
||||||
|
Converters = { Converter }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddLogType(string typeName, Type type) {
|
||||||
|
Converter.AddLogType(typeName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(LogContainer logContainer) {
|
||||||
|
return JsonConvert.SerializeObject(logContainer, SerializationSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize(Log log) {
|
||||||
|
return JsonConvert.SerializeObject(log, SerializationSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogContainer? DeserializeLogs(string json) {
|
||||||
|
return JsonConvert.DeserializeObject<LogContainer>(json, SerializationSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/AppCenterSDK/Models/StackFrame.cs
Normal file
34
src/AppCenterSDK/Models/StackFrame.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
public class StackFrame {
|
||||||
|
|
||||||
|
public StackFrame(string address, string code, string className, string methodName, int? lineNumber, string fileName) {
|
||||||
|
Address = address;
|
||||||
|
Code = code;
|
||||||
|
ClassName = className;
|
||||||
|
MethodName = methodName;
|
||||||
|
LineNumber = lineNumber;
|
||||||
|
FileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "address")]
|
||||||
|
public string Address { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "className")]
|
||||||
|
public string ClassName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "methodName")]
|
||||||
|
public string MethodName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "lineNumber")]
|
||||||
|
public int? LineNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "fileName")]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
17
src/AppCenterSDK/Models/StartServiceLog.cs
Normal file
17
src/AppCenterSDK/Models/StartServiceLog.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class StartServiceLog : Log {
|
||||||
|
|
||||||
|
public const string JsonIdentifier = "startService";
|
||||||
|
|
||||||
|
public StartServiceLog(params string[] services) {
|
||||||
|
Services = services;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "services")]
|
||||||
|
public string[] Services { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
8
src/AppCenterSDK/Models/StartSessionLog.cs
Normal file
8
src/AppCenterSDK/Models/StartSessionLog.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement.AppCenterSDK.Models;
|
||||||
|
|
||||||
|
[JsonObject(JsonIdentifier)]
|
||||||
|
public class StartSessionLog : Log {
|
||||||
|
public const string JsonIdentifier = "startSession";
|
||||||
|
}
|
||||||
@@ -171,5 +171,4 @@ public static class Export {
|
|||||||
var b = Utils.GetBucketFileAsByteArray("schicksal/metadata");
|
var b = Utils.GetBucketFileAsByteArray("schicksal/metadata");
|
||||||
return AchievementInfo.Parser.ParseFrom(b);
|
return AchievementInfo.Parser.ParseFrom(b);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
106
src/Extensions.cs
Normal file
106
src/Extensions.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Web;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace YaeAchievement;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||||
|
public static class Extensions {
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static readonly Lazy<Aes> aes = new (Aes.Create);
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static readonly Lazy<MD5> md5 = new (MD5.Create);
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static readonly Lazy<SHA1> sha1 = new (SHA1.Create);
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static readonly Lazy<HttpClient> defaultClient = new (() => new HttpClient(new HttpClientHandler {
|
||||||
|
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null
|
||||||
|
}) {
|
||||||
|
DefaultRequestHeaders = {{ "User-Agent", "UnityPlayer/2017.4.30f1 (UnityWebRequest/1.0, libcurl/7.51.0-DEV)" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
public static byte[] ToBytes(this string text) {
|
||||||
|
return Encoding.UTF8.GetBytes(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DecodeToString(this byte[] bytes) {
|
||||||
|
return Encoding.UTF8.GetString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public static string MD5Hash(this string text) {
|
||||||
|
return md5.Value.ComputeHash(text.ToBytes()).ToHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public static string SHA1Hash(this string text, bool base64 = true) {
|
||||||
|
var bytes = sha1.Value.ComputeHash(text.ToBytes());
|
||||||
|
return base64 ? bytes.ToBase64() : bytes.ToHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpResponseMessage Send(this HttpRequestMessage message, HttpClient? client = null) {
|
||||||
|
Logger.Trace($"{message.Method} {message.RequestUri?.GetFullPath()}");
|
||||||
|
return (client ?? defaultClient.Value).Send(message); // dispose message?
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? Send<T>(this HttpRequestMessage message, HttpClient? client = null, Func<string, string>? onPreDeserialize = null) {
|
||||||
|
Logger.Trace($"{message.Method} {message.RequestUri?.GetFullPath()}");
|
||||||
|
using var response = (client ?? defaultClient.Value).Send(message);
|
||||||
|
var text = response.Content.ReadAsStringAsync().Result;
|
||||||
|
if (onPreDeserialize != null) {
|
||||||
|
text = onPreDeserialize.Invoke(text);
|
||||||
|
}
|
||||||
|
return JsonConvert.DeserializeObject<T>(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToQueryString(this NameValueCollection collection, bool escape = true) {
|
||||||
|
var items = collection.AllKeys
|
||||||
|
.Select(key => escape ?
|
||||||
|
$"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(collection[key])}" :
|
||||||
|
$"{key}={collection[key]}");
|
||||||
|
return string.Join("&", items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToQueryString(this IEnumerable<KeyValuePair<string, object>> dict, bool escape = true) {
|
||||||
|
var items = dict
|
||||||
|
.Select(pair => escape ?
|
||||||
|
$"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value.ToString())}" :
|
||||||
|
$"{pair.Key}={pair.Value}");
|
||||||
|
return string.Join("&", items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToJsonString<TKey, TValue>(this Dictionary<TKey, TValue> data) where TKey : notnull {
|
||||||
|
return JsonConvert.SerializeObject(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFullPath(this Uri uri) {
|
||||||
|
return $"{uri.Scheme}://{uri.Host}{uri.AbsolutePath}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<KeyValuePair<string, string>> ToKeyValuePairs(this NameValueCollection collection) {
|
||||||
|
return collection.AllKeys
|
||||||
|
.Select(key => new KeyValuePair<string, string>(key!, collection[key] ?? string.Empty))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string UrlEncode(this string text) {
|
||||||
|
return HttpUtility.UrlEncode(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToHex(this byte[] bytes) {
|
||||||
|
return Convert.ToHexString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToBase64(this byte[] bytes) {
|
||||||
|
return Convert.ToBase64String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] FromBase64(this string text) {
|
||||||
|
return Convert.FromBase64String(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,21 +3,25 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
|
|
||||||
[SuppressMessage("Usage", "CA2211:非常量字段应当不可见")]
|
|
||||||
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
|
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
|
||||||
|
|
||||||
|
#pragma warning disable CA2211
|
||||||
public static class GlobalVars {
|
public static class GlobalVars {
|
||||||
|
|
||||||
public static bool Verbose = false;
|
public static bool DebugProxy = false;
|
||||||
|
public static bool CheckGamePath = true;
|
||||||
public static bool UnexpectedExit = true;
|
public static bool UnexpectedExit = true;
|
||||||
public static string GamePath = "";
|
public static string GamePath = null!;
|
||||||
|
public static Logger.Level LogLevel = Logger.Level.Info;
|
||||||
public static Version AppVersion = Assembly.GetEntryAssembly()!.GetName().Version!;
|
public static Version AppVersion = Assembly.GetEntryAssembly()!.GetName().Version!;
|
||||||
|
|
||||||
public const uint AppVersionCode = 77;
|
public const uint AppVersionCode = 28;
|
||||||
public const string AppVersionName = "2.?";
|
public const string AppVersionName = "2.0";
|
||||||
public const string LibName = "YaeLib.dll";
|
public const string LibName = "YaeLib.dll";
|
||||||
public const string PipeName = "YaeAchievementPipe";
|
public const string PipeName = "YaeAchievementPipe";
|
||||||
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
||||||
public const string ConfigFileName = "YaeAchievement.runtimeconfig.json";
|
public const string ConfigFileName = "YaeAchievement.runtimeconfig.json";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA2211
|
||||||
|
|||||||
40
src/Logger.cs
Normal file
40
src/Logger.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
namespace YaeAchievement;
|
||||||
|
|
||||||
|
public static class Logger {
|
||||||
|
|
||||||
|
public enum Level {
|
||||||
|
Trace, Debug, Info, Warn, Error
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(string msg) {
|
||||||
|
Log(msg, Level.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Warn(string msg) {
|
||||||
|
Log(msg, Level.Warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Info(string msg) {
|
||||||
|
Log(msg, Level.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Debug(string msg) {
|
||||||
|
Log(msg, Level.Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Trace(string msg) {
|
||||||
|
Log(msg, Level.Trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Log(string msg, Level level) {
|
||||||
|
if (level >= GlobalVars.LogLevel) {
|
||||||
|
Console.WriteLine($"{DateTime.Now:MM/dd HH:mm:ss} {level.ToString().ToUpper().PadLeft(5)} : {msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteLog(string msg, Level level = Level.Info) {
|
||||||
|
if (level >= GlobalVars.LogLevel) {
|
||||||
|
Console.Write($"{DateTime.Now:MM/dd HH:mm:ss} {level.ToString().ToUpper().PadLeft(5)} : {msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using YaeAchievement;
|
using YaeAchievement;
|
||||||
|
using YaeAchievement.AppCenterSDK;
|
||||||
|
using YaeAchievement.AppCenterSDK.Models;
|
||||||
using static YaeAchievement.Utils;
|
using static YaeAchievement.Utils;
|
||||||
|
|
||||||
InstallExitHook();
|
InstallExitHook();
|
||||||
|
CheckSelfIsRunning();
|
||||||
TryDisableQuickEdit();
|
TryDisableQuickEdit();
|
||||||
InstallExceptionHook();
|
InstallExceptionHook();
|
||||||
CheckGenshinIsRunning();
|
CheckGenshinIsRunning();
|
||||||
@@ -13,6 +16,13 @@ Console.WriteLine("----------------------------------------------------");
|
|||||||
|
|
||||||
LoadConfig();
|
LoadConfig();
|
||||||
CheckUpdate();
|
CheckUpdate();
|
||||||
|
AppCenter.Init();
|
||||||
|
new EventLog("AppInit") {
|
||||||
|
Properties = {
|
||||||
|
{ "AppVersion", GlobalVars.AppVersionName },
|
||||||
|
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
|
||||||
|
}
|
||||||
|
}.Enqueue();
|
||||||
StartAndWaitResult(GlobalVars.GamePath, str => {
|
StartAndWaitResult(GlobalVars.GamePath, str => {
|
||||||
GlobalVars.UnexpectedExit = false;
|
GlobalVars.UnexpectedExit = false;
|
||||||
var list = AchievementAllDataNotify.Parser.ParseFrom(Convert.FromBase64String(str));
|
var list = AchievementAllDataNotify.Parser.ParseFrom(Convert.FromBase64String(str));
|
||||||
|
|||||||
44
src/Utils.cs
44
src/Utils.cs
@@ -9,6 +9,7 @@ using System.Security.Cryptography;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
|
using YaeAchievement.AppCenterSDK;
|
||||||
using YaeAchievement.Win32;
|
using YaeAchievement.Win32;
|
||||||
using static YaeAchievement.Win32.OpenFileFlags;
|
using static YaeAchievement.Win32.OpenFileFlags;
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ public static class Utils {
|
|||||||
|
|
||||||
public static readonly Lazy<HttpClient> CHttpClient = new (() => {
|
public static readonly Lazy<HttpClient> CHttpClient = new (() => {
|
||||||
var c = new HttpClient(new HttpClientHandler {
|
var c = new HttpClient(new HttpClientHandler {
|
||||||
Proxy = new WebProxy("http://127.0.0.1:8888"),
|
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null,
|
||||||
AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip
|
AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip
|
||||||
}) {
|
}) {
|
||||||
DefaultRequestHeaders = {
|
DefaultRequestHeaders = {
|
||||||
@@ -99,10 +100,10 @@ public static class Utils {
|
|||||||
|
|
||||||
public static void LoadConfig() {
|
public static void LoadConfig() {
|
||||||
var conf = JsonNode.Parse(File.ReadAllText(GlobalVars.ConfigFileName))!;
|
var conf = JsonNode.Parse(File.ReadAllText(GlobalVars.ConfigFileName))!;
|
||||||
var path = conf["genshinPath"];
|
var path = conf["location"];
|
||||||
if (path == null || CheckGamePathValid(path.GetValue<string>())) {
|
if (path == null || CheckGamePathValid(path.GetValue<string>())) {
|
||||||
GlobalVars.GamePath = SelectGameExecutable();
|
GlobalVars.GamePath = SelectGameExecutable();
|
||||||
conf["genshinPath"] = GlobalVars.GamePath;
|
conf["location"] = GlobalVars.GamePath;
|
||||||
File.WriteAllText(GlobalVars.ConfigFileName, conf.ToJsonString());
|
File.WriteAllText(GlobalVars.ConfigFileName, conf.ToJsonString());
|
||||||
} else {
|
} else {
|
||||||
GlobalVars.GamePath = path.GetValue<string>();
|
GlobalVars.GamePath = path.GetValue<string>();
|
||||||
@@ -116,15 +117,15 @@ public static class Utils {
|
|||||||
Console.WriteLine($"更新内容: \n{info.Description}");
|
Console.WriteLine($"更新内容: \n{info.Description}");
|
||||||
if (info.EnableAutoDownload) {
|
if (info.EnableAutoDownload) {
|
||||||
Console.WriteLine("正在下载更新包...");
|
Console.WriteLine("正在下载更新包...");
|
||||||
var fullPath = Path.GetFullPath("update.7z");
|
var fullPath = Path.GetFullPath($"update.{Path.GetExtension(info.PackageLink)}");
|
||||||
File.WriteAllBytes(fullPath, GetBucketFileAsByteArray(info.PackageLink));
|
File.WriteAllBytes(fullPath, GetBucketFileAsByteArray(info.PackageLink));
|
||||||
Console.WriteLine("下载完毕! 关闭程序后, 将压缩包解压至当前目录即可完成更新.");
|
Console.WriteLine("关闭程序后, 将压缩包解压至当前目录即可完成更新.");
|
||||||
ShellOpen(fullPath);
|
ShellOpen(fullPath);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
Console.WriteLine($"下载地址: {info.PackageLink}");
|
Console.WriteLine($"下载地址: {info.PackageLink}");
|
||||||
if (info.ForceUpdate) {
|
if (info.ForceUpdate) {
|
||||||
Console.WriteLine("在完成此次更新前, 程序可能无法正常使用.");
|
//Console.WriteLine("在完成此次更新前, 程序可能无法正常使用.");
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +133,19 @@ public static class Utils {
|
|||||||
File.WriteAllBytes("YaeLib.dll", GetBucketFileAsByteArray("schicksal/lib.dll"));
|
File.WriteAllBytes("YaeLib.dll", GetBucketFileAsByteArray("schicksal/lib.dll"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CheckSelfIsRunning() {
|
||||||
|
Process.EnterDebugMode();
|
||||||
|
var cur = Process.GetCurrentProcess();
|
||||||
|
foreach (var process in Process.GetProcesses().Where(process => process.Id != cur.Id)) {
|
||||||
|
if (process.ProcessName == cur.ProcessName) {
|
||||||
|
Logger.Error("另一个实例正在运行,请关闭后重试");
|
||||||
|
Environment.Exit(302);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Process.LeaveDebugMode();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool ShellOpen(string path) {
|
public static bool ShellOpen(string path) {
|
||||||
return new Process {
|
return new Process {
|
||||||
StartInfo = {
|
StartInfo = {
|
||||||
@@ -144,7 +157,8 @@ public static class Utils {
|
|||||||
|
|
||||||
private static bool CheckGamePathValid(string path) {
|
private static bool CheckGamePathValid(string path) {
|
||||||
var dir = Path.GetDirectoryName(path)!;
|
var dir = Path.GetDirectoryName(path)!;
|
||||||
return File.Exists($"{dir}/UnityPlayer.dll") && File.Exists($"{dir}/mhypbase.dll");
|
return !GlobalVars.CheckGamePath ||
|
||||||
|
File.Exists($"{dir}/UnityPlayer.dll") && File.Exists($"{dir}/mhypbase.dll");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SelectGameExecutable() {
|
private static string SelectGameExecutable() {
|
||||||
@@ -202,9 +216,13 @@ public static class Utils {
|
|||||||
Process.LeaveDebugMode();
|
Process.LeaveDebugMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static Process? proc;
|
||||||
|
|
||||||
public static void InstallExitHook() {
|
public static void InstallExitHook() {
|
||||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||||
Console.WriteLine("按任意键退出");
|
proc?.Kill();
|
||||||
|
Logger.Info("按任意键退出");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -212,7 +230,9 @@ public static class Utils {
|
|||||||
public static void InstallExceptionHook() {
|
public static void InstallExceptionHook() {
|
||||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => {
|
AppDomain.CurrentDomain.UnhandledException += (_, e) => {
|
||||||
Console.WriteLine(e.ExceptionObject.ToString());
|
Console.WriteLine(e.ExceptionObject.ToString());
|
||||||
Console.WriteLine("发生错误,请联系开发者以获取帮助");
|
Logger.Error("正在上报错误信息...");
|
||||||
|
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||||
|
AppCenter.Upload();
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -227,10 +247,12 @@ public static class Utils {
|
|||||||
Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail"));
|
Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var proc = Process.GetProcessById(Convert.ToInt32(pid));
|
Logger.Info($"原神正在启动 ({pid})");
|
||||||
|
proc = Process.GetProcessById(Convert.ToInt32(pid));
|
||||||
proc.EnableRaisingEvents = true;
|
proc.EnableRaisingEvents = true;
|
||||||
proc.Exited += (_, _) => {
|
proc.Exited += (_, _) => {
|
||||||
if (GlobalVars.UnexpectedExit) {
|
if (GlobalVars.UnexpectedExit) {
|
||||||
|
proc = null;
|
||||||
Console.WriteLine("游戏进程异常退出");
|
Console.WriteLine("游戏进程异常退出");
|
||||||
Environment.Exit(114514);
|
Environment.Exit(114514);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using YaeAchievement.AppCenterSDK;
|
||||||
|
|
||||||
namespace YaeAchievement.Win32;
|
namespace YaeAchievement.Win32;
|
||||||
|
|
||||||
public static class Extensions {
|
public static class Extensions {
|
||||||
|
|
||||||
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
||||||
Console.WriteLine($"{msg}: {ex.Message}");
|
Logger.Error($"{msg}: {ex.Message}");
|
||||||
if (GlobalVars.Verbose) {
|
AppCenter.TrackCrash(ex, false);
|
||||||
Console.WriteLine(ex.StackTrace);
|
|
||||||
}
|
|
||||||
return ex.NativeErrorCode;
|
return ex.NativeErrorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,4 +138,13 @@ public static class Native {
|
|||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||||
|
|
||||||
|
[DllImport("gdi32.dll")]
|
||||||
|
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern IntPtr GetDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hdc);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user