refactor(battlenode): low-churn §B/§D/§E/§F quality cleanups

Behavior-preserving; 231 BattleNode tests green.

- §D: MsgEnvelope.Try -> RetryAttempt (drops keyword-escape; wire key stays "try");
  SocketIoFrame.AckResponse arg -> pubSeqEcho.
- §B: Gungnir.EmitInterval -> BattleNodeOptions.AliveEmitInterval (unused literal
  moved to its config home); deck-idx 4L -> InitialHand.Length + 1.
- §E: shared Wire.WireJsonOptions.CamelCase replaces the duplicated camelCase
  JsonSerializerOptions in EngineIoHandshake and MsgEnvelope.
- §F: do-NOT-consistency-fix polarity notes on TurnEndFinalHandler (From wins)
  and RetireKillHandler (From loses).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-04 23:06:44 -04:00
parent e70f32db79
commit 7d4da69f22
22 changed files with 84 additions and 62 deletions

View File

@@ -16,6 +16,13 @@ public sealed class BattleNodeOptions
/// </summary> /// </summary>
public TimeSpan WaitingRoomTimeout { get; set; } = TimeSpan.FromSeconds(60); public TimeSpan WaitingRoomTimeout { get; set; } = TimeSpan.FromSeconds(60);
/// <summary>
/// Cadence of the server→client alive ("Gungnir") keepalive emit. The driving timer/loop
/// (to live on <see cref="Sessions.BattleSession"/>) is deferred in v1; this is its future
/// home so the interval isn't a magic literal stranded on the <c>Gungnir</c> body factory.
/// </summary>
public TimeSpan AliveEmitInterval { get; set; } = TimeSpan.FromSeconds(5);
/// <summary> /// <summary>
/// When true, <see cref="Sessions.Participants.RealParticipant"/> emits per-frame /// When true, <see cref="Sessions.Participants.RealParticipant"/> emits per-frame
/// diagnostic logs at Information level: <c>[sio-in]</c> on every inbound msg/alive/hand /// diagnostic logs at Information level: <c>[sio-in]</c> on every inbound msg/alive/hand

View File

