Files
RpgRoller/RpgRoller/Api/StateEventEndpoints.cs
2026-04-05 01:40:26 +02:00

63 lines
2.7 KiB
C#

using RpgRoller.Contracts;
using RpgRoller.Services;
namespace RpgRoller.Api;
internal static class StateEventEndpoints
{
public static RouteGroupBuilder MapStateEventEndpoints(this RouteGroupBuilder group)
{
group.MapGet("/events/state", async Task<IResult> (Guid campaignId, HttpContext context, IGameService game) =>
{
var sessionToken = context.GetRequiredSessionToken();
var stateResult = game.GetCampaignStateSnapshot(sessionToken, campaignId);
if (!stateResult.Succeeded)
return stateResult.Error!.Code == "unauthorized" ? TypedResults.Unauthorized() : TypedResults.BadRequest(new ApiError(stateResult.Error.Message, stateResult.Error.Code));
context.Response.Headers.CacheControl = "no-cache";
context.Response.Headers.Connection = "keep-alive";
context.Response.ContentType = "text/event-stream";
var lastState = stateResult.Value!;
await WriteStateEventAsync(context.Response, lastState);
await context.Response.Body.FlushAsync();
try
{
while (!context.RequestAborted.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), context.RequestAborted);
var currentStateResult = game.GetCampaignStateSnapshot(sessionToken, campaignId);
if (!currentStateResult.Succeeded)
break;
var currentState = currentStateResult.Value!;
if (currentState.TotalVersion != lastState.TotalVersion)
{
lastState = currentState;
await WriteStateEventAsync(context.Response, currentState);
}
else
await context.Response.WriteAsync(": heartbeat\n\n");
await context.Response.Body.FlushAsync();
}
}
catch (OperationCanceledException)
{
}
return TypedResults.Empty;
});
return group;
}
private static Task WriteStateEventAsync(HttpResponse response, CampaignStateSnapshot snapshot)
{
var characterVersions = string.Join(",", snapshot.CharacterVersions.Select(version => $"{{\"characterId\":\"{version.CharacterId}\",\"version\":{version.Version}}}"));
return response.WriteAsync($"event: state\ndata: {{\"campaignId\":\"{snapshot.CampaignId}\",\"totalVersion\":{snapshot.TotalVersion},\"rosterVersion\":{snapshot.RosterVersion},\"logVersion\":{snapshot.LogVersion},\"characterVersions\":[{characterVersions}]}}\n\n");
}
}