From c7dfd43daa7b2897944a14f1b2052dc2a9c86980 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 27 May 2026 00:00:24 -0400 Subject: [PATCH] review(bp): doc fixes + sales_period_info non-nullable + drop checked cast + TODO Co-Authored-By: Claude Sonnet 4.6 --- .../BattlePass/IViewerBattlePassRepository.cs | 6 ++++-- .../Controllers/BattlePassController.cs | 12 +++++++++++- .../Dtos/BattlePass/BattlePassItemListResponse.cs | 3 ++- .../Services/BattlePassService.cs | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/SVSim.Database/Repositories/BattlePass/IViewerBattlePassRepository.cs b/SVSim.Database/Repositories/BattlePass/IViewerBattlePassRepository.cs index 633335e..60730f3 100644 --- a/SVSim.Database/Repositories/BattlePass/IViewerBattlePassRepository.cs +++ b/SVSim.Database/Repositories/BattlePass/IViewerBattlePassRepository.cs @@ -6,8 +6,10 @@ namespace SVSim.Database.Repositories.BattlePass; public interface IViewerBattlePassRepository { /// - /// Get-or-create progress row for (viewer, season). New rows are added to the change-tracker - /// but NOT saved — caller batches with other mutations. + /// Get-or-create progress row for (viewer, season). New rows are saved IMMEDIATELY (with + /// DbUpdateException catch-and-retry to handle the concurrent-first-visit race against + /// the (ViewerId, SeasonId) unique index). Existing rows are returned tracked so callers + /// can mutate them and batch the save with other changes. /// Task GetOrCreateProgressAsync(long viewerId, int seasonId, CancellationToken ct); diff --git a/SVSim.EmulatedEntrypoint/Controllers/BattlePassController.cs b/SVSim.EmulatedEntrypoint/Controllers/BattlePassController.cs index d136156..a032ec9 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/BattlePassController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/BattlePassController.cs @@ -25,7 +25,12 @@ public class BattlePassController : SVSimController if (!TryGetViewerId(out long viewerId)) return Unauthorized(); var info = await _battlePass.GetInfoAsync(viewerId, ct); - if (info is null) return Ok(new { }); // off-season: empty payload + // TODO(off-season-crash): Empty {} body crashes BattlePassInfoTask.Parse() on the + // client (unconditional jsonData["season_info"] access). Unreachable in practice — + // season 23 outlasts the Cygames shutdown. If a season-24+ ever lands, set + // data_headers.result_code != 1 here so base.Parse() short-circuits before the + // subclass Parse runs. + if (info is null) return Ok(new { }); return Ok(info); } @@ -35,6 +40,11 @@ public class BattlePassController : SVSimController if (!TryGetViewerId(out long viewerId)) return Unauthorized(); var list = await _battlePass.GetItemListAsync(viewerId, ct); + // TODO(off-season-crash): Empty {} body crashes BattlePassPurchaseInfoTask.Parse() on the + // client (unconditional jsonData["premium_pass_description"] access). Unreachable in + // practice — season 23 outlasts the Cygames shutdown. If a season-24+ ever lands, set + // data_headers.result_code != 1 here so base.Parse() short-circuits before the + // subclass Parse runs. if (list is null) return Ok(new { }); return Ok(list); } diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/BattlePass/BattlePassItemListResponse.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/BattlePass/BattlePassItemListResponse.cs index 4c7a090..2475522 100644 --- a/SVSim.EmulatedEntrypoint/Models/Dtos/BattlePass/BattlePassItemListResponse.cs +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/BattlePass/BattlePassItemListResponse.cs @@ -16,7 +16,8 @@ public class BattlePassItemListResponse [JsonPropertyName("sales_period_info")] [Key("sales_period_info")] - public BattlePassSalesPeriodInfoDto? SalesPeriodInfo { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public BattlePassSalesPeriodInfoDto SalesPeriodInfo { get; set; } = new(); [JsonPropertyName("products")] [Key("products")] diff --git a/SVSim.EmulatedEntrypoint/Services/BattlePassService.cs b/SVSim.EmulatedEntrypoint/Services/BattlePassService.cs index 136bdf7..91a8ce1 100644 --- a/SVSim.EmulatedEntrypoint/Services/BattlePassService.cs +++ b/SVSim.EmulatedEntrypoint/Services/BattlePassService.cs @@ -203,7 +203,7 @@ public sealed class BattlePassService : IBattlePassService // append the post-deduction total so the client gets the correct final balance. postState.RemoveAll(r => r.RewardType == (int)UserGoodsType.Crystal); postState.Add(new GrantedReward( - (int)UserGoodsType.Crystal, 0, checked((int)viewer.Currency.Crystals))); + (int)UserGoodsType.Crystal, 0, (int)viewer.Currency.Crystals)); return new BattlePassBuyOutcome(1, achieved, postState); }