@@ -90,13 +90,14 @@ public static class ServerBattleFrames
/// <summary> /// <summary>
/// Compute the player's hand after a mulligan. For every idx in <paramref name="swapIndices"/> /// Compute the player's hand after a mulligan. For every idx in <paramref name="swapIndices"/>
/// that is currently in the hand, replace it with the next unused deck idx (starting at 4, /// that is currently in the hand, replace it with the next unused deck idx (the first idx past
/// since 1..3 were dealt). Positions of kept cards are preserved. /// the opening hand — <see cref="InitialHand"/> is 1-based and contiguous, so that's
/// <c>InitialHand.Length + 1</c>). Positions of kept cards are preserved.
/// </summary> /// </summary>
public static long[] ComputeHandAfterSwap(IReadOnlyList<long> swapIndices) public static long[] ComputeHandAfterSwap(IReadOnlyList<long> swapIndices)
{ {
var hand = InitialHand.ToArray(); var hand = InitialHand.ToArray();
var nextDeckIdx = 4L; var nextDeckIdx = (long)(InitialHand.Length + 1);
for (var pos = 0; pos < hand.Length; pos++) for (var pos = 0; pos < hand.Length; pos++)
{ {
if (swapIndices.Contains(hand[pos])) if (swapIndices.Contains(hand[pos]))
@@ -151,7 +152,7 @@ public static class ServerBattleFrames
ViewerId: FakeOpponentViewerId, ViewerId: FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: bid, Bid: bid,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,

View File

@@ -1,6 +1,6 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Text.Json.Serialization; using SVSim.BattleNode.Wire;
namespace SVSim.BattleNode.Protocol; namespace SVSim.BattleNode.Protocol;
@@ -14,32 +14,21 @@ public sealed record MsgEnvelope(
long ViewerId, long ViewerId,
string Uuid, string Uuid,
string? Bid, string? Bid,
int Try, int RetryAttempt,
EmitCategory Cat, EmitCategory Cat,
long? PubSeq, long? PubSeq,
long? PlaySeq, long? PlaySeq,
IMsgBody Body) IMsgBody Body)
{ {
private static readonly JsonSerializerOptions Options = CreateOptions(); // Bare-camelCase wire serialization, single-sourced in Wire.WireJsonOptions (shared with
// EngineIoHandshake). Every wire key here is explicit via the manual ToJson layering below.
private static readonly JsonSerializerOptions Options = WireJsonOptions.CamelCase;
private static readonly HashSet<string> ReservedEnvelopeKeys = new() private static readonly HashSet<string> ReservedEnvelopeKeys = new()
{ {
"uri", "viewerId", "uuid", "bid", "try", "cat", "pubSeq", "playSeq", "uri", "viewerId", "uuid", "bid", "try", "cat", "pubSeq", "playSeq",
}; };
private static JsonSerializerOptions CreateOptions()
{
var opt = new JsonSerializerOptions
{
// Wire-key casing is bare camelCase via per-field [JsonPropertyName] —
// NOT EmulatedEntrypoint's snake_case policy. The naming-policy line
// that was here previously was dead code (every wire key is explicit).
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
opt.Converters.Add(new JsonStringEnumConverter());
return opt;
}
public static string ToJson(MsgEnvelope env) public static string ToJson(MsgEnvelope env)
{ {
// Envelope fields MUST come before body fields on the wire. The client's // Envelope fields MUST come before body fields on the wire. The client's
@@ -51,7 +40,7 @@ public sealed record MsgEnvelope(
result["uri"] = env.Uri.ToString(); result["uri"] = env.Uri.ToString();
result["viewerId"] = env.ViewerId; result["viewerId"] = env.ViewerId;
result["uuid"] = env.Uuid; result["uuid"] = env.Uuid;
result["try"] = env.Try; result["try"] = env.RetryAttempt;
result["cat"] = (int)env.Cat; result["cat"] = (int)env.Cat;
if (env.Bid is not null) result["bid"] = env.Bid; if (env.Bid is not null) result["bid"] = env.Bid;
if (env.PubSeq.HasValue) result["pubSeq"] = env.PubSeq.Value; if (env.PubSeq.HasValue) result["pubSeq"] = env.PubSeq.Value;
@@ -133,7 +122,7 @@ public sealed record MsgEnvelope(
var viewerId = root.GetProperty("viewerId").GetInt64(); var viewerId = root.GetProperty("viewerId").GetInt64();
var uuid = root.GetProperty("uuid").GetString()!; var uuid = root.GetProperty("uuid").GetString()!;
var bid = root.TryGetProperty("bid", out var bidEl) ? bidEl.GetString() : null; var bid = root.TryGetProperty("bid", out var bidEl) ? bidEl.GetString() : null;
var @try = root.TryGetProperty("try", out var tryEl) ? tryEl.GetInt32() : 0; var retryAttempt = root.TryGetProperty("try", out var tryEl) ? tryEl.GetInt32() : 0;
var cat = root.TryGetProperty("cat", out var catEl) ? (EmitCategory)catEl.GetInt32() : EmitCategory.Battle; var cat = root.TryGetProperty("cat", out var catEl) ? (EmitCategory)catEl.GetInt32() : EmitCategory.Battle;
var pubSeq = root.TryGetProperty("pubSeq", out var psEl) ? psEl.GetInt64() : (long?)null; var pubSeq = root.TryGetProperty("pubSeq", out var psEl) ? psEl.GetInt64() : (long?)null;
var playSeq = root.TryGetProperty("playSeq", out var plsEl) ? plsEl.GetInt64() : (long?)null; var playSeq = root.TryGetProperty("playSeq", out var plsEl) ? plsEl.GetInt64() : (long?)null;
@@ -145,7 +134,7 @@ public sealed record MsgEnvelope(
bodyDict[prop.Name] = ToObject(prop.Value); bodyDict[prop.Name] = ToObject(prop.Value);
} }
return new MsgEnvelope(uri, viewerId, uuid, bid, @try, cat, pubSeq, playSeq, new RawBody(bodyDict)); return new MsgEnvelope(uri, viewerId, uuid, bid, retryAttempt, cat, pubSeq, playSeq, new RawBody(bodyDict));
} }
private static object? ToObject(JsonElement el) => el.ValueKind switch private static object? ToObject(JsonElement el) => el.ValueKind switch

View File

@@ -3,8 +3,9 @@ namespace SVSim.BattleNode.Reliability;
/// <summary> /// <summary>
/// Body builders for the alive channel ("Gungnir" is the client's codename for the /// Body builders for the alive channel ("Gungnir" is the client's codename for the
/// keepalive/connection-status channel — see <see cref="Protocol.Bodies.AlivePushBody"/>). /// keepalive/connection-status channel — see <see cref="Protocol.Bodies.AlivePushBody"/>).
/// The timer/loop that drives <see cref="EmitInterval"/> emits lives on /// The timer/loop that would drive the emit cadence
/// BattleSession; this class is just the pure body-shape factory. /// (<see cref="Bridge.BattleNodeOptions.AliveEmitInterval"/>) is to live on BattleSession;
/// this class is just the pure body-shape factory.
/// v1 always reports scs/ocs=ONLINE — real disconnect detection is deferred. The push /// v1 always reports scs/ocs=ONLINE — real disconnect detection is deferred. The push
/// body itself is constructed inline in BattleSession.HandleAliveEventAsync using /// body itself is constructed inline in BattleSession.HandleAliveEventAsync using
/// AlivePushBody; only the emit body (sent by us TO the client on the alive channel, /// AlivePushBody; only the emit body (sent by us TO the client on the alive channel,
@@ -12,8 +13,6 @@ namespace SVSim.BattleNode.Reliability;
/// </summary> /// </summary>
public static class Gungnir public static class Gungnir
{ {
public static readonly TimeSpan EmitInterval = TimeSpan.FromSeconds(5);
public static Dictionary<string, object?> BuildAliveEmitBody(InboundTracker tracker) => new() public static Dictionary<string, object?> BuildAliveEmitBody(InboundTracker tracker) => new()
{ {
["currentSeq"] = tracker.HighWaterMark, ["currentSeq"] = tracker.HighWaterMark,

View File

@@ -13,7 +13,7 @@ internal static class BattleFrames
ViewerId: ServerBattleFrames.FakeOpponentViewerId, ViewerId: ServerBattleFrames.FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.General, Cat: EmitCategory.General,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,
@@ -24,7 +24,7 @@ internal static class BattleFrames
ViewerId: ServerBattleFrames.FakeOpponentViewerId, ViewerId: ServerBattleFrames.FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,
@@ -35,7 +35,7 @@ internal static class BattleFrames
ViewerId: ServerBattleFrames.FakeOpponentViewerId, ViewerId: ServerBattleFrames.FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,
@@ -46,7 +46,7 @@ internal static class BattleFrames
ViewerId: ServerBattleFrames.FakeOpponentViewerId, ViewerId: ServerBattleFrames.FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,

View File

@@ -7,6 +7,9 @@ internal sealed class RetireKillHandler : IFrameHandler
public IReadOnlyList<DispatchRoute> Handle(FrameDispatchContext ctx) public IReadOnlyList<DispatchRoute> Handle(FrameDispatchContext ctx)
{ {
ctx.State.SessionPhase = BattleSessionPhase.Terminal; ctx.State.SessionPhase = BattleSessionPhase.Terminal;
// Polarity: the SENDER retired, so From LOSES / Other WINS. This is the OPPOSITE of
// TurnEndFinalHandler (From WINS there — sender dealt the lethal). Intentional — do NOT
// "consistency-fix" the two handlers to match; a swap here silently reverses every retire.
return new[] return new[]
{ {
new DispatchRoute(ctx.From, BattleFrames.BuildBattleFinish(BattleResult.RetireLose), Stock.Bypass), new DispatchRoute(ctx.From, BattleFrames.BuildBattleFinish(BattleResult.RetireLose), Stock.Bypass),

View File

@@ -14,6 +14,10 @@ internal sealed class TurnEndFinalHandler : IFrameHandler
if (ctx.SenderPhase == BattleSessionPhase.AfterReady) if (ctx.SenderPhase == BattleSessionPhase.AfterReady)
{ {
ctx.State.SessionPhase = BattleSessionPhase.Terminal; ctx.State.SessionPhase = BattleSessionPhase.Terminal;
// Polarity: the SENDER dealt the lethal, so From WINS / Other LOSES. This is the
// OPPOSITE of RetireKillHandler (From LOSES there — retire is self-inflicted).
// Intentional — do NOT "consistency-fix" the two handlers to match; a swap here
// silently reverses every lethal-turn outcome.
return new[] return new[]
{ {
new DispatchRoute(ctx.Other, ctx.Env, Stock.Normal), new DispatchRoute(ctx.Other, ctx.Env, Stock.Normal),

View File

@@ -404,7 +404,7 @@ public sealed class RealParticipant : IBattleParticipant, IHasHandshakePhase
ViewerId: SVSim.BattleNode.Lifecycle.ServerBattleFrames.FakeOpponentViewerId, ViewerId: SVSim.BattleNode.Lifecycle.ServerBattleFrames.FakeOpponentViewerId,
Uuid: WireConstants.ServerUuid, Uuid: WireConstants.ServerUuid,
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.General, Cat: EmitCategory.General,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,

View File

@@ -12,11 +12,5 @@ public sealed record EngineIoHandshake(
[property: JsonPropertyName("pingInterval")] int PingInterval, [property: JsonPropertyName("pingInterval")] int PingInterval,
[property: JsonPropertyName("pingTimeout")] int PingTimeout) [property: JsonPropertyName("pingTimeout")] int PingTimeout)
{ {
// Wire-key casing here is bare camelCase — NOT EmulatedEntrypoint's snake_case policy. public string ToJson() => JsonSerializer.Serialize(this, WireJsonOptions.CamelCase);
private static readonly JsonSerializerOptions Options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
public string ToJson() => JsonSerializer.Serialize(this, Options);
} }

View File

@@ -158,10 +158,11 @@ public sealed class SocketIoFrame
binaryAttachments: attachments); binaryAttachments: attachments);
} }
/// <summary>Build an ack response with a single int argument (the spec's pubSeq echo).</summary> /// <summary>Build an ack response whose single argument echoes the inbound frame's pubSeq
public static SocketIoFrame AckResponse(int ackId, int arg) /// (the client's ordered-delivery cursor — load-bearing, not a placeholder).</summary>
public static SocketIoFrame AckResponse(int ackId, int pubSeqEcho)
{ {
var args = new JsonArray { arg }; var args = new JsonArray { pubSeqEcho };
return new SocketIoFrame( return new SocketIoFrame(
SocketIoPacketType.Ack, ackId, 0, null, NodesToElements(args), Array.Empty<byte[]>()); SocketIoPacketType.Ack, ackId, 0, null, NodesToElements(args), Array.Empty<byte[]>());
} }

View File

@@ -0,0 +1,24 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SVSim.BattleNode.Wire;
/// <summary>Shared System.Text.Json options for the bare-camelCase Socket.IO / Engine.IO wire:
/// per-field <c>[JsonPropertyName]</c> casing (NOT EmulatedEntrypoint's snake_case policy), null
/// fields omitted, and unattributed enums written as their name. Single-sourced here because
/// <see cref="EngineIoHandshake"/> and <see cref="Protocol.MsgEnvelope"/> previously each built a
/// byte-identical block in their own namespace — a drift hazard.</summary>
internal static class WireJsonOptions
{
public static readonly JsonSerializerOptions CamelCase = Create();
private static JsonSerializerOptions Create()
{
var opt = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
opt.Converters.Add(new JsonStringEnumConverter());
return opt;
}
}

View File

@@ -119,7 +119,7 @@ public class BattleNodeFlowTests
private static MsgEnvelope MakeEnvelopeWith(long vid, NetworkBattleUri uri, long pubSeq, private static MsgEnvelope MakeEnvelopeWith(long vid, NetworkBattleUri uri, long pubSeq,
Dictionary<string, object?>? body = null) => Dictionary<string, object?>? body = null) =>
new(uri, ViewerId: vid, Uuid: "udid-test", Bid: null, Try: 0, new(uri, ViewerId: vid, Uuid: "udid-test", Bid: null, RetryAttempt: 0,
Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General
: uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching : uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching
: EmitCategory.Battle, : EmitCategory.Battle,

View File

@@ -250,7 +250,7 @@ public class CaptureConformanceTests
private static MsgEnvelope MakeEnvelope(long vid, NetworkBattleUri uri, long pubSeq, private static MsgEnvelope MakeEnvelope(long vid, NetworkBattleUri uri, long pubSeq,
Dictionary<string, object?>? body = null) => Dictionary<string, object?>? body = null) =>
new(uri, ViewerId: vid, Uuid: "udid-test", Bid: null, Try: 0, new(uri, ViewerId: vid, Uuid: "udid-test", Bid: null, RetryAttempt: 0,
Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General Cat: uri == NetworkBattleUri.InitNetwork ? EmitCategory.General
: uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching : uri == NetworkBattleUri.InitBattle ? EmitCategory.Matching
: EmitCategory.Battle, : EmitCategory.Battle,
@@ -287,7 +287,7 @@ public class CaptureConformanceTests
var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody(
PlayIdx: 17, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null); PlayIdx: 17, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null);
var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body);
using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env));
@@ -335,7 +335,7 @@ public class CaptureConformanceTests
var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody(
PlayIdx: 38, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null); PlayIdx: 38, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null);
var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body);
using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env));
@@ -376,7 +376,7 @@ public class CaptureConformanceTests
var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody(
PlayIdx: 37, Type: 30, KnownList: null, OppoTargetList: null, UList: relayed); PlayIdx: 37, Type: 30, KnownList: null, OppoTargetList: null, UList: relayed);
var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body);
using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env));
@@ -455,7 +455,7 @@ public class CaptureConformanceTests
var keyActionOut = SVSim.BattleNode.Sessions.Dispatch.KnownListBuilder.StripKeyActionForOpponent(keyActionIn); var keyActionOut = SVSim.BattleNode.Sessions.Dispatch.KnownListBuilder.StripKeyActionForOpponent(keyActionIn);
var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody(
PlayIdx: 18, Type: 30, KnownList: new[] { played! }, OppoTargetList: null, KeyAction: keyActionOut); PlayIdx: 18, Type: 30, KnownList: new[] { played! }, OppoTargetList: null, KeyAction: keyActionOut);
var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body);
using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env));
@@ -520,7 +520,7 @@ public class CaptureConformanceTests
var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody( var body = new SVSim.BattleNode.Protocol.Bodies.PlayActionsBroadcastBody(
PlayIdx: 46, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null); PlayIdx: 46, Type: 30, KnownList: new[] { entry! }, OppoTargetList: null);
var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, var env = new MsgEnvelope(NetworkBattleUri.PlayActions, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: body);
using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env)); using var ourDoc = JsonDocument.Parse(MsgEnvelope.ToJson(env));

