diff --git a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
index 49c50cab..9dd3079a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/RuntimeOptions.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Microsoft.Extensions.Options;
using Microsoft.Web.WebView2.Core;
using Microsoft.Win32;
using Snap.Hutao.Core.Setting;
@@ -12,28 +11,16 @@ using Windows.Storage;
namespace Snap.Hutao.Core;
-///
-/// 存储环境相关的选项
-/// 运行时运算得到的选项,无数据库交互
-///
[Injection(InjectAs.Singleton)]
-internal sealed class RuntimeOptions : IOptions
+internal sealed class RuntimeOptions
{
- private readonly ILogger logger;
-
private readonly bool isWebView2Supported;
private readonly string webView2Version = SH.CoreWebView2HelperVersionUndetected;
private bool? isElevated;
- ///
- /// 构造一个新的胡桃选项
- ///
- /// 日志器
public RuntimeOptions(ILogger logger)
{
- this.logger = logger;
-
AppLaunchTime = DateTimeOffset.UtcNow;
DataFolder = GetDataFolderPath();
@@ -45,117 +32,95 @@ internal sealed class RuntimeOptions : IOptions
UserAgent = $"Snap Hutao/{Version}";
DeviceId = GetUniqueUserId();
- DetectWebView2Environment(ref webView2Version, ref isWebView2Supported);
+ DetectWebView2Environment(logger, out webView2Version, out isWebView2Supported);
+
+ static string GetDataFolderPath()
+ {
+ string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty);
+
+ if (!string.IsNullOrEmpty(preferredPath))
+ {
+ Directory.CreateDirectory(preferredPath);
+ return preferredPath;
+ }
+
+ // Fallback to MyDocuments
+ string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+
+#if RELEASE
+ // 将测试版与正式版的文件目录分离
+ string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
+#else
+ // 使得迁移能正常生成
+ string folderName = "Hutao";
+#endif
+ string path = Path.GetFullPath(Path.Combine(myDocuments, folderName));
+ Directory.CreateDirectory(path);
+ return path;
+ }
+
+ static string GetUniqueUserId()
+ {
+ string userName = Environment.UserName;
+ object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName);
+ return Convert.ToMd5HexString($"{userName}{machineGuid}");
+ }
+
+ static void DetectWebView2Environment(ILogger logger, out string webView2Version, out bool isWebView2Supported)
+ {
+ try
+ {
+ webView2Version = CoreWebView2Environment.GetAvailableBrowserVersionString();
+ isWebView2Supported = true;
+ }
+ catch (FileNotFoundException ex)
+ {
+ webView2Version = SH.CoreWebView2HelperVersionUndetected;
+ isWebView2Supported = false;
+ logger.LogError(ex, "WebView2 Runtime not installed.");
+ }
+ }
}
- ///
- /// 当前版本
- ///
public Version Version { get; }
- ///
- /// 标准UA
- ///
public string UserAgent { get; }
- ///
- /// 安装位置
- ///
public string InstalledLocation { get; }
- ///
- /// 数据文件夹路径
- ///
public string DataFolder { get; }
- ///
- /// 本地缓存
- ///
public string LocalCache { get; }
- ///
- /// 包家族名称
- ///
public string FamilyName { get; }
- ///
- /// 设备Id
- ///
public string DeviceId { get; }
- ///
- /// WebView2 版本
- ///
public string WebView2Version { get => webView2Version; }
- ///
- /// 是否支持 WebView2
- ///
public bool IsWebView2Supported { get => isWebView2Supported; }
- ///
- /// 是否为提升的权限
- ///
- public bool IsElevated { get => isElevated ??= GetElevated(); }
+ public bool IsElevated
+ {
+ get
+ {
+ return isElevated ??= GetElevated();
+
+ static bool GetElevated()
+ {
+ if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false))
+ {
+ return true;
+ }
+
+ using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
+ {
+ WindowsPrincipal principal = new(identity);
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+ }
+ }
+ }
public DateTimeOffset AppLaunchTime { get; }
-
- ///
- public RuntimeOptions Value { get => this; }
-
- private static string GetDataFolderPath()
- {
- string preferredPath = LocalSetting.Get(SettingKeys.DataFolderPath, string.Empty);
-
- if (!string.IsNullOrEmpty(preferredPath) && Directory.Exists(preferredPath))
- {
- return preferredPath;
- }
-
- string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
-#if RELEASE
- // 将测试版与正式版的文件目录分离
- string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
-#else
- // 使得迁移能正常生成
- string folderName = "Hutao";
-#endif
- string path = Path.GetFullPath(Path.Combine(myDocument, folderName));
- Directory.CreateDirectory(path);
- return path;
- }
-
- private static string GetUniqueUserId()
- {
- string userName = Environment.UserName;
- object? machineGuid = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\", "MachineGuid", userName);
- return Convert.ToMd5HexString($"{userName}{machineGuid}");
- }
-
- private static bool GetElevated()
- {
- if (LocalSetting.Get(SettingKeys.OverrideElevationRequirement, false))
- {
- return true;
- }
-
- using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
- {
- WindowsPrincipal principal = new(identity);
- return principal.IsInRole(WindowsBuiltInRole.Administrator);
- }
- }
-
- private void DetectWebView2Environment(ref string webView2Version, ref bool isWebView2Supported)
- {
- try
- {
- webView2Version = CoreWebView2Environment.GetAvailableBrowserVersionString();
- isWebView2Supported = true;
- }
- catch (FileNotFoundException ex)
- {
- logger.LogError(ex, "WebView2 Runtime not installed.");
- }
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
index 867ef104..f31d6af8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
@@ -11,35 +11,27 @@ internal static class DateTimeOffsetExtension
{
public static readonly DateTimeOffset DatebaseDefaultTime = new(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0));
- ///
- /// 从Unix时间戳转换
- ///
- /// 时间戳
- /// 默认值
- /// 转换的时间
- public static DateTimeOffset FromUnixTime(long? timestamp, in DateTimeOffset defaultValue)
+ public static DateTimeOffset UnsafeRelaxedFromUnixTime(long? timestamp, in DateTimeOffset defaultValue)
{
- if (timestamp is { } value)
- {
- try
- {
- return DateTimeOffset.FromUnixTimeSeconds(value);
- }
- catch (ArgumentOutOfRangeException)
- {
- try
- {
- return DateTimeOffset.FromUnixTimeMilliseconds(value);
- }
- catch (ArgumentOutOfRangeException)
- {
- return defaultValue;
- }
- }
- }
- else
+ if (timestamp is not { } value)
{
return defaultValue;
}
+
+ try
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(value);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ try
+ {
+ return DateTimeOffset.FromUnixTimeMilliseconds(value);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ return defaultValue;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
index eaa5e998..3ff7c070 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
@@ -17,19 +17,6 @@ internal static partial class EnumerableExtension
return source.ElementAtOrDefault(index) ?? source.LastOrDefault();
}
- ///
- /// 如果传入集合不为空则原路返回,
- /// 如果传入集合为空返回一个集合的空集
- ///
- /// 源类型
- /// 源
- /// 源集合或空集
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static IEnumerable EmptyIfNull(this IEnumerable? source)
- {
- return source ?? Enumerable.Empty();
- }
-
///
/// 寻找枚举中唯一的值,找不到时
/// 回退到首个或默认值
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/NullableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/NullableExtension.cs
index 85f31b64..7c25a349 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/NullableExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/NullableExtension.cs
@@ -17,4 +17,22 @@ internal static class NullableExtension
value = default;
return false;
}
+
+ public static string ToStringOrEmpty(this in T? nullable)
+ where T : struct
+ {
+ string? result = default;
+
+ if (nullable.HasValue)
+ {
+ result = nullable.Value.ToString();
+ }
+
+ if (string.IsNullOrEmpty(result))
+ {
+ result = string.Empty;
+ }
+
+ return result;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs
index dee1a93c..67435eb5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/SpanExtension.cs
@@ -18,9 +18,9 @@ internal static class SpanExtension
/// Span
/// 最大值的下标
public static int IndexOfMax(this in ReadOnlySpan span)
- where T : INumber
+ where T : INumber, IMinMaxValue
{
- T max = T.Zero;
+ T max = T.MinValue;
int maxIndex = 0;
for (int i = 0; i < span.Length; i++)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAFInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAFInfo.cs
index e15c6147..0155e807 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAFInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Achievement/UIAFInfo.cs
@@ -30,7 +30,7 @@ internal sealed class UIAFInfo : IMappingFrom
[JsonIgnore]
public DateTimeOffset ExportDateTime
{
- get => DateTimeOffsetExtension.FromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
+ get => DateTimeOffsetExtension.UnsafeRelaxedFromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
index b5302513..f0eeae6e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
@@ -38,7 +38,7 @@ internal sealed class UIGFInfo : IMappingFrom DateTimeOffsetExtension.FromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
+ get => DateTimeOffsetExtension.UnsafeRelaxedFromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
index a17ddfad..1ef79580 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFInfo.cs
@@ -35,7 +35,7 @@ internal sealed class UIIFInfo
[JsonIgnore]
public DateTimeOffset ExportDateTime
{
- get => DateTimeOffsetExtension.FromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
+ get => DateTimeOffsetExtension.UnsafeRelaxedFromUnixTime(ExportTimestamp, DateTimeOffset.MinValue);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index f6d73cbe..e79abdea 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -2250,7 +2250,7 @@
设备 ID
- IP:{0},归属:{1}
+ IP:{0} 归属服务器:{1}
设备 IP
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
index 4b0f32d8..e02352d9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
@@ -11,25 +11,10 @@ using System.IO;
namespace Snap.Hutao.Service;
-///
-/// 应用程序选项
-/// 存储服务相关的选项
-///
[ConstructorGenerated(CallBaseConstructor = true)]
[Injection(InjectAs.Singleton)]
internal sealed partial class AppOptions : DbStoreOptions
{
- private readonly List> supportedBackdropTypesInner = CollectionsNameValue.FromEnum();
-
- private readonly List> supportedCulturesInner =
- [
- ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")),
- ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")),
- ToNameValue(CultureInfo.GetCultureInfo("en")),
- ToNameValue(CultureInfo.GetCultureInfo("ko")),
- ToNameValue(CultureInfo.GetCultureInfo("ja")),
- ];
-
private string? gamePath;
private string? powerShellPath;
private bool? isEmptyHistoryWishVisible;
@@ -38,75 +23,67 @@ internal sealed partial class AppOptions : DbStoreOptions
private bool? isAdvancedLaunchOptionsEnabled;
private string? geetestCustomCompositeUrl;
- ///
- /// 游戏路径
- ///
public string GamePath
{
get => GetOption(ref gamePath, SettingEntry.GamePath);
set => SetOption(ref gamePath, SettingEntry.GamePath, value);
}
- ///
- /// PowerShell 路径
- ///
public string PowerShellPath
{
- get => GetOption(ref powerShellPath, SettingEntry.PowerShellPath, GetPowerShellLocationOrEmpty);
+ get
+ {
+ return GetOption(ref powerShellPath, SettingEntry.PowerShellPath, GetDefaultPowerShellLocationOrEmpty);
+
+ static string GetDefaultPowerShellLocationOrEmpty()
+ {
+ string? paths = Environment.GetEnvironmentVariable("Path");
+ if (!string.IsNullOrEmpty(paths))
+ {
+ foreach (StringSegment path in new StringTokenizer(paths, [';']))
+ {
+ if (path is { HasValue: true, Length: > 0 })
+ {
+ if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase))
+ {
+ return Path.Combine(path.Value, "powershell.exe");
+ }
+ }
+ }
+ }
+
+ return string.Empty;
+ }
+ }
set => SetOption(ref powerShellPath, SettingEntry.PowerShellPath, value);
}
- ///
- /// 游戏路径
- ///
public bool IsEmptyHistoryWishVisible
{
get => GetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible);
set => SetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible, value);
}
- ///
- /// 所有支持的背景样式
- ///
- public List> BackdropTypes { get => supportedBackdropTypesInner; }
+ public List> BackdropTypes { get; } = CollectionsNameValue.FromEnum();
- ///
- /// 背景类型 默认 Mica
- ///
public BackdropType BackdropType
{
get => GetOption(ref backdropType, SettingEntry.SystemBackdropType, v => Enum.Parse(v), BackdropType.Mica).Value;
- set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, value => value.ToString()!);
+ set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, value => value.ToStringOrEmpty());
}
- ///
- /// 所有支持的语言
- ///
- public List> Cultures { get => supportedCulturesInner; }
+ public List> Cultures { get; } = SupportedCultures.Get();
- ///
- /// 初始化前的语言
- /// 通过设置与获取此属性,就可以获取到与系统同步的语言
- ///
- public CultureInfo PreviousCulture { get; set; } = default!;
-
- ///
- /// 当前语言
- /// 默认为系统语言
- ///
public CultureInfo CurrentCulture
{
get => GetOption(ref currentCulture, SettingEntry.Culture, CultureInfo.GetCultureInfo, CultureInfo.CurrentCulture);
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
}
- ///
- /// 是否启用高级功能
- /// DO NOT MOVE TO OTHER CLASS
- /// We are binding this property in SettingPage
- ///
public bool IsAdvancedLaunchOptionsEnabled
{
+ // DO NOT MOVE TO OTHER CLASS
+ // We use this property in SettingPage binding
get => GetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled);
set => SetOption(ref isAdvancedLaunchOptionsEnabled, SettingEntry.IsAdvancedLaunchOptionsEnabled, value);
}
@@ -117,28 +94,5 @@ internal sealed partial class AppOptions : DbStoreOptions
set => SetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl, value);
}
- private static NameValue ToNameValue(CultureInfo info)
- {
- return new(info.NativeName, info);
- }
-
- private static string GetPowerShellLocationOrEmpty()
- {
- string? paths = Environment.GetEnvironmentVariable("Path");
- if (!string.IsNullOrEmpty(paths))
- {
- foreach (StringSegment path in new StringTokenizer(paths, [';']))
- {
- if (path is { HasValue: true, Length: > 0 })
- {
- if (path.Value.Contains("WindowsPowerShell", StringComparison.OrdinalIgnoreCase))
- {
- return Path.Combine(path.Value, "powershell.exe");
- }
- }
- }
- }
-
- return string.Empty;
- }
+ internal CultureInfo PreviousCulture { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
index a146c2d3..9e7b98fc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
@@ -33,7 +33,7 @@ internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryP
if (query.TryGetValue("auth_appid", out string? appId) && appId is "webview_gacha")
{
string? queryLanguageCode = query["lang"];
- if (metadataOptions.IsCurrentLocale(queryLanguageCode))
+ if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
{
return new(true, new(queryString));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
index b36ce4a4..247fe149 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
@@ -90,7 +90,7 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
NameValueCollection query = HttpUtility.ParseQueryString(result.TrimEnd("#/log"));
string? queryLanguageCode = query["lang"];
- if (metadataOptions.IsCurrentLocale(queryLanguageCode))
+ if (metadataOptions.LanguageCodeFitsCurrentLocale(queryLanguageCode))
{
return new(true, new(result));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
index ed63e016..6f82560e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
@@ -37,7 +37,7 @@ internal sealed partial class UIGFImportService : IUIGFImportService
// v2.1 only support CHS
if (version is UIGFVersion.Major2Minor2OrLower)
{
- if (!metadataOptions.IsCurrentLocale(uigf.Info.Language))
+ if (!metadataOptions.LanguageCodeFitsCurrentLocale(uigf.Info.Language))
{
string message = SH.FormatServiceGachaUIGFImportLanguageNotMatch(uigf.Info.Language, metadataOptions.LanguageCode);
ThrowHelper.InvalidOperation(message);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
index 5876b870..4241bba3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
@@ -40,10 +40,6 @@ internal sealed class LaunchOptions : DbStoreOptions
private bool? useStarwardPlayTimeStatistics;
private bool? setDiscordActivityWhenPlaying;
- ///
- /// 构造一个新的启动游戏选项
- ///
- /// 服务提供器
public LaunchOptions(IServiceProvider serviceProvider)
: base(serviceProvider)
{
@@ -53,47 +49,66 @@ internal sealed class LaunchOptions : DbStoreOptions
InitializeMonitors(Monitors);
InitializeScreenFps(out primaryScreenFps);
+
+ static void InitializeMonitors(List> monitors)
+ {
+ try
+ {
+ // This list can't use foreach
+ // https://github.com/microsoft/CsWinRT/issues/747
+ IReadOnlyList displayAreas = DisplayArea.FindAll();
+ for (int i = 0; i < displayAreas.Count; i++)
+ {
+ DisplayArea displayArea = displayAreas[i];
+ int index = i + 1;
+ monitors.Add(new($"{displayArea.DisplayId.Value:X8}:{index}", index));
+ }
+ }
+ catch
+ {
+ monitors.Clear();
+ }
+ }
+
+ static void InitializeScreenFps(out int fps)
+ {
+ HDC hDC = default;
+ try
+ {
+ hDC = GetDC(HWND.Null);
+ fps = GetDeviceCaps(hDC, GET_DEVICE_CAPS_INDEX.VREFRESH);
+ }
+ finally
+ {
+ _ = ReleaseDC(HWND.Null, hDC);
+ }
+ }
}
- ///
- /// 是否启用启动参数
- ///
public bool IsEnabled
{
get => GetOption(ref isEnabled, SettingEntry.LaunchIsLaunchOptionsEnabled, true);
set => SetOption(ref isEnabled, SettingEntry.LaunchIsLaunchOptionsEnabled, value);
}
- ///
- /// 是否全屏
- ///
public bool IsFullScreen
{
get => GetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen);
set => SetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen, value);
}
- ///
- /// 是否无边框
- ///
public bool IsBorderless
{
get => GetOption(ref isBorderless, SettingEntry.LaunchIsBorderless);
set => SetOption(ref isBorderless, SettingEntry.LaunchIsBorderless, value);
}
- ///
- /// 是否独占全屏
- ///
public bool IsExclusive
{
get => GetOption(ref isExclusive, SettingEntry.LaunchIsExclusive);
set => SetOption(ref isExclusive, SettingEntry.LaunchIsExclusive, value);
}
- ///
- /// 屏幕宽度
- ///
public int ScreenWidth
{
get => GetOption(ref screenWidth, SettingEntry.LaunchScreenWidth, primaryScreenWidth);
@@ -106,9 +121,6 @@ internal sealed class LaunchOptions : DbStoreOptions
set => SetOption(ref isScreenWidthEnabled, SettingEntry.LaunchIsScreenWidthEnabled, value);
}
- ///
- /// 屏幕高度
- ///
public int ScreenHeight
{
get => GetOption(ref screenHeight, SettingEntry.LaunchScreenHeight, primaryScreenHeight);
@@ -121,32 +133,20 @@ internal sealed class LaunchOptions : DbStoreOptions
set => SetOption(ref isScreenHeightEnabled, SettingEntry.LaunchIsScreenHeightEnabled, value);
}
- ///
- /// 是否全屏
- ///
public bool UnlockFps
{
get => GetOption(ref unlockFps, SettingEntry.LaunchUnlockFps);
set => SetOption(ref unlockFps, SettingEntry.LaunchUnlockFps, value);
}
- ///
- /// 目标帧率
- ///
public int TargetFps
{
get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps);
set => SetOption(ref targetFps, SettingEntry.LaunchTargetFps, value);
}
- ///
- /// 所有监视器
- ///
public List> Monitors { get; } = [];
- ///
- /// 目标帧率
- ///
[AllowNull]
public NameValue Monitor
{
@@ -196,31 +196,4 @@ internal sealed class LaunchOptions : DbStoreOptions
get => GetOption(ref setDiscordActivityWhenPlaying, SettingEntry.LaunchSetDiscordActivityWhenPlaying, true);
set => SetOption(ref setDiscordActivityWhenPlaying, SettingEntry.LaunchSetDiscordActivityWhenPlaying, value);
}
-
- private static void InitializeMonitors(List> monitors)
- {
- // This list can't use foreach
- // https://github.com/microsoft/CsWinRT/issues/747
- IReadOnlyList displayAreas = DisplayArea.FindAll();
- for (int i = 0; i < displayAreas.Count; i++)
- {
- DisplayArea displayArea = displayAreas[i];
- int index = i + 1;
- monitors.Add(new($"{displayArea.DisplayId.Value:X8}:{index}", index));
- }
- }
-
- private static void InitializeScreenFps(out int fps)
- {
- HDC hDC = default;
- try
- {
- hDC = GetDC(HWND.Null);
- fps = GetDeviceCaps(hDC, GET_DEVICE_CAPS_INDEX.VREFRESH);
- }
- finally
- {
- _ = ReleaseDC(HWND.Null, hDC);
- }
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
index 71988df7..71fabc3c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
@@ -2,141 +2,38 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
-using Microsoft.Extensions.Options;
-using Snap.Hutao.Core.Setting;
-using Snap.Hutao.Web.Hutao;
-using System.Globalization;
-using System.Text.RegularExpressions;
namespace Snap.Hutao.Service.Hutao;
-///
-/// 胡桃用户选项
-///
[Injection(InjectAs.Singleton)]
-internal sealed class HutaoUserOptions : ObservableObject, IOptions
+internal sealed class HutaoUserOptions : ObservableObject
{
- private readonly TaskCompletionSource initializedTaskCompletionSource = new();
+ private readonly TaskCompletionSource initialization = new();
+
private string? userName = SH.ViewServiceHutaoUserLoginOrRegisterHint;
- private string? token;
private bool isLoggedIn;
- private bool isHutaoCloudServiceAllowed;
+ private bool isCloudServiceAllowed;
private bool isLicensedDeveloper;
+ private bool isMaintainer;
private string? gachaLogExpireAt;
private string? gachaLogExpireAtSlim;
- private bool isMaintainer;
+ private string? token;
- ///
- /// 用户名
- ///
public string? UserName { get => userName; set => SetProperty(ref userName, value); }
- ///
- /// 真正的用户名
- ///
- public string? ActualUserName { get => IsLoggedIn ? UserName : null; }
-
- ///
- /// 是否已登录
- ///
public bool IsLoggedIn { get => isLoggedIn; set => SetProperty(ref isLoggedIn, value); }
- ///
- /// 胡桃云服务是否可用
- ///
- public bool IsCloudServiceAllowed { get => isHutaoCloudServiceAllowed; set => SetProperty(ref isHutaoCloudServiceAllowed, value); }
+ public bool IsCloudServiceAllowed { get => isCloudServiceAllowed; set => SetProperty(ref isCloudServiceAllowed, value); }
- ///
- /// 是否为开发者
- ///
public bool IsLicensedDeveloper { get => isLicensedDeveloper; set => SetProperty(ref isLicensedDeveloper, value); }
public bool IsMaintainer { get => isMaintainer; set => SetProperty(ref isMaintainer, value); }
- ///
- /// 祈愿记录服务到期时间
- ///
public string? GachaLogExpireAt { get => gachaLogExpireAt; set => SetProperty(ref gachaLogExpireAt, value); }
public string? GachaLogExpireAtSlim { get => gachaLogExpireAtSlim; set => SetProperty(ref gachaLogExpireAtSlim, value); }
- ///
- public HutaoUserOptions Value { get => this; }
+ internal string? Token { get => token; set => token = value; }
- public async ValueTask PostLoginSucceedAsync(HutaoPassportClient passportClient, ITaskContext taskContext, string username, string password, string? token)
- {
- LocalSetting.Set(SettingKeys.PassportUserName, username);
- LocalSetting.Set(SettingKeys.PassportPassword, password);
-
- await taskContext.SwitchToMainThreadAsync();
- UserName = username;
- this.token = token;
- IsLoggedIn = true;
- initializedTaskCompletionSource.TrySetResult();
-
- await taskContext.SwitchToBackgroundAsync();
- Web.Response.Response userInfoResponse = await passportClient.GetUserInfoAsync(default).ConfigureAwait(false);
- if (userInfoResponse.IsOk())
- {
- await taskContext.SwitchToMainThreadAsync();
- UpdateUserInfo(userInfoResponse.Data);
- return true;
- }
-
- return false;
- }
-
- public void LogoutOrUnregister()
- {
- LocalSetting.Set(SettingKeys.PassportUserName, string.Empty);
- LocalSetting.Set(SettingKeys.PassportPassword, string.Empty);
-
- UserName = null;
- token = null;
- IsLoggedIn = false;
- ClearUserInfo();
- }
-
- ///
- /// 登录失败
- ///
- public void LoginFailed()
- {
- UserName = SH.ViewServiceHutaoUserLoginFailHint;
- initializedTaskCompletionSource.TrySetResult();
- }
-
- public void SkipLogin()
- {
- initializedTaskCompletionSource.TrySetResult();
- }
-
- ///
- /// 刷新用户信息
- ///
- /// 用户信息
- public void UpdateUserInfo(UserInfo userInfo)
- {
- IsLicensedDeveloper = userInfo.IsLicensedDeveloper;
- IsMaintainer = userInfo.IsMaintainer;
- string unescaped = Regex.Unescape(SH.ServiceHutaoUserGachaLogExpiredAt);
- GachaLogExpireAt = string.Format(CultureInfo.CurrentCulture, unescaped, userInfo.GachaLogExpireAt);
- GachaLogExpireAtSlim = $"{userInfo.GachaLogExpireAt:yyyy.MM.dd HH:mm:ss}";
- IsCloudServiceAllowed = IsLicensedDeveloper || userInfo.GachaLogExpireAt > DateTimeOffset.UtcNow;
- }
-
- public async ValueTask GetTokenAsync()
- {
- await initializedTaskCompletionSource.Task.ConfigureAwait(false);
- return token;
- }
-
- private void ClearUserInfo()
- {
- IsLicensedDeveloper = false;
- IsMaintainer = false;
- GachaLogExpireAt = null;
- GachaLogExpireAtSlim = null;
- IsCloudServiceAllowed = false;
- }
+ internal TaskCompletionSource Initialization { get => initialization; }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptionsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptionsExtension.cs
new file mode 100644
index 00000000..d7bf4d84
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptionsExtension.cs
@@ -0,0 +1,88 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Setting;
+using Snap.Hutao.Web.Hutao;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace Snap.Hutao.Service.Hutao;
+
+internal static class HutaoUserOptionsExtension
+{
+ public static string? GetActualUserName(this HutaoUserOptions options)
+ {
+ return options.IsLoggedIn ? options.UserName : null;
+ }
+
+ public static async ValueTask GetTokenAsync(this HutaoUserOptions options)
+ {
+ await options.Initialization.Task.ConfigureAwait(false);
+ return options.Token;
+ }
+
+ public static async ValueTask PostLoginSucceedAsync(this HutaoUserOptions options, HutaoPassportClient passportClient, ITaskContext taskContext, string username, string password, string? token)
+ {
+ LocalSetting.Set(SettingKeys.PassportUserName, username);
+ LocalSetting.Set(SettingKeys.PassportPassword, password);
+
+ await taskContext.SwitchToMainThreadAsync();
+ options.UserName = username;
+ options.Token = token;
+ options.IsLoggedIn = true;
+ options.Initialization.TrySetResult();
+
+ await taskContext.SwitchToBackgroundAsync();
+ Web.Response.Response userInfoResponse = await passportClient.GetUserInfoAsync(default).ConfigureAwait(false);
+ if (userInfoResponse.IsOk())
+ {
+ await taskContext.SwitchToMainThreadAsync();
+ UpdateUserInfo(options, userInfoResponse.Data);
+ return true;
+ }
+
+ return false;
+
+ static void UpdateUserInfo(HutaoUserOptions options, UserInfo userInfo)
+ {
+ options.IsLicensedDeveloper = userInfo.IsLicensedDeveloper;
+ options.IsMaintainer = userInfo.IsMaintainer;
+
+ options.IsCloudServiceAllowed = options.IsLicensedDeveloper || userInfo.GachaLogExpireAt > DateTimeOffset.UtcNow;
+ string unescaped = Regex.Unescape(SH.ServiceHutaoUserGachaLogExpiredAt);
+ options.GachaLogExpireAt = string.Format(CultureInfo.CurrentCulture, unescaped, userInfo.GachaLogExpireAt);
+ options.GachaLogExpireAtSlim = $"{userInfo.GachaLogExpireAt:yyyy.MM.dd HH:mm:ss}";
+ }
+ }
+
+ public static void PostLogoutOrUnregister(this HutaoUserOptions options)
+ {
+ LocalSetting.Set(SettingKeys.PassportUserName, string.Empty);
+ LocalSetting.Set(SettingKeys.PassportPassword, string.Empty);
+
+ options.UserName = null;
+ options.Token = null;
+ options.IsLoggedIn = false;
+ ClearUserInfo(options);
+
+ static void ClearUserInfo(HutaoUserOptions options)
+ {
+ options.IsLicensedDeveloper = false;
+ options.IsMaintainer = false;
+ options.GachaLogExpireAt = null;
+ options.GachaLogExpireAtSlim = null;
+ options.IsCloudServiceAllowed = false;
+ }
+ }
+
+ public static void PostLoginFailed(this HutaoUserOptions options)
+ {
+ options.UserName = SH.ViewServiceHutaoUserLoginFailHint;
+ options.Initialization.TrySetResult();
+ }
+
+ public static void PostLoginSkipped(this HutaoUserOptions options)
+ {
+ options.Initialization.TrySetResult();
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
index 1156614b..34527a60 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
@@ -36,7 +36,7 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
{
- options.SkipLogin();
+ options.PostLoginSkipped();
}
else
{
@@ -52,7 +52,7 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe
else
{
await taskContext.SwitchToMainThreadAsync();
- options.LoginFailed();
+ options.PostLoginFailed();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs
index 5fe5a13b..3bb1b91c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptions.cs
@@ -1,19 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Microsoft.Extensions.Options;
using Snap.Hutao.Core;
-using System.Globalization;
using System.IO;
namespace Snap.Hutao.Service.Metadata;
-///
-/// 元数据选项
-///
[ConstructorGenerated]
[Injection(InjectAs.Singleton)]
-internal sealed partial class MetadataOptions : IOptions
+internal sealed partial class MetadataOptions
{
private readonly AppOptions appOptions;
private readonly RuntimeOptions hutaoOptions;
@@ -22,9 +17,6 @@ internal sealed partial class MetadataOptions : IOptions
private string? fallbackDataFolder;
private string? localizedDataFolder;
- ///
- /// 中文数据文件夹
- ///
public string FallbackDataFolder
{
get
@@ -39,9 +31,6 @@ internal sealed partial class MetadataOptions : IOptions
}
}
- ///
- /// 本地化数据文件夹
- ///
public string LocalizedDataFolder
{
get
@@ -56,17 +45,11 @@ internal sealed partial class MetadataOptions : IOptions
}
}
- ///
- /// 当前使用的元数据本地化名称
- ///
public string LocaleName
{
- get => localeName ??= GetLocaleName(appOptions.CurrentCulture);
+ get => localeName ??= MetadataOptionsExtension.GetLocaleName(appOptions.CurrentCulture);
}
- ///
- /// 当前语言代码
- ///
public string LanguageCode
{
get
@@ -79,63 +62,4 @@ internal sealed partial class MetadataOptions : IOptions
throw new KeyNotFoundException($"Invalid localeName: '{LocaleName}'");
}
}
-
- ///
- public MetadataOptions Value { get => this; }
-
- ///
- /// 获取语言名称
- ///
- /// 语言信息
- /// 元数据语言名称
- public static string GetLocaleName(CultureInfo cultureInfo)
- {
- while (true)
- {
- if (LocaleNames.TryGetLocaleNameFromLanguageName(cultureInfo.Name, out string? localeName))
- {
- return localeName;
- }
- else
- {
- cultureInfo = cultureInfo.Parent;
- }
- }
- }
-
- ///
- /// 检查是否为当前语言名称
- ///
- /// 语言代码
- /// 是否为当前语言名称
- public bool IsCurrentLocale(string? languageCode)
- {
- if (string.IsNullOrEmpty(languageCode))
- {
- return false;
- }
-
- CultureInfo cultureInfo = CultureInfo.GetCultureInfo(languageCode);
- return GetLocaleName(cultureInfo) == LocaleName;
- }
-
- ///
- /// 获取本地的本地化元数据文件
- ///
- /// 文件名
- /// 本地的本地化元数据文件
- public string GetLocalizedLocalFile(string fileNameWithExtension)
- {
- return Path.Combine(LocalizedDataFolder, fileNameWithExtension);
- }
-
- ///
- /// 获取服务器上的本地化元数据文件
- ///
- /// 文件名
- /// 服务器上的本地化元数据文件
- public string GetLocalizedRemoteFile(string fileNameWithExtension)
- {
- return Web.HutaoEndpoints.Metadata(LocaleName, fileNameWithExtension);
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptionsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptionsExtension.cs
new file mode 100644
index 00000000..59c17e93
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataOptionsExtension.cs
@@ -0,0 +1,47 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Globalization;
+using System.IO;
+
+namespace Snap.Hutao.Service.Metadata;
+
+internal static class MetadataOptionsExtension
+{
+ public static string GetLocalizedLocalFile(this MetadataOptions options, string fileNameWithExtension)
+ {
+ return Path.Combine(options.LocalizedDataFolder, fileNameWithExtension);
+ }
+
+ public static string GetLocalizedRemoteFile(this MetadataOptions options, string fileNameWithExtension)
+ {
+ return Web.HutaoEndpoints.Metadata(options.LocaleName, fileNameWithExtension);
+ }
+
+ public static bool LanguageCodeFitsCurrentLocale(this MetadataOptions options, string? languageCode)
+ {
+ if (string.IsNullOrEmpty(languageCode))
+ {
+ return false;
+ }
+
+ // We want to make sure code fits in 1 of 15 metadata locales
+ CultureInfo cultureInfo = CultureInfo.GetCultureInfo(languageCode);
+ return GetLocaleName(cultureInfo) == options.LocaleName;
+ }
+
+ internal static string GetLocaleName(CultureInfo cultureInfo)
+ {
+ while (true)
+ {
+ if (LocaleNames.TryGetLocaleNameFromLanguageName(cultureInfo.Name, out string? localeName))
+ {
+ return localeName;
+ }
+ else
+ {
+ cultureInfo = cultureInfo.Parent;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SupportedCultures.cs b/src/Snap.Hutao/Snap.Hutao/Service/SupportedCultures.cs
new file mode 100644
index 00000000..8e352ef8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/SupportedCultures.cs
@@ -0,0 +1,29 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model;
+using System.Globalization;
+
+namespace Snap.Hutao.Service;
+
+internal static class SupportedCultures
+{
+ private static readonly List> Cultures =
+ [
+ ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")),
+ ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")),
+ ToNameValue(CultureInfo.GetCultureInfo("en")),
+ ToNameValue(CultureInfo.GetCultureInfo("ko")),
+ ToNameValue(CultureInfo.GetCultureInfo("ja")),
+ ];
+
+ public static List> Get()
+ {
+ return Cultures;
+ }
+
+ private static NameValue ToNameValue(CultureInfo info)
+ {
+ return new(info.NativeName, info);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs
index 22ce7a2b..e06e0bc7 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/HutaoPassportViewModel.cs
@@ -77,7 +77,7 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel
infoBarService.Information(response.GetLocalizationMessageOrMessage());
await taskContext.SwitchToMainThreadAsync();
- hutaoUserOptions.LogoutOrUnregister();
+ hutaoUserOptions.PostLogoutOrUnregister();
}
}
}
@@ -110,7 +110,7 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel
[Command("LogoutCommand")]
private void LogoutAsync()
{
- hutaoUserOptions.LogoutOrUnregister();
+ hutaoUserOptions.PostLogoutOrUnregister();
}
[Command("ResetPasswordCommand")]
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/IPInformation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/IPInformation.cs
index 1ce1379d..8546fc7c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/IPInformation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/IPInformation.cs
@@ -8,7 +8,7 @@ internal sealed class IPInformation
public static IPInformation Default { get; } = new()
{
Ip = "Unknown",
- Division = "Unknown"
+ Division = "Unknown",
};
[JsonPropertyName("ip")]
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/HutaoSpiralAbyssClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/HutaoSpiralAbyssClient.cs
index 5283549c..d4aa1546 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/HutaoSpiralAbyssClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/SpiralAbyss/HutaoSpiralAbyssClient.cs
@@ -233,7 +233,7 @@ internal sealed partial class HutaoSpiralAbyssClient
if (spiralAbyssResponse.IsOk())
{
HutaoUserOptions options = serviceProvider.GetRequiredService();
- return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data, options.ActualUserName);
+ return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data, options.GetActualUserName());
}
}
}