Files
SVSimServer/SVSim.BattleNode/Protocol/NumericBoolJsonConverter.cs
gamer147 24180d5b4b refactor(battle-node): de-magic wire flags and scattered constants
Quality pass from the 2026-06-04 BattleNode review (audit in the outer
repo). All changes are behavior-preserving — identical wire bytes,
verified by the full 1008-test suite staying green.

- Name scattered magic numbers: crypto key/IV lengths, outbound-sequencer
  base, WS receive buffer / EIO ping / SID length, polite-close timeout,
  upgrade-credential keys, battle-id digit math, deterministic-turn spin.
- resultCode = 1 -> (int)ReceiveNodeResultCode.Success across body records.
- Pong "3" -> EngineIoPacketType.Pong; remove dead NoOpBotParticipant.Touch
  (replace with #pragma warning disable CS0067).
- Wire-flag enums, serialized as numbers via JsonNumberEnumConverter:
  turnState -> TurnState{First,Second}, isSelf -> CardOwner{Opponent,Self},
  open -> ChoiceVisibility{Hidden,Open}.
- isOfficial / isInvoke -> bool / bool? via new NumericBoolJsonConverter
  (reads/writes 0/1; TDD'd). Scoped to the BattleNode wire boundary only;
  MatchContext and the HTTP/AI-start path stay int (AI-start uses -1 as a
  sentinel, so it is not boolean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 20:46:09 -04:00

30 lines
1.5 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
namespace SVSim.BattleNode.Protocol;
/// <summary>
/// Serializes a <see cref="bool"/> as the wire's numeric 0/1. The client reads these flags via
/// <c>Convert.ToInt32</c> / <c>Convert.ToBoolean</c> (e.g. <c>isOfficial</c>, <c>isInvoke</c>) —
/// never as a JSON <c>true</c>/<c>false</c> token — so a real <c>bool</c> property must still emit
/// a number. Read accepts a JSON number (0 = false, non-zero = true) and, defensively, a
/// <c>true</c>/<c>false</c> token or a numeric string. Applied per-field via
/// <c>[JsonConverter(typeof(NumericBoolJsonConverter))]</c>; works on <c>bool?</c> too (System.Text.Json
/// wraps a <c>JsonConverter&lt;bool&gt;</c> for the nullable case).
/// </summary>
public sealed class NumericBoolJsonConverter : JsonConverter<bool>
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
JsonTokenType.Number => reader.GetInt64() != 0,
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.String => long.TryParse(reader.GetString(), out var n) && n != 0,
_ => throw new JsonException($"Cannot convert token {reader.TokenType} to a numeric bool"),
};
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
=> writer.WriteNumberValue(value ? 1 : 0);
}