View File

@@ -35,7 +35,7 @@ public class MsgEnvelopeTests
ViewerId: 906243102, ViewerId: 906243102,
Uuid: "udid-1234", Uuid: "udid-1234",
Bid: "597830888107", Bid: "597830888107",
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.General, Cat: EmitCategory.General,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,
@@ -60,7 +60,7 @@ public class MsgEnvelopeTests
ViewerId: 1, ViewerId: 1,
Uuid: "u", Uuid: "u",
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: 5, PlaySeq: 5,
@@ -92,7 +92,7 @@ public class MsgEnvelopeTests
ViewerId: 1, ViewerId: 1,
Uuid: "u", Uuid: "u",
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,
@@ -110,7 +110,7 @@ public class MsgEnvelopeTests
ViewerId: 1, ViewerId: 1,
Uuid: "u", Uuid: "u",
Bid: null, Bid: null,
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: null, PubSeq: null,
PlaySeq: null, PlaySeq: null,

View File

@@ -20,7 +20,7 @@ public class MsgPayloadCodecTests
ViewerId: 906243102, ViewerId: 906243102,
Uuid: "udid", Uuid: "udid",
Bid: "1234", Bid: "1234",
Try: 0, RetryAttempt: 0,
Cat: EmitCategory.Battle, Cat: EmitCategory.Battle,
PubSeq: 3, PubSeq: 3,
PlaySeq: null, PlaySeq: null,

