diff --git a/SVSim.BattleNode/Bridge/BattleNodeOptions.cs b/SVSim.BattleNode/Bridge/BattleNodeOptions.cs index 1cd35971..b9f7d374 100644 --- a/SVSim.BattleNode/Bridge/BattleNodeOptions.cs +++ b/SVSim.BattleNode/Bridge/BattleNodeOptions.cs @@ -1,13 +1,25 @@ namespace SVSim.BattleNode.Bridge; /// -/// DI-injected options for the battle node. NodeServerUrl matches the prod -/// do_matching wire format: host:port/socket.io/, no scheme prefix. -/// BestHTTP's SocketManager parses it as the Socket.IO v2 endpoint URL. +/// DI-injected options for the battle node. /// public sealed class BattleNodeOptions { - public string NodeServerUrl { get; set; } = "localhost:5148/socket.io/"; + /// + /// The Socket.IO v2 endpoint URL echoed back to the client on /*/do_matching + /// success (matching_state 3004/3007/3011). Matches the prod do_matching wire format: + /// host[:port]/socket.io/ — no scheme prefix, must end with /socket.io/. + /// The client's BestHTTP SocketManager parses this string directly; a leading + /// http:///https:// or a missing trailing slash will make the client + /// fail to connect. Host may be an IP, hostname, or FQDN. + /// + /// Deployment-time value — no hardcoded fallback. Must be provided via the + /// "BattleNode:NodeServerUrl" key in appsettings.json (or an + /// equivalently-named env var); + /// validates presence at startup and throws if empty. + /// + /// + public string NodeServerUrl { get; set; } = ""; /// /// How long the first arriver's WS waits for a partner before disconnecting. diff --git a/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs b/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs index 8c07fb8f..f2aa5125 100644 --- a/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs +++ b/SVSim.BattleNode/Hosting/BattleNodeExtensions.cs @@ -18,15 +18,26 @@ public static class BattleNodeExtensions /// 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. + /// Callback to populate . Must set + /// — there is no hardcoded fallback; the + /// value is a deployment concern (localhost during dev, a real host[:port]/socket.io/ + /// when the node runs behind a reverse proxy or on a separate box). Startup throws + /// if it's still empty after the callback runs. /// + /// + /// is null, empty, or whitespace after + /// runs. + /// public static IServiceCollection AddBattleNode(this IServiceCollection services, Action? configure = null) { var options = new BattleNodeOptions(); configure?.Invoke(options); + if (string.IsNullOrWhiteSpace(options.NodeServerUrl)) + { + throw new InvalidOperationException( + "BattleNode:NodeServerUrl is not configured. Set it in appsettings.json under " + + "the \"BattleNode\" section (format: \"host[:port]/socket.io/\", no scheme prefix)."); + } services.AddSingleton(options); services.AddSingleton(); services.AddSingleton(); diff --git a/SVSim.EmulatedEntrypoint/Program.cs b/SVSim.EmulatedEntrypoint/Program.cs index 2acb5338..6c5c9164 100644 --- a/SVSim.EmulatedEntrypoint/Program.cs +++ b/SVSim.EmulatedEntrypoint/Program.cs @@ -165,11 +165,10 @@ public class Program builder.Services.AddBattleNode(opt => { - // Matches the prod do_matching wire format: host:port/socket.io/, no scheme prefix. - // BestHTTP's SocketManager parses this as the Socket.IO v2 endpoint URL. - opt.NodeServerUrl = "localhost:5148/socket.io/"; - // Any field in BattleNodeOptions can be overridden via the "BattleNode" section - // in appsettings*.json — see appsettings.Development.json for DiagnosticLogging. + // Every field on BattleNodeOptions is populated from the "BattleNode" section in + // appsettings*.json. NodeServerUrl has no hardcoded fallback — AddBattleNode + // throws at startup if it's missing/blank. See BattleNodeOptions.NodeServerUrl + // for the required wire format. builder.Configuration.GetSection("BattleNode").Bind(opt); }); // In-process FCFS pair-up for TK2 PvP /do_matching, plus rank-battle's AI-fallback diff --git a/SVSim.EmulatedEntrypoint/appsettings.json b/SVSim.EmulatedEntrypoint/appsettings.json index dee87515..d0efef73 100644 --- a/SVSim.EmulatedEntrypoint/appsettings.json +++ b/SVSim.EmulatedEntrypoint/appsettings.json @@ -27,5 +27,13 @@ "Deck": { "MaxDeckSlots": 36 }, + "BattleNode": { + // Socket.IO v2 endpoint URL echoed to the client on /*/do_matching success. + // Wire format: "host[:port]/socket.io/" — no scheme prefix, trailing slash REQUIRED. + // Host may be an IP, hostname, or FQDN. BestHTTP's SocketManager parses this string + // directly; a leading http:// or https:// will make the client fail to connect. + // Startup throws if this value is missing or blank; there is no hardcoded fallback. + "NodeServerUrl": "localhost:5148/socket.io/" + }, "AllowedHosts": "*" }