Compare commits

...

1 Commits

Author SHA1 Message Date
qhy040404
d2874ce291 add ability to fix missing config.ini 2024-04-02 23:36:04 +08:00
10 changed files with 223 additions and 34 deletions

View File

@@ -1334,6 +1334,9 @@
<data name="ViewDialogLaunchGameAccountTitle" xml:space="preserve">
<value>为账号命名</value>
</data>
<data name="ViewDialogLaunchGameConfigurationFixDialogTitle" xml:space="preserve">
<value>正在修复配置文件</value>
</data>
<data name="ViewDialogLaunchGamePackageConvertHint" xml:space="preserve">
<value>转换可能需要花费一段时间,请勿关闭胡桃</value>
</data>
@@ -1625,6 +1628,12 @@
<data name="ViewModelLaunchGameEnsureGameResourceFail" xml:space="preserve">
<value>切换服务器失败</value>
</data>
<data name="ViewModelLaunchGameFixConfigurationFileButtonText" xml:space="preserve">
<value>修复配置文件</value>
</data>
<data name="ViewModelLaunchGameFixConfigurationFileSuccess" xml:space="preserve">
<value>修复完成</value>
</data>
<data name="ViewModelLaunchGameIdentifyMonitorsAction" xml:space="preserve">
<value>识别显示器</value>
</data>
@@ -1637,6 +1646,9 @@
<data name="ViewModelLaunchGameSchemeNotSelected" xml:space="preserve">
<value>尚未选择任何服务器</value>
</data>
<data name="ViewModelLaunchGameSetGamePathButtonText" xml:space="preserve">
<value>设置游戏目录</value>
</data>
<data name="ViewModelLaunchGameSwitchGameAccountFail" xml:space="preserve">
<value>切换账号失败</value>
</data>

View File

@@ -11,5 +11,5 @@ internal interface IInfoBarService
{
ObservableCollection<InfoBar> Collection { get; }
void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay);
void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay, string? buttonContent, ICommand? buttonCommand);
}

View File

@@ -34,22 +34,33 @@ internal sealed class InfoBarService : IInfoBarService
get => collection ??= [];
}
public void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
public void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay, string? buttonContent = default, ICommand? buttonCommand = default)
{
if (collection is null)
{
return;
}
PrepareInfoBarAndShowCoreAsync(severity, title, message, delay).SafeForget(logger);
PrepareInfoBarAndShowCoreAsync(severity, title, message, delay, buttonContent, buttonCommand).SafeForget(logger);
}
private async ValueTask PrepareInfoBarAndShowCoreAsync(InfoBarSeverity severity, string? title, string? message, int delay)
private async ValueTask PrepareInfoBarAndShowCoreAsync(InfoBarSeverity severity, string? title, string? message, int delay, string? buttonContent, ICommand? buttonCommand)
{
await taskContext.SwitchToMainThreadAsync();
Button? actionButton = default;
if (buttonContent is not null)
{
actionButton = new()
{
Content = buttonContent,
Command = buttonCommand,
};
}
InfoBar infoBar = new()
{
ActionButton = actionButton,
Severity = severity,
Title = title,
Message = message,

View File

@@ -9,51 +9,101 @@ internal static class InfoBarServiceExtension
{
public static void Information(this IInfoBarService infoBarService, string message, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, null, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, null, message, delay, null, null);
}
public static void Information(this IInfoBarService infoBarService, string title, string message, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay, null, null);
}
public static void Information(this IInfoBarService infoBarService, string title, string message, string buttonContent, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay, buttonContent, null);
}
public static void Information(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay, buttonContent, buttonCommand);
}
public static void Success(this IInfoBarService infoBarService, string message, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, null, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, null, message, delay, null, null);
}
public static void Success(this IInfoBarService infoBarService, string title, string message, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay, null, null);
}
public static void Success(this IInfoBarService infoBarService, string title, string message, string buttonContent, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay, buttonContent, null);
}
public static void Success(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int delay = 5000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay, buttonContent, buttonCommand);
}
public static void Warning(this IInfoBarService infoBarService, string message, int delay = 30000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, null, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, null, message, delay, null, null);
}
public static void Warning(this IInfoBarService infoBarService, string title, string message, int delay = 30000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, title, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, title, message, delay, null, null);
}
public static void Warning(this IInfoBarService infoBarService, string title, string message, string buttonContent, int delay = 30000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, title, message, delay, buttonContent, null);
}
public static void Warning(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int delay = 30000)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Warning, title, message, delay, buttonContent, buttonCommand);
}
public static void Error(this IInfoBarService infoBarService, string message, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, null, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, null, message, delay, null, null);
}
public static void Error(this IInfoBarService infoBarService, string title, string message, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, title, message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, title, message, delay, null, null);
}
public static void Error(this IInfoBarService infoBarService, string title, string message, string buttonContent, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, title, message, delay, buttonContent, null);
}
public static void Error(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, title, message, delay, buttonContent, buttonCommand);
}
public static void Error(this IInfoBarService infoBarService, Exception ex, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, ex.Message, delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, ex.Message, delay, null, null);
}
public static void Error(this IInfoBarService infoBarService, Exception ex, string title, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay, null, null);
}
public static void Error(this IInfoBarService infoBarService, Exception ex, string title, string buttonContent, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay, buttonContent, null);
}
public static void Error(this IInfoBarService infoBarService, Exception ex, string title, string buttonContent, ICommand buttonCommand, int delay = 0)
{
infoBarService.PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay, buttonContent, buttonCommand);
}
}

