fix(battle-node): preserve long type on numeric array elements in FromJson
Root cause for the lingering mulligan failure: the inline conditional
expression in MsgEnvelope.ToObject
JsonValueKind.Number => el.TryGetInt64(out var l) ? l : el.GetDouble(),
unified its branches to the common implicit-convertible type. long→double
is implicit, so both branches collapsed to double and the integer value
silently widened. Inside an array (idxList:[2]), each element came back
as boxed double; OfType<long> in ExtractIdxList then filtered every
entry out, so swapIndices arrived empty and BuildSwapResponse echoed
the unchanged hand — exactly the diff-against-Deal mismatch the client
flagged as "Card swap failed: AbandonCards[2]/DrawCards[]".
Extract a ParseNumber helper that returns object explicitly so each
branch boxes its own runtime type. Also harden ExtractIdxList to accept
any boxed numeric type (long/int/double/decimal/string) so a future
JSON-parser drift can't silently regress this path again.
Two regression tests:
- FromJson_NumericArray_PreservesLongTypeOnEachElement: confirms the
fix at the JSON-parse layer with a hardcoded "{\"idxList\":[2,3]}".
- Swap_WithIdxListContainingTwo_ProducesHandWithFreshIdxAtPosition1:
exercises the dispatch end-to-end with a Body holding a real boxed
long; asserts position 1 of the response hand is the fresh deck idx 4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,14 @@ public sealed record MsgEnvelope(
|
||||
private static object? ToObject(JsonElement el) => el.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => el.GetString(),
|
||||
JsonValueKind.Number => el.TryGetInt64(out var l) ? l : el.GetDouble(),
|
||||
// Extracted to a helper because writing the conditional inline as
|
||||
// el.TryGetInt64(out var l) ? l : el.GetDouble()
|
||||
// unifies the conditional's branches to the common implicit-convertible type. long→double
|
||||
// is implicit; so the result type collapses to double and the long value silently widens.
|
||||
// Downstream OfType<long> filters then drop the (now boxed-double) entries, which broke
|
||||
// the mulligan idxList extraction. Separate method returns object explicitly so each
|
||||
// branch boxes its own runtime type.
|
||||
JsonValueKind.Number => ParseNumber(el),
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
JsonValueKind.Null => null,
|
||||
@@ -97,4 +104,10 @@ public sealed record MsgEnvelope(
|
||||
JsonValueKind.Object => el.EnumerateObject().ToDictionary(p => p.Name, p => ToObject(p.Value)),
|
||||
_ => el.GetRawText(),
|
||||
};
|
||||
|
||||
private static object ParseNumber(JsonElement el)
|
||||
{
|
||||
if (el.TryGetInt64(out var l)) return l;
|
||||
return el.GetDouble();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user