View File

@@ -8,7 +8,7 @@ namespace SVSim.UnitTests.BattleNode.Reliability;
public class OutboundSequencerTests public class OutboundSequencerTests
{ {
private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri) => private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, Cat: EmitCategory.Battle, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0, Cat: EmitCategory.Battle,
PubSeq: null, PlaySeq: null, Body: new RawBody(new Dictionary<string, object?>())); PubSeq: null, PlaySeq: null, Body: new RawBody(new Dictionary<string, object?>()));
[Test] [Test]

View File

@@ -56,7 +56,7 @@ public class BattleSessionDispatchConcurrencyTests
} }
private static MsgEnvelope Env(NetworkBattleUri uri) => private static MsgEnvelope Env(NetworkBattleUri uri) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null,
Body: new RawBody(new Dictionary<string, object?>())); Body: new RawBody(new Dictionary<string, object?>()));

View File

@@ -1068,12 +1068,12 @@ public class BattleSessionDispatchTests
BattleType: 11); BattleType: 11);
private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) => private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null,
Body: new RawBody(new Dictionary<string, object?>())); Body: new RawBody(new Dictionary<string, object?>()));
private static MsgEnvelope EnvWith(NetworkBattleUri uri, Dictionary<string, object?> body) => private static MsgEnvelope EnvWith(NetworkBattleUri uri, Dictionary<string, object?> body) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: new RawBody(body)); Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Body: new RawBody(body));
private static Dictionary<string, object?> MoveOrderList(int idx, int from, int to) => new() private static Dictionary<string, object?> MoveOrderList(int idx, int from, int to) => new()

