diff --git a/bin/unlockfps.exe b/bin/unlockfps.exe
new file mode 100644
index 0000000..122be81
Binary files /dev/null and b/bin/unlockfps.exe differ
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/Process/NullProcess.cs b/src/Snap.Hutao/Snap.Hutao/Factory/Process/NullProcess.cs
new file mode 100644
index 0000000..25e084c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/Process/NullProcess.cs
@@ -0,0 +1,45 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Diagnostics;
+using Snap.Hutao.Win32.Foundation;
+
+namespace Snap.Hutao.Factory.Process;
+
+internal sealed class NullProcess : IProcess
+{
+ public int Id => 0;
+
+ public nint Handle => 0;
+
+ public HWND MainWindowHandle => default;
+
+ public bool HasExited => true;
+
+ public int ExitCode => 0;
+
+ public void Start()
+ {
+ // Do nothing
+ }
+
+ public void ResumeMainThread()
+ {
+ // Do nothing
+ }
+
+ public void WaitForExit()
+ {
+ // Do nothing
+ }
+
+ public void Kill()
+ {
+ // Do nothing
+ }
+
+ public void Dispose()
+ {
+ // Do nothing
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/WeaponIds.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/WeaponIds.cs
index ca5b5eb..a933b8d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/WeaponIds.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/WeaponIds.cs
@@ -34,7 +34,7 @@ internal static class WeaponIds
13502U, 13505U,
14501U, 14502U,
15501U, 15502U,
- 15515U, 15518U
+ 15515U, 11518U
];
public static bool IsOrangeStandardWish(in WeaponId weaponId)
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 43f146d..2e373a5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -1,4 +1,4 @@
-
+
+ Version="1.18.0.0" />
Snap Hutao
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/FpsConfigTest.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/FpsConfigTest.cs
new file mode 100644
index 0000000..240400f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/FpsConfigTest.cs
@@ -0,0 +1,55 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Setting;
+using System.IO;
+
+namespace Snap.Hutao.Service.Game.Island;
+
+internal static class FpsConfigTest
+{
+ // 测试用,手动更新FPS配置文件
+ public static void TestConfigUpdate()
+ {
+ // 直接从LocalSetting读取当前FPS设置
+ int currentFps = LocalSetting.Get(SettingKeys.LaunchTargetFps, 60);
+
+ // 配置文件路径
+ string configPath = Path.Combine(AppContext.BaseDirectory, "fps_config.ini");
+
+ // 读取当前配置
+ if (File.Exists(configPath))
+ {
+ string[] lines = File.ReadAllLines(configPath);
+ int configFps = 60;
+
+ foreach (string line in lines)
+ {
+ if (line.StartsWith("FPS="))
+ {
+ configFps = int.Parse(line.Substring(4));
+ break;
+ }
+ }
+
+ System.Diagnostics.Debug.WriteLine($"Current FPS from LocalSetting: {currentFps}");
+ System.Diagnostics.Debug.WriteLine($"Current FPS from config file: {configFps}");
+
+ if (currentFps != configFps)
+ {
+ // 更新配置文件
+ for (int i = 0; i < lines.Length; i++)
+ {
+ if (lines[i].StartsWith("FPS="))
+ {
+ lines[i] = $"FPS={currentFps}";
+ break;
+ }
+ }
+
+ File.WriteAllLines(configPath, lines);
+ System.Diagnostics.Debug.WriteLine($"Updated config file with FPS: {currentFps}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/GameFpsUnlockInterop.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/GameFpsUnlockInterop.cs
new file mode 100644
index 0000000..ff87617
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Island/GameFpsUnlockInterop.cs
@@ -0,0 +1,350 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core;
+using Snap.Hutao.Core.Diagnostics;
+using Snap.Hutao.Core.ExceptionService;
+using Snap.Hutao.Core.Setting;
+using Snap.Hutao.Service.Game.FileSystem;
+using Snap.Hutao.Service.Game.Launching.Context;
+using Snap.Hutao.Web.Hutao;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+namespace Snap.Hutao.Service.Game.Island;
+
+internal sealed class GameFpsUnlockInterop : IGameIslandInterop, IDisposable
+{
+ private const string UnlockerExecutableName = "unlockfps.exe";
+ private const string UnlockerConfigName = "fps_config.ini";
+
+ private readonly bool resume;
+
+ private string? unlockerPath;
+ private string? gamePath;
+ private Process? unlockerProcess;
+
+ public GameFpsUnlockInterop(bool resume)
+ {
+ this.resume = resume;
+ }
+
+ public async ValueTask BeforeAsync(BeforeLaunchExecutionContext context)
+ {
+ if (resume)
+ {
+ return;
+ }
+
+ // 获取unlocker.exe路径,放在Snap.Hutao同一目录下
+ string hutaoDirectory = AppContext.BaseDirectory;
+ unlockerPath = Path.Combine(hutaoDirectory, UnlockerExecutableName);
+
+ if (!File.Exists(unlockerPath))
+ {
+ throw HutaoException.InvalidOperation("未找到unlockfps.exe文件,请将genshin-fps-unlock-master编译后的unlockfps.exe放置在Snap.Hutao同目录下");
+ }
+
+ // 获取游戏路径
+ gamePath = context.FileSystem.GameFilePath;
+
+ // 验证游戏路径
+ SentrySdk.AddBreadcrumb(
+ $"Game path from Snap.Hutao: {gamePath}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Info);
+
+
+ if (!File.Exists(gamePath))
+ {
+ throw HutaoException.InvalidOperation($"游戏文件不存在: {gamePath}");
+ }
+
+ // 创建配置文件
+ await CreateUnlockerConfigAsync(context).ConfigureAwait(false);
+
+ // 启动解锁器进程
+ await StartUnlockerProcessAsync(context, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ public async ValueTask WaitForExitAsync(LaunchExecutionContext context, CancellationToken token = default)
+ {
+ if (resume)
+ {
+ // 恢复模式下,尝试连接已存在的解锁器进程
+ await MonitorExistingUnlockerAsync(context, token).ConfigureAwait(false);
+ return;
+ }
+
+ // 监控解锁器进程状态(解锁器会自动启动并监控游戏)
+ await MonitorUnlockerProcessAsync(context, token).ConfigureAwait(false);
+ }
+
+ private async ValueTask CreateUnlockerConfigAsync(BeforeLaunchExecutionContext context)
+ {
+ if (string.IsNullOrEmpty(gamePath))
+ {
+ throw HutaoException.NotSupported("游戏路径未初始化");
+ }
+
+ // 直接在unlocker同目录创建配置文件
+ string unlockerConfigPath = Path.Combine(Path.GetDirectoryName(unlockerPath)!, UnlockerConfigName);
+ int targetFps = context.LaunchOptions.TargetFps.Value;
+
+ string configContent = $"[Setting]\nPath={gamePath}\nFPS={targetFps}";
+
+ // 添加重试机制处理可能的权限问题
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ await File.WriteAllTextAsync(unlockerConfigPath, configContent).ConfigureAwait(false);
+ break; // 成功写入,退出循环
+ }
+ catch (UnauthorizedAccessException)
+ {
+ if (i == 2)
+ {
+ throw HutaoException.InvalidOperation($"无法写入配置文件 {unlockerConfigPath},请检查权限");
+ }
+ await Task.Delay(500).ConfigureAwait(false);
+ }
+ catch (IOException)
+ {
+ if (i == 2)
+ {
+ throw HutaoException.InvalidOperation($"无法写入配置文件 {unlockerConfigPath},文件可能被占用");
+ }
+ await Task.Delay(500).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async ValueTask StartUnlockerProcessAsync(BeforeLaunchExecutionContext context, CancellationToken token)
+ {
+ try
+ {
+
+ string configPath = Path.Combine(Path.GetDirectoryName(unlockerPath)!, UnlockerConfigName);
+ if (!File.Exists(configPath))
+ {
+ throw HutaoException.InvalidOperation($"配置文件不存在: {configPath}");
+ }
+
+
+ string configContent = await File.ReadAllTextAsync(configPath).ConfigureAwait(false);
+ SentrySdk.AddBreadcrumb(
+ $"Starting unlocker with config: {configContent}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Info);
+
+ ProcessStartInfo startInfo = new()
+ {
+ FileName = unlockerPath,
+ WorkingDirectory = Path.GetDirectoryName(unlockerPath),
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ WindowStyle = ProcessWindowStyle.Normal,
+ };
+
+ unlockerProcess = new Process { StartInfo = startInfo };
+
+
+ unlockerProcess.Start();
+
+
+ Task outputTask = Task.Run(async () =>
+ {
+ while (!unlockerProcess.StandardOutput.EndOfStream)
+ {
+ string line = await unlockerProcess.StandardOutput.ReadLineAsync().ConfigureAwait(false);
+ if (line != null)
+ {
+ SentrySdk.AddBreadcrumb(
+ $"Unlocker output: {line}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Info);
+ }
+ }
+ });
+
+ Task errorTask = Task.Run(async () =>
+ {
+ while (!unlockerProcess.StandardError.EndOfStream)
+ {
+ string line = await unlockerProcess.StandardError.ReadLineAsync().ConfigureAwait(false);
+ if (line != null)
+ {
+ SentrySdk.AddBreadcrumb(
+ $"Unlocker error: {line}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Error);
+ }
+ }
+ });
+
+ // 等待解锁器初始化
+ await Task.Delay(5000).ConfigureAwait(false);
+
+
+ }
+ catch (Exception ex)
+ {
+ throw HutaoException.Throw($"启动FPS解锁器失败: {ex.Message}", ex);
+ }
+ }
+
+ private async ValueTask MonitorExistingUnlockerAsync(LaunchExecutionContext context, CancellationToken token)
+ {
+ // 恢复模式下,检查是否有解锁器进程在运行
+ Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(UnlockerExecutableName));
+ if (processes.Length == 0)
+ {
+ // 没有找到解锁器进程,但游戏在运行,这是正常情况
+ return;
+ }
+
+ unlockerProcess = processes[0];
+ await MonitorUnlockerProcessAsync(context, token).ConfigureAwait(false);
+ }
+
+ private async ValueTask MonitorUnlockerProcessAsync(LaunchExecutionContext context, CancellationToken token)
+ {
+ if (unlockerProcess is null)
+ {
+ return;
+ }
+
+ using (PeriodicTimer timer = new(TimeSpan.FromSeconds(2)))
+ {
+ while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
+ {
+ // 检查解锁器进程状态
+ if (unlockerProcess.HasExited)
+ {
+ // 解锁器已退出,这意味着游戏也已退出
+ break;
+ }
+
+ // 同步FPS设置(如果用户在运行时修改了)
+ await SyncFpsSettingsAsync(context.LaunchOptions).ConfigureAwait(false);
+ }
+ }
+
+ // 确保解锁器进程已清理
+ CleanupUnlockerProcess();
+ }
+
+ private async ValueTask SyncFpsSettingsAsync(LaunchOptions launchOptions)
+ {
+ if (unlockerProcess is null || unlockerProcess.HasExited)
+ {
+ return;
+ }
+
+ try
+ {
+ string configPath = Path.Combine(Path.GetDirectoryName(unlockerPath)!, UnlockerConfigName);
+ if (File.Exists(configPath))
+ {
+ string[] lines = await File.ReadAllLinesAsync(configPath).ConfigureAwait(false);
+ int currentFps = launchOptions.TargetFps.Value;
+
+ bool needsUpdate = false;
+ for (int i = 0; i < lines.Length; i++)
+ {
+ if (lines[i].StartsWith("FPS="))
+ {
+ int configFps = int.Parse(lines[i].Substring(4));
+ if (configFps != currentFps)
+ {
+ lines[i] = $"FPS={currentFps}";
+ needsUpdate = true;
+ }
+ break;
+ }
+ }
+
+ if (needsUpdate)
+ {
+ // 添加重试机制处理可能的权限问题
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ await File.WriteAllLinesAsync(configPath, lines).ConfigureAwait(false);
+ break; // 成功写入,退出循环
+ }
+ catch (UnauthorizedAccessException)
+ {
+ if (i == 2) // 最后一次尝试
+ {
+ SentrySdk.AddBreadcrumb(
+ $"无法写入配置文件 {configPath},请检查权限",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Error);
+ return;
+ }
+ await Task.Delay(500).ConfigureAwait(false); // 等待500ms后重试
+ }
+ catch (IOException)
+ {
+ if (i == 2) // 最后一次尝试
+ {
+ SentrySdk.AddBreadcrumb(
+ $"无法写入配置文件 {configPath},文件可能被占用",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Error);
+ return;
+ }
+ await Task.Delay(500).ConfigureAwait(false); // 等待500ms后重试
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // 同步配置失败,记录但不影响主流程
+ SentrySdk.AddBreadcrumb(
+ $"Failed to sync FPS settings: {ex.Message}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Warning);
+ }
+ }
+
+
+
+ private void CleanupUnlockerProcess()
+ {
+ if (unlockerProcess is not null && !unlockerProcess.HasExited)
+ {
+ try
+ {
+ unlockerProcess.Kill();
+ unlockerProcess.WaitForExit();
+ }
+ catch (Exception ex)
+ {
+ // 忽略清理过程中的错误
+ SentrySdk.AddBreadcrumb(
+ $"Failed to cleanup unlocker process: {ex.Message}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Warning);
+ }
+ finally
+ {
+ unlockerProcess.Dispose();
+ unlockerProcess = null;
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ CleanupUnlockerProcess();
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
index 8a447f3..c30747f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
@@ -12,6 +12,8 @@ using Snap.Hutao.Service.Game.FileSystem;
using Snap.Hutao.Service.Game.PathAbstraction;
using Snap.Hutao.Win32;
using System.Collections.Immutable;
+using System.IO;
+using System.Threading.Tasks;
namespace Snap.Hutao.Service.Game;
@@ -106,7 +108,7 @@ internal sealed partial class LaunchOptions : DbStoreOptions, IRestrictedGamePat
public IObservableProperty IsSetTargetFrameRateEnabled { get => field ??= CreateProperty(SettingKeys.LaunchIsSetTargetFrameRateEnabled, true); }
[field: MaybeNull]
- public IObservableProperty TargetFps { get => field ??= CreateProperty(SettingKeys.LaunchTargetFps, InitializeTargetFpsWithScreenFps); }
+ public IObservableProperty TargetFps { get => field ??= CreateProperty(SettingKeys.LaunchTargetFps, InitializeTargetFpsWithScreenFps).WithValueChangedCallback(OnTargetFpsChanged); }
[field: MaybeNull]
public IObservableProperty RemoveOpenTeamProgress { get => field ??= CreateProperty(SettingKeys.LaunchRemoveOpenTeamProgress, false); }
@@ -165,6 +167,98 @@ internal sealed partial class LaunchOptions : DbStoreOptions, IRestrictedGamePat
return HutaoNative.Instance.MakeDeviceCapabilities().GetPrimaryScreenVerticalRefreshRate();
}
+ private static void OnTargetFpsChanged(int newFps)
+ {
+ // 异步更新配置文件,避免阻塞UI线程
+ Task.Run(async () =>
+ {
+ try
+ {
+ string configPath = Path.Combine(AppContext.BaseDirectory, "fps_config.ini");
+
+
+ if (File.Exists(configPath))
+ {
+ string[] lines = await File.ReadAllLinesAsync(configPath).ConfigureAwait(false);
+ bool needsUpdate = true;
+
+
+ foreach (string line in lines)
+ {
+ if (line.StartsWith("FPS="))
+ {
+ int configFps = int.Parse(line.Substring(4));
+ if (configFps == newFps)
+ {
+ needsUpdate = false;
+ }
+ break;
+ }
+ }
+
+ // 更新配置文件
+ if (needsUpdate)
+ {
+ for (int i = 0; i < lines.Length; i++)
+ {
+ if (lines[i].StartsWith("FPS="))
+ {
+ lines[i] = $"FPS={newFps}";
+ break;
+ }
+ }
+
+
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ await File.WriteAllLinesAsync(configPath, lines).ConfigureAwait(false);
+ SentrySdk.AddBreadcrumb(
+ $"Updated fps_config.ini with new FPS: {newFps}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Info);
+ break;
+ }
+ catch (UnauthorizedAccessException)
+ {
+ if (i == 2)
+ {
+ SentrySdk.AddBreadcrumb(
+ $"无法写入配置文件 {configPath},请检查权限",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Error);
+ return;
+ }
+ await Task.Delay(500).ConfigureAwait(false);
+ }
+ catch (IOException)
+ {
+ if (i == 2)
+ {
+ SentrySdk.AddBreadcrumb(
+ $"无法写入配置文件 {configPath},文件可能被占用",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Error);
+ return;
+ }
+ await Task.Delay(500).ConfigureAwait(false);
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // 记录错误
+ SentrySdk.AddBreadcrumb(
+ $"Failed to update fps_config.ini: {ex.Message}",
+ category: "fps.unlocker",
+ level: Sentry.BreadcrumbLevel.Warning);
+ }
+ });
+ }
+
private static ImmutableArray> InitializeMonitors()
{
ImmutableArray>.Builder monitors = ImmutableArray.CreateBuilder>();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameIslandHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameIslandHandler.cs
index 8f91374..00a3c38 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameIslandHandler.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameIslandHandler.cs
@@ -8,10 +8,10 @@ using Snap.Hutao.Service.Notification;
namespace Snap.Hutao.Service.Game.Launching.Handler;
-internal sealed class LaunchExecutionGameIslandHandler : AbstractLaunchExecutionHandler
+internal sealed class LaunchExecutionGameIslandHandler : AbstractLaunchExecutionHandler, IDisposable
{
private readonly bool resume;
- private GameIslandInterop? interop;
+ private GameFpsUnlockInterop? interop;
public LaunchExecutionGameIslandHandler(bool resume)
{
@@ -63,4 +63,9 @@ internal sealed class LaunchExecutionGameIslandHandler : AbstractLaunchExecution
GameLifeCycle.IsIslandConnected.Value = false;
}
}
+
+ public void Dispose()
+ {
+ interop?.Dispose();
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameProcessStartHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameProcessStartHandler.cs
index 41268d4..44fb52f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameProcessStartHandler.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Handler/LaunchExecutionGameProcessStartHandler.cs
@@ -26,6 +26,14 @@ internal sealed class LaunchExecutionGameProcessStartHandler : AbstractLaunchExe
public override async ValueTask ExecuteAsync(LaunchExecutionContext context)
{
+ // 如果启用了Island(FPS解锁),则跳过启动游戏进程
+ // 因为unlockfps.exe会负责启动游戏
+ if (context.LaunchOptions.IsIslandEnabled.Value)
+ {
+ context.Progress.Report(new(SH.ServiceGameLaunchPhaseProcessStarted));
+ return;
+ }
+
try
{
context.Process.Start();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
index 3d54608..7e649e9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
@@ -4,6 +4,7 @@
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Factory.Progress;
+using Snap.Hutao.Factory.Process;
using Snap.Hutao.Service.Game.FileSystem;
using Snap.Hutao.Service.Game.Launching.Context;
using Snap.Hutao.Service.Game.Launching.Handler;
@@ -100,9 +101,16 @@ internal abstract class AbstractLaunchExecutionInvoker
fileSystemReference.Exchange(beforeContext.FileSystem);
- using (IProcess? process = CreateProcess(beforeContext))
+ // unlockfps.exe会负责启动游戏
+ IProcess? process = null;
+ if (!beforeContext.LaunchOptions.IsIslandEnabled.Value)
{
- if (process is null)
+ process = CreateProcess(beforeContext);
+ }
+
+ using (process)
+ {
+ if (process is null && !beforeContext.LaunchOptions.IsIslandEnabled.Value)
{
return;
}
@@ -114,7 +122,7 @@ internal abstract class AbstractLaunchExecutionInvoker
TaskContext = taskContext,
Messenger = context.ServiceProvider.GetRequiredService(),
LaunchOptions = context.LaunchOptions,
- Process = process,
+ Process = process ?? new NullProcess(),
IsOversea = targetScheme.IsOversea,
};
@@ -123,7 +131,8 @@ internal abstract class AbstractLaunchExecutionInvoker
await handler.ExecuteAsync(executionContext).ConfigureAwait(false);
}
- if (process.IsRunning)
+ // 只有在没有启用Island且进程存在时才等待退出
+ if (process is { IsRunning: true })
{
progress.Report(new(SH.ServiceGameLaunchPhaseWaitingProcessExit));
try
@@ -139,6 +148,12 @@ internal abstract class AbstractLaunchExecutionInvoker
return;
}
}
+ else if (beforeContext.LaunchOptions.IsIslandEnabled.Value)
+ {
+ progress.Report(new(SH.ServiceGameLaunchPhaseWaitingProcessExit));
+ await taskContext.SwitchToBackgroundAsync();
+ await Task.Delay(30000).ConfigureAwait(false);
+ }
}
progress.Report(new(SH.ServiceGameLaunchPhaseProcessExited));
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/DefaultLaunchExecutionInvoker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/DefaultLaunchExecutionInvoker.cs
index 03d65b1..7a098c5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/DefaultLaunchExecutionInvoker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/DefaultLaunchExecutionInvoker.cs
@@ -16,8 +16,8 @@ internal sealed class DefaultLaunchExecutionInvoker : AbstractLaunchExecutionInv
new LaunchExecutionGameResourceHandler(convertOnly: false),
new LaunchExecutionGameIdentityHandler(),
new LaunchExecutionWindowsHDRHandler(),
- new LaunchExecutionGameProcessStartHandler(),
new LaunchExecutionGameIslandHandler(resume: false),
+ new LaunchExecutionGameProcessStartHandler(),
new LaunchExecutionOverlayHandler(),
new LaunchExecutionStarwardPlayTimeStatisticsHandler(),
new LaunchExecutionBetterGenshinImpactAutomationHandler()
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 8cbe51d..c16c400 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -11,7 +11,7 @@
true
False
- 1.17.4.0
+ 1.18.0.0
False
False
@@ -71,6 +71,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/LaunchGamePage.xaml
index d5a68d9..3d8323b 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/LaunchGamePage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Page/LaunchGamePage.xaml
@@ -641,30 +641,31 @@
Grid.Row="0"
Grid.Column="0"
Header="{shuxm:ResourceString Name=ViewPageLaunchGameTargetFovHotSwitchHeader}"
- IsOn="{Binding LaunchOptions.IsSetFieldOfViewEnabled.Value, Mode=TwoWay}"
- ToolTipService.ToolTip="{shuxm:ResourceString Name=ViewPageLaunchGameHotSwitchDescription}"/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ ToolTipService.ToolTip=""
+ Value="45"/>
+ ToolTipService.ToolTip=""/>
-
+
+
+
+
+
+
+
+
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip="">
@@ -756,10 +763,11 @@
x:Name="HideQuestBannerToggleSwitch"
Grid.Row="2"
Grid.Column="0"
- IsOn="{Binding LaunchOptions.HideQuestBanner.Value, Mode=TwoWay}"
+ IsEnabled="False"
+ IsOn="False"
OffContent="{shuxm:ResourceString Name=ViewPageLaunchGameDisableFogOff}"
OnContent="{shuxm:ResourceString Name=ViewPageLaunchGameDisableFogOn}"
- ToolTipService.ToolTip="{shuxm:ResourceString Name=ViewPageLaunchGameHotSwitchDescription}">
+ ToolTipService.ToolTip="">
@@ -798,34 +806,38 @@
Grid.Row="2"
Grid.Column="1"
Header="{shuxm:ResourceString Name=ViewPageLaunchGameRemoveOpenTeamProgressHeader}"
- IsOn="{Binding LaunchOptions.RemoveOpenTeamProgress.Value, Mode=TwoWay}"
+ IsEnabled="False"
+ IsOn="False"
OffContent="{shuxm:ResourceString Name=ViewPageLaunchGameDisableFogOff}"
OnContent="{shuxm:ResourceString Name=ViewPageLaunchGameDisableFogOn}"
- ToolTipService.ToolTip="{shuxm:ResourceString Name=ViewPageLaunchGameRemoveOpenTeamProgressDescription}"/>
+ ToolTipService.ToolTip=""/>
+ ToolTipService.ToolTip=""/>
+ ToolTipService.ToolTip=""/>
+ ToolTipService.ToolTip="">
@@ -867,35 +879,46 @@
Grid.Row="3"
Grid.Column="2"
Header="{shuxm:ResourceString Name=ViewPageLaunchGameIslandUsingTouchScreenHeader}"
- IsEnabled="{Binding LaunchOptions.IsGameRunning.Value, Converter={StaticResource BoolNegationConverter}}"
- IsOn="{Binding LaunchOptions.UsingTouchScreen.Value, Mode=TwoWay}"/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>
+ IsEnabled="False"
+ IsOn="False"
+ ToolTipService.ToolTip=""/>