diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index 47df058f..0d1f4619 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -9,7 +9,6 @@ using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Threading; using Snap.Hutao.Extension; -using Snap.Hutao.Service.AppCenter; using Snap.Hutao.Service.Metadata; using System.Diagnostics; using Windows.Storage; @@ -29,13 +28,13 @@ public partial class App : Application /// /// 日志器 /// App Center - public App(ILogger logger, AppCenter appCenter) + public App(ILogger logger) { // load app resource InitializeComponent(); this.logger = logger; - _ = new ExceptionRecorder(this, logger, appCenter); + _ = new ExceptionRecorder(this, logger); } /// @@ -61,10 +60,6 @@ public partial class App : Application .ImplictAs()? .InitializeInternalAsync() .SafeForget(logger); - - Ioc.Default - .GetRequiredService() - .Initialize(); } else { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs index 3758457d..39c3fedb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs @@ -18,5 +18,5 @@ internal interface ISupportAsyncInitialization /// /// 取消令牌 /// 初始化任务 - ValueTask InitializeAsync(CancellationToken token = default); + ValueTask InitializeAsync(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index 4f049ed9..41a38eb7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -53,9 +53,9 @@ internal static class CoreEnvironment public static readonly string HoyolabDeviceId; /// - /// AppCenter 设备Id + /// 胡桃设备Id /// - public static readonly string AppCenterDeviceId; + public static readonly string HutaoDeviceId; /// /// 默认的Json序列化选项 @@ -78,7 +78,7 @@ internal static class CoreEnvironment // simply assign a random guid HoyolabDeviceId = Guid.NewGuid().ToString(); - AppCenterDeviceId = GetUniqueUserID(); + HutaoDeviceId = GetUniqueUserID(); } private static string GetUniqueUserID() diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Exception/ExceptionRecorder.cs b/src/Snap.Hutao/Snap.Hutao/Core/Exception/ExceptionRecorder.cs index f9fe94b7..79590bdf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Exception/ExceptionRecorder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Exception/ExceptionRecorder.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml; using Snap.Hutao.Core.Logging; -using Snap.Hutao.Service.AppCenter; +using Snap.Hutao.Web.Hutao; namespace Snap.Hutao.Core.Exception; @@ -13,26 +13,24 @@ namespace Snap.Hutao.Core.Exception; internal class ExceptionRecorder { private readonly ILogger logger; - private readonly AppCenter appCenter; /// /// 构造一个新的异常记录器 /// /// 应用程序 /// 日志器 - /// App Center - public ExceptionRecorder(Application application, ILogger logger, AppCenter appCenter) + public ExceptionRecorder(Application application, ILogger logger) { this.logger = logger; - this.appCenter = appCenter; application.UnhandledException += OnAppUnhandledException; application.DebugSettings.BindingFailed += OnXamlBindingFailed; } + [SuppressMessage("", "VSTHRD002")] private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { - appCenter.TrackCrash(e.Exception); + Ioc.Default.GetRequiredService().UploadLogAsync(e.Exception).GetAwaiter().GetResult(); logger.LogError(EventIds.UnhandledException, e.Exception, "未经处理的异常"); foreach (ILoggerProvider provider in Ioc.Default.GetRequiredService>()) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/CancellationTokenTaskCompletionSource.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/CancellationTokenTaskCompletionSource.cs new file mode 100644 index 00000000..80afb439 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/CancellationTokenTaskCompletionSource.cs @@ -0,0 +1,46 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Threading; + +/// +/// Holds the task for a cancellation token, as well as the token registration. The registration is disposed when this instance is disposed. +/// +/// 包装类型 +public sealed class CancellationTokenTaskCompletionSource : IDisposable +{ + /// + /// The cancellation token registration, if any. This is null if the registration was not necessary. + /// + private readonly IDisposable? registration; + + /// + /// Creates a task for the specified cancellation token, registering with the token if necessary. + /// + /// The cancellation token to observe. + public CancellationTokenTaskCompletionSource(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + Task = Task.CompletedTask; + return; + } + + var tcs = new TaskCompletionSource(); + registration = cancellationToken.Register(() => tcs.TrySetResult(), useSynchronizationContext: false); + Task = tcs.Task; + } + + /// + /// Gets the task for the source cancellation token. + /// + public Task Task { get; private set; } + + /// + /// Disposes the cancellation token registration, if any. Note that this may cause to never complete. + /// + public void Dispose() + { + registration?.Dispose(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs index 93924cfb..1bb621d1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs @@ -28,4 +28,4 @@ internal class ConcurrentCancellationTokenSource return waitingItems.GetOrAdd(item, new CancellationTokenSource()).Token; } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs index ef5bb0b5..48c5acfe 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs @@ -14,8 +14,8 @@ namespace Snap.Hutao.Core; /// internal abstract class WebView2Helper { - private static bool hasEverDetected = false; - private static bool isSupported = false; + private static bool hasEverDetected; + private static bool isSupported; private static string version = "未检测到 WebView2 运行时"; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs index 324c8dce..02af6bb5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs @@ -101,7 +101,7 @@ public class SystemBackdrop private class DispatcherQueueHelper { - private object? dispatcherQueueController = null; + private object? dispatcherQueueController; /// /// 确保系统调度队列控制器存在 diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs index 2ffc73b6..9ecedc68 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs @@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.Input; using Snap.Hutao.Core.Logging; using Snap.Hutao.Factory.Abstraction; -using Snap.Hutao.Service.AppCenter; +using Snap.Hutao.Web.Hutao; namespace Snap.Hutao.Factory; @@ -13,17 +13,14 @@ namespace Snap.Hutao.Factory; internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory { private readonly ILogger logger; - private readonly AppCenter appCenter; /// /// 构造一个新的异步命令工厂 /// /// 日志器 - /// App Center - public AsyncRelayCommandFactory(ILogger logger, AppCenter appCenter) + public AsyncRelayCommandFactory(ILogger logger) { this.logger = logger; - this.appCenter = appCenter; } /// @@ -86,6 +83,7 @@ internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory return command; } + [SuppressMessage("", "VSTHRD002")] private void ReportException(IAsyncRelayCommand command) { command.PropertyChanged += (sender, args) => @@ -98,7 +96,7 @@ internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory { Exception baseException = exception.GetBaseException(); logger.LogError(EventIds.AsyncCommandException, baseException, "{name} Exception", nameof(AsyncRelayCommand)); - appCenter.TrackError(exception); + Ioc.Default.GetRequiredService().UploadLogAsync(baseException).GetAwaiter().GetResult(); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Reliquary.cs index dc099768..efc6d8cb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Reliquary.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/AvatarProperty/Reliquary.cs @@ -8,12 +8,6 @@ namespace Snap.Hutao.Model.Binding.AvatarProperty; /// public class Reliquary : EquipBase { - /// - /// 副属性列表 - /// - [Obsolete] - public List SubProperties { get; set; } = default!; - /// /// 初始词条 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs index 89935dc9..7fe8588d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAF.cs @@ -31,6 +31,6 @@ public class UIAF /// 当前UIAF对象是否受支持 public bool IsCurrentVersionSupported() { - return SupportedVersion.Contains(Info.UIAFVersion ?? string.Empty); + return SupportedVersion.Contains(Info?.UIAFVersion ?? string.Empty); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs index aa0c305d..3e8b7612 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/AvatarIds.cs @@ -14,7 +14,9 @@ public static class AvatarIds public static readonly AvatarId Ayaka = 10000002; public static readonly AvatarId Qin = 10000003; + public static readonly AvatarId PlayerBoy = 10000005; public static readonly AvatarId Lisa = 10000006; + public static readonly AvatarId PlayerGirl = 10000007; public static readonly AvatarId Barbara = 10000014; public static readonly AvatarId Kaeya = 10000015; @@ -75,4 +77,16 @@ public static class AvatarIds public static readonly AvatarId Candace = 10000072; public static readonly AvatarId Nahida = 10000073; public static readonly AvatarId Layla = 10000074; + public static readonly AvatarId Wanderer = 10000075; + public static readonly AvatarId Faruzan = 10000076; + + /// + /// 检查该角色是否为主角 + /// + /// 角色Id + /// 角色是否为主角 + public static bool IsPlayer(AvatarId avatarId) + { + return avatarId == PlayerBoy || avatarId == PlayerGirl; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index ce720832..befe664e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -10,7 +10,7 @@ + Version="1.1.20.0" /> 胡桃 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/AppCenter.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/AppCenter.cs deleted file mode 100644 index 042cebd6..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/AppCenter.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core; -using Snap.Hutao.Core.Threading; -using Snap.Hutao.Service.AppCenter.Model; -using Snap.Hutao.Service.AppCenter.Model.Log; -using Snap.Hutao.Web.Hoyolab; -using System.Net.Http; - -namespace Snap.Hutao.Service.AppCenter; - -[SuppressMessage("", "SA1600")] -[Injection(InjectAs.Singleton)] -public sealed class AppCenter : IDisposable -{ - private const string AppSecret = "de5bfc48-17fc-47ee-8e7e-dee7dc59d554"; - private const string API = "https://in.appcenter.ms/logs?api-version=1.0.0"; - - private readonly TaskCompletionSource uploadTaskCompletionSource = new(); - private readonly CancellationTokenSource uploadTaskCancllationTokenSource = new(); - private readonly HttpClient httpClient; - private readonly List queue; - private readonly Device deviceInfo; - private readonly JsonSerializerOptions options; - - private Guid sessionID; - - public AppCenter() - { - options = new(CoreEnvironment.JsonOptions); - options.Converters.Add(new LogConverter()); - - httpClient = new() { DefaultRequestHeaders = { { "Install-ID", CoreEnvironment.AppCenterDeviceId }, { "App-Secret", AppSecret } } }; - queue = new List(); - deviceInfo = new Device(); - Task.Run(async () => - { - while (!uploadTaskCancllationTokenSource.Token.IsCancellationRequested) - { - await UploadAsync().ConfigureAwait(false); - await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false); - } - - uploadTaskCompletionSource.TrySetResult(); - }).SafeForget(); - } - - public async Task UploadAsync() - { - if (queue.Count == 0) - { - return; - } - - string? uploadStatus = null; - do - { - queue.ForEach(log => log.Status = LogStatus.Uploading); - LogContainer container = new(queue); - - LogUploadResult? response = await httpClient - .TryCatchPostAsJsonAsync(API, container, options) - .ConfigureAwait(false); - uploadStatus = response?.Status; - } - while (uploadStatus != "Success"); - - queue.RemoveAll(log => log.Status == LogStatus.Uploading); - } - - public void Initialize() - { - sessionID = Guid.NewGuid(); - queue.Add(new StartServiceLog("Analytics", "Crashes").Initialize(sessionID, deviceInfo)); - queue.Add(new StartSessionLog().Initialize(sessionID, deviceInfo).Initialize(sessionID, deviceInfo)); - } - - public void TrackCrash(Exception exception, bool isFatal = true) - { - queue.Add(new ManagedErrorLog(exception, isFatal).Initialize(sessionID, deviceInfo)); - } - - public void TrackError(Exception exception) - { - queue.Add(new HandledErrorLog(exception).Initialize(sessionID, deviceInfo)); - } - - [SuppressMessage("", "VSTHRD002")] - public void Dispose() - { - uploadTaskCancllationTokenSource.Cancel(); - uploadTaskCompletionSource.Task.GetAwaiter().GetResult(); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/DeviceHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/DeviceHelper.cs deleted file mode 100644 index a45b48c0..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/DeviceHelper.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.UI.Windowing; -using Microsoft.Win32; -using Windows.Graphics; - -namespace Snap.Hutao.Service.AppCenter; - -/// -/// 设备帮助类 -/// -[SuppressMessage("", "SA1600")] -public static class DeviceHelper -{ - private static readonly RegistryKey? BiosKey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS"); - private static readonly RegistryKey? GeoKey = Registry.CurrentUser.OpenSubKey("Control Panel\\International\\Geo"); - private static readonly RegistryKey? CurrentVersionKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); - - public static string? GetOem() - { - string? oem = BiosKey?.GetValue("SystemManufacturer") as string; - return oem == "System manufacturer" ? null : oem; - } - - public static string? GetModel() - { - string? model = BiosKey?.GetValue("SystemProductName") as string; - return model == "System Product Name" ? null : model; - } - - public static string GetScreenSize() - { - RectInt32 screen = DisplayArea.Primary.OuterBounds; - return $"{screen.Width}x{screen.Height}"; - } - - public static string? GetCountry() - { - return GeoKey?.GetValue("Name") as string; - } - - public static string GetSystemVersion() - { - object? majorVersion = CurrentVersionKey?.GetValue("CurrentMajorVersionNumber"); - if (majorVersion != null) - { - object? minorVersion = CurrentVersionKey?.GetValue("CurrentMinorVersionNumber", "0"); - object? buildNumber = CurrentVersionKey?.GetValue("CurrentBuildNumber", "0"); - return $"{majorVersion}.{minorVersion}.{buildNumber}"; - } - else - { - object? version = CurrentVersionKey?.GetValue("CurrentVersion", "0.0"); - object? buildNumber = CurrentVersionKey?.GetValue("CurrentBuild", "0"); - return $"{version}.{buildNumber}"; - } - } - - public static int GetSystemBuild() - { - return (int)(CurrentVersionKey?.GetValue("UBR") ?? 0); - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/AppCenterException.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/AppCenterException.cs deleted file mode 100644 index 97f30077..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/AppCenterException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model; - -[SuppressMessage("", "SA1600")] -public class AppCenterException -{ - [JsonPropertyName("type")] - public string Type { get; set; } = "UnknownType"; - - [JsonPropertyName("message")] - public string? Message { get; set; } - - [JsonPropertyName("stackTrace")] - public string? StackTrace { get; set; } - - [JsonPropertyName("innerExceptions")] - public List? InnerExceptions { get; set; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Device.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Device.cs deleted file mode 100644 index e92c2880..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Device.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core; -using System.Globalization; - -namespace Snap.Hutao.Service.AppCenter.Model; - -[SuppressMessage("", "SA1600")] -public class Device -{ - [JsonPropertyName("sdkName")] - public string SdkName { get; set; } = "appcenter.winui"; - - [JsonPropertyName("sdkVersion")] - public string SdkVersion { get; set; } = "4.5.0"; - - [JsonPropertyName("osName")] - public string OsName { get; set; } = "WINDOWS"; - - [JsonPropertyName("osVersion")] - public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion(); - - [JsonPropertyName("osBuild")] - public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}"; - - [JsonPropertyName("model")] - public string? Model { get; set; } = DeviceHelper.GetModel(); - - [JsonPropertyName("oemName")] - public string? OemName { get; set; } = DeviceHelper.GetOem(); - - [JsonPropertyName("screenSize")] - public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize(); - - [JsonPropertyName("carrierCountry")] - public string Country { get; set; } = DeviceHelper.GetCountry() ?? "CN"; - - [JsonPropertyName("locale")] - public string Locale { get; set; } = CultureInfo.CurrentCulture.Name; - - [JsonPropertyName("timeZoneOffset")] - public int TimeZoneOffset { get; set; } = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes; - - [JsonPropertyName("appVersion")] - public string AppVersion { get; set; } = CoreEnvironment.Version.ToString(); - - [JsonPropertyName("appBuild")] - public string AppBuild { get; set; } = CoreEnvironment.Version.ToString(); - - [JsonPropertyName("appNamespace")] - public string AppNamespace { get; set; } = typeof(App).Namespace ?? string.Empty; -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/EventLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/EventLog.cs deleted file mode 100644 index dd5354ed..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/EventLog.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class EventLog : PropertiesLog -{ - public EventLog(string name) - { - Name = name; - } - - [JsonPropertyName("type")] - public override string Type { get => "event"; } - - [JsonPropertyName("id")] - public Guid Id { get; set; } = Guid.NewGuid(); - - [JsonPropertyName("name")] - public string Name { get; set; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/HandledErrorLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/HandledErrorLog.cs deleted file mode 100644 index 9ab5a763..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/HandledErrorLog.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class HandledErrorLog : PropertiesLog -{ - public HandledErrorLog(Exception exception) - { - Id = Guid.NewGuid(); - Exception = LogHelper.Create(exception); - } - - [JsonPropertyName("id")] - public Guid? Id { get; set; } - - [JsonPropertyName("exception")] - public AppCenterException Exception { get; set; } - - [JsonPropertyName("type")] - public override string Type { get => "handledError"; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/Log.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/Log.cs deleted file mode 100644 index 5375dc75..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/Log.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public abstract class Log -{ - [JsonIgnore] - public LogStatus Status { get; set; } = LogStatus.Pending; - - [JsonPropertyName("type")] - public abstract string Type { get; } - - [JsonPropertyName("sid")] - public Guid Session { get; set; } - - [JsonPropertyName("timestamp")] - public string Timestamp { get; set; } = DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"); - - [JsonPropertyName("device")] - public Device Device { get; set; } = default!; -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogContainer.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogContainer.cs deleted file mode 100644 index dd4d8c02..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogContainer.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class LogContainer -{ - public LogContainer(IEnumerable logs) - { - Logs = logs; - } - - [JsonPropertyName("logs")] - public IEnumerable Logs { get; set; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogConverter.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogConverter.cs deleted file mode 100644 index ada58c59..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogConverter.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -/// -/// 日志转换器 -/// -public class LogConverter : JsonConverter -{ - /// - public override Log? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - throw Must.NeverHappen(); - } - - /// - public override void Write(Utf8JsonWriter writer, Log value, JsonSerializerOptions options) - { - JsonSerializer.Serialize(writer, value, value.GetType(), options); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogHelper.cs deleted file mode 100644 index 2f281056..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogHelper.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using System.Diagnostics; - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public static class LogHelper -{ - public static T Initialize(this T log, Guid sid, Device device) - where T : Log - { - log.Session = sid; - log.Device = device; - - return log; - } - - public static AppCenterException Create(Exception exception) - { - AppCenterException current = new() - { - Type = exception.GetType().ToString(), - Message = exception.Message, - StackTrace = exception.StackTrace, - }; - - if (exception is AggregateException aggregateException) - { - if (aggregateException.InnerExceptions.Count != 0) - { - current.InnerExceptions = new(); - foreach (var innerException in aggregateException.InnerExceptions) - { - current.InnerExceptions.Add(Create(innerException)); - } - } - } - - if (exception.InnerException != null) - { - current.InnerExceptions ??= new(); - current.InnerExceptions.Add(Create(exception.InnerException)); - } - - StackTrace stackTrace = new(exception, true); - StackFrame[] frames = stackTrace.GetFrames(); - - if (frames.Length > 0 && frames[0].HasNativeImage()) - { - } - - return current; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogStatus.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogStatus.cs deleted file mode 100644 index 5f570a90..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/LogStatus.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -[SuppressMessage("", "SA1602")] -public enum LogStatus -{ - Pending, - Uploading, - Uploaded, -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/ManagedErrorLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/ManagedErrorLog.cs deleted file mode 100644 index f071b88d..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/ManagedErrorLog.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core; -using System.Diagnostics; - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class ManagedErrorLog : Log -{ - public ManagedErrorLog(Exception exception, bool fatal = true) - { - var p = Process.GetCurrentProcess(); - Id = Guid.NewGuid(); - Fatal = fatal; - UserId = CoreEnvironment.AppCenterDeviceId; - ProcessId = p.Id; - Exception = LogHelper.Create(exception); - ProcessName = p.ProcessName; - Architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); - AppLaunchTimestamp = p.StartTime.ToUniversalTime(); - } - - [JsonPropertyName("id")] - public Guid Id { get; set; } - - [JsonPropertyName("userId")] - public string? UserId { get; set; } - - [JsonPropertyName("processId")] - public int ProcessId { get; set; } - - [JsonPropertyName("processName")] - public string ProcessName { get; set; } - - [JsonPropertyName("fatal")] - public bool Fatal { get; set; } - - [JsonPropertyName("appLaunchTimestamp")] - public DateTime? AppLaunchTimestamp { get; set; } - - [JsonPropertyName("architecture")] - public string? Architecture { get; set; } - - [JsonPropertyName("exception")] - public AppCenterException Exception { get; set; } - - [JsonPropertyName("type")] - public override string Type { get => "managedError"; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PageLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PageLog.cs deleted file mode 100644 index a3409652..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PageLog.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class PageLog : PropertiesLog -{ - public PageLog(string name) - { - Name = name; - } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("type")] - public override string Type { get => "page"; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PropertiesLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PropertiesLog.cs deleted file mode 100644 index cc4eb32b..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/PropertiesLog.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public abstract class PropertiesLog : Log -{ - [JsonPropertyName("properties")] - public IDictionary Properties { get; set; } = new Dictionary(); -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartServiceLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartServiceLog.cs deleted file mode 100644 index 06370d89..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartServiceLog.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class StartServiceLog : Log -{ - public StartServiceLog(params string[] services) - { - Services = services; - } - - [JsonPropertyName("services")] - public string[] Services { get; set; } - - [JsonPropertyName("type")] - public override string Type { get => "startService"; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartSessionLog.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartSessionLog.cs deleted file mode 100644 index c690f38a..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/Log/StartSessionLog.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model.Log; - -[SuppressMessage("", "SA1600")] -public class StartSessionLog : Log -{ - [JsonPropertyName("type")] - public override string Type { get => "startSession"; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/LogUploadResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/LogUploadResult.cs deleted file mode 100644 index 95d6aaac..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/AppCenter/Model/LogUploadResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Service.AppCenter.Model; - -[SuppressMessage("", "SA1600")] -public class LogUploadResult -{ - [JsonPropertyName("status")] - public string Status { get; set; } = null!; - - [JsonPropertyName("validDiagnosticsIds")] - public List ValidDiagnosticsIds { get; set; } = null!; - - [JsonPropertyName("throttledDiagnosticsIds")] - public List ThrottledDiagnosticsIds { get; set; } = null!; - - [JsonPropertyName("correlationId")] - public Guid CorrelationId { get; set; } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs index 87b24585..70b77c42 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs @@ -2,10 +2,12 @@ // Licensed under the MIT license. using Snap.Hutao.Context.Database; +using Snap.Hutao.Core.Database; using Snap.Hutao.Core.Diagnostics; using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Threading; using Snap.Hutao.Model.Binding.AvatarProperty; +using Snap.Hutao.Model.Metadata; using Snap.Hutao.Service.AvatarInfo.Factory; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Web.Enka; @@ -51,11 +53,14 @@ internal class AvatarInfoService : IAvatarInfoService /// public async Task> GetSummaryAsync(PlayerUid uid, RefreshOption refreshOption, CancellationToken token = default) { - if (await metadataService.InitializeAsync(token).ConfigureAwait(false)) + if (await metadataService.InitializeAsync().ConfigureAwait(false)) { + token.ThrowIfCancellationRequested(); + if (HasOption(refreshOption, RefreshOption.RequestFromAPI)) { EnkaResponse? resp = await GetEnkaResponseAsync(uid, token).ConfigureAwait(false); + token.ThrowIfCancellationRequested(); if (resp == null) { return new(RefreshResult.APIUnavailable, null); @@ -67,7 +72,8 @@ internal class AvatarInfoService : IAvatarInfoService ? UpdateDbAvatarInfo(uid.Value, resp.AvatarInfoList) : resp.AvatarInfoList; - Summary summary = await GetSummaryCoreAsync(resp.PlayerInfo, list).ConfigureAwait(false); + Summary summary = await GetSummaryCoreAsync(resp.PlayerInfo, list, token).ConfigureAwait(false); + token.ThrowIfCancellationRequested(); return new(RefreshResult.Ok, summary); } else @@ -79,7 +85,8 @@ internal class AvatarInfoService : IAvatarInfoService { PlayerInfo info = PlayerInfo.CreateEmpty(uid.Value); - Summary summary = await GetSummaryCoreAsync(info, GetDbAvatarInfos(uid.Value)).ConfigureAwait(false); + Summary summary = await GetSummaryCoreAsync(info, GetDbAvatarInfos(uid.Value), token).ConfigureAwait(false); + token.ThrowIfCancellationRequested(); return new(RefreshResult.Ok, summary); } } @@ -94,10 +101,10 @@ internal class AvatarInfoService : IAvatarInfoService return (source & define) == define; } - private async Task GetSummaryCoreAsync(PlayerInfo info, IEnumerable avatarInfos) + private async Task GetSummaryCoreAsync(PlayerInfo info, IEnumerable avatarInfos, CancellationToken token) { ValueStopwatch stopwatch = ValueStopwatch.StartNew(); - Summary summary = await summaryFactory.CreateAsync(info, avatarInfos).ConfigureAwait(false); + Summary summary = await summaryFactory.CreateAsync(info, avatarInfos, token).ConfigureAwait(false); logger.LogInformation(EventIds.AvatarInfoGeneration, "AvatarInfoSummary Generation toke {time} ms.", stopwatch.GetElapsedTime().TotalMilliseconds); return summary; @@ -117,7 +124,7 @@ internal class AvatarInfoService : IAvatarInfoService foreach (Web.Enka.Model.AvatarInfo webInfo in webInfos) { - if (webInfo.AvatarId == 10000005 || webInfo.AvatarId == 10000007) + if (AvatarIds.IsPlayer(webInfo.AvatarId)) { continue; } @@ -127,28 +134,31 @@ internal class AvatarInfoService : IAvatarInfoService if (entity == null) { entity = Model.Entity.AvatarInfo.Create(uid, webInfo); - appDbContext.Add(entity); + appDbContext.AvatarInfos.AddAndSave(entity); } else { entity.Info = webInfo; - appDbContext.Update(entity); + appDbContext.AvatarInfos.UpdateAndSave(entity); } } - appDbContext.SaveChanges(); - return GetDbAvatarInfos(uid); } private List GetDbAvatarInfos(string uid) { - return appDbContext.AvatarInfos - .Where(i => i.Uid == uid) - .Select(i => i.Info) - - // .AsEnumerable() - // .OrderByDescending(i => i.AvatarId) - .ToList(); + try + { + return appDbContext.AvatarInfos + .Where(i => i.Uid == uid) + .Select(i => i.Info) + .ToList(); + } + catch (ObjectDisposedException) + { + // appDbContext can be disposed unexpectedly + return new(); + } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs index d7c8d20b..f1fb0fe4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs @@ -16,6 +16,7 @@ internal interface ISummaryFactory /// /// 玩家信息 /// 角色列表 + /// 取消令牌 /// 简述对象 - Task CreateAsync(PlayerInfo playerInfo, IEnumerable avatarInfos); + Task CreateAsync(PlayerInfo playerInfo, IEnumerable avatarInfos, CancellationToken token); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs index 295bd025..a6ab5157 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs @@ -31,15 +31,15 @@ internal class SummaryFactory : ISummaryFactory } /// - public async Task CreateAsync(ModelPlayerInfo playerInfo, IEnumerable avatarInfos) + public async Task CreateAsync(ModelPlayerInfo playerInfo, IEnumerable avatarInfos, CancellationToken token) { - Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); - Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); - Dictionary idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync().ConfigureAwait(false); - Dictionary idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync().ConfigureAwait(false); + Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false); + Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false); + Dictionary idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false); + Dictionary idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false); - List reliqueryLevels = await metadataService.GetReliquaryLevelsAsync().ConfigureAwait(false); - List reliquaries = await metadataService.GetReliquariesAsync().ConfigureAwait(false); + List reliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false); + List reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false); SummaryFactoryImplementation inner = new(idAvatarMap, idWeaponMap, idRelicMainPropMap, idReliquaryAffixMap, reliqueryLevels, reliquaries); return inner.Create(playerInfo, avatarInfos); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs index 6abd051e..782698b4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs @@ -3,6 +3,7 @@ using Snap.Hutao.Model.Binding.AvatarProperty; using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Reliquary; using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary; @@ -60,7 +61,7 @@ internal class SummaryFactoryImplementation return new() { Player = SummaryHelper.CreatePlayer(playerInfo), - Avatars = avatarInfos.Select(a => + Avatars = avatarInfos.Where(a => !AvatarIds.IsPlayer(a.AvatarId)).Select(a => { SummaryAvatarFactory summaryAvatarFactory = new( idAvatarMap, diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs index 5fc538ce..2dc38fe6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryHelper.cs @@ -209,7 +209,8 @@ internal static class SummaryHelper (2, 0) => 100, (2, 1) => 80, - _ => throw Must.NeverHappen(), + // TODO: Not quite sure why can we hit this branch. + _ => 0, }; } @@ -226,16 +227,6 @@ internal static class SummaryHelper return 100 * ((cr * 2) + cd); } - private static string FormatValue(FormatMethod method, double value) - { - return method switch - { - FormatMethod.Integer => Math.Round((double)value, MidpointRounding.AwayFromZero).ToString(), - FormatMethod.Percent => string.Format("{0:P1}", value), - _ => value.ToString(), - }; - } - private static FightProperty GetBonusFightProperty(IDictionary fightPropMap) { if (fightPropMap.ContainsKey(FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index da78ae13..22d46a20 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -123,15 +123,15 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization } /// - public async ValueTask InitializeAsync(CancellationToken token = default) + public async ValueTask InitializeAsync() { - if (await metadataService.InitializeAsync(token).ConfigureAwait(false)) + if (await metadataService.InitializeAsync().ConfigureAwait(false)) { - nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false); - nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false); + nameAvatarMap = await metadataService.GetNameToAvatarMapAsync().ConfigureAwait(false); + nameWeaponMap = await metadataService.GetNameToWeaponMapAsync().ConfigureAwait(false); - idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false); - idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false); + idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); + idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); IsInitialized = true; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs index 9b2a0565..8cf2027b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs @@ -58,7 +58,7 @@ internal interface IGachaLogService /// /// 取消令牌 /// 是否初始化成功 - ValueTask InitializeAsync(CancellationToken token = default); + ValueTask InitializeAsync(); /// /// 刷新祈愿记录 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs index 75898956..b2ba3b74 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Snap.Hutao.Context.Database; @@ -59,7 +60,7 @@ internal class GameService : IGameService { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out bool added); + SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, string.Empty), out bool added); // Cannot find in setting if (added) @@ -89,6 +90,11 @@ internal class GameService : IGameService } } + if (entry.Value == null) + { + return new(false, null!); + } + // Set cache and return. string path = memoryCache.Set(GamePathKey, Must.NotNull(entry.Value!)); return new(true, path); @@ -139,7 +145,7 @@ internal class GameService : IGameService public MultiChannel GetMultiChannel() { string gamePath = GetGamePathSkipLocator(); - string configPath = Path.Combine(Path.GetDirectoryName(gamePath)!, ConfigFile); + string configPath = Path.Combine(Path.GetDirectoryName(gamePath) ?? string.Empty, ConfigFile); using (FileStream stream = File.OpenRead(configPath)) { @@ -221,6 +227,11 @@ internal class GameService : IGameService string gamePath = GetGamePathSkipLocator(); + if (string.IsNullOrWhiteSpace(gamePath)) + { + return; + } + // https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html string commandLine = new CommandLineBuilder() .AppendIf("-popupwindow", configuration.IsBorderless) @@ -317,14 +328,6 @@ internal class GameService : IGameService using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - IQueryable oldAccounts = appDbContext.GameAccounts.Where(a => a.AttachUid == uid); - - foreach (GameAccount account in oldAccounts) - { - account.UpdateAttachUid(null); - appDbContext.GameAccounts.UpdateAndSave(account); - } - gameAccount.UpdateAttachUid(uid); appDbContext.GameAccounts.UpdateAndSave(gameAccount); } @@ -358,7 +361,14 @@ internal class GameService : IGameService using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - appDbContext.GameAccounts.RemoveAndSave(gameAccount); + try + { + appDbContext.GameAccounts.RemoveAndSave(gameAccount); + } + catch (DbUpdateConcurrencyException) + { + // This gameAccount has already been deleted. + } } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs index 69b4a44d..d75aa184 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs @@ -46,6 +46,8 @@ internal class ManualGameLocator : IGameLocator picker.FileTypeFilter.Add(".exe"); picker.SuggestedStartLocation = PickerLocationId.ComputerFolder; + // System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component. + // Not sure what's going on here. if (await picker.PickSingleFileAsync() is StorageFile file) { string path = file.Path; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs index 4884b33e..fea53c96 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Binding.Hutao; +using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Service.Metadata; @@ -111,8 +112,8 @@ internal class HutaoCache : IHutaoCache Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); idAvatarExtendedMap = new(idAvatarMap) { - [10000005] = new() { Name = "旅行者", Icon = "UI_AvatarIcon_PlayerBoy", Quality = Model.Intrinsic.ItemQuality.QUALITY_ORANGE }, - [10000007] = new() { Name = "旅行者", Icon = "UI_AvatarIcon_PlayerGirl", Quality = Model.Intrinsic.ItemQuality.QUALITY_ORANGE }, + [AvatarIds.PlayerBoy] = new() { Name = "旅行者", Icon = "UI_AvatarIcon_PlayerBoy", Quality = Model.Intrinsic.ItemQuality.QUALITY_ORANGE }, + [AvatarIds.PlayerGirl] = new() { Name = "旅行者", Icon = "UI_AvatarIcon_PlayerGirl", Quality = Model.Intrinsic.ItemQuality.QUALITY_ORANGE }, }; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs index 6e5fb619..a8bbb426 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs @@ -18,9 +18,8 @@ internal interface IMetadataService /// /// 异步初始化服务,尝试更新元数据 /// - /// 取消令牌 /// 初始化是否成功 - ValueTask InitializeAsync(CancellationToken token = default); + ValueTask InitializeAsync(); /// /// 异步获取成就列表 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs index 52ca3590..ddc2aee3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs @@ -69,7 +69,7 @@ internal partial class MetadataService : IMetadataService, IMetadataInitializer, public bool IsInitialized { get => isInitialized; private set => isInitialized = value; } /// - public async ValueTask InitializeAsync(CancellationToken token = default) + public async ValueTask InitializeAsync() { await initializeCompletionSource.Task.ConfigureAwait(false); return IsInitialized; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index c6b52f4f..ec122468 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -161,6 +161,7 @@ internal class UserService : IUserService if (cookie.ContainsSToken()) { // insert stoken + await ThreadHelper.SwitchToMainThreadAsync(); userWithSameUid.UpdateSToken(uid, cookie); appDbContext.Users.UpdateAndSave(userWithSameUid.Entity); @@ -169,6 +170,7 @@ internal class UserService : IUserService if (cookie.ContainsLTokenAndCookieToken()) { + await ThreadHelper.SwitchToMainThreadAsync(); userWithSameUid.Cookie = cookie; appDbContext.Users.UpdateAndSave(userWithSameUid.Entity); diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml index a268a968..70c7cdfa 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml @@ -24,7 +24,7 @@ 0 - + + Description="Github 上反馈的问题会优先处理"> + NavigateUri="https://hut.ao/statements/bug-report.html"/> @@ -75,7 +75,11 @@ IsOpen="True" CornerRadius="0,0,4,4"> - public ICommand SetGamePathCommand { get; } + /// + /// 调试异常命令 + /// + public ICommand DebugExceptionCommand { get; } + private async Task SetGamePathAsync() { IGameLocator locator = Ioc.Default.GetRequiredService>() @@ -103,4 +111,11 @@ internal class SettingViewModel : ObservableObject GamePath = path; } } + + private void DebugThrowException() + { +#if DEBUG + throw new InvalidOperationException("测试用异常"); +#endif + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs index 27eb5682..123403b0 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs @@ -161,6 +161,8 @@ internal class WikiAvatarViewModel : ObservableObject await CombineWithAvatarCollocationsAsync(sorted).ConfigureAwait(false); await ThreadHelper.SwitchToMainThreadAsync(); + + // RPC_E_WRONG_THREAD ? Avatars = new AdvancedCollectionView(sorted, true); Selected = Avatars.Cast().FirstOrDefault(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs index 28477dff..e8159f3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs @@ -8,6 +8,8 @@ namespace Snap.Hutao.Web.Hoyolab; /// public struct PlayerUid { + [SuppressMessage("", "CA1805")] + [SuppressMessage("", "IDE0079")] private string? region = default; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs index bd3b5c1e..4699d5e8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs @@ -202,4 +202,4 @@ internal class HomaClient { return httpClient.TryCatchPostAsJsonAsync>($"{HutaoAPI}/Record/Upload", playerRecord, options, logger, token); } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs new file mode 100644 index 00000000..d6b3af74 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs @@ -0,0 +1,52 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Web.Hoyolab; +using Snap.Hutao.Web.Hutao.Log; +using Snap.Hutao.Web.Response; +using System.Net.Http; + +namespace Snap.Hutao.Web.Hutao; + +/// +/// 胡桃日志客户端 +/// +[HttpClient(HttpClientConfigration.Default)] +internal class HomaClient2 +{ + private const string HutaoAPI = "https://homa.snapgenshin.com"; + private readonly HttpClient httpClient; + private readonly JsonSerializerOptions options; + + /// + /// 构造一个新的胡桃日志客户端 + /// + /// Http客户端 + /// Json序列化选项 + public HomaClient2(HttpClient httpClient, JsonSerializerOptions options) + { + this.httpClient = httpClient; + this.options = options; + } + + /// + /// 上传日志 + /// + /// 异常 + /// 任务 + public async Task UploadLogAsync(Exception exception) + { + HutaoLog log = new() + { + Id = Core.CoreEnvironment.HutaoDeviceId, + Time = DateTimeOffset.Now.ToUnixTimeMilliseconds(), + Info = exception.ToString(), + }; + + Response? a = await httpClient + .TryCatchPostAsJsonAsync>($"{HutaoAPI}/HutaoLog/Upload", log, options) + .ConfigureAwait(false); + return a?.Data; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Log/HutaoLog.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Log/HutaoLog.cs new file mode 100644 index 00000000..b3b4dede --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Log/HutaoLog.cs @@ -0,0 +1,25 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hutao.Log; + +/// +/// 胡桃日志 +/// +public class HutaoLog +{ + /// + /// 设备Id + /// + public string Id { get; set; } = default!; + + /// + /// 崩溃时间 + /// + public long Time { get; set; } + + /// + /// 错误信息 + /// + public string Info { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleRank.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleRank.cs index cfadd411..07ca511d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleRank.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleRank.cs @@ -14,7 +14,7 @@ public class SimpleRank /// 构造一个新的数值 /// /// 排行 - public SimpleRank(Rank rank) + private SimpleRank(Rank rank) { AvatarId = rank.AvatarId; Value = rank.Value; @@ -29,4 +29,19 @@ public class SimpleRank /// 值 /// public int Value { get; set; } + + /// + /// 构造一个新的简单数值 + /// + /// 排行 + /// 新的简单数值 + public static SimpleRank? FromRank(Rank? rank) + { + if (rank == null) + { + return null; + } + + return new SimpleRank(rank); + } } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleSpiralAbyss.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleSpiralAbyss.cs index 768f9c9d..5e2f90cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleSpiralAbyss.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/SimpleSpiralAbyss.cs @@ -17,8 +17,8 @@ public class SimpleSpiralAbyss public SimpleSpiralAbyss(SpiralAbyss spiralAbyss) { ScheduleId = spiralAbyss.ScheduleId; - Damage = new(spiralAbyss.DamageRank.Single()); - TakeDamage = new(spiralAbyss.TakeDamageRank.Single()); + Damage = SimpleRank.FromRank(spiralAbyss.DamageRank.SingleOrDefault()); + TakeDamage = SimpleRank.FromRank(spiralAbyss.TakeDamageRank.SingleOrDefault()); Floors = spiralAbyss.Floors.Select(f => new SimpleFloor(f)); } @@ -30,12 +30,12 @@ public class SimpleSpiralAbyss /// /// 造成伤害 /// - public SimpleRank Damage { get; set; } = default!; + public SimpleRank? Damage { get; set; } = default!; /// /// 受到伤害 /// - public SimpleRank TakeDamage { get; set; } = default!; + public SimpleRank? TakeDamage { get; set; } = default!; /// /// 层