View File

@@ -176,6 +176,7 @@
<None Remove="View\Dialog\HutaoPassportResetPasswordDialog.xaml" />
<None Remove="View\Dialog\HutaoPassportUnregisterDialog.xaml" />
<None Remove="View\Dialog\LaunchGameAccountNameDialog.xaml" />
<None Remove="View\Dialog\LaunchGameConfigurationFixDialog.xaml" />
<None Remove="View\Dialog\LaunchGamePackageConvertDialog.xaml" />
<None Remove="View\Dialog\ReconfirmDialog.xaml" />
<None Remove="View\Dialog\UserDialog.xaml" />
@@ -349,6 +350,11 @@
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<Page Update="View\Dialog\LaunchGameConfigurationFixDialog.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Control\Theme\SegmentedOverride.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,25 @@
<ContentDialog
x:Class="Snap.Hutao.View.Dialog.LaunchGameConfigurationFixDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shccs="using:Snap.Hutao.Control.Collection.Selector"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shvd="using:Snap.Hutao.View.Dialog"
Title="{shcm:ResourceString Name=ViewDialogLaunchGameConfigurationFixDialogTitle}"
d:DataContext="{d:DesignInstance shvd:LaunchGameConfigurationFixDialog}"
CloseButtonText="{shcm:ResourceString Name=ContentDialogCancelCloseButtonText}"
PrimaryButtonText="{shcm:ResourceString Name=ContentDialogConfirmPrimaryButtonText}"
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">
<shccs:ComboBox2
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
DisplayMemberPath="DisplayName"
EnableMemberPath="IsNotCompatOnly"
ItemsSource="{x:Bind KnownSchemes}"
SelectedItem="{x:Bind SelectedScheme, Mode=TwoWay}"
Style="{StaticResource DefaultComboBoxStyle}"/>
</ContentDialog>

View File

@@ -0,0 +1,24 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Service.Game.Scheme;
namespace Snap.Hutao.View.Dialog;
[DependencyProperty("KnownSchemes", typeof(IEnumerable<LaunchScheme>))]
[DependencyProperty("SelectedScheme", typeof(LaunchScheme))]
internal sealed partial class LaunchGameConfigurationFixDialog : ContentDialog
{
public LaunchGameConfigurationFixDialog()
{
InitializeComponent();
}
public async ValueTask<ValueResult<bool, LaunchScheme?>> GetLaunchSchemeAsync()
{
ContentDialogResult result = await ShowAsync();
return new(result == ContentDialogResult.Primary, SelectedScheme);
}
}

View File