View File

@@ -47,7 +47,7 @@ public class BattleSessionTerminateCascadeTests
} }
private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri) => private static MsgEnvelope MakeEnvelope(NetworkBattleUri uri) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, Cat: EmitCategory.Battle, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0, Cat: EmitCategory.Battle,
PubSeq: null, PlaySeq: null, Body: new RawBody(new Dictionary<string, object?>())); PubSeq: null, PlaySeq: null, Body: new RawBody(new Dictionary<string, object?>()));
private static MatchContext MakeFakeContext() => new( private static MatchContext MakeFakeContext() => new(

View File

@@ -18,7 +18,7 @@ public class NoOpBotParticipantTests
p.FrameEmitted += (_, _) => { fired++; return Task.CompletedTask; }; p.FrameEmitted += (_, _) => { fired++; return Task.CompletedTask; };
var env = new MsgEnvelope( var env = new MsgEnvelope(
NetworkBattleUri.TurnEnd, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, NetworkBattleUri.TurnEnd, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null,
Body: new ResultCodeOnlyBody()); Body: new ResultCodeOnlyBody());

View File

@@ -177,7 +177,7 @@ public class RealParticipantTests
BattleType: 11); BattleType: 11);
private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) => private static MsgEnvelope NewEnvelope(NetworkBattleUri uri) =>
new(uri, ViewerId: 1, Uuid: "u", Bid: null, Try: 0, new(uri, ViewerId: 1, Uuid: "u", Bid: null, RetryAttempt: 0,
Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null, Cat: EmitCategory.Battle, PubSeq: null, PlaySeq: null,
Body: new ResultCodeOnlyBody()); Body: new ResultCodeOnlyBody());
} }

View File

@@ -72,7 +72,7 @@ public class SocketIoFrameTests
[Test] [Test]
public void Encode_AckResponse_IsTypeIdAndArrayOfArgs() public void Encode_AckResponse_IsTypeIdAndArrayOfArgs()
{ {
var frame = SocketIoFrame.AckResponse(ackId: 7, arg: 123); var frame = SocketIoFrame.AckResponse(ackId: 7, pubSeqEcho: 123);
var (text, bins) = frame.Encode(); var (text, bins) = frame.Encode();
Assert.That(text, Is.EqualTo("37[123]")); Assert.That(text, Is.EqualTo("37[123]"));