From 2f7a2305da2872f1c7faf6efb979ae40a0a2f596 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 10 Jun 2026 07:51:00 -0400 Subject: [PATCH] feat(replay): add /replay/info and /replay/detail DTOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All numeric fields on ReplayInfoItemDto ship as string — matches prod capture (frame 96 of traffic_prod_misc_clicking.ndjson). api-spec doc will be updated separately. Co-Authored-By: Claude Opus 4.7 --- .../Dtos/Replay/ReplayDetailRequestDto.cs | 17 +++++ .../Models/Dtos/Replay/ReplayInfoItemDto.cs | 74 +++++++++++++++++++ .../Dtos/Replay/ReplayInfoResponseDto.cs | 18 +++++ 3 files changed, 109 insertions(+) create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayDetailRequestDto.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoItemDto.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoResponseDto.cs diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayDetailRequestDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayDetailRequestDto.cs new file mode 100644 index 0000000..55a25d6 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayDetailRequestDto.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using MessagePack; +using SVSim.EmulatedEntrypoint.Models.Dtos.Requests; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Replay; + +/// +/// /replay/detail request. ReplayDetailTaskParam declares new int viewer_id +/// shadowing the inherited field — wire ships both. We accept both; the body's +/// viewer_id identifies the replay owner (typically same as caller). +/// +[MessagePackObject] +public sealed class ReplayDetailRequestDto : BaseRequest +{ + [JsonPropertyName("battle_id"), Key("battle_id")] + public long BattleId { get; set; } +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoItemDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoItemDto.cs new file mode 100644 index 0000000..f08e0e2 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoItemDto.cs @@ -0,0 +1,74 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Replay; + +/// +/// One entry in /replay/info's replay_list. Every numeric field ships as STRING +/// on the wire — matches prod capture verbatim (see data_dumps/captures/ +/// traffic_prod_misc_clicking.ndjson, frame 96). The api-spec doc shows some +/// fields as number; capture overrides. +/// +[MessagePackObject] +public sealed class ReplayInfoItemDto +{ + [JsonPropertyName("battle_type"), Key("battle_type")] + public string BattleType { get; set; } = "0"; + + [JsonPropertyName("two_pick_type"), Key("two_pick_type")] + public string TwoPickType { get; set; } = "0"; + + [JsonPropertyName("deck_format"), Key("deck_format")] + public string DeckFormat { get; set; } = "0"; + + [JsonPropertyName("battle_id"), Key("battle_id")] + public string BattleId { get; set; } = "0"; + + [JsonPropertyName("is_limit_turn"), Key("is_limit_turn")] + public string IsLimitTurn { get; set; } = "0"; + + [JsonPropertyName("opponent_name"), Key("opponent_name")] + public string OpponentName { get; set; } = ""; + + [JsonPropertyName("class_id"), Key("class_id")] + public string ClassId { get; set; } = "0"; + + [JsonPropertyName("opponent_class_id"), Key("opponent_class_id")] + public string OpponentClassId { get; set; } = "0"; + + [JsonPropertyName("sub_class_id"), Key("sub_class_id")] + public string SubClassId { get; set; } = "0"; + + [JsonPropertyName("opponent_sub_class_id"), Key("opponent_sub_class_id")] + public string OpponentSubClassId { get; set; } = "0"; + + [JsonPropertyName("rotation_id"), Key("rotation_id")] + public string RotationId { get; set; } = "0"; + + [JsonPropertyName("opponent_rotation_id"), Key("opponent_rotation_id")] + public string OpponentRotationId { get; set; } = "0"; + + [JsonPropertyName("opponent_country_code"), Key("opponent_country_code")] + public string OpponentCountryCode { get; set; } = ""; + + [JsonPropertyName("chara_id"), Key("chara_id")] + public string CharaId { get; set; } = "0"; + + [JsonPropertyName("opponent_chara_id"), Key("opponent_chara_id")] + public string OpponentCharaId { get; set; } = "0"; + + [JsonPropertyName("opponent_emblem_id"), Key("opponent_emblem_id")] + public string OpponentEmblemId { get; set; } = "0"; + + [JsonPropertyName("opponent_degree_id"), Key("opponent_degree_id")] + public string OpponentDegreeId { get; set; } = "0"; + + [JsonPropertyName("is_win"), Key("is_win")] + public string IsWin { get; set; } = "0"; + + [JsonPropertyName("battle_start_time"), Key("battle_start_time")] + public string BattleStartTime { get; set; } = ""; + + [JsonPropertyName("create_time"), Key("create_time")] + public string CreateTime { get; set; } = ""; +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoResponseDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoResponseDto.cs new file mode 100644 index 0000000..4813275 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Replay/ReplayInfoResponseDto.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Replay; + +[MessagePackObject] +public sealed class ReplayInfoResponseDto +{ + /// + /// Required — client does not guard with Keys.Contains. Emit empty list, never null. + /// + [JsonPropertyName("replay_list"), Key("replay_list")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public List ReplayList { get; set; } = new(); + + // feature_maintenance_list intentionally omitted — optional per spec, never set + // because we don't gate replay viewing. WhenWritingNull drops it from the wire. +}