@@ -2,39 +2,98 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Game.Configuration;
using Snap.Hutao.Service.Game.Scheme;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.View.Page;
using System.IO;
namespace Snap.Hutao.ViewModel.Game;
internal static class LaunchGameShared
[Injection(InjectAs.Transient)]
[ConstructorGenerated]
internal sealed partial class LaunchGameShared
{
public static LaunchScheme? GetCurrentLaunchSchemeFromConfigFile(IGameServiceFacade gameService, IInfoBarService infoBarService)
private readonly IContentDialogFactory contentDialogFactory;
private readonly INavigationService navigationService;
private readonly IGameServiceFacade gameService;
private readonly IInfoBarService infoBarService;
private readonly LaunchOptions launchOptions;
private readonly ITaskContext taskContext;
public LaunchScheme? GetCurrentLaunchSchemeFromConfigFile(IGameServiceFacade gameService, IInfoBarService infoBarService)
{
ChannelOptions options = gameService.GetChannelOptions();
if (options.ErrorKind is ChannelOptionsErrorKind.None)
switch (options.ErrorKind)
{
try
{
return KnownLaunchSchemes.Get().Single(scheme => scheme.Equals(options));
}
catch (InvalidOperationException)
{
if (!IgnoredInvalidChannelOptions.Contains(options))
case ChannelOptionsErrorKind.None:
try
{
// 后台收集
throw ThrowHelper.NotSupported($"不支持的 MultiChannel: {options}");
return KnownLaunchSchemes.Get().Single(scheme => scheme.Equals(options));
}
}
}
else
{
infoBarService.Warning($"{options.ErrorKind}", SH.FormatViewModelLaunchGameMultiChannelReadFail(options.FilePath));
catch (InvalidOperationException)
{
if (!IgnoredInvalidChannelOptions.Contains(options))
{
// 后台收集
throw ThrowHelper.NotSupported($"不支持的 MultiChannel: {options}");
}
}
break;
case ChannelOptionsErrorKind.ConfigurationFileNotFound:
infoBarService.Warning($"{options.ErrorKind}", SH.FormatViewModelLaunchGameMultiChannelReadFail(options.FilePath), SH.ViewModelLaunchGameFixConfigurationFileButtonText, HandleConfigurationFileNotFoundCommand);
break;
case ChannelOptionsErrorKind.GamePathNullOrEmpty:
infoBarService.Warning($"{options.ErrorKind}", SH.FormatViewModelLaunchGameMultiChannelReadFail(options.FilePath), SH.ViewModelLaunchGameSetGamePathButtonText, HandleGamePathNullOrEmptyCommand);
break;
}
return default;
}
[Command("HandleConfigurationFileNotFoundCommand")]
private async void HandleConfigurationFileNotFoundAsync()
{
launchOptions.TryGetGameFileSystem(out GameFileSystem? gameFileSystem);
ArgumentNullException.ThrowIfNull(gameFileSystem);
bool isOversea = LaunchScheme.ExecutableIsOversea(gameFileSystem.GameFileName);
string version = await File.ReadAllTextAsync(Path.Combine(gameFileSystem.GameDirectory, isOversea ? GameConstants.GenshinImpactData : GameConstants.YuanShenData, "Persistent", "ScriptVersion")).ConfigureAwait(false);
LaunchGameConfigurationFixDialog dialog = await contentDialogFactory.CreateInstanceAsync<LaunchGameConfigurationFixDialog>().ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync();
dialog.KnownSchemes = KnownLaunchSchemes.Get().Where(scheme => scheme.IsOversea == isOversea);
dialog.SelectedScheme = dialog.KnownSchemes.First(scheme => scheme.IsNotCompatOnly);
(bool isOk, LaunchScheme? launchScheme) = await dialog.GetLaunchSchemeAsync().ConfigureAwait(false);
if (isOk)
{
ArgumentNullException.ThrowIfNull(launchScheme);
string content = $"""
[General]
channel={(int)launchScheme.Channel}
cps=mihoyo
game_version={version}
sub_channel={(int)launchScheme.SubChannel}
sdk_version=
game_biz=hk4e_{(launchScheme.IsOversea ? "global" : "cn")}
""";
await File.WriteAllTextAsync(gameFileSystem.GameConfigFilePath, content).ConfigureAwait(false);
infoBarService.Success(SH.ViewModelLaunchGameFixConfigurationFileSuccess);
}
}
[Command("HandleGamePathNullOrEmptyCommand")]
private void HandleGamePathNullOrEmpty()
{
navigationService.Navigate<LaunchGamePage>(INavigationAwaiter.Default);
}
}

View File

@@ -40,6 +40,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
private readonly LaunchStatusOptions launchStatusOptions;
private readonly IGameLocatorFactory gameLocatorFactory;
private readonly ILogger<LaunchGameViewModel> logger;
private readonly LaunchGameShared launchGameShared;
private readonly IInfoBarService infoBarService;
private readonly ResourceClient resourceClient;
private readonly RuntimeOptions runtimeOptions;
@@ -99,7 +100,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
{
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
LaunchScheme? scheme = LaunchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
LaunchScheme? scheme = launchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
await taskContext.SwitchToMainThreadAsync();
await SetSelectedSchemeAsync(scheme).ConfigureAwait(true);

View File

@@ -23,6 +23,7 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
{
private readonly LaunchStatusOptions launchStatusOptions;
private readonly ILogger<LaunchGameViewModelSlim> logger;
private readonly LaunchGameShared launchGameShared;
private readonly IInfoBarService infoBarService;
private readonly IGameServiceFacade gameService;
private readonly ITaskContext taskContext;
@@ -47,7 +48,7 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
/// <inheritdoc/>
protected override async Task OpenUIAsync()
{
LaunchScheme? scheme = LaunchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
LaunchScheme? scheme = launchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
ObservableCollection<GameAccount> accounts = gameService.GameAccountCollection;
try
@@ -76,7 +77,7 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
private async Task LaunchAsync()
{
IInfoBarService infoBarService = ServiceProvider.GetRequiredService<IInfoBarService>();
LaunchScheme? scheme = LaunchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
LaunchScheme? scheme = launchGameShared.GetCurrentLaunchSchemeFromConfigFile(gameService, infoBarService);
try
{