using SpaceGame.Simulation.Api.Contracts; using SpaceGame.Simulation.Api.Simulation; using System.Text.Json; var builder = WebApplication.CreateBuilder(args); var sseJsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); builder.WebHost.UseUrls("http://127.0.0.1:5079"); builder.Services.AddCors((options) => { options.AddDefaultPolicy((policy) => { policy .AllowAnyHeader() .AllowAnyMethod() .AllowAnyOrigin(); }); }); builder.Services.AddSingleton(); builder.Services.AddHostedService(); var app = builder.Build(); app.UseCors(); app.MapGet("/", () => Results.Redirect("/api/world")); app.MapGet("/api/world", (WorldService worldService) => Results.Ok(worldService.GetSnapshot())); app.MapGet("/api/world/stream", async (HttpContext httpContext, WorldService worldService, CancellationToken cancellationToken) => { httpContext.Response.Headers.Append("Cache-Control", "no-cache"); httpContext.Response.Headers.Append("Content-Type", "text/event-stream"); var afterSequenceRaw = httpContext.Request.Query["afterSequence"].ToString(); _ = long.TryParse(afterSequenceRaw, out var afterSequence); var scopeKind = httpContext.Request.Query["scopeKind"].ToString(); if (string.IsNullOrWhiteSpace(scopeKind)) { scopeKind = httpContext.Request.Query["scope"].ToString(); } if (string.IsNullOrWhiteSpace(scopeKind)) { scopeKind = "universe"; } var systemId = httpContext.Request.Query["systemId"].ToString(); var bubbleId = httpContext.Request.Query["bubbleId"].ToString(); var scope = new ObserverScope( scopeKind, string.IsNullOrWhiteSpace(systemId) ? null : systemId, string.IsNullOrWhiteSpace(bubbleId) ? null : bubbleId); var stream = worldService.Subscribe(scope, afterSequence, cancellationToken); await httpContext.Response.WriteAsync(": connected\n\n", cancellationToken); await httpContext.Response.Body.FlushAsync(cancellationToken); await foreach (var delta in stream.ReadAllAsync(cancellationToken)) { var payload = JsonSerializer.Serialize(delta, sseJsonOptions); await httpContext.Response.WriteAsync($"event: world-delta\ndata: {payload}\n\n", cancellationToken); await httpContext.Response.Body.FlushAsync(cancellationToken); } }); app.MapGet("/api/world/health", (WorldService worldService) => Results.Ok(new { ok = true, sequence = worldService.GetStatus().Sequence, generatedAtUtc = worldService.GetStatus().GeneratedAtUtc, })); app.MapPost("/api/world/reset", (WorldService worldService) => { var snapshot = worldService.Reset(); return Results.Ok(snapshot); }); app.Run();