From 09b8c4974336918892e9840cd6f8c56126584432 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Sun, 31 May 2026 11:27:02 -0400 Subject: [PATCH] feat(http): ArenaTwoPickBattleController (do_matching stub + finish) Co-Authored-By: Claude Sonnet 4.6 --- .../ArenaTwoPickBattleController.cs | 47 ++++++++++++ .../ArenaTwoPick/BattleFinishRequest.cs | 16 ++++ .../ArenaTwoPick/DoMatchingRequest.cs | 16 ++++ .../ArenaTwoPick/BattleFinishResponseDto.cs | 74 +++++++++++++++++++ .../ArenaTwoPick/DoMatchingResponseDto.cs | 30 ++++++++ .../ArenaTwoPickBattleControllerTests.cs | 24 ++++++ 6 files changed, 207 insertions(+) create mode 100644 SVSim.EmulatedEntrypoint/Controllers/ArenaTwoPickBattleController.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/BattleFinishRequest.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/DoMatchingRequest.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/BattleFinishResponseDto.cs create mode 100644 SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/DoMatchingResponseDto.cs create mode 100644 SVSim.UnitTests/Controllers/ArenaTwoPickBattleControllerTests.cs diff --git a/SVSim.EmulatedEntrypoint/Controllers/ArenaTwoPickBattleController.cs b/SVSim.EmulatedEntrypoint/Controllers/ArenaTwoPickBattleController.cs new file mode 100644 index 0000000..d326da1 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Controllers/ArenaTwoPickBattleController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc; +using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaTwoPick; +using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.ArenaTwoPick; +using SVSim.EmulatedEntrypoint.Services; + +namespace SVSim.EmulatedEntrypoint.Controllers; + +[Route("arena_two_pick_battle")] +public class ArenaTwoPickBattleController : SVSimController +{ + private readonly IArenaTwoPickService _svc; + public ArenaTwoPickBattleController(IArenaTwoPickService svc) => _svc = svc; + + [HttpPost("do_matching")] + public IActionResult DoMatching([FromBody] DoMatchingRequest req) + { + if (!TryGetViewerId(out _)) return Unauthorized(); + return Ok(new DoMatchingResponseDto()); + } + + [HttpPost("finish")] + public async Task Finish([FromBody] BattleFinishRequest req) + { + if (!TryGetViewerId(out var vid)) return Unauthorized(); + try + { + var result = await _svc.RecordBattleResultAsync(vid, req.BattleResult == 1); + return Ok(new BattleFinishResponseDto + { + BattleResult = result.BattleResult, + GetClassExperience = result.GetClassExperience, + ClassExperience = result.ClassExperience, + ClassLevel = result.ClassLevel, + SpotPointInfo = new SpotPointInfoDto + { + BeforeSpotPoint = result.BeforeSpotPoint, + AddSpotPoint = result.AddSpotPoint, + AfterSpotPoint = result.AfterSpotPoint, + }, + }); + } + catch (ArenaTwoPickException ex) + { + return BadRequest(new { error_code = ex.ErrorCode }); + } + } +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/BattleFinishRequest.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/BattleFinishRequest.cs new file mode 100644 index 0000000..eb0ff6b --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/BattleFinishRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaTwoPick; + +[MessagePackObject] +public class BattleFinishRequest +{ + [JsonPropertyName("class_id")] [Key("class_id")] public int ClassId { get; set; } + [JsonPropertyName("total_turn")] [Key("total_turn")] public int TotalTurn { get; set; } + [JsonPropertyName("evolve_count")] [Key("evolve_count")] public int EvolveCount { get; set; } + [JsonPropertyName("enemy_evolve_count")] [Key("enemy_evolve_count")] public int EnemyEvolveCount { get; set; } + [JsonPropertyName("battle_result")] [Key("battle_result")] public int BattleResult { get; set; } + [JsonPropertyName("is_retire")] [Key("is_retire")] public int IsRetire { get; set; } + [JsonPropertyName("SDTRB")] [Key("SDTRB")] public int Sdtrb { get; set; } +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/DoMatchingRequest.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/DoMatchingRequest.cs new file mode 100644 index 0000000..6526931 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaTwoPick/DoMatchingRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using MessagePack; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaTwoPick; + +[MessagePackObject] +public class DoMatchingRequest +{ + [JsonPropertyName("card_master_hash")] [Key("card_master_hash")] public string? CardMasterHash { get; set; } + [JsonPropertyName("deck_no")] [Key("deck_no")] public long DeckNo { get; set; } + [JsonPropertyName("need_init")] [Key("need_init")] public int NeedInit { get; set; } + [JsonPropertyName("log")] [Key("log")] public int Log { get; set; } + [JsonPropertyName("excluded_field_id_list")] [Key("excluded_field_id_list")] public List ExcludedFieldIdList { get; set; } = new(); + [JsonPropertyName("use_stage_select")] [Key("use_stage_select")] public int UseStageSelect { get; set; } + [JsonPropertyName("is_default_skin")] [Key("is_default_skin")] public int IsDefaultSkin { get; set; } +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/BattleFinishResponseDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/BattleFinishResponseDto.cs new file mode 100644 index 0000000..946088f --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/BattleFinishResponseDto.cs @@ -0,0 +1,74 @@ +using System.Text.Json.Serialization; +using MessagePack; +using SVSim.EmulatedEntrypoint.Models.Dtos.Common; +using SVSim.EmulatedEntrypoint.Models.Dtos.Common.ArenaTwoPick; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.ArenaTwoPick; + +[MessagePackObject] +public class BattleFinishResponseDto +{ + [JsonPropertyName("class_experience")] [Key("class_experience")] + public int ClassExperience { get; set; } + + [JsonPropertyName("get_class_experience")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("get_class_experience")] + public int GetClassExperience { get; set; } + + [JsonPropertyName("class_level")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("class_level")] + public int ClassLevel { get; set; } + + [JsonPropertyName("battle_result")] [Key("battle_result")] + public int BattleResult { get; set; } + + [JsonPropertyName("spot_point_info")] [Key("spot_point_info")] + public SpotPointInfoDto SpotPointInfo { get; set; } = new(); + + [JsonPropertyName("achieved_info")] [Key("achieved_info")] + public ArenaTwoPickAchievedInfoDto AchievedInfo { get; set; } = new(); + + [JsonPropertyName("result_decision")] [Key("result_decision")] + public int ResultDecision { get; set; } = 1; + + [JsonPropertyName("reward_list")] [Key("reward_list")] + public List RewardList { get; set; } = new(); + + [JsonPropertyName("gathering_notification")] [Key("gathering_notification")] + public GatheringNotificationDto GatheringNotification { get; set; } = new(); + + [JsonPropertyName("battle_dialog_list")] [Key("battle_dialog_list")] + public List BattleDialogList { get; set; } = new(); +} + +[MessagePackObject] +public class SpotPointInfoDto +{ + [JsonPropertyName("before_spot_point")] [Key("before_spot_point")] public int BeforeSpotPoint { get; set; } + [JsonPropertyName("add_spot_point")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("add_spot_point")] public int AddSpotPoint { get; set; } + [JsonPropertyName("after_spot_point")] [Key("after_spot_point")] public int AfterSpotPoint { get; set; } +} + +[MessagePackObject] +public class GatheringNotificationDto +{ + [JsonPropertyName("matching_established_message")] [Key("matching_established_message")] + public string MatchingEstablishedMessage { get; set; } = ""; +} + +[MessagePackObject] +public class ArenaTwoPickAchievedInfoDto +{ + [JsonPropertyName("achieved_achievement_list")] [Key("achieved_achievement_list")] + public List AchievedAchievementList { get; set; } = new(); + + [JsonPropertyName("achieved_mission_list")] [Key("achieved_mission_list")] + public List AchievedMissionList { get; set; } = new(); + + [JsonPropertyName("achieved_mission_reward_list")] [Key("achieved_mission_reward_list")] + public List AchievedMissionRewardList { get; set; } = new(); + + [JsonPropertyName("grand_master_reward_list")] [Key("grand_master_reward_list")] + public List GrandMasterRewardList { get; set; } = new(); + + [JsonPropertyName("mission_start_data")] [Key("mission_start_data")] + public List MissionStartData { get; set; } = new(); +} diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/DoMatchingResponseDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/DoMatchingResponseDto.cs new file mode 100644 index 0000000..0a14d50 --- /dev/null +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaTwoPick/DoMatchingResponseDto.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; +using MessagePack; +using SVSim.EmulatedEntrypoint.Models.Dtos.Common; + +namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.ArenaTwoPick; + +[MessagePackObject] +public class DoMatchingResponseDto +{ + [JsonPropertyName("timeout_period")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("timeout_period")] + public int TimeoutPeriod { get; set; } = 50; + + [JsonPropertyName("retry_period")] [JsonConverter(typeof(StringifiedIntConverter))] [Key("retry_period")] + public int RetryPeriod { get; set; } = 3; + + [JsonPropertyName("node_server_url")] [Key("node_server_url")] + public string NodeServerUrl { get; set; } = ""; + + [JsonPropertyName("room_param")] [Key("room_param")] + public string RoomParam { get; set; } = ""; + + [JsonPropertyName("matching_state")] [Key("matching_state")] + public int MatchingState { get; set; } = 3002; + + [JsonPropertyName("mission_parameter")] [Key("mission_parameter")] + public Dictionary MissionParameter { get; set; } = new() + { + ["follower_play_count_for_mission"] = "{me.game_play_cards_other_self.unit.count}", + }; +} diff --git a/SVSim.UnitTests/Controllers/ArenaTwoPickBattleControllerTests.cs b/SVSim.UnitTests/Controllers/ArenaTwoPickBattleControllerTests.cs new file mode 100644 index 0000000..e0537d3 --- /dev/null +++ b/SVSim.UnitTests/Controllers/ArenaTwoPickBattleControllerTests.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Net.Http.Json; +using SVSim.UnitTests.Infrastructure; + +namespace SVSim.UnitTests.Controllers; + +public class ArenaTwoPickBattleControllerTests +{ + [Test] + public async Task DoMatching_returns_perpetual_searching_with_empty_node_url() + { + using var factory = new SVSimTestFactory(); + var viewerId = await factory.SeedViewerAsync(); + using var client = factory.CreateAuthenticatedClient(viewerId); + var req = new { deck_no = 1L, need_init = 1, log = 1, excluded_field_id_list = new long[] { }, use_stage_select = 1, is_default_skin = 0 }; + var resp = await client.PostAsync("/arena_two_pick_battle/do_matching", JsonContent.Create(req)); + + Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.OK)); + var body = await resp.Content.ReadAsStringAsync(); + StringAssert.Contains("\"matching_state\":3002", body); + StringAssert.Contains("\"node_server_url\":\"\"", body); + StringAssert.DoesNotContain("\"battle_id\"", body); + } +}