create infrastructure

This commit is contained in:
DismissedLight
2024-01-04 22:51:58 +08:00
parent c434521004
commit f2f858de15
8 changed files with 199 additions and 0 deletions

View File

@@ -875,6 +875,9 @@
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
<value>游戏文件操作失败:{0}</value>
</data>
<data name="ServiceGameLaunchExecutionSetChannelOptionsGamePathNotValid" xml:space="preserve">
<value>请选择游戏路径</value>
</data>
<data name="ServiceGameLaunchPhaseProcessExited" xml:space="preserve">
<value>游戏进程已退出</value>
</data>

View File

@@ -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<LaunchExecutionContext> LaunchExecutionDelegate();
internal interface ILaunchExecutionDelegateHandler
{
ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next);
}

View File

@@ -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!;
}

View File

@@ -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);
}
}

View File

@@ -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<ILaunchExecutionDelegateHandler> handlers;
public LaunchExecutionInvoker()
{
handlers = [];
handlers.Enqueue(new LaunchExecutionEnsureSchemeNotExistsHandler());
handlers.Enqueue(new LaunchExecutionSetChannelOptionsHandler());
}
public async ValueTask<LaunchExecutionResult> LaunchAsync(LaunchExecutionContext context)
{
await ExecuteAsync(context).ConfigureAwait(false);
return context.Result;
}
private async ValueTask<LaunchExecutionContext> 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;
}
}

View File

@@ -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)
{
}
}

View File

@@ -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,
}

View File

@@ -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<IniElement> 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);
}
}