Add Avalonia server-host launcher (MVP scaffold)

Cross-platform C# GUI to replace the Qt/C++ Host GUI, on the same dotnet
toolchain as the server. MVP: load/save commonwealth-server.json, Start/Stop
the CommonwealthOnline.Server process (published exe, then dll, then source
run), and stream its output to a live log. Themed to the Commonwealth Online
brand (near-black + amber). Builds clean on net8.0.

Next: player list + kick/ban via the admin port, then retire the Qt host.
This commit is contained in:
NomadsReach
2026-08-16 18:15:40 -04:00
parent 6861fa4276
commit 1f01e68794
11 changed files with 431 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
using System;
using System.Diagnostics;
using System.IO;
namespace CommonwealthOnline.Host.Services;
// Launches the CommonwealthOnline.Server process, preferring a published
// apphost, then a framework-dependent DLL, then a source-tree dotnet run.
public sealed class ServerController
{
private Process? _process;
public bool IsRunning => _process is { HasExited: false };
public event Action<string>? LogReceived;
public event Action<bool>? RunningChanged;
public void Start(string serverDir, string configPath)
{
if (IsRunning)
{
return;
}
var startInfo = ResolveLaunch(serverDir, configPath);
var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true };
process.OutputDataReceived += (_, e) => Emit(e.Data);
process.ErrorDataReceived += (_, e) => Emit(e.Data);
process.Exited += (_, _) => RunningChanged?.Invoke(false);
_process = process;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
RunningChanged?.Invoke(true);
}
public void Stop()
{
if (_process is { HasExited: false } process)
{
try
{
process.Kill(entireProcessTree: true);
}
catch (Exception)
{
// Process already gone or not killable; RunningChanged fires on Exited.
}
}
}
private void Emit(string? line)
{
if (!string.IsNullOrEmpty(line))
{
LogReceived?.Invoke(line);
}
}
private static ProcessStartInfo ResolveLaunch(string serverDir, string configPath)
{
var exeName = OperatingSystem.IsWindows()
? "CommonwealthOnline.Server.exe"
: "CommonwealthOnline.Server";
var apphost = Path.Combine(serverDir, exeName);
var dll = Path.Combine(serverDir, "CommonwealthOnline.Server.dll");
var project = Path.Combine(serverDir, "CommonwealthOnline.Server.csproj");
var info = new ProcessStartInfo
{
WorkingDirectory = serverDir,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
if (File.Exists(apphost))
{
info.FileName = apphost;
info.ArgumentList.Add("serve");
}
else if (File.Exists(dll))
{
info.FileName = "dotnet";
info.ArgumentList.Add(dll);
info.ArgumentList.Add("serve");
}
else
{
info.FileName = "dotnet";
info.ArgumentList.Add("run");
info.ArgumentList.Add("--project");
info.ArgumentList.Add(project);
info.ArgumentList.Add("-c");
info.ArgumentList.Add("Release");
info.ArgumentList.Add("--");
info.ArgumentList.Add("serve");
}
info.ArgumentList.Add("--config");
info.ArgumentList.Add(configPath);
return info;
}
}