Deck list work
This commit is contained in:
@@ -16,9 +16,29 @@ public class DeckListResponse
|
||||
[JsonPropertyName("maintenance_card_list")]
|
||||
[Key("maintenance_card_list")] public List<long> MaintenanceCardList { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Single-format viewer decks. Emitted when the request specified a specific format
|
||||
/// (e.g. Rotation, Unlimited) — mutually exclusive with the per-format keys below.
|
||||
/// </summary>
|
||||
[JsonPropertyName("user_deck_list")]
|
||||
[Key("user_deck_list")] public List<UserDeck>? UserDeckList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Per-format viewer decks. Emitted when the request specified All format (deck_format=0).
|
||||
/// Prod's <c>DeckListUtility.ParseDeckInfoResponceData</c> All-format branch only walks these
|
||||
/// per-format keys (not user_deck_list), so the controller swaps shape based on the request.
|
||||
/// The PreRotation / Crossover / Avatar siblings exist in client code but prod omits them
|
||||
/// for fresh viewers; we mirror that omission.
|
||||
/// </summary>
|
||||
[JsonPropertyName("user_deck_rotation")]
|
||||
[Key("user_deck_rotation")] public List<UserDeck>? UserDeckRotation { get; set; }
|
||||
|
||||
[JsonPropertyName("user_deck_unlimited")]
|
||||
[Key("user_deck_unlimited")] public List<UserDeck>? UserDeckUnlimited { get; set; }
|
||||
|
||||
[JsonPropertyName("user_deck_my_rotation")]
|
||||
[Key("user_deck_my_rotation")] public List<UserDeck>? UserDeckMyRotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Global starter decks, keyed by deck_no as string (prod ids 91-98 — one per class).
|
||||
/// </summary>
|
||||
|
||||
@@ -89,6 +89,48 @@ public class MyPageIndexResponse
|
||||
[Key("is_available_colosseum_free_entry")]
|
||||
public bool IsAvailableColosseumFreeEntry { get; set; }
|
||||
|
||||
// ── Sealed Arena season ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// sealed_info is consumed by ArenaData.SetSealedMyPageResponseData (Keys.Contains-guarded),
|
||||
/// but post-parse-consumer policy says we emit anyway. Defaults to a zeroed-out SealedInfo
|
||||
/// when no current season is seeded — Enable=0 means the UI treats Sealed as inactive.
|
||||
/// </summary>
|
||||
[JsonPropertyName("sealed_info")]
|
||||
[Key("sealed_info")]
|
||||
public SealedInfo SealedInfo { get; set; } = new();
|
||||
|
||||
// ── Mypage banner carousel ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// banner is consumed by per-entry parsing inside a TryGetValue guard
|
||||
/// (Wizard/MyPageBannerBase.BannerInfo.Parse iterates the array if present). We always emit
|
||||
/// the list — empty when no rows have been imported. See SVSim.Bootstrap.GlobalsImporter.ImportBanners.
|
||||
/// </summary>
|
||||
[JsonPropertyName("banner")]
|
||||
[Key("banner")]
|
||||
public List<BannerInfo> Banner { get; set; } = new();
|
||||
|
||||
/// <summary>Prod sends explicit null. Override WhenWritingNull so the key survives serialization.</summary>
|
||||
[JsonPropertyName("sub_banner")]
|
||||
[Key("sub_banner")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public object? SubBanner { get; set; }
|
||||
|
||||
[JsonPropertyName("sub_banner_list")]
|
||||
[Key("sub_banner_list")]
|
||||
public List<object> SubBannerList { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("home_dialog_list")]
|
||||
[Key("home_dialog_list")]
|
||||
public List<object> HomeDialogList { get; set; } = new();
|
||||
|
||||
// ── Room type in session (Special-format windows) ──────────────────────
|
||||
|
||||
[JsonPropertyName("room_type_in_session")]
|
||||
[Key("room_type_in_session")]
|
||||
public RoomTypeInSession RoomTypeInSession { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Required — ColosseumEntryInfoTask.SetColosseumInfo indexes this key
|
||||
/// directly (Wizard/ColosseumEntryInfoTask.cs:102) and reads
|
||||
@@ -104,25 +146,37 @@ public class MyPageIndexResponse
|
||||
[Key("convention")]
|
||||
public Convention Convention { get; set; } = new();
|
||||
|
||||
// ── Battle / room recovery (optional) ─────────────────────────────────
|
||||
/// <summary>
|
||||
/// Required — MyPageTask.cs:110 constructs ArenaCompetition(responseData)
|
||||
/// which indexes data.competition_info.is_competition_period unconditionally
|
||||
/// (ArenaCompetition.cs:232-233). When false, the rest of the block is
|
||||
/// skipped, so a default-constructed CompetitionInfo is sufficient.
|
||||
/// </summary>
|
||||
[JsonPropertyName("competition_info")]
|
||||
[Key("competition_info")]
|
||||
public CompetitionInfo CompetitionInfo { get; set; } = new();
|
||||
|
||||
// ── Battle / room recovery ─────────────────────────────────────────────
|
||||
|
||||
/// <summary>Prod always sends concrete bool here even for fresh viewers — emit always.</summary>
|
||||
[JsonPropertyName("unfinished_battle_exists")]
|
||||
[Key("unfinished_battle_exists")]
|
||||
public bool? UnfinishedBattleExists { get; set; }
|
||||
public bool UnfinishedBattleExists { get; set; }
|
||||
|
||||
/// <summary>Only meaningful when UnfinishedBattleExists is true. Keep nullable + omitted otherwise — prod also omits it for fresh viewers.</summary>
|
||||
[JsonPropertyName("battle_finish_wait_time")]
|
||||
[Key("battle_finish_wait_time")]
|
||||
public int? BattleFinishWaitTime { get; set; }
|
||||
|
||||
[JsonPropertyName("is_joined_room")]
|
||||
[Key("is_joined_room")]
|
||||
public bool? IsJoinedRoom { get; set; }
|
||||
public bool IsJoinedRoom { get; set; }
|
||||
|
||||
// ── Login bonus (optional) ─────────────────────────────────────────────
|
||||
// ── Login bonus ────────────────────────────────────────────────────────
|
||||
|
||||
[JsonPropertyName("can_give_daily_login_bonus")]
|
||||
[Key("can_give_daily_login_bonus")]
|
||||
public bool? CanGiveDailyLoginBonus { get; set; }
|
||||
public bool CanGiveDailyLoginBonus { get; set; }
|
||||
|
||||
// ── User config (settings echo) ────────────────────────────────────────
|
||||
|
||||
@@ -210,14 +264,45 @@ public class MyPageIndexResponse
|
||||
[Key("story_notification")]
|
||||
public StoryNotification StoryNotification { get; set; } = new();
|
||||
|
||||
// ── Optional UI surface area ───────────────────────────────────────────
|
||||
// ── Per-viewer / event state ───────────────────────────────────────────
|
||||
|
||||
/// <summary>Updated item counts. Refreshes Data.Load.data._userItemDict when present.</summary>
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[JsonPropertyName("user_item_list")]
|
||||
[Key("user_item_list")]
|
||||
public List<UserItem>? UserItemList { get; set; }
|
||||
public List<UserItem> UserItemList { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("gathering_info")]
|
||||
[Key("gathering_info")]
|
||||
public GatheringInfo? GatheringInfo { get; set; }
|
||||
public GatheringInfo GatheringInfo { get; set; } = new();
|
||||
|
||||
/// <summary>Per-viewer offline-event participation. Empty for fresh viewers; prod also sends [].</summary>
|
||||
[JsonPropertyName("user_offline_event")]
|
||||
[Key("user_offline_event")]
|
||||
public List<object> UserOfflineEvent { get; set; } = new();
|
||||
|
||||
// ── Fields prod sends as explicit null ─────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// CRITICAL — emitting this field (even as null) routes MyPageTask.Parse through
|
||||
/// CampaignBattleWin.Clear() which initializes RewardList = new List<...>(). Without it,
|
||||
/// RewardList stays null and MyPageMenu.GetMyPageInfo NREs on its foreach iteration.
|
||||
/// See [[project-wire-null-policy]] for the broader "post-parse-consumer" rationale.
|
||||
/// </summary>
|
||||
[JsonPropertyName("treasure_info")]
|
||||
[Key("treasure_info")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public object? TreasureInfo { get; set; }
|
||||
|
||||
[JsonPropertyName("lottery_period_info")]
|
||||
[Key("lottery_period_info")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public object? LotteryPeriodInfo { get; set; }
|
||||
|
||||
[JsonPropertyName("all_card_enabled_period")]
|
||||
[Key("all_card_enabled_period")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public object? AllCardEnabledPeriod { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using MessagePack;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
|
||||
|
||||
/// <summary>
|
||||
/// /mypage/refresh response — a slim notification-delta payload, NOT a full state refresh.
|
||||
/// Prod sends exactly 3 top-level keys, all of which the client reads unconditionally:
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item><c>friend_battle_invite_count</c> — int, viewer's room-invite count
|
||||
/// (consumed at <c>MyPageRefreshTask.cs:29</c>).</item>
|
||||
/// <item><c>shop_notification</c> — same nested shape as /mypage/index's shop_notification.
|
||||
/// The side-effect call <c>ShopNotification.SetShopNotification</c> unconditionally indexes
|
||||
/// all four sub-keys (card_pack / build_deck / sleeve / leader_skin), already handled by
|
||||
/// our <see cref="ShopNotification"/> DTO's field initializers.</item>
|
||||
/// <item><c>gathering_notification</c> — new shape distinct from /mypage/index's gathering_info.
|
||||
/// Carries only the matching-established message string.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// All three fields are required-present per the new "anything prod emits, we emit" methodology
|
||||
/// — even though the third call site looks tolerant, omitting the key would throw
|
||||
/// KeyNotFoundException at LitJson's indexer.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public class MyPageRefreshResponse
|
||||
{
|
||||
[JsonPropertyName("friend_battle_invite_count")]
|
||||
[Key("friend_battle_invite_count")]
|
||||
public int FriendBattleInviteCount { get; set; }
|
||||
|
||||
[JsonPropertyName("shop_notification")]
|
||||
[Key("shop_notification")]
|
||||
public ShopNotification ShopNotification { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("gathering_notification")]
|
||||
[Key("gathering_notification")]
|
||||
public GatheringNotification GatheringNotification { get; set; } = new();
|
||||
}
|
||||
Reference in New Issue
Block a user