feat(launcher): prepare Campaign LA11 user gate
This commit is contained in:
parent
09d84387a8
commit
f881e5b467
24 changed files with 3538 additions and 58 deletions
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AcDream.Launcher.Core\AcDream.Launcher.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using AcDream.Launcher.Core.Launching;
|
||||
|
||||
if (args.Length != 3)
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
string resultPath = Path.GetFullPath(args[0]);
|
||||
string dotnetPath = args[1];
|
||||
string childAssembly = Path.GetFullPath(args[2]);
|
||||
string root = Path.Combine(
|
||||
Path.GetDirectoryName(resultPath)!,
|
||||
"consoleless-children");
|
||||
Directory.CreateDirectory(root);
|
||||
string targetReady = Path.Combine(root, "target.ready.json");
|
||||
string targetBreak = Path.Combine(root, "target.break");
|
||||
string siblingReady = Path.Combine(root, "sibling.ready.json");
|
||||
string siblingBreak = Path.Combine(root, "sibling.break");
|
||||
|
||||
bool parentHadConsoleBefore = HasConsole();
|
||||
using var target = new LauncherProcessSupervisor();
|
||||
using var sibling = new LauncherProcessSupervisor();
|
||||
try
|
||||
{
|
||||
target.Start(
|
||||
Spec(dotnetPath, childAssembly, targetReady, targetBreak, "target"),
|
||||
"stdin-from-consoleless-parent");
|
||||
sibling.Start(
|
||||
Spec(dotnetPath, childAssembly, siblingReady, siblingBreak, "sibling"),
|
||||
password: null);
|
||||
WaitForFile(targetReady, target);
|
||||
WaitForFile(siblingReady, sibling);
|
||||
bool parentHadConsoleAfterStarts = HasConsole();
|
||||
|
||||
target.Stop(TimeSpan.FromSeconds(10));
|
||||
bool siblingUnaffected = sibling.State != LauncherSessionState.Exited
|
||||
&& !File.Exists(siblingBreak);
|
||||
sibling.Stop(TimeSpan.FromSeconds(10));
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(resultPath)!);
|
||||
File.WriteAllText(
|
||||
resultPath,
|
||||
JsonSerializer.Serialize(new
|
||||
{
|
||||
parentHadConsoleBefore,
|
||||
parentHadConsoleAfterStarts,
|
||||
targetExitCode = target.ExitCode,
|
||||
siblingExitCode = sibling.ExitCode,
|
||||
targetBreakObserved = File.Exists(targetBreak),
|
||||
siblingBreakObserved = File.Exists(siblingBreak),
|
||||
siblingUnaffected,
|
||||
}));
|
||||
return 0;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(resultPath)!);
|
||||
File.WriteAllText(
|
||||
resultPath,
|
||||
JsonSerializer.Serialize(new
|
||||
{
|
||||
parentHadConsoleBefore,
|
||||
error = error.GetType().Name + ": " + error.Message,
|
||||
}));
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ForceStop(target);
|
||||
ForceStop(sibling);
|
||||
}
|
||||
|
||||
static LauncherProcessSpec Spec(
|
||||
string dotnetPath,
|
||||
string childAssembly,
|
||||
string ready,
|
||||
string breakMarker,
|
||||
string label) =>
|
||||
new(
|
||||
dotnetPath,
|
||||
[
|
||||
childAssembly,
|
||||
"wait-for-break",
|
||||
ready,
|
||||
breakMarker,
|
||||
label,
|
||||
"argument with spaces",
|
||||
]);
|
||||
|
||||
static void WaitForFile(string path, LauncherProcessSupervisor supervisor)
|
||||
{
|
||||
DateTime deadline = DateTime.UtcNow + TimeSpan.FromSeconds(10);
|
||||
while (!File.Exists(path))
|
||||
{
|
||||
if (supervisor.State == LauncherSessionState.Exited)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Child exited early with {supervisor.ExitCode}.");
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= deadline)
|
||||
{
|
||||
throw new TimeoutException("Child did not become ready.");
|
||||
}
|
||||
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
}
|
||||
|
||||
static void ForceStop(LauncherProcessSupervisor supervisor)
|
||||
{
|
||||
try
|
||||
{
|
||||
supervisor.Stop(TimeSpan.Zero);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static bool HasConsole()
|
||||
{
|
||||
uint[] processes = new uint[1];
|
||||
return Native.GetConsoleProcessList(processes, 1) != 0;
|
||||
}
|
||||
|
||||
internal static partial class Native
|
||||
{
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
internal static partial uint GetConsoleProcessList(
|
||||
[Out] uint[] processList,
|
||||
uint processCount);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue