add ability to fix missing config.ini

This commit is contained in:
qhy040404
2024-04-02 23:36:04 +08:00
parent 5093246571
commit bf67fcf3a2
9 changed files with 170 additions and 22 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

@@ -80,4 +80,4 @@ internal sealed class InfoBarService : IInfoBarService
taskContext.BeginInvokeOnMainThread(() => collection.Remove(sender));
sender.Closed -= infobarClosedEventHandler;
}
}
}

View File

@@ -18,6 +18,11 @@ internal static class InfoBarServiceExtension
infoBarService.Information(builder => builder.SetTitle(title).SetMessage(message).SetDelay(milliSeconds));
}
public static void Information(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 5000)
{
infoBarService.Information(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(delay));
}
public static void Information(this IInfoBarService infoBarService, Action<IInfoBarOptionsBuilder> configure)
{
infoBarService.PrepareInfoBarAndShow(builder => builder.SetSeverity(InfoBarSeverity.Informational).Configure(configure));
@@ -48,6 +53,11 @@ internal static class InfoBarServiceExtension
infoBarService.Warning(builder => builder.SetTitle(title).SetMessage(message).SetDelay(milliSeconds));
}
public static void Warning(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 30000)
{
infoBarService.Warning(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(delay));
}
public static void Warning(this IInfoBarService infoBarService, Action<IInfoBarOptionsBuilder> configure)
{
infoBarService.PrepareInfoBarAndShow(builder => builder.SetSeverity(InfoBarSeverity.Warning).Configure(configure));
@@ -63,6 +73,11 @@ internal static class InfoBarServiceExtension
infoBarService.Error(builder => builder.SetTitle(title).SetMessage(message).SetDelay(milliSeconds));
}
public static void Error(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 0)
{
infoBarService.Error(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(delay));
}
public static void Error(this IInfoBarService infoBarService, Exception ex, int milliSeconds = 0)
{
infoBarService.Error(builder => builder.SetTitle(ex.GetType().Name).SetMessage(ex.Message).SetDelay(milliSeconds));
@@ -73,8 +88,13 @@ internal static class InfoBarServiceExtension
infoBarService.Error(builder => builder.SetTitle(ex.GetType().Name).SetMessage($"{subtitle}\n{ex.Message}").SetDelay(milliSeconds));
}
public static void Error(this IInfoBarService infoBarService, Exception ex, string subtitle, string buttonContent, ICommand buttonCommand, int milliSeconds = 0)
{
infoBarService.Error(builder => builder.SetTitle(ex.GetType().Name).SetMessage($"{subtitle}\n{ex.Message}").SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(milliSeconds));
}
public static void Error(this IInfoBarService infoBarService, Action<IInfoBarOptionsBuilder> configure)
{
infoBarService.PrepareInfoBarAndShow(builder => builder.SetSeverity(InfoBarSeverity.Error).Configure(configure));
}
}
}

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
{