diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index bb761610..3e49c037 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -875,6 +875,9 @@ 游戏文件操作失败:{0} + + 请选择游戏路径 + 游戏进程已退出 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/ILaunchExecutionDelegateHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/ILaunchExecutionDelegateHandler.cs new file mode 100644 index 00000000..78ff79ad --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/ILaunchExecutionDelegateHandler.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Launching; + +internal delegate ValueTask LaunchExecutionDelegate(); + +internal interface ILaunchExecutionDelegateHandler +{ + ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionContext.cs new file mode 100644 index 00000000..0e2e2b86 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionContext.cs @@ -0,0 +1,17 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Service.Game.Scheme; + +namespace Snap.Hutao.Service.Game.Launching; + +internal sealed class LaunchExecutionContext +{ + public LaunchExecutionResult Result { get; } = new(); + + public ILogger Logger { get; set; } = default!; + + public LaunchScheme Scheme { get; set; } = default!; + + public LaunchOptions Options { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionEnsureSchemeNotExistsHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionEnsureSchemeNotExistsHandler.cs new file mode 100644 index 00000000..1cc14fe6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionEnsureSchemeNotExistsHandler.cs @@ -0,0 +1,19 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Launching; + +internal sealed class LaunchExecutionEnsureSchemeNotExistsHandler : ILaunchExecutionDelegateHandler +{ + public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next) + { + if (context.Scheme is null) + { + context.Result.Kind = LaunchExecutionResultKind.NoActiveScheme; + context.Result.ErrorMessage = SH.ViewModelLaunchGameSchemeNotSelected; + return; + } + + await next().ConfigureAwait(false); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionInvoker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionInvoker.cs new file mode 100644 index 00000000..1e2f8829 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionInvoker.cs @@ -0,0 +1,37 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Launching; + +[Injection(InjectAs.Transient)] +internal sealed class LaunchExecutionInvoker +{ + private readonly Queue handlers; + + public LaunchExecutionInvoker() + { + handlers = []; + handlers.Enqueue(new LaunchExecutionEnsureSchemeNotExistsHandler()); + handlers.Enqueue(new LaunchExecutionSetChannelOptionsHandler()); + } + + public async ValueTask LaunchAsync(LaunchExecutionContext context) + { + await ExecuteAsync(context).ConfigureAwait(false); + return context.Result; + } + + private async ValueTask ExecuteAsync(LaunchExecutionContext context) + { + if (handlers.TryDequeue(out ILaunchExecutionDelegateHandler? handler)) + { + string handlerName = handler.GetType().Name; + + context.Logger.LogInformation("Handler[{Handler}] begin execute", handlerName); + await handler.OnExecutionAsync(context, () => ExecuteAsync(context)).ConfigureAwait(false); + context.Logger.LogInformation("Handler[{Handler}] end execute", handlerName); + } + + return context; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionPackageConvertHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionPackageConvertHandler.cs new file mode 100644 index 00000000..2d7fc6c8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionPackageConvertHandler.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Launching; + +internal sealed class LaunchExecutionPackageConvertHandler : ILaunchExecutionDelegateHandler +{ + public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next) + { + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionResult.cs new file mode 100644 index 00000000..4461cf8d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionResult.cs @@ -0,0 +1,21 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Launching; + +internal sealed class LaunchExecutionResult +{ + public LaunchExecutionResultKind Kind { get; set; } + + public string ErrorMessage { get; set; } = default!; +} + +internal enum LaunchExecutionResultKind +{ + Ok, + NoActiveScheme, + NoActiveGamePath, + GameConfigFileNotFound, + GameConfigDirectoryNotFound, + GameConfigUnauthorizedAccess, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionSetChannelOptionsHandler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionSetChannelOptionsHandler.cs new file mode 100644 index 00000000..60bd081b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/LaunchExecutionSetChannelOptionsHandler.cs @@ -0,0 +1,80 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.IO.Ini; +using Snap.Hutao.Service.Game.Configuration; +using System.IO; + +namespace Snap.Hutao.Service.Game.Launching; + +internal sealed class LaunchExecutionSetChannelOptionsHandler : ILaunchExecutionDelegateHandler +{ + private const string ConfigFileName = "config.ini"; + + public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next) + { + if (!context.Options.TryGetGamePathAndFilePathByName(ConfigFileName, out string gamePath, out string? configPath)) + { + context.Result.Kind = LaunchExecutionResultKind.NoActiveGamePath; + context.Result.ErrorMessage = SH.ServiceGameLaunchExecutionSetChannelOptionsGamePathNotValid; + return; + } + + List elements = default!; + try + { + using (FileStream readStream = File.OpenRead(configPath)) + { + elements = [.. IniSerializer.Deserialize(readStream)]; + } + } + catch (FileNotFoundException) + { + context.Result.Kind = LaunchExecutionResultKind.GameConfigFileNotFound; + context.Result.ErrorMessage = SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath); + return; + } + catch (DirectoryNotFoundException) + { + context.Result.Kind = LaunchExecutionResultKind.GameConfigDirectoryNotFound; + context.Result.ErrorMessage = SH.FormatServiceGameSetMultiChannelConfigFileNotFound(configPath); + return; + } + catch (UnauthorizedAccessException) + { + context.Result.Kind = LaunchExecutionResultKind.GameConfigUnauthorizedAccess; + context.Result.ErrorMessage = SH.ServiceGameSetMultiChannelUnauthorizedAccess; + return; + } + + bool changed = false; + + foreach (IniElement element in elements) + { + if (element is IniParameter parameter) + { + if (parameter.Key is ChannelOptions.ChannelName) + { + changed = parameter.Set(context.Scheme.Channel.ToString("D")) || changed; + continue; + } + + if (parameter.Key is ChannelOptions.SubChannelName) + { + changed = parameter.Set(context.Scheme.SubChannel.ToString("D")) || changed; + continue; + } + } + } + + if (changed) + { + using (FileStream writeStream = File.Create(configPath)) + { + IniSerializer.Serialize(writeStream, elements); + } + } + + await next().ConfigureAwait(false); + } +} \ No newline at end of file