support game resource download switch

This commit is contained in:
DismissedLight
2023-03-07 16:28:00 +08:00
parent 128b985609
commit c71ecd89e3
14 changed files with 553 additions and 291 deletions

View File

@@ -66,6 +66,7 @@
<!-- Converters -->
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<cwuc:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>

View File

@@ -8,6 +8,7 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
using System.Diagnostics;
using Windows.ApplicationModel.Background;
using Windows.Storage;
namespace Snap.Hutao;

View File

@@ -10,7 +10,6 @@ using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Navigation;
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
using Windows.ApplicationModel;
@@ -164,10 +163,7 @@ internal static class Activation
default:
{
// Increase launch times
LocalSetting.Set(SettingKeys.LaunchTimes, LocalSetting.Get(SettingKeys.LaunchTimes, 0) + 1);
await WaitMainWindowAsync().ConfigureAwait(false);
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
break;
}
}
@@ -175,6 +171,14 @@ internal static class Activation
}
}
private static async Task HandleNormalLaunchActionAsync()
{
// Increase launch times
LocalSetting.Set(SettingKeys.LaunchTimes, LocalSetting.Get(SettingKeys.LaunchTimes, 0) + 1);
await WaitMainWindowAsync().ConfigureAwait(false);
}
private static async Task WaitMainWindowAsync()
{
await ThreadHelper.SwitchToMainThreadAsync();
@@ -211,6 +215,12 @@ internal static class Activation
await HandleDailyNoteActionAsync(action, parameter, isRedirected).ConfigureAwait(false);
break;
}
default:
{
await HandleNormalLaunchActionAsync().ConfigureAwait(false);
break;
}
}
}

View File

@@ -3714,6 +3714,51 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 游戏选项 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameOptionsHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameOptionsHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 增量包 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameResourceDiffHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameResourceDiffHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 资源下载 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameResourceHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameResourceHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 客户端 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameResourceLatestHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameResourceLatestHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 预下载 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameResourcePreDownloadHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameResourcePreDownloadHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 在游戏时可以随时调整 的本地化字符串。
/// </summary>

View File

@@ -1335,6 +1335,21 @@
<data name="ViewPageLaunchGameMonitorsHeader" xml:space="preserve">
<value>显示器</value>
</data>
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
<value>游戏选项</value>
</data>
<data name="ViewPageLaunchGameResourceDiffHeader" xml:space="preserve">
<value>增量包</value>
</data>
<data name="ViewPageLaunchGameResourceHeader" xml:space="preserve">
<value>资源下载</value>
</data>
<data name="ViewPageLaunchGameResourceLatestHeader" xml:space="preserve">
<value>客户端</value>
</data>
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
<value>预下载</value>
</data>
<data name="ViewPageLaunchGameSetFpsDescription" xml:space="preserve">
<value>在游戏时可以随时调整</value>
</data>

View File

