mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support dynamically change game fps
This commit is contained in:
@@ -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>()?
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -46,7 +46,7 @@ public static partial class Program
|
||||
{
|
||||
IServiceProvider serviceProvider = Ioc.Default;
|
||||
|
||||
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
_ = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
_ = serviceProvider.GetRequiredService<App>();
|
||||
}
|
||||
}
|
||||
@@ -2103,6 +2103,15 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 有新的通知 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ViewInfoBarToggleTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ViewInfoBarToggleTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 启动游戏 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -2010,4 +2010,7 @@
|
||||
<data name="ViewPageHomeGreetingTextEpic1" xml:space="preserve">
|
||||
<value>呐,旅行者,这是你来到提瓦特大陆的第 {0} 天哦</value>
|
||||
</data>
|
||||
<data name="ViewInfoBarToggleTitle" xml:space="preserve">
|
||||
<value>有新的通知</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -9,11 +9,6 @@ namespace Snap.Hutao.Service.Game.Unlocker;
|
||||
[HighQuality]
|
||||
internal interface IGameFpsUnlocker
|
||||
{
|
||||
/// <summary>
|
||||
/// 目标FPS,运行动态设置以动态更改帧率
|
||||
/// </summary>
|
||||
int TargetFps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 异步的解锁帧数限制
|
||||
/// </summary>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ internal sealed partial class UserService : IUserService
|
||||
get => currentUser;
|
||||
set
|
||||
{
|
||||
if (currentUser == value)
|
||||
if (currentUser?.Entity.InnerId == value?.Entity.InnerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
101
src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml
Normal file
101
src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml
Normal 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>
|
||||
48
src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml.cs
Normal file
48
src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>()?
|
||||
|
||||
Reference in New Issue
Block a user