From 5d05c31af591333631b05ac203e93e6d152ca6ec Mon Sep 17 00:00:00 2001
From: Lightczx <1686188646@qq.com>
Date: Fri, 29 Dec 2023 10:05:03 +0800
Subject: [PATCH] fix startup crash
---
.../CodeAnalysis/AlsoAsyncSetsAttribute.cs | 23 ++++
.../CodeAnalysis/AlsoSetsAttribute.cs | 23 ++++
.../Core/ExceptionService/HutaoException.cs | 26 ++++
.../Snap.Hutao/View/Page/LaunchGamePage.xaml | 2 +-
.../ViewModel/Game/LaunchGameViewModel.cs | 124 +++++++++---------
.../ViewModel/Game/LaunchGameViewModelSlim.cs | 4 +-
.../ViewModel/Setting/SettingViewModel.cs | 1 -
7 files changed, 134 insertions(+), 69 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoAsyncSetsAttribute.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoSetsAttribute.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoException.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoAsyncSetsAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoAsyncSetsAttribute.cs
new file mode 100644
index 00000000..d31233df
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoAsyncSetsAttribute.cs
@@ -0,0 +1,23 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Core.Diagnostics.CodeAnalysis;
+
+///
+/// 指示此特性附加的属性会在属性改变后会异步地设置的其他属性
+///
+[AttributeUsage(AttributeTargets.Property, Inherited = false)]
+internal sealed class AlsoAsyncSetsAttribute : Attribute
+{
+ public AlsoAsyncSetsAttribute(string propertyName)
+ {
+ }
+
+ public AlsoAsyncSetsAttribute(string propertyName1, string propertyName2)
+ {
+ }
+
+ public AlsoAsyncSetsAttribute(params string[] propertyNames)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoSetsAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoSetsAttribute.cs
new file mode 100644
index 00000000..fd07a2a9
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/CodeAnalysis/AlsoSetsAttribute.cs
@@ -0,0 +1,23 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Core.Diagnostics.CodeAnalysis;
+
+///
+/// 指示此特性附加的属性会在属性改变后会设置的其他属性
+///
+[AttributeUsage(AttributeTargets.Property, Inherited = false)]
+internal sealed class AlsoSetsAttribute : Attribute
+{
+ public AlsoSetsAttribute(string propertyName)
+ {
+ }
+
+ public AlsoSetsAttribute(string propertyName1, string propertyName2)
+ {
+ }
+
+ public AlsoSetsAttribute(params string[] propertyNames)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoException.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoException.cs
new file mode 100644
index 00000000..90c02aef
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/HutaoException.cs
@@ -0,0 +1,26 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Core.ExceptionService;
+
+internal sealed class HutaoException : Exception
+{
+ public HutaoException(HutaoExceptionKind kind, string message, Exception? innerException)
+ : this(message, innerException)
+ {
+ Kind = kind;
+ }
+
+ public HutaoException(string message, Exception? innerException)
+ : base($"{message}\n{innerException?.Message}", innerException)
+ {
+ }
+
+ public HutaoExceptionKind Kind { get; private set; }
+}
+
+internal enum HutaoExceptionKind
+{
+ None,
+
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
index a23d2af8..372c0b72 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
@@ -198,7 +198,7 @@
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
index bee15112..3c64dd3b 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
@@ -2,10 +2,10 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Collections;
-using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core;
+using Snap.Hutao.Core.Diagnostics.CodeAnalysis;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Factory.Progress;
@@ -64,8 +64,17 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
private GamePathEntry? selectedGamePathEntry;
private GameAccountFilter? gameAccountFilter;
+ public LaunchOptions LaunchOptions { get => launchOptions; }
+
+ public LaunchStatusOptions LaunchStatusOptions { get => launchStatusOptions; }
+
+ public RuntimeOptions RuntimeOptions { get => runtimeOptions; }
+
+ public AppOptions AppOptions { get => appOptions; }
+
public List KnownSchemes { get; } = KnownLaunchSchemes.Get();
+ [AlsoAsyncSets(nameof(GameResource), nameof(GameAccountsView))]
public LaunchScheme? SelectedScheme
{
get => selectedScheme;
@@ -76,16 +85,9 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
- public LaunchOptions LaunchOptions { get => launchOptions; }
-
- public LaunchStatusOptions LaunchStatusOptions { get => launchStatusOptions; }
-
- public RuntimeOptions RuntimeOptions { get => runtimeOptions; }
-
- public AppOptions AppOptions { get => appOptions; }
-
public GameResource? GameResource { get => gameResource; set => SetProperty(ref gameResource, value); }
+ [AlsoAsyncSets(nameof(SelectedScheme), nameof(GameAccountsView))]
public bool GamePathSelectedAndValid
{
get => gamePathSelectedAndValid;
@@ -106,24 +108,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
await taskContext.SwitchToMainThreadAsync();
await SetSelectedSchemeAsync(scheme).ConfigureAwait(true);
-
- // Sync uid, almost never hit, so we are not so care about performance
- if (memoryCache.TryRemove(DesiredUid, out object? value) && value is string uid)
- {
- ArgumentNullException.ThrowIfNull(GameAccountsView);
-
- // Exists in the source collection
- if (GameAccountsView.SourceCollection.Cast().FirstOrDefault(g => g.AttachUid == uid) is { } sourceAccount)
- {
- SelectedGameAccount = GameAccountsView.Cast().FirstOrDefault(g => g.AttachUid == uid);
-
- // But not exists in the view for current scheme
- if (SelectedGameAccount is null)
- {
- infoBarService.Warning(SH.FormatViewModelLaunchGameUnableToSwitchUidAttachedGameAccount(uid, sourceAccount.Name));
- }
- }
- }
+ TrySetGameAccountByDesiredUid();
// Try set to the current account.
if (SelectedScheme is not null)
@@ -142,59 +127,68 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
infoBarService.Error(ex);
}
}
+
+ void TrySetGameAccountByDesiredUid()
+ {
+ // Sync uid, almost never hit, so we are not so care about performance
+ if (memoryCache.TryRemove(DesiredUid, out object? value) && value is string uid)
+ {
+ ArgumentNullException.ThrowIfNull(GameAccountsView);
+
+ // Exists in the source collection
+ if (GameAccountsView.SourceCollection.Cast().FirstOrDefault(g => g.AttachUid == uid) is { } sourceAccount)
+ {
+ SelectedGameAccount = GameAccountsView.Cast().FirstOrDefault(g => g.AttachUid == uid);
+
+ // But not exists in the view for current scheme
+ if (SelectedGameAccount is null)
+ {
+ infoBarService.Warning(SH.FormatViewModelLaunchGameUnableToSwitchUidAttachedGameAccount(uid, sourceAccount.Name));
+ }
+ }
+ }
+ }
}
}
public ImmutableList GamePathEntries { get => gamePathEntries; set => SetProperty(ref gamePathEntries, value); }
+ [AlsoSets(nameof(GamePathSelectedAndValid))]
public GamePathEntry? SelectedGamePathEntry
{
get => selectedGamePathEntry;
- set => UpdateSelectedGamePathEntry(value, true);
+ set
+ {
+ if (SetProperty(ref selectedGamePathEntry, value, nameof(SelectedGamePathEntry)))
+ {
+ if (IsViewDisposed)
+ {
+ return;
+ }
+
+ launchOptions.GamePath = value?.Path ?? string.Empty;
+ GamePathSelectedAndValid = File.Exists(launchOptions.GamePath);
+ }
+ }
}
protected override ValueTask InitializeUIAsync()
{
- GamePathEntries = launchOptions.GetGamePathEntries(out GamePathEntry? entry);
- SelectedGamePathEntry = entry;
+ SyncGamePathEntriesAndSelectedGamePathEntryFromLaunchOptions();
return ValueTask.FromResult(true);
}
- private void UpdateSelectedGamePathEntry(GamePathEntry? value, bool setBack)
- {
- if (SetProperty(ref selectedGamePathEntry, value, nameof(SelectedGamePathEntry)) && setBack)
- {
- if (IsViewDisposed)
- {
- return;
- }
-
- launchOptions.GamePath = value?.Path ?? string.Empty;
- GamePathSelectedAndValid = File.Exists(launchOptions.GamePath);
- }
- }
-
[Command("SetGamePathCommand")]
private async Task SetGamePathAsync()
{
- IGameLocator locator = gameLocatorFactory.Create(GameLocationSource.Manual);
-
- (bool isOk, string path) = await locator.LocateGamePathAsync().ConfigureAwait(false);
+ (bool isOk, string path) = await gameLocatorFactory.LocateAsync(GameLocationSource.Manual).ConfigureAwait(false);
if (!isOk)
{
return;
}
await taskContext.SwitchToMainThreadAsync();
- try
- {
- GamePathEntries = launchOptions.UpdateGamePathAndRefreshEntries(path);
- }
- catch (SqliteException ex)
- {
- // 文件夹权限不足,无法写入数据库
- infoBarService.Error(ex, SH.ViewModelSettingSetGamePathDatabaseFailedTitle);
- }
+ GamePathEntries = launchOptions.UpdateGamePathAndRefreshEntries(path);
}
[Command("ResetGamePathCommand")]
@@ -237,18 +231,14 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
else
{
await taskContext.SwitchToMainThreadAsync();
- GamePathEntries = launchOptions.GetGamePathEntries(out GamePathEntry? entry);
- UpdateSelectedGamePathEntry(entry, true);
+ SyncGamePathEntriesAndSelectedGamePathEntryFromLaunchOptions();
}
}
- if (SelectedGameAccount is not null)
+ if (SelectedGameAccount is not null && !gameService.SetGameAccount(SelectedGameAccount))
{
- if (!gameService.SetGameAccount(SelectedGameAccount))
- {
- infoBarService.Warning(SH.ViewModelLaunchGameSwitchGameAccountFail);
- return;
- }
+ infoBarService.Warning(SH.ViewModelLaunchGameSwitchGameAccountFail);
+ return;
}
IProgress launchProgress = progressFactory.CreateForMainThread(status => launchStatusOptions.LaunchStatus = status);
@@ -377,4 +367,10 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
};
}
}
+
+ private void SyncGamePathEntriesAndSelectedGamePathEntryFromLaunchOptions()
+ {
+ GamePathEntries = launchOptions.GetGamePathEntries(out GamePathEntry? entry);
+ SelectedGamePathEntry = entry;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModelSlim.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModelSlim.cs
index b1743441..d61fbf5e 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModelSlim.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModelSlim.cs
@@ -5,9 +5,7 @@ using CommunityToolkit.WinUI.Collections;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Factory.Progress;
using Snap.Hutao.Model.Entity;
-using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Service.Game;
-using Snap.Hutao.Service.Game.Configuration;
using Snap.Hutao.Service.Game.Scheme;
using Snap.Hutao.Service.Notification;
using System.Collections.ObjectModel;
@@ -43,6 +41,7 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
protected override async Task OpenUIAsync()
{
LaunchScheme? scheme = LaunchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
+ ObservableCollection accounts = gameService.GameAccountCollection;
try
{
@@ -58,7 +57,6 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
}
gameAccountFilter = new(scheme?.GetSchemeType());
- ObservableCollection accounts = gameService.GameAccountCollection;
await taskContext.SwitchToMainThreadAsync();
GameAccountsView = new(accounts, true)
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
index 9ff8767f..c6a183af 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
@@ -15,7 +15,6 @@ using Snap.Hutao.Model;
using Snap.Hutao.Service;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Service.Game;
-using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;