Merge pull request #598 from GashByte/main

Add 'Mult-Start' & fix a start game bug
This commit is contained in:
DismissedLight
2023-03-12 22:32:23 +08:00
committed by GitHub
7 changed files with 149 additions and 13 deletions

View File

@@ -86,6 +86,11 @@ internal sealed class SettingEntry
/// </summary>
public const string LaunchMonitor = "Launch.Monitor";
/// <summary>
/// 启动游戏 多倍启动
/// </summary>
public const string MultipleInstances = "Launch.MultipleInstances";
/// <summary>
/// 语言
/// </summary>

View File

@@ -3723,6 +3723,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 多倍启动你的原神,你可以使用胡桃来多次打开原神并且不受到影响 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameMultipleInstancesDescription {
get {
return ResourceManager.GetString("ViewPageLaunchGameMultipleInstancesDescription", resourceCulture);
}
}
/// <summary>
/// 查找类似 多倍启动 的本地化字符串。
/// </summary>
internal static string ViewPageLaunchGameMultipleInstancesHeader {
get {
return ResourceManager.GetString("ViewPageLaunchGameMultipleInstancesHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 游戏选项 的本地化字符串。
/// </summary>

View File

@@ -1338,6 +1338,12 @@
<data name="ViewPageLaunchGameMonitorsHeader" xml:space="preserve">
<value>显示器</value>
</data>
<data name="ViewPageLaunchGameMultipleInstancesDescription" xml:space="preserve">
<value>多倍启动你的原神,你可以使用胡桃来多次打开原神并且不受到影响</value>
</data>
<data name="ViewPageLaunchGameMultipleInstancesHeader" xml:space="preserve">
<value>多倍启动</value>
</data>
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
<value>游戏选项</value>
</data>

View File

@@ -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
/// <inheritdoc/>
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);
}
}
}
/// <summary>
/// 为了实现多开 需要修改mhypbase.dll名称 这是必须的步骤
/// </summary>
/// <param name="gameProcess">游戏线程</param>
/// <param name="gamePath">游戏路径</param>
/// <returns>是否成功替换文件</returns>
public async Task<bool> 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;
}
/// <inheritdoc/>
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;
}
}
}

View File

@@ -35,6 +35,7 @@ internal sealed class LaunchOptions : ObservableObject, IOptions<LaunchOptions>
private bool? unlockFps;
private int? targetFps;
private NameValue<int>? monitor;
private bool? multipleInstances;
/// <summary>
/// 构造一个新的启动游戏选项
@@ -357,6 +358,40 @@ internal sealed class LaunchOptions : ObservableObject, IOptions<LaunchOptions>
}
}
/// <summary>
/// 多次启动原神
/// </summary>
public bool MultipleInstances
{
get
{
if (multipleInstances == null)
{
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
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>();
appDbContext.Settings.ExecuteDeleteWhere(e => e.Key == SettingEntry.MultipleInstances);
appDbContext.Settings.AddAndSave(new(SettingEntry.MultipleInstances, value.ToString()));
}
}
}
}
/// <inheritdoc/>
public LaunchOptions Value { get => this; }

View File

@@ -257,6 +257,17 @@
IsOpen="{Binding IsElevated}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameAdvanceHint}"
Severity="Error"/>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameMultipleInstancesDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameMultipleInstancesHeader}"
Icon="&#xE14D;">
<wsc:Setting.ActionContent>
<ToggleSwitch
Width="120"
IsOn="{Binding Options.MultipleInstances, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchSettingStyle}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
<wsc:Setting
Description="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsDescription}"
Header="{shcm:ResourceString Name=ViewPageLaunchGameUnlockFpsHeader}"

View File

@@ -205,7 +205,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
{
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (gameService.IsGameRunning())
if (!Options.MultipleInstances && gameService.IsGameRunning())
{
return;
}