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">
-
+
@@ -105,7 +109,7 @@
-
+
-
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
index ffaa7611..48ebf071 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
@@ -272,7 +272,7 @@ internal class AchievementViewModel
[ThreadAccess(ThreadAccessState.MainThread)]
private async Task OpenUIAsync()
{
- bool metaInitialized = await metadataService.InitializeAsync(CancellationToken).ConfigureAwait(false);
+ bool metaInitialized = await metadataService.InitializeAsync().ConfigureAwait(false);
if (metaInitialized)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
index d32215c1..99bee95e 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
@@ -83,7 +83,7 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
{
if (user.SelectedUserGameRole is UserGameRole role)
{
- return RefreshCoreAsync((PlayerUid)role, RefreshOption.DatabaseOnly);
+ return RefreshCoreAsync((PlayerUid)role, RefreshOption.DatabaseOnly, CancellationToken);
}
}
@@ -96,7 +96,7 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
{
if (user.SelectedUserGameRole is UserGameRole role)
{
- return RefreshCoreAsync((PlayerUid)role, RefreshOption.Standard);
+ return RefreshCoreAsync((PlayerUid)role, RefreshOption.Standard, CancellationToken);
}
}
@@ -110,31 +110,37 @@ internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
if (isOk)
{
- await RefreshCoreAsync(uid, RefreshOption.RequestFromAPI).ConfigureAwait(false);
+ await RefreshCoreAsync(uid, RefreshOption.RequestFromAPI, CancellationToken).ConfigureAwait(false);
}
}
- private async Task RefreshCoreAsync(PlayerUid uid, RefreshOption option)
+ private async Task RefreshCoreAsync(PlayerUid uid, RefreshOption option, CancellationToken token)
{
- (RefreshResult result, Summary? summary) = await avatarInfoService.GetSummaryAsync(uid, option).ConfigureAwait(false);
+ try
+ {
+ (RefreshResult result, Summary? summary) = await avatarInfoService.GetSummaryAsync(uid, option, token).ConfigureAwait(false);
- if (result == RefreshResult.Ok)
- {
- await ThreadHelper.SwitchToMainThreadAsync();
- Summary = summary;
- SelectedAvatar = Summary?.Avatars.FirstOrDefault();
- }
- else
- {
- switch (result)
+ if (result == RefreshResult.Ok)
{
- case RefreshResult.APIUnavailable:
- infoBarService.Warning("角色信息服务当前不可用");
- break;
- case RefreshResult.ShowcaseNotOpen:
- infoBarService.Warning("角色橱窗尚未开启,请前往游戏操作后重试");
- break;
+ await ThreadHelper.SwitchToMainThreadAsync();
+ Summary = summary;
+ SelectedAvatar = Summary?.Avatars.FirstOrDefault();
}
+ else
+ {
+ switch (result)
+ {
+ case RefreshResult.APIUnavailable:
+ infoBarService.Warning("角色信息服务当前不可用");
+ break;
+ case RefreshResult.ShowcaseNotOpen:
+ infoBarService.Warning("角色橱窗尚未开启,请前往游戏操作后重试");
+ break;
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
index 86cfd206..7a66a3f5 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs
@@ -66,15 +66,20 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
{
HomaClient homaClient = Ioc.Default.GetRequiredService();
IUserService userService = Ioc.Default.GetRequiredService();
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
if (userService.Current is Model.Binding.User user)
{
+ if (user.SelectedUserGameRole == null)
+ {
+ infoBarService.Warning("尚未选择角色");
+ }
+
SimpleRecord record = await homaClient.GetPlayerRecordAsync(user).ConfigureAwait(false);
Web.Response.Response? response = await homaClient.UploadRecordAsync(record).ConfigureAwait(false);
if (response != null && response.IsOk())
{
- IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
infoBarService.Success(response.Message);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
index 1c4b3e72..f647b9a8 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
@@ -37,6 +37,7 @@ internal class GachaLogViewModel : ObservableObject, ISupportCancellation
private GachaStatistics? statistics;
private bool isAggressiveRefresh;
private HistoryWish? selectedHistoryWish;
+ private bool isInitialized;
///
/// 构造一个新的祈愿记录视图模型
@@ -112,6 +113,11 @@ internal class GachaLogViewModel : ObservableObject, ISupportCancellation
///
public bool IsAggressiveRefresh { get => isAggressiveRefresh; set => SetProperty(ref isAggressiveRefresh, value); }
+ ///
+ /// 初始化是否完成
+ ///
+ public bool IsInitialized { get => isInitialized; set => SetProperty(ref isInitialized, value); }
+
///
/// 页面加载命令
///
@@ -183,6 +189,9 @@ internal class GachaLogViewModel : ObservableObject, ISupportCancellation
{
infoBarService.Information("请刷新或导入祈愿记录");
}
+
+ await ThreadHelper.SwitchToMainThreadAsync();
+ IsInitialized = true;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
index 88518429..3b7ff5eb 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs
@@ -13,9 +13,11 @@ using Snap.Hutao.Model.Binding.LaunchGame;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Game;
+using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using System.Collections.ObjectModel;
+using System.IO;
namespace Snap.Hutao.ViewModel;
@@ -159,9 +161,9 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
private async Task OpenUIAsync()
{
- (bool isOk, string gamePath) = await gameService.GetGamePathAsync().ConfigureAwait(false);
+ bool gameExists = File.Exists(gameService.GetGamePathSkipLocator());
- if (isOk)
+ if (gameExists)
{
MultiChannel multi = gameService.GetMultiChannel();
SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
@@ -170,6 +172,11 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
// Sync from Settings
RetiveSetting();
}
+ else
+ {
+ Ioc.Default.GetRequiredService().Warning("游戏路径不正确,前往设置更改游戏路径。");
+ await Ioc.Default.GetRequiredService().NavigateAsync(INavigationAwaiter.Default, true).ConfigureAwait(false);
+ }
}
private void RetiveSetting()
@@ -209,6 +216,8 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
private async Task LaunchAsync()
{
+ IInfoBarService infoBarService = Ioc.Default.GetRequiredService();
+
if (gameService.IsGameRunning())
{
return;
@@ -216,7 +225,14 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation
if (SelectedScheme != null)
{
- gameService.SetMultiChannel(SelectedScheme);
+ try
+ {
+ gameService.SetMultiChannel(SelectedScheme);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ infoBarService.Warning("切换服务器失败,保存配置文件时发生异常\n请以管理员模式启动胡桃。");
+ }
}
if (SelectedGameAccount != null)
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
index 6781fa30..b884f214 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Threading;
@@ -46,11 +47,13 @@ internal class SettingViewModel : ObservableObject
GamePath = gameService.GetGamePathSkipLocator();
SetGamePathCommand = asyncRelayCommandFactory.Create(SetGamePathAsync);
+ DebugExceptionCommand = new RelayCommand(DebugThrowException);
}
///
/// 版本
///
+ [SuppressMessage("", "CA1822")]
public string AppVersion
{
get => Core.CoreEnvironment.Version.ToString();
@@ -90,6 +93,11 @@ internal class SettingViewModel : ObservableObject
///
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!;
///
/// 层