@@ -237,10 +237,14 @@ internal sealed class GameService : IGameService
string gameFileName = Path.GetFileName(gamePath);
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
Response<GameResource> response = await Ioc.Default
.GetRequiredService<ResourceClient>()
.GetResourceAsync(launchScheme)
.ConfigureAwait(false);
Response<GameResource> response;
using (IServiceScope scope = scopeFactory.CreateScope())
{
response = await scope.ServiceProvider
.GetRequiredService<ResourceClient>()
.GetResourceAsync(launchScheme)
.ConfigureAwait(false);
}
if (response.IsOk())
{

View File

@@ -40,14 +40,14 @@ internal sealed class PackageConverter
/// 调用前需要确认本地文件与服务器上的不同
/// </summary>
/// <param name="targetScheme">目标启动方案</param>
/// <param name="gameResouce">游戏资源</param>
/// <param name="gameResource">游戏资源</param>
/// <param name="gameFolder">游戏目录</param>
/// <param name="progress">进度</param>
/// <returns>替换结果与资源</returns>
public async Task<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GameResource gameResouce, string gameFolder, IProgress<PackageReplaceStatus> progress)
public async Task<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GameResource gameResource, string gameFolder, IProgress<PackageReplaceStatus> progress)
{
await ThreadHelper.SwitchToBackgroundAsync();
string scatteredFilesUrl = gameResouce.Game.Latest.DecompressedPath;
string scatteredFilesUrl = gameResource.Game.Latest.DecompressedPath;
Uri pkgVersionUri = $"{scatteredFilesUrl}/pkg_version".ToUri();
ConvertDirection direction = targetScheme.IsOversea ? ConvertDirection.ChineseToOversea : ConvertDirection.OverseaToChinese;
@@ -68,7 +68,7 @@ internal sealed class PackageConverter
Dictionary<string, VersionItem> localItems;
using (FileStream localSteam = File.OpenRead(Path.Combine(gameFolder, "pkg_version")))
{
localItems = await GetLocalVersionItemsAsync(localSteam, direction, ConvertRemoteName).ConfigureAwait(false);
localItems = await GetLocalVersionItemsAsync(localSteam, direction).ConfigureAwait(false);
}
IEnumerable<ItemOperationInfo> diffOperations = GetItemOperationInfos(remoteItems, localItems).OrderBy(i => (int)i.Type);
@@ -134,22 +134,6 @@ internal sealed class PackageConverter
}
}
private static string ConvertRemoteName(string remoteName, ConvertDirection direction)
{
// 我们已经提前重命名了整个 Data 文件夹 所以需要将 RemoteName 中的 Data 同样替换
if (remoteName.StartsWith(YuanShenData) || remoteName.StartsWith(GenshinImpactData))
{
return direction switch
{
ConvertDirection.OverseaToChinese => $"{YuanShenData}{remoteName[GenshinImpactData.Length..]}",
ConvertDirection.ChineseToOversea => $"{GenshinImpactData}{remoteName[YuanShenData.Length..]}",
_ => remoteName,
};
}
return remoteName;
}
private static IEnumerable<ItemOperationInfo> GetItemOperationInfos(Dictionary<string, VersionItem> remote, Dictionary<string, VersionItem> local)
{
foreach ((string remoteName, VersionItem remoteItem) in remote)
@@ -368,7 +352,7 @@ internal sealed class PackageConverter
return results;
}
private async Task<Dictionary<string, VersionItem>> GetLocalVersionItemsAsync(Stream stream, ConvertDirection direction, Func<string, ConvertDirection, string> nameConverter)
private async Task<Dictionary<string, VersionItem>> GetLocalVersionItemsAsync(Stream stream, ConvertDirection direction)
{
Dictionary<string, VersionItem> results = new();
@@ -379,7 +363,21 @@ internal sealed class PackageConverter
if (!string.IsNullOrEmpty(raw))
{
VersionItem item = JsonSerializer.Deserialize<VersionItem>(raw, options)!;
results.Add(nameConverter(item.RemoteName, direction), item);
string remoteName = item.RemoteName;
// 我们已经提前重命名了整个 Data 文件夹 所以需要将 RemoteName 中的 Data 同样替换
if (remoteName.StartsWith(YuanShenData) || remoteName.StartsWith(GenshinImpactData))
{
remoteName = direction switch
{
ConvertDirection.OverseaToChinese => $"{YuanShenData}{remoteName[GenshinImpactData.Length..]}",
ConvertDirection.ChineseToOversea => $"{GenshinImpactData}{remoteName[YuanShenData.Length..]}",
_ => remoteName,
};
}
results.Add(remoteName, item);
}
}
}

View File

