From b5e33c15f6ae76c581f684ac09c40d361b29cbc3 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Thu, 28 May 2026 18:03:50 -0400 Subject: [PATCH] fix(mypage): populate user_item_list from viewer.Items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MyPageTask.Parse (Wizard/MyPageTask.cs:155-163) does `_userItemDict.Clear();` the moment `user_item_list` is present in the response body — not when it's non-empty — then re-populates from the wire. Our /mypage/index was emitting [] by default (the field initializer on the DTO), which wiped the inventory that /load/index had just populated. Downstream consequence: the client's PackChildGachaInfo.CostGoodsCount reads from _userItemDict, so a wiped dict makes every ticket-cost pack report CostGoodsCount=0, PackConfig.EnableBuyPack returns false, and is_hide=1 packs (including the tutorial legendary starter 99047) disappear from the rotation pack list — even though the gift bundle just granted the ticket and the DB row exists. The tutorial then auto-selects whatever non-tutorial pack happens to be at index 0 of the filtered list, the user can't afford it, and the flow is stuck. Fix: - MyPageController.Index now sets UserItemList from viewer.Items (already loaded by GetViewerByShortUdid's home-screen graph). - DTO docstring rewritten to call out the presence-sensitive semantics and the load-bearing path through PackConfig.EnableBuyPack, so the next developer doesn't get the "empty is fine" hint the old comment implied. Co-Authored-By: Claude Opus 4.7 --- .../Controllers/MyPageController.cs | 7 +++++++ .../Models/Dtos/Responses/MyPageIndexResponse.cs | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/SVSim.EmulatedEntrypoint/Controllers/MyPageController.cs b/SVSim.EmulatedEntrypoint/Controllers/MyPageController.cs index dec7263..fe63bbb 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/MyPageController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/MyPageController.cs @@ -100,6 +100,13 @@ public class MyPageController : SVSimController }, BasicPuzzle = new Models.Dtos.Common.BadgeFlag { IsDisplayBadge = false }, // TODO(mypage-stub): viewer practice-puzzle progress IsBattlePassPeriod = rotation.IsBattlePassPeriod, + // The client's MyPageTask.Parse (line 155-163) does `_userItemDict.Clear();` whenever + // user_item_list is present in the response — not when it's non-empty — and then + // repopulates from the wire. Emitting [] here wipes the inventory the client populated + // from /load/index, which makes PackChildGachaInfo.CostGoodsCount return 0 and filters + // out is_hide=1 tutorial packs (the legendary starter 99047) via PackConfig.EnableBuyPack. + // Populate from viewer.Items so the client's dict stays in sync with the DB. + UserItemList = viewer.Items.Select(i => new UserItem(i)).ToList(), SpecialCrystalInfo = new(), // TODO(mypage-stub): same shape/source as /load/index // CompetitionInfo, ShopNotification, StoryNotification, GuildNotification, GatheringInfo, // IsHiddenBossAppeared, SubBanner/SubBannerList/HomeDialogList/UserOfflineEvent/UserItemList, diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/MyPageIndexResponse.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/MyPageIndexResponse.cs index 7a1d329..c042f57 100644 --- a/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/MyPageIndexResponse.cs +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/MyPageIndexResponse.cs @@ -267,8 +267,14 @@ public class MyPageIndexResponse // ── Per-viewer / event state ─────────────────────────────────────────── /// - /// Updated item counts. Empty list = "no items to update" (client iterates 0 times, no UI change). - /// Per-viewer state — populate from viewer.Items when that wiring lands. + /// Full snapshot of the viewer's owned items — NOT a delta. The client's + /// MyPageTask.Parse (line 155-163) clears _userItemDict the moment it sees + /// this key, then re-populates from the wire list. Emitting [] wipes whatever + /// /load/index populated, breaking any client logic that reads from the dict — most + /// load-bearingly PackChildGachaInfo.CostGoodsCount, which gates tutorial-pack + /// visibility via PackConfig.EnableBuyPack. Controllers MUST populate the full + /// owned-items snapshot from viewer.Items; an empty list is correct only when the + /// viewer genuinely owns nothing. /// [JsonPropertyName("user_item_list")] [Key("user_item_list")]