diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs
index cacd06f3..6bda9d53 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs
@@ -86,6 +86,11 @@ internal sealed class SettingEntry
///
public const string LaunchMonitor = "Launch.Monitor";
+ ///
+ /// 启动游戏 多倍启动
+ ///
+ public const string MultipleInstances = "Launch.MultipleInstances";
+
///
/// 语言
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
index 9a58c27a..b3ea980d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -3723,6 +3723,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 多倍启动你的原神,你可以使用胡桃来多次打开原神并且不受到影响 的本地化字符串。
+ ///
+ internal static string ViewPageLaunchGameMultipleInstancesDescription {
+ get {
+ return ResourceManager.GetString("ViewPageLaunchGameMultipleInstancesDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 多倍启动 的本地化字符串。
+ ///
+ internal static string ViewPageLaunchGameMultipleInstancesHeader {
+ get {
+ return ResourceManager.GetString("ViewPageLaunchGameMultipleInstancesHeader", resourceCulture);
+ }
+ }
+
///
/// 查找类似 游戏选项 的本地化字符串。
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index f306750c..52e80233 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -1338,6 +1338,12 @@
显示器
+
+ 多倍启动你的原神,你可以使用胡桃来多次打开原神并且不受到影响
+
+
+ 多倍启动
+
游戏选项
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
index 46c10a46..aa5bdda8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -8,6 +8,7 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.Ini;
+using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Service.Game.Locator;
@@ -311,7 +312,7 @@ internal sealed class GameService : IGameService
///
public async ValueTask LaunchAsync(LaunchOptions options)
{
- if (IsGameRunning())
+ if (!options.MultipleInstances && IsGameRunning())
{
return;
}
@@ -347,6 +348,15 @@ internal sealed class GameService : IGameService
using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
{
+ if (options.MultipleInstances && Activation.GetElevated())
+ {
+ await LaunchGameAsync(game, gamePath);
+ }
+ else
+ {
+ await LaunchGameAsync(game);
+ }
+
if (options.UnlockFps)
{
IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, options.TargetFps);
@@ -355,21 +365,52 @@ internal sealed class GameService : IGameService
TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
- if (game.Start())
- {
- await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
- }
- }
- else
- {
- if (game.Start())
- {
- await game.WaitForExitAsync().ConfigureAwait(false);
- }
+ await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
}
}
}
+ ///
+ /// 为了实现多开 需要修改mhypbase.dll名称 这是必须的步骤
+ ///
+ /// 游戏线程
+ /// 游戏路径
+ /// 是否成功替换文件
+ public async Task LaunchMultipleInstancesGameAsync(Process gameProcess, string? gamePath)
+ {
+ if (gamePath == null)
+ {
+ return false;
+ }
+
+ DirectoryInfo directoryInfo = new DirectoryInfo(gamePath);
+ if (directoryInfo.Parent == null)
+ {
+ return false;
+ }
+
+ string? gameDirectory = directoryInfo.Parent.FullName.ToString();
+ string? mhypbasePath = $@"{gameDirectory}\mhypbase.dll";
+ string? tempPath = $@"{gameDirectory}\mhypbase.dll.backup";
+ if (File.Exists(mhypbasePath))
+ {
+ File.Move(mhypbasePath, tempPath);
+ }
+ else if (!File.Exists(tempPath))
+ {
+ return false;
+ }
+
+ gameProcess.Start();
+
+ // wait 12sec for loading library files
+ await Task.Delay(12000);
+
+ File.Move(tempPath, mhypbasePath);
+
+ return false;
+ }
+
///
public async ValueTask DetectGameAccountAsync()
{
@@ -470,4 +511,24 @@ internal sealed class GameService : IGameService
return (launchScheme.IsOversea && gameFileName == GenshinImpactFileName)
|| (!launchScheme.IsOversea && gameFileName == YuanShenFileName);
}
+
+ private async Task LaunchGameAsync(Process gameProcess, string? gamePath = null)
+ {
+ try
+ {
+ if (gamePath == null)
+ {
+ gameProcess.Start();
+ }
+ else
+ {
+ await LaunchMultipleInstancesGameAsync(gameProcess, gamePath);
+ return;
+ }
+ }
+ catch
+ {
+ return;
+ }
+ }
}
\ 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 2ce84853..554c4cec 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
@@ -35,6 +35,7 @@ internal sealed class LaunchOptions : ObservableObject, IOptions
private bool? unlockFps;
private int? targetFps;
private NameValue? monitor;
+ private bool? multipleInstances;
///
/// 构造一个新的启动游戏选项
@@ -357,6 +358,40 @@ internal sealed class LaunchOptions : ObservableObject, IOptions
}
}
+ ///
+ /// 多次启动原神
+ ///
+ public bool MultipleInstances
+ {
+ get
+ {
+ if (multipleInstances == null)
+ {
+ using (IServiceScope scope = serviceScopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ string? value = appDbContext.Settings.SingleOrDefault(e => e.Key == SettingEntry.MultipleInstances)?.Value;
+ multipleInstances = value != null && bool.Parse(value);
+ }
+ }
+
+ return multipleInstances.Value;
+ }
+
+ set
+ {
+ if (SetProperty(ref multipleInstances, value))
+ {
+ using (IServiceScope scope = serviceScopeFactory.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ appDbContext.Settings.ExecuteDeleteWhere(e => e.Key == SettingEntry.MultipleInstances);
+ appDbContext.Settings.AddAndSave(new(SettingEntry.MultipleInstances, value.ToString()));
+ }
+ }
+ }
+ }
+
///
public LaunchOptions Value { get => this; }
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
index b8e58b5a..d33f1ace 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LaunchGamePage.xaml
@@ -257,6 +257,17 @@
IsOpen="{Binding IsElevated}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHint}"
Severity="Error"/>
+
+
+
+
+
();
- if (gameService.IsGameRunning())
+ if (!Options.MultipleInstances && gameService.IsGameRunning())
{
return;
}