@@ -76,6 +76,7 @@
<None Remove="View\Control\BottomTextControl.xaml" />
<None Remove="View\Control\DescParamComboBox.xaml" />
<None Remove="View\Control\ItemIcon.xaml" />
<None Remove="View\Control\LaunchGameResourceExpander.xaml" />
<None Remove="View\Control\LoadingView.xaml" />
<None Remove="View\Control\SkillPivot.xaml" />
<None Remove="View\Control\StatisticsCard.xaml" />
@@ -446,6 +447,11 @@
<LastGenOutput>SH.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="View\Control\LaunchGameResourceExpander.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Page\WikiMonsterPage.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,99 @@
<Expander
x:Class="Snap.Hutao.View.Control.LaunchGameResourceExpander"
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"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsExpanded="True"
mc:Ignorable="d">
<Grid Margin="16,16,16,16">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding DisplayName}"/>
<StackPanel
Grid.Row="1"
Margin="0,4,0,0"
Orientation="Horizontal">
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xF012;"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
HorizontalAlignment="Left"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xE8B7;"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xEDAD;"/>
<TextBlock
Margin="8,0,0,0"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Md5}"/>
</StackPanel>
<HyperlinkButton
Grid.RowSpan="2"
Height="38.4"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="&#xE8A7;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
NavigateUri="{Binding Path}"/>
<MenuFlyoutSeparator Grid.Row="2" Margin="4,16,4,0"/>
<ItemsControl Grid.Row="3" ItemsSource="{Binding VoicePacks}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,16,0,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding DisplayName}"/>
<StackPanel
Grid.Row="1"
Margin="0,4,0,0"
Orientation="Horizontal">
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xF012;"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
HorizontalAlignment="Left"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xE8B7;"/>
<TextBlock
Width="80"
Margin="8,0,0,0"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="&#xEDAD;"/>
<TextBlock
Margin="8,0,0,0"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding Md5}"/>
</StackPanel>
<HyperlinkButton
Grid.RowSpan="2"
Height="38.4"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="&#xE8A7;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
NavigateUri="{Binding Path}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Expander>

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.View.Control;
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>Դ Expander
/// </summary>
internal sealed partial class LaunchGameResourceExpander : Expander
{
/// <summary>
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD><C2B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>Դ Expander
/// </summary>
public LaunchGameResourceExpander()
{
InitializeComponent();
}
}

View File

