using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using SVSim.BattleNode.Bridge; using SVSim.BattleNode.Sessions; namespace SVSim.BattleNode.Hosting; /// /// Registration + pipeline extensions that turn an arbitrary ASP.NET Core host into a battle /// node. The library has no dependency on any specific host project — call both methods from /// wherever you build your . /// public static class BattleNodeExtensions { /// /// Register the battle node's services in DI. All four are singletons because none of them /// carry per-request state — per-battle state lives on the /// instance the WebSocket handler constructs on connect. /// /// /// Optional callback to override defaults. The default /// NodeServerUrl assumes the EmulatedEntrypoint host on /// http://localhost:5148 and shares the port for the Socket.IO endpoint. Override /// when the node runs on a different port/host or behind a reverse proxy. /// public static IServiceCollection AddBattleNode(this IServiceCollection services, Action? configure = null) { var options = new BattleNodeOptions(); configure?.Invoke(options); services.AddSingleton(options); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); return services; } /// /// Wire up the WebSocket middleware and map the Socket.IO endpoint at /socket.io/. /// Call this AFTER any HTTP middleware that should still see non-WS requests (auth, /// routing, controllers) and BEFORE MapControllers(). The endpoint accepts any /// path under /socket.io; the handler doesn't read the sub-path, so default /// Socket.IO clients targeting /socket.io/?EIO=3&transport=websocket work /// without configuration. /// /// /// Steam auth gets a free pass on WS upgrades — see /// SteamSessionAuthenticationHandler's header-based bypass. The node has its own /// per-connection auth (encrypted viewerId in the upgrade headers, validated against the /// matched battle id in ). /// public static IApplicationBuilder UseBattleNode(this IApplicationBuilder app) { app.UseWebSockets(); app.Map("/socket.io", branch => branch.Run(HandleSocketIoAsync)); return app; } /// /// Terminal handler for /socket.io/* — resolves the singleton /// from DI and hands the request over. /// Extracted from the inline lambda in so stack traces /// show a real method name during WS connect failures. /// private static async Task HandleSocketIoAsync(Microsoft.AspNetCore.Http.HttpContext ctx) { var handler = ctx.RequestServices.GetRequiredService(); await handler.HandleAsync(ctx); } }