Add a per-project README in SVSim.BattleNode/ that covers: - Architecture (the six concern folders) - The connect-handshake sequence verified end-to-end at smoke - A wire-format-gotchas table for the spec divergences caught during v1 (headers vs query for credentials, schemeless node URL with /socket.io/ path, required card_master_id, required resultCode=1, Matched in response to InitBattle not InitNetwork, EIO3 0x04 prefix on binary frames, FromJson conditional-expression number-boxing) - What the v1 scripted opponent does and what is hardcoded - A "where to extend" table for v2 work - The full test layout and cross-references to specs/plans Fill in XML docs on the public surface that previously had none: - BattleNodeExtensions.AddBattleNode / UseBattleNode (DI + middleware wiring, including the pipeline-order note that auth runs before UseWebSockets) - BattleNodeWebSocketHandler class + HandleAsync (the validation chain) - BattleSession.ComputeResponses (the lifecycle state machine, with the NoStock flag's meaning) - ScriptedLifecycle class (v1 scope, resultCode injection rule, pointer to the "where to extend" section) - MatchingBridge class (mint-id + register flow) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
32 lines
1.3 KiB
C#
32 lines
1.3 KiB
C#
using SVSim.BattleNode.Sessions;
|
|
|
|
namespace SVSim.BattleNode.Bridge;
|
|
|
|
/// <summary>
|
|
/// In-process implementation of <see cref="IMatchingBridge"/>. The HTTP-side
|
|
/// <c>do_matching</c> controller calls <see cref="RegisterPendingBattle"/>, which mints a
|
|
/// 12-digit decimal battle id, stashes a <see cref="PendingBattle"/> entry in the
|
|
/// <see cref="IBattleSessionStore"/>, and returns the node URL the client should connect to.
|
|
/// The WebSocket handler resolves the same battle id back to the viewer on connect.
|
|
/// </summary>
|
|
public sealed class MatchingBridge : IMatchingBridge
|
|
{
|
|
private readonly IBattleSessionStore _store;
|
|
private readonly BattleNodeOptions _options;
|
|
|
|
public MatchingBridge(IBattleSessionStore store, BattleNodeOptions options)
|
|
{
|
|
_store = store;
|
|
_options = options;
|
|
}
|
|
|
|
public PendingMatch RegisterPendingBattle(long viewerId)
|
|
{
|
|
// 12-digit decimal battle id mirrors the captures (e.g. "975695075012").
|
|
// Cast to long before Math.Abs to avoid OverflowException on int.MinValue.
|
|
var battleId = (Math.Abs((long)Guid.NewGuid().GetHashCode()) % 1_000_000_000_000L).ToString("D12");
|
|
_store.RegisterPending(new PendingBattle(battleId, viewerId));
|
|
return new PendingMatch(battleId, _options.NodeServerUrl);
|
|
}
|
|
}
|