refactor(battlenode): single-source MsgEnvelope envelope keys (§E)
Behavior-preserving; 231 BattleNode tests green. The envelope key set was encoded three times (ReservedEnvelopeKeys, the ToJson writes, the FromJson reads). Added a private nested MsgEnvelope.Keys with a const per key; the reserved set, writes, and reads now all draw from it, so a key added in one place but not another (letting a body key shadow an envelope field) can no longer happen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,9 +24,25 @@ public sealed record MsgEnvelope(
|
|||||||
// EngineIoHandshake). Every wire key here is explicit via the manual ToJson layering below.
|
// EngineIoHandshake). Every wire key here is explicit via the manual ToJson layering below.
|
||||||
private static readonly JsonSerializerOptions Options = WireJsonOptions.CamelCase;
|
private static readonly JsonSerializerOptions Options = WireJsonOptions.CamelCase;
|
||||||
|
|
||||||
|
/// <summary>The fixed envelope wire keys, single-sourced. <see cref="ReservedEnvelopeKeys"/>,
|
||||||
|
/// the <see cref="ToJson"/> writes, and the <see cref="FromJson"/> reads all draw from here, so
|
||||||
|
/// the three encodings can't drift — adding a key in one place but not another (which would let a
|
||||||
|
/// body key silently shadow an envelope field) is no longer possible.</summary>
|
||||||
|
private static class Keys
|
||||||
|
{
|
||||||
|
public const string Uri = "uri";
|
||||||
|
public const string ViewerId = "viewerId";
|
||||||
|
public const string Uuid = "uuid";
|
||||||
|
public const string Bid = "bid";
|
||||||
|
public const string Try = "try";
|
||||||
|
public const string Cat = "cat";
|
||||||
|
public const string PubSeq = "pubSeq";
|
||||||
|
public const string PlaySeq = "playSeq";
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly HashSet<string> ReservedEnvelopeKeys = new()
|
private static readonly HashSet<string> ReservedEnvelopeKeys = new()
|
||||||
{
|
{
|
||||||
"uri", "viewerId", "uuid", "bid", "try", "cat", "pubSeq", "playSeq",
|
Keys.Uri, Keys.ViewerId, Keys.Uuid, Keys.Bid, Keys.Try, Keys.Cat, Keys.PubSeq, Keys.PlaySeq,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string ToJson(MsgEnvelope env)
|
public static string ToJson(MsgEnvelope env)
|
||||||
@@ -37,14 +53,14 @@ public sealed record MsgEnvelope(
|
|||||||
// field processed before "uri" is wiped before Matching.StartBattleLoad reads
|
// field processed before "uri" is wiped before Matching.StartBattleLoad reads
|
||||||
// it back. The prod wire emits envelope keys first; we must too.
|
// it back. The prod wire emits envelope keys first; we must too.
|
||||||
var result = new JsonObject();
|
var result = new JsonObject();
|
||||||
result["uri"] = env.Uri.ToString();
|
result[Keys.Uri] = env.Uri.ToString();
|
||||||
result["viewerId"] = env.ViewerId;
|
result[Keys.ViewerId] = env.ViewerId;
|
||||||
result["uuid"] = env.Uuid;
|
result[Keys.Uuid] = env.Uuid;
|
||||||
result["try"] = env.RetryAttempt;
|
result[Keys.Try] = env.RetryAttempt;
|
||||||
result["cat"] = (int)env.Cat;
|
result[Keys.Cat] = (int)env.Cat;
|
||||||
if (env.Bid is not null) result["bid"] = env.Bid;
|
if (env.Bid is not null) result[Keys.Bid] = env.Bid;
|
||||||
if (env.PubSeq.HasValue) result["pubSeq"] = env.PubSeq.Value;
|
if (env.PubSeq.HasValue) result[Keys.PubSeq] = env.PubSeq.Value;
|
||||||
if (env.PlaySeq.HasValue) result["playSeq"] = env.PlaySeq.Value;
|
if (env.PlaySeq.HasValue) result[Keys.PlaySeq] = env.PlaySeq.Value;
|
||||||
|
|
||||||
if (env.Body is RawBody raw)
|
if (env.Body is RawBody raw)
|
||||||
{
|
{
|
||||||
@@ -118,14 +134,14 @@ public sealed record MsgEnvelope(
|
|||||||
using var doc = JsonDocument.Parse(json);
|
using var doc = JsonDocument.Parse(json);
|
||||||
var root = doc.RootElement;
|
var root = doc.RootElement;
|
||||||
|
|
||||||
var uri = Enum.Parse<NetworkBattleUri>(root.GetProperty("uri").GetString()!);
|
var uri = Enum.Parse<NetworkBattleUri>(root.GetProperty(Keys.Uri).GetString()!);
|
||||||
var viewerId = root.GetProperty("viewerId").GetInt64();
|
var viewerId = root.GetProperty(Keys.ViewerId).GetInt64();
|
||||||
var uuid = root.GetProperty("uuid").GetString()!;
|
var uuid = root.GetProperty(Keys.Uuid).GetString()!;
|
||||||
var bid = root.TryGetProperty("bid", out var bidEl) ? bidEl.GetString() : null;
|
var bid = root.TryGetProperty(Keys.Bid, out var bidEl) ? bidEl.GetString() : null;
|
||||||
var retryAttempt = root.TryGetProperty("try", out var tryEl) ? tryEl.GetInt32() : 0;
|
var retryAttempt = root.TryGetProperty(Keys.Try, out var tryEl) ? tryEl.GetInt32() : 0;
|
||||||
var cat = root.TryGetProperty("cat", out var catEl) ? (EmitCategory)catEl.GetInt32() : EmitCategory.Battle;
|
var cat = root.TryGetProperty(Keys.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(Keys.PubSeq, out var psEl) ? psEl.GetInt64() : (long?)null;
|
||||||
var playSeq = root.TryGetProperty("playSeq", out var plsEl) ? plsEl.GetInt64() : (long?)null;
|
var playSeq = root.TryGetProperty(Keys.PlaySeq, out var plsEl) ? plsEl.GetInt64() : (long?)null;
|
||||||
|
|
||||||
var bodyDict = new Dictionary<string, object?>();
|
var bodyDict = new Dictionary<string, object?>();
|
||||||
foreach (var prop in root.EnumerateObject())
|
foreach (var prop in root.EnumerateObject())
|
||||||
|
|||||||
Reference in New Issue
Block a user