using System.Net.WebSockets; using Backend; var builder = WebApplication.CreateBuilder(args); // -------- Config -------- builder.Services.Configure(builder.Configuration.GetSection("Lander")); // -------- Singletons -------- builder.Services.AddSingleton(sp => { var cfg = sp.GetRequiredService>().Value; var absolute = PathResolver.Resolve(cfg.ModelPath); return new PolicyRunner(absolute); }); builder.Services.AddLogging(); var app = builder.Build(); // Fail-fast: eagerly resolve PolicyRunner so a missing ONNX file surfaces at // startup rather than on the first WebSocket connect. var startupLogger = app.Services.GetRequiredService().CreateLogger("Startup"); var policy = app.Services.GetRequiredService(); var modelPath = PathResolver.Resolve( app.Services.GetRequiredService>().Value.ModelPath); startupLogger.LogInformation("Loaded PPO policy from {ModelPath}", modelPath); app.UseWebSockets(); // -------- Health -------- app.MapGet("/", () => "GameCli Backend"); // -------- WebSocket endpoint -------- app.Map("/ws/game", async (HttpContext ctx, PolicyRunner policy, Microsoft.Extensions.Options.IOptions cfg, ILoggerFactory loggerFactory) => { if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = 400; return; } using var socket = await ctx.WebSockets.AcceptWebSocketAsync(); var cliPath = PathResolver.Resolve(cfg.Value.CliPath); var proc = new GameProcess(cliPath); var logger = loggerFactory.CreateLogger(); await using var session = new GameSession(socket, proc, policy, logger); try { await session.RunAsync(ctx.RequestAborted); } catch (WebSocketException) { /* client disconnected */ } catch (OperationCanceledException) { /* shutdown */ } }); app.Run(); // -------- Public program class so Backend.Tests can use WebApplicationFactory -------- public partial class Program { }