@@ -12,6 +12,7 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shv="using:Snap.Hutao.ViewModel"
xmlns:shvc="using:Snap.Hutao.View.Control"
xmlns:wsc="using:WinUICommunity.SettingsUI.Controls"
d:DataContext="{d:DesignInstance shv:LaunchGameViewModel}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
@@ -23,13 +24,10 @@
<Page.Resources>
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
<Visibility x:Key="VisibilityCollapsed">Collapsed</Visibility>
<Style BasedOn="{StaticResource SettingButtonStyle}" TargetType="Button">
<Setter Property="MinWidth" Value="156"/>
</Style>
<Style BasedOn="{StaticResource HyperlinkButtonStyle}" TargetType="HyperlinkButton">
<Setter Property="MinWidth" Value="156"/>
</Style>
<Style BasedOn="{StaticResource DefaultComboBoxStyle}" TargetType="ComboBox">
<Setter Property="MinWidth" Value="156"/>
</Style>
@@ -38,267 +36,291 @@
</Style>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.RowSpan="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="800"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="16">
<InfoBar
IsClosable="False"
IsOpen="True"
Message="{shcm:ResourceString Name=ViewPageLaunchGameConfigurationSaveHint}"
Severity="Informational"/>
<InfoBar
Margin="0,2,0,0"
IsClosable="False"
IsOpen="{Binding IsElevated, Converter={StaticResource BoolNegationConverter}}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameElevationHint}"
Severity="Warning"/>
<wsc:SettingsGroup Margin="0,0,0,0" Header="{shcm:ResourceString Name=ViewPageLaunchGameCommonHeader}">
<InfoBar
IsClosable="False"
IsOpen="{Binding IsElevated}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeHint}"
Severity="Informational"/>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeHeader}"
Icon="&#xE8AB;"
IsEnabled="{Binding IsElevated}">
<wsc:Setting.ActionContent>
<ComboBox
DisplayMemberPath="DisplayName"
ItemsSource="{Binding KnownSchemes}"
SelectedItem="{Binding SelectedScheme, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:SettingExpander IsExpanded="True">
<wsc:SettingExpander.Header>
<Rectangle
Height="48"
VerticalAlignment="Top"
Fill="{ThemeResource CardBackgroundFillColorDefaultBrush}"
IsHitTestVisible="False"/>
<Pivot>
<Pivot.RightHeader>
<CommandBar DefaultLabelPosition="Right">
<AppBarButton
Command="{Binding LaunchCommand}"
Icon="{shcm:FontIcon Glyph=&#xE7FC;}"
Label="{shcm:ResourceString Name=ViewPageLaunchGameAction}"/>
</CommandBar>
</Pivot.RightHeader>
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameOptionsHeader}">
<ScrollViewer Grid.RowSpan="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="1000"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="16">
<InfoBar
IsClosable="False"
IsOpen="True"
Message="{shcm:ResourceString Name=ViewPageLaunchGameConfigurationSaveHint}"
Severity="Informational"/>
<InfoBar
Margin="0,2,0,0"
IsClosable="False"
IsOpen="{Binding IsElevated, Converter={StaticResource BoolNegationConverter}}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameElevationHint}"
Severity="Warning"/>
<wsc:SettingsGroup Margin="0,0,0,0" Header="{shcm:ResourceString Name=ViewPageLaunchGameCommonHeader}">
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountHeader}"
Icon="&#xE748;">
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchSchemeHeader}"
Icon="&#xE8AB;"
IsEnabled="{Binding IsElevated}">
<wsc:Setting.ActionContent>
<Button
Grid.Column="1"
MinWidth="124"
Margin="0,0,8,0"
HorizontalAlignment="Right"
Command="{Binding DetectGameAccountCommand}"
Content="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDetectAction}"/>
<ComboBox
DisplayMemberPath="DisplayName"
ItemsSource="{Binding KnownSchemes}"
SelectedItem="{Binding SelectedScheme, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingExpander.Header>
<ListView ItemsSource="{Binding GameAccounts}" SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Margin="0,12">
<TextBlock Text="{Binding Name}"/>
<TextBlock
Opacity="0.8"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding AttachUid, TargetNullValue={shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountAttachUidNull}}"/>
</StackPanel>
<StackPanel
x:Name="ButtonPanel"
HorizontalAlignment="Right"
Orientation="Horizontal"
Visibility="Collapsed">
<wsc:SettingExpander IsExpanded="True">
<wsc:SettingExpander.Header>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountHeader}"
Icon="&#xE748;">
<wsc:Setting.ActionContent>
<Button
MinWidth="48"
Margin="4,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.AttachGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE723;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountAttachUidToolTip}"/>
<Button
MinWidth="48"
Margin="4,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.ModifyGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE8AC;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRenameToolTip}"/>
<Button
MinWidth="48"
Margin="4,8,0,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.RemoveGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE74D;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRemoveToolTip}"/>
</StackPanel>
Grid.Column="1"
MinWidth="124"
Margin="0,0,8,0"
HorizontalAlignment="Right"
Command="{Binding DetectGameAccountCommand}"
Content="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountDetectAction}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingExpander.Header>
<ListView ItemsSource="{Binding GameAccounts}" SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Margin="0,12">
<TextBlock Text="{Binding Name}"/>
<TextBlock
Opacity="0.8"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding AttachUid, TargetNullValue={shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountAttachUidNull}}"/>
</StackPanel>
<StackPanel
x:Name="ButtonPanel"
HorizontalAlignment="Right"
Orientation="Horizontal"
Visibility="Collapsed">
<Button
MinWidth="48"
Margin="4,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.AttachGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE723;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountAttachUidToolTip}"/>
<Button
MinWidth="48"
Margin="4,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.ModifyGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE8AC;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRenameToolTip}"/>
<Button
MinWidth="48"
Margin="4,8,0,8"
VerticalAlignment="Stretch"
Command="{Binding DataContext.RemoveGameAccountCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
Content="&#xE74D;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageLaunchGameSwitchAccountRemoveToolTip}"/>
</StackPanel>
<Grid.Resources>
<Storyboard x:Name="ButtonPanelVisibleStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Grid.Resources>
<Storyboard x:Name="ButtonPanelVisibleStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="ButtonPanelCollapsedStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Storyboard x:Name="ButtonPanelCollapsedStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonPanel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="PointerEntered">
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelVisibleStoryboard}"/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerExited">
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelCollapsedStoryboard}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</wsc:SettingExpander>
</wsc:SettingsGroup>
<wsc:SettingsGroup Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceHeader}">
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveHeader}"
Icon="&#xE740;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsExclusive, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenHeader}"
Icon="&#xE740;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsFullScreen, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessHeader}"
Icon="&#xE737;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsBorderless, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="PointerEntered">
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelVisibleStoryboard}"/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerExited">
<mxim:ControlStoryboardAction Storyboard="{StaticResource ButtonPanelCollapsedStoryboard}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</wsc:SettingExpander>
</wsc:SettingsGroup>
<wsc:SettingsGroup Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceHeader}">
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceExclusiveHeader}"
Icon="&#xE740;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsExclusive, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceFullscreenHeader}"
Icon="&#xE740;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsFullScreen, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceBorderlessHeader}"
Icon="&#xE737;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.IsBorderless, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Margin="0,6,0,0"
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthHeader}"
Icon="&#xE76F;">
<wsc:Setting.ActionContent>
<NumberBox Width="156" Value="{Binding Options.ScreenWidth, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightHeader}"
Icon="&#xE784;">
<wsc:Setting.ActionContent>
<NumberBox Width="156" Value="{Binding Options.ScreenHeight, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Margin="0,6,0,0"
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenWidthHeader}"
Icon="&#xE76F;">
<wsc:Setting.ActionContent>
<NumberBox Width="156" Value="{Binding Options.ScreenWidth, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameAppearanceScreenHeightHeader}"
Icon="&#xE784;">
<wsc:Setting.ActionContent>
<NumberBox Width="156" Value="{Binding Options.ScreenHeight, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsHeader}"
Icon="&#xE975;">
<wsc:Setting.ActionContent>
<ComboBox
DisplayMemberPath="Name"
ItemsSource="{Binding Options.Monitors}"
SelectedItem="{Binding Options.Monitor, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingsGroup>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameMonitorsHeader}"
Icon="&#xE975;">
<wsc:Setting.ActionContent>
<ComboBox
DisplayMemberPath="Name"
ItemsSource="{Binding Options.Monitors}"
SelectedItem="{Binding Options.Monitor, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingsGroup>
<wsc:SettingsGroup Header="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHeader}" IsEnabled="{Binding IsElevated}">
<InfoBar
IsClosable="False"
IsOpen="{Binding IsElevated}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHint}"
Severity="Error"/>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsHeader}"
Icon="&#xE785;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.UnlockFps, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOff}"
OnContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOn}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting Header="{shcm:ResourceString Name=ViewPageLaunchGameSetFpsHeader}">
<wsc:Setting.Description>
<StackPanel>
<TextBlock Text="{shcm:ResourceString Name=ViewPageLaunchGameSetFpsDescription}"/>
<TextBlock Text="{Binding Options.TargetFps}"/>
</StackPanel>
</wsc:Setting.Description>
<wsc:Setting.ActionContent>
<Slider
Width="400"
Maximum="360"
Minimum="60"
Value="{Binding Options.TargetFps, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingsGroup>
</StackPanel>
</Grid>
<mxi:Interaction.Behaviors>
<cwub:AutoFocusBehavior/>
</mxi:Interaction.Behaviors>
</ScrollViewer>
<Grid Grid.Row="1" VerticalAlignment="Bottom">
<Button
Grid.Column="3"
Width="100"
Height="80"
MinWidth="80"
Margin="24"
HorizontalAlignment="Right"
Command="{Binding LaunchCommand}"
Style="{StaticResource AccentButtonStyle}">
<StackPanel>
<FontIcon FontSize="36" Glyph="&#xE7FC;"/>
<TextBlock Margin="0,4,0,0" Text="{shcm:ResourceString Name=ViewPageLaunchGameAction}"/>
</StackPanel>
</Button>
</Grid>
<wsc:SettingsGroup Header="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHeader}" IsEnabled="{Binding IsElevated}">
<InfoBar
IsClosable="False"
IsOpen="{Binding IsElevated}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHint}"
Severity="Error"/>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsHeader}"
Icon="&#xE785;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.UnlockFps, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOff}"
OnContent="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsOn}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting Header="{shcm:ResourceString Name=ViewPageLaunchGameSetFpsHeader}">
<wsc:Setting.Description>
<StackPanel>
<TextBlock Text="{shcm:ResourceString Name=ViewPageLaunchGameSetFpsDescription}"/>
<TextBlock Text="{Binding Options.TargetFps}"/>
</StackPanel>
</wsc:Setting.Description>
<wsc:Setting.ActionContent>
<Slider
Width="400"
Maximum="360"
Minimum="60"
Value="{Binding Options.TargetFps, Mode=TwoWay}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingsGroup>
</StackPanel>
</Grid>
</ScrollViewer>
</PivotItem>
<PivotItem Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceHeader}">
<Grid>
<ScrollViewer Visibility="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolConverter}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="1000"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<StackPanel>
<shvc:LaunchGameResourceExpander
Margin="16,16,16,0"
DataContext="{Binding GameResource.PreDownloadGame.Latest, Mode=OneWay}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourcePreDownloadHeader}"
Visibility="{Binding FallbackValue={StaticResource VisibilityCollapsed}, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
<shvc:LaunchGameResourceExpander
Margin="16,16,16,0"
DataContext="{Binding GameResource.Game.Latest, Mode=OneWay}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceLatestHeader}"/>
<ItemsControl Margin="0,0,0,16" ItemsSource="{Binding GameResource.Game.Diffs, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<shvc:LaunchGameResourceExpander
Margin="16,16,16,0"
DataContext="{Binding Mode=OneWay}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceDiffHeader}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</ScrollViewer>
<shvc:LoadingView IsLoading="{Binding GameResource, Converter={StaticResource EmptyObjectToBoolRevertConverter}}"/>
</Grid>
</PivotItem>
</Pivot>
</Grid>
</shc:ScopedPage>

View File

@@ -15,6 +15,7 @@ using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using System.Collections.ObjectModel;
using System.IO;
@@ -35,7 +36,6 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
private readonly IServiceProvider serviceProvider;
private readonly IGameService gameService;
private readonly AppDbContext appDbContext;
private readonly IMemoryCache memoryCache;
private readonly List<LaunchScheme> knownSchemes = LaunchScheme.KnownSchemes.ToList();
@@ -43,6 +43,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
private LaunchScheme? selectedScheme;
private ObservableCollection<GameAccount>? gameAccounts;
private GameAccount? selectedGameAccount;
private GameResource? gameResource;
/// <summary>
/// 构造一个新的启动游戏视图模型
@@ -51,7 +52,6 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
public LaunchGameViewModel(IServiceProvider serviceProvider)
{
gameService = serviceProvider.GetRequiredService<IGameService>();
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
Options = serviceProvider.GetRequiredService<LaunchOptions>();
this.serviceProvider = serviceProvider;
@@ -71,7 +71,19 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
/// <summary>
/// 当前选择的服务器方案
/// </summary>
public LaunchScheme? SelectedScheme { get => selectedScheme; set => SetProperty(ref selectedScheme, value); }
public LaunchScheme? SelectedScheme
{
get => selectedScheme; set
{
if (SetProperty(ref selectedScheme, value))
{
if (value != null)
{
UpdateGameResourceAsync(value).SafeForget();
}
}
}
}
/// <summary>
/// 游戏账号集合
@@ -88,6 +100,11 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
/// </summary>
public LaunchOptions Options { get; }
/// <summary>
/// 游戏资源
/// </summary>
public GameResource? GameResource { get => gameResource; set => SetProperty(ref gameResource, value); }
/// <summary>
/// 是否提权
/// </summary>
@@ -122,6 +139,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
/// <inheritdoc/>
protected override async Task OpenUIAsync()
{
await ThreadHelper.SwitchToBackgroundAsync();
if (File.Exists(gameService.GetGamePathSkipLocator()))
{
try
@@ -131,6 +149,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
MultiChannel multi = gameService.GetMultiChannel();
if (string.IsNullOrEmpty(multi.ConfigFilePath))
{
await ThreadHelper.SwitchToMainThreadAsync();
SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
}
else
@@ -138,8 +157,10 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGameMultiChannelReadFail);
}
GameAccounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(true);
ObservableCollection<GameAccount> accounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
GameAccounts = accounts;
// Sync uid
if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
{
@@ -155,12 +176,28 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
else
{
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGamePathInvalid);
await ThreadHelper.SwitchToMainThreadAsync();
await serviceProvider.GetRequiredService<INavigationService>()
.NavigateAsync<View.Page.SettingPage>(INavigationAwaiter.Default, true)
.ConfigureAwait(false);
}
}
private async Task UpdateGameResourceAsync(LaunchScheme scheme)
{
await ThreadHelper.SwitchToBackgroundAsync();
Web.Response.Response<GameResource> response = await serviceProvider
.GetRequiredService<ResourceClient>()
.GetResourceAsync(scheme)
.ConfigureAwait(false);
if (response.IsOk())
{
await ThreadHelper.SwitchToMainThreadAsync();
GameResource = response.Data;
}
}
private async Task LaunchAsync()
{
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();

View File

@@ -20,4 +20,9 @@ internal class PathMd5
/// </summary>
[JsonPropertyName("md5")]
public string Md5 { get; set; } = default!;
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get => System.IO.Path.GetFileName(Path.ToUri().LocalPath); }
}

View File

@@ -5,7 +5,6 @@ using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using Windows.Graphics;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.System.Diagnostics.ToolHelp;
using Windows.Win32.UI.WindowsAndMessaging;