using System.Diagnostics;
namespace DorkNet.Launcher.Backend;
/// Invokes the per-version branch's install-plugin.ps1
/// (or install-legacy-client.ps1 as a fallback) on the user's
/// Rec Room install. The patcher zip is unpacked by
/// first; we just
/// run the script from inside that unpacked tree.
public sealed class ClientPatcher
{
public async Task ApplyAsync(
string patcherDir,
string recRoomPath,
string photonAppId,
string photonVoiceAppId,
string apexHost,
CancellationToken ct = default)
{
var script = Path.Combine(patcherDir, "install-plugin.ps1");
if (!File.Exists(script))
{
// Some branches may ship only the legacy installer.
script = Path.Combine(patcherDir, "install-legacy-client.ps1");
if (!File.Exists(script))
{
return PatchResult.Failure(
"Patcher zip is missing install-plugin.ps1 and install-legacy-client.ps1. " +
"Open an issue with the version key + the contents of " +
$"{patcherDir}.");
}
}
var psi = new ProcessStartInfo
{
FileName = "powershell.exe",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = patcherDir,
};
psi.ArgumentList.Add("-NoProfile");
psi.ArgumentList.Add("-ExecutionPolicy"); psi.ArgumentList.Add("Bypass");
psi.ArgumentList.Add("-File"); psi.ArgumentList.Add(script);
psi.ArgumentList.Add("-RecRoomPath"); psi.ArgumentList.Add(recRoomPath);
psi.ArgumentList.Add("-PhotonAppId"); psi.ArgumentList.Add(photonAppId);
psi.ArgumentList.Add("-PhotonVoiceAppId");
psi.ArgumentList.Add(string.IsNullOrEmpty(photonVoiceAppId) ? photonAppId : photonVoiceAppId);
if (!string.IsNullOrEmpty(apexHost))
{
psi.ArgumentList.Add("-ServerHost"); psi.ArgumentList.Add(apexHost);
}
using var p = Process.Start(psi)
?? throw new InvalidOperationException("Failed to spawn powershell.exe");
var stdout = await p.StandardOutput.ReadToEndAsync(ct);
var stderr = await p.StandardError.ReadToEndAsync(ct);
await p.WaitForExitAsync(ct);
return p.ExitCode == 0
? PatchResult.Success(stdout)
: PatchResult.Failure(string.IsNullOrEmpty(stderr) ? stdout : stderr);
}
}
public sealed record PatchResult(bool Ok, string Log)
{
public static PatchResult Success(string log) => new(true, log);
public static PatchResult Failure(string log) => new(false, log);
}