support dynamically change game fps

This commit is contained in:
Lightczx
2023-05-10 19:52:22 +08:00
parent b386a35f07
commit e147b8773f
17 changed files with 197 additions and 101 deletions

View File

@@ -193,11 +193,6 @@ internal static class Activation
MainWindowReference.SetTarget(serviceProvider.GetRequiredService<MainWindow>());
await serviceProvider
.GetRequiredService<IInfoBarService>()
.WaitInitializationAsync()
.ConfigureAwait(false);
serviceProvider
.GetRequiredService<IMetadataService>()
.As<IMetadataServiceInitialization>()?

View File

@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Setting;
internal static class SettingKeys
{
/// <summary>
/// 窗体左侧
/// 窗体矩形
/// </summary>
public const string WindowRect = "WindowRect";
@@ -40,6 +40,11 @@ internal static class SettingKeys
/// </summary>
public const string PassportPassword = "PassportPassword";
/// <summary>
/// 消息是否显示
/// </summary>
public const string IsInfoBarToggleChecked = "IsInfoBarToggleChecked";
#region StaticResource
/// <summary>

View File

@@ -46,7 +46,7 @@ public static partial class Program
{
IServiceProvider serviceProvider = Ioc.Default;
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
_ = serviceProvider.GetRequiredService<ITaskContext>();
_ = serviceProvider.GetRequiredService<App>();
}
}

View File

@@ -2103,6 +2103,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 有新的通知 的本地化字符串。
/// </summary>
internal static string ViewInfoBarToggleTitle {
get {
return ResourceManager.GetString("ViewInfoBarToggleTitle", resourceCulture);
}
}
/// <summary>
/// 查找类似 启动游戏 的本地化字符串。
/// </summary>

View File

@@ -2010,4 +2010,7 @@
<data name="ViewPageHomeGreetingTextEpic1" xml:space="preserve">
<value>呐,旅行者,这是你来到提瓦特大陆的第 {0} 天哦</value>
</data>
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
<value>有新的通知</value>
</data>
</root>

View File

@@ -285,12 +285,12 @@ internal sealed partial class GameService : IGameService
try
{
Interlocked.Increment(ref runningGamesCounter);
bool isfirstInstance = Interlocked.Increment(ref runningGamesCounter) == 1;
game.Start();
bool isAdvancedOptionsAllowed = Activation.GetElevated() && appOptions.IsAdvancedLaunchOptionsEnabled;
if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances)
if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isfirstInstance)
{
ProcessInterop.DisableProtection(game, gamePath);
}

View File

@@ -52,7 +52,7 @@ internal static class ProcessInterop
/// <returns>任务</returns>
public static Task UnlockFpsAsync(Process game, LaunchOptions options)
{
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, options.TargetFps);
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game);
TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);

View File

@@ -19,6 +19,7 @@ namespace Snap.Hutao.Service.Game.Unlocker;
internal sealed class GameFpsUnlocker : IGameFpsUnlocker
{
private readonly Process gameProcess;
private readonly LaunchOptions launchOptions;
private nuint fpsAddress;
private bool isValid = true;
@@ -32,20 +33,11 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
/// 非管理员模式不能解锁
/// </summary>
/// <param name="gameProcess">游戏进程</param>
/// <param name="targetFPS">目标fps</param>
public GameFpsUnlocker(Process gameProcess, int targetFPS)
public GameFpsUnlocker(Process gameProcess)
{
Must.Range(targetFPS >= 30 && targetFPS <= 2000, "Target FPS threshold exceeded");
TargetFps = targetFPS;
this.gameProcess = gameProcess;
// TODO: use UnlockerOptions to replace parameters
}
/// <inheritdoc/>
public int TargetFps { get; set; }
/// <inheritdoc/>
public async Task UnlockAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit, TimeSpan adjustFpsDelay)
{
@@ -130,7 +122,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
{
if (!gameProcess.HasExited && fpsAddress != 0)
{
UnsafeWriteModuleMemory(gameProcess, fpsAddress, TargetFps);
UnsafeWriteModuleMemory(gameProcess, fpsAddress, launchOptions.TargetFps);
}
else
{

View File

@@ -9,11 +9,6 @@ namespace Snap.Hutao.Service.Game.Unlocker;
[HighQuality]
internal interface IGameFpsUnlocker
{
/// <summary>
/// 目标FPS,运行动态设置以动态更改帧率
/// </summary>
int TargetFps { get; set; }
/// <summary>
/// 异步的解锁帧数限制
/// </summary>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.Notification;
@@ -11,6 +12,11 @@ namespace Snap.Hutao.Service.Notification;
[HighQuality]
internal interface IInfoBarService
{
/// <summary>
/// 获取操作的集合
/// </summary>
ObservableCollection<InfoBar> Collection { get; }
/// <summary>
/// 显示错误消息
/// </summary>
@@ -56,12 +62,6 @@ internal interface IInfoBarService
/// <param name="delay">关闭延迟</param>
void Information(string title, string message, int delay = 5000);
/// <summary>
/// 使用指定的 <see cref="StackPanel"/> 初始化服务
/// </summary>
/// <param name="container">信息条的目标容器</param>
void Initialize(StackPanel container);
/// <summary>
/// 显示成功信息
/// </summary>

View File

@@ -3,6 +3,7 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.Notification;
@@ -15,13 +16,12 @@ internal sealed partial class InfoBarService : IInfoBarService
private readonly ITaskContext taskContext;
private readonly TaskCompletionSource initializaionCompletionSource;
private StackPanel? infoBarStack;
private ObservableCollection<InfoBar>? collection;
/// <inheritdoc/>
public void Initialize(StackPanel container)
public ObservableCollection<InfoBar> Collection
{
infoBarStack = container;
initializaionCompletionSource.TrySetResult();
get => collection ??= new();
}
/// <summary>
@@ -96,7 +96,7 @@ internal sealed partial class InfoBarService : IInfoBarService
private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
{
if (infoBarStack is null)
if (collection is null)
{
return;
}
@@ -125,12 +125,12 @@ internal sealed partial class InfoBarService : IInfoBarService
};
infoBar.Closed += OnInfoBarClosed;
infoBarStack!.Children.Add(infoBar);
collection!.Add(infoBar);
if (delay > 0)
{
await Task.Delay(delay).ConfigureAwait(true);
infoBarStack.Children.Remove(infoBar);
collection.Remove(infoBar);
infoBar.IsOpen = false;
}
}
@@ -140,7 +140,7 @@ internal sealed partial class InfoBarService : IInfoBarService
{
await taskContext.SwitchToMainThreadAsync();
infoBarStack!.Children.Remove(sender);
collection!.Remove(sender);
sender.Closed -= OnInfoBarClosed;
}
}

View File

@@ -37,7 +37,7 @@ internal sealed partial class UserService : IUserService
get => currentUser;
set
{
if (currentUser == value)
if (currentUser?.Entity.InnerId == value?.Entity.InnerId)
{
return;
}

View File

@@ -136,6 +136,7 @@
<None Remove="View\Dialog\LaunchGamePackageConvertDialog.xaml" />
<None Remove="View\Dialog\SignInWebViewDialog.xaml" />
<None Remove="View\Dialog\UserDialog.xaml" />
<None Remove="View\InfoBarView.xaml" />
<None Remove="View\MainView.xaml" />
<None Remove="View\Page\AchievementPage.xaml" />
<None Remove="View\Page\AnnouncementContentPage.xaml" />
@@ -301,6 +302,12 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="View\InfoBarView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Card\DailyNoteCard.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,101 @@
<UserControl
x:Class="Snap.Hutao.View.InfoBarView"
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:shcm="using:Snap.Hutao.Control.Markup"
mc:Ignorable="d">
<Grid>
<ItemsControl
MaxWidth="640"
Margin="32,48,32,32"
VerticalAlignment="Bottom"
ItemsSource="{x:Bind InfoBars}"
Visibility="{Binding ElementName=VisibilityButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
<ItemsControl.Transitions>
<RepositionThemeTransition/>
</ItemsControl.Transitions>
<ItemsControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<AcrylicBrush
x:Key="InfoBarErrorSeverityBackgroundBrush"
FallbackColor="#FDE7E9"
TintColor="#FDE7E9"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarWarningSeverityBackgroundBrush"
FallbackColor="#FFF4CE"
TintColor="#FFF4CE"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarSuccessSeverityBackgroundBrush"
FallbackColor="#DFF6DD"
TintColor="#DFF6DD"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarInformationalSeverityBackgroundBrush"
FallbackColor="#80F6F6F6"
TintColor="#80F6F6F6"
TintOpacity="0.6"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<AcrylicBrush
x:Key="InfoBarErrorSeverityBackgroundBrush"
FallbackColor="#442726"
TintColor="#442726"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarWarningSeverityBackgroundBrush"
FallbackColor="#433519"
TintColor="#433519"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarSuccessSeverityBackgroundBrush"
FallbackColor="#393D1B"
TintColor="#393D1B"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarInformationalSeverityBackgroundBrush"
FallbackColor="#34424d"
TintColor="#34424d"
TintOpacity="0.6"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ItemsControl.Resources>
</ItemsControl>
<Border
Margin="16"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Background="{ThemeResource SystemControlAcrylicElementBrush}"
CornerRadius="{ThemeResource ControlCornerRadius}"
Visibility="{x:Bind InfoBars.Count, Mode=OneWay, Converter={StaticResource Int32ToVisibilityConverter}}">
<ToggleButton
Name="VisibilityButton"
Checked="OnVisibilityButtonCheckedChanged"
Unchecked="OnVisibilityButtonCheckedChanged">
<ToggleButton.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition MinWidth="32"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,6,0" Text="{shcm:ResourceString Name=ViewInfoBarToggleTitle}"/>
<InfoBadge
Grid.Column="1"
Padding="0,1,0,0"
HorizontalAlignment="Right"
Style="{ThemeResource CriticalValueInfoBadgeStyle}"
Value="{x:Bind InfoBars.Count, Mode=OneWay}"/>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,48 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Snap.Hutao.Control;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Notification;
using System.Collections.ObjectModel;
namespace Snap.Hutao.View;
/// <summary>
/// 信息条视图
/// </summary>
internal sealed partial class InfoBarView : UserControl
{
private static readonly DependencyProperty InfoBarsProperty = Property<InfoBarView>.Depend<ObservableCollection<InfoBar>>(nameof(InfoBars));
private readonly IInfoBarService infoBarService;
/// <summary>
/// 构造一个新的信息条视图
/// </summary>
public InfoBarView()
{
InitializeComponent();
IServiceProvider serviceProvider = Ioc.Default;
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
InfoBars = infoBarService.Collection;
VisibilityButton.IsChecked = LocalSetting.Get(SettingKeys.IsInfoBarToggleChecked, true);
}
/// <summary>
/// 信息条
/// </summary>
public ObservableCollection<InfoBar> InfoBars
{
get => (ObservableCollection<InfoBar>)GetValue(InfoBarsProperty);
set => SetValue(InfoBarsProperty, value);
}
private void OnVisibilityButtonCheckedChanged(object sender, RoutedEventArgs e)
{
LocalSetting.Set(SettingKeys.IsInfoBarToggleChecked, ((ToggleButton)sender).IsChecked ?? false);
}
}

View File

@@ -96,61 +96,6 @@
</Frame>
</NavigationView>
<StackPanel
x:Name="InfoBarStack"
MaxWidth="640"
Margin="32,48,32,32"
VerticalAlignment="Bottom">
<StackPanel.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<AcrylicBrush
x:Key="InfoBarErrorSeverityBackgroundBrush"
FallbackColor="#FDE7E9"
TintColor="#FDE7E9"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarWarningSeverityBackgroundBrush"
FallbackColor="#FFF4CE"
TintColor="#FFF4CE"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarSuccessSeverityBackgroundBrush"
FallbackColor="#DFF6DD"
TintColor="#DFF6DD"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarInformationalSeverityBackgroundBrush"
FallbackColor="#80F6F6F6"
TintColor="#80F6F6F6"
TintOpacity="0.6"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<AcrylicBrush
x:Key="InfoBarErrorSeverityBackgroundBrush"
FallbackColor="#442726"
TintColor="#442726"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarWarningSeverityBackgroundBrush"
FallbackColor="#433519"
TintColor="#433519"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarSuccessSeverityBackgroundBrush"
FallbackColor="#393D1B"
TintColor="#393D1B"
TintOpacity="0.6"/>
<AcrylicBrush
x:Key="InfoBarInformationalSeverityBackgroundBrush"
FallbackColor="#34424d"
TintColor="#34424d"
TintOpacity="0.6"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</StackPanel.Resources>
</StackPanel>
<shv:InfoBarView/>
</Grid>
</UserControl>

View File

@@ -15,7 +15,6 @@ namespace Snap.Hutao.View;
internal sealed partial class MainView : UserControl
{
private readonly INavigationService navigationService;
private readonly IInfoBarService infoBarService;
/// <summary>
/// 构造一个新的主视图
@@ -26,9 +25,6 @@ internal sealed partial class MainView : UserControl
IServiceProvider serviceProvider = Ioc.Default;
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
infoBarService.Initialize(InfoBarStack);
navigationService = serviceProvider.GetRequiredService<INavigationService>();
navigationService
.As<INavigationInitialization>()?