Getting ready to seed more data

This commit is contained in:
gamer147
2026-05-23 15:47:23 -04:00
parent 631e42289a
commit 5f44ee0c7e
74 changed files with 499 additions and 50 deletions

View File

@@ -1,15 +1,17 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
/// <summary>
/// Shape consumed by `DeckGroupListData(jsonData, format)` for a single-format call
/// the format-scoped decks land under `user_deck_list` (vs. the per-format keys used
/// Shape consumed by `DeckGroupListData(jsonData, format)` for a single-format call 窶・/// the format-scoped decks land under `user_deck_list` (vs. the per-format keys used
/// by /practice/deck_list with Format.All).
/// </summary>
[MessagePackObject]
public class DeckListResponse
{
[JsonPropertyName("maintenance_card_list")]
[Key("maintenance_card_list")] public List<long> MaintenanceCardList { get; set; } = new();
[JsonPropertyName("user_deck_list")]
[Key("user_deck_list")] public List<UserDeck>? UserDeckList { get; set; }
}

View File

@@ -1,5 +1,6 @@
using MessagePack;
using SVSim.EmulatedEntrypoint.Models.Dtos.Common;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
@@ -11,7 +12,10 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
[MessagePackObject]
public class DeckUpdateResponse
{
[JsonPropertyName("user_deck_list")]
[Key("user_deck_list")] public List<UserDeck>? UserDeckList { get; set; }
[JsonPropertyName("achieved_info")]
[Key("achieved_info")] public Dictionary<string, object> AchievedInfo { get; set; } = new();
[JsonPropertyName("reward_list")]
[Key("reward_list")] public List<Reward> RewardList { get; set; } = new();
}

View File

@@ -1,4 +1,5 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
@@ -6,5 +7,6 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
public class EmptyDeckNumberResponse
{
/// <summary>The next free deck slot number. 0 indicates "no slots available".</summary>
[JsonPropertyName("empty_deck_num")]
[Key("empty_deck_num")] public int EmptyDeckNum { get; set; }
}

View File

@@ -1,15 +1,17 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Deck;
/// <summary>
/// Single-deck-update response. Consumed by DeckListUtility.DeckUpdate(user_deck,
/// format, DeckAttributeType.CustomDeck). Shape is "one UserDeck wrapped under
/// `user_deck` key" same for update_name, update_sleeve, update_leader_skin,
/// `user_deck` key" 窶・same for update_name, update_sleeve, update_leader_skin,
/// update_random_leader_skin.
/// </summary>
[MessagePackObject]
public class SingleDeckResponse
{
[JsonPropertyName("user_deck")]
[Key("user_deck")] public UserDeck? UserDeck { get; set; }
}

View File

@@ -1,11 +1,12 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
/// <summary>
/// Wire-shape mirrors production's <c>/check/game_start</c> response. Several fields here are
/// NOT read by <c>Cute/GameStartCheckTask.Parse</c> (<c>now_viewer_id</c>, <c>now_name</c>,
/// <c>now_rank</c> those are consumed by sibling tasks); they're included because prod sends
/// <c>now_rank</c> 窶・those are consumed by sibling tasks); they're included because prod sends
/// them and the boot worked when we matched prod exactly. Removing them is a regression risk
/// even though the parse-time decompile says they're unused.
/// </summary>
@@ -13,6 +14,7 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
public class GameStartResponse
{
/// <summary>The signed-in viewer's internal id. Prod always sends.</summary>
[JsonPropertyName("now_viewer_id")]
[Key("now_viewer_id")]
public long NowViewerId { get; set; }
@@ -20,10 +22,12 @@ public class GameStartResponse
/// Whether the user has set a data-transfer password. Prod sends a non-null bool;
/// <c>GameStartCheckTask.Parse</c> gates the read with <c>Keys.Contains</c>.
/// </summary>
[JsonPropertyName("is_set_transition_password")]
[Key("is_set_transition_password")]
public bool IsSetTransitionPassword { get; set; }
/// <summary>Viewer display name. Not read by GameStartCheckTask but sent by prod.</summary>
[JsonPropertyName("now_name")]
[Key("now_name")]
public string NowName { get; set; } = string.Empty;
@@ -32,60 +36,71 @@ public class GameStartResponse
/// Stub for now until rank state is persisted; pinned to RankName_010 / RankName_017
/// (matches prod's shape).
/// </summary>
[JsonPropertyName("now_rank")]
[Key("now_rank")]
public Dictionary<string, string> NowRank { get; set; } = new();
/// <summary>
/// Tutorial progress **sent as a string on the wire** ("100" = tutorial complete).
/// Tutorial progress 窶・**sent as a string on the wire** ("100" = tutorial complete).
/// <c>GameStartCheckTask.Parse</c> calls <c>.ToInt()</c> so LitJson coerces.
/// </summary>
[JsonPropertyName("now_tutorial_step")]
[Key("now_tutorial_step")]
public string NowTutorialStep { get; set; } = "100";
/// <summary>
/// Linked social accounts. Per-entry shape in <see cref="TransitionAccountData"/>.
/// </summary>
[JsonPropertyName("transition_account_data")]
[Key("transition_account_data")]
public List<TransitionAccountData> TransitionAccountData { get; set; } = new();
/// <summary>
/// When present, client overwrites <c>Certification.ViewerId</c> with this value. Optional
/// leave null to omit. The serialization pipeline (JSON + msgpack via the translation
/// 窶・leave null to omit. The serialization pipeline (JSON + msgpack via the translation
/// middleware) drops null properties end-to-end, so the client sees the key as absent.
/// </summary>
[JsonPropertyName("rewrite_viewer_id")]
[Key("rewrite_viewer_id")]
public long? RewriteViewerId { get; set; }
/// <summary>
/// Presence indicates the user has applied for account deletion (value ignored by client at
/// this stage). Optional leave null to omit.
/// this stage). Optional 窶・leave null to omit.
/// </summary>
[JsonPropertyName("account_delete_reservation_status")]
[Key("account_delete_reservation_status")]
public int? AccountDeleteReservationStatus { get; set; }
// --- Agreement / consent state (all required) ---
/// <summary><c>PlayerStaticData.AgreementState</c> enum.</summary>
[JsonPropertyName("tos_state")]
[Key("tos_state")]
public int TosState { get; set; }
/// <summary><c>PlayerStaticData.AgreementState</c> enum.</summary>
[JsonPropertyName("policy_state")]
[Key("policy_state")]
public int PolicyState { get; set; }
/// <summary><c>PlayerStaticData.AgreementState</c> enum.</summary>
[JsonPropertyName("kor_authority_state")]
[Key("kor_authority_state")]
public int KorAuthorityState { get; set; }
/// <summary>Current Terms of Service document id.</summary>
[JsonPropertyName("tos_id")]
[Key("tos_id")]
public int TosId { get; set; }
/// <summary>Current Privacy Policy document id.</summary>
[JsonPropertyName("policy_id")]
[Key("policy_id")]
public int PolicyId { get; set; }
/// <summary>Current Korean authority consent document id.</summary>
[JsonPropertyName("kor_authority_id")]
[Key("kor_authority_id")]
public int KorAuthorityId { get; set; }
}

View File

@@ -1,5 +1,6 @@
using MessagePack;
using SVSim.Database.Enums;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
@@ -8,20 +9,28 @@ public class IndexResponse
{
#region Primitive Returns
[JsonPropertyName("spot_point")]
[Key("spot_point")]
public int SpotPoint { get; set; }
[JsonPropertyName("is_available_colosseum_free_entry")]
[Key("is_available_colosseum_free_entry")]
public bool IsAvailableColosseumFreeEntry { get; set; }
[JsonPropertyName("friend_battle_invite_count")]
[Key("friend_battle_invite_count")]
public int FriendBattleInviteCount { get; set; }
[JsonPropertyName("battle_recovery_status")]
[Key("battle_recovery_status")]
public int BattleRecoveryStatus { get; set; }
[JsonPropertyName("room_recovery_status")]
[Key("room_recovery_status")]
public int RoomRecoveryStatus { get; set; }
[JsonPropertyName("is_battle_pass_period")]
[Key("is_battle_pass_period")]
public int IsBattlePassPeriod { get; set; }
[JsonPropertyName("card_set_id_for_resource_dl_view")]
[Key("card_set_id_for_resource_dl_view")]
public int CardSetIdForResourceDlView { get; set; }
[JsonPropertyName("deck_format")]
[Key("deck_format")]
public int DeckFormat { get; set; } = 1;
@@ -29,12 +38,15 @@ public class IndexResponse
#region Basic User Data
[JsonPropertyName("user_tutorial")]
[Key("user_tutorial")]
public UserTutorial UserTutorial { get; set; } = new UserTutorial();
[JsonPropertyName("user_info")]
[Key("user_info")]
public UserInfo UserInfo { get; set; } = new UserInfo();
[JsonPropertyName("user_crystal_count")]
[Key("user_crystal_count")]
public UserCurrency UserCurrency { get; set; } = new UserCurrency();
@@ -42,45 +54,56 @@ public class IndexResponse
#region Inventory Data
[JsonPropertyName("user_item_list")]
[Key("user_item_list")]
public List<UserItem> UserItems { get; set; } = new();
[JsonPropertyName("user_deck_rotation")]
[Key("user_deck_rotation")]
public UserFormatDeckInfo UserRotationDecks { get; set; } = new();
[JsonPropertyName("user_deck_unlimited")]
[Key("user_deck_unlimited")]
public UserFormatDeckInfo UserUnlimitedDecks { get; set; } = new();
[JsonPropertyName("user_deck_my_rotation")]
[Key("user_deck_my_rotation")]
public UserFormatDeckInfo UserMyRotationDecks { get; set; } = new();
[JsonPropertyName("user_card_list")]
[Key("user_card_list")]
public List<UserCard> UserCards { get; set; } = new();
[JsonPropertyName("user_class_list")]
[Key("user_class_list")]
public List<UserClass> UserClasses { get; set; } = new();
/// <summary>
/// Wire is an array; parser iterates by index (LoadDetail.cs:358-360).
/// </summary>
[JsonPropertyName("user_sleeve_list")]
[Key("user_sleeve_list")]
public List<SleeveIdentifier> Sleeves { get; set; } = new();
[JsonPropertyName("user_emblem_list")]
[Key("user_emblem_list")]
public List<EmblemIdentifier> UserEmblems { get; set; } = new();
[JsonPropertyName("user_degree_list")]
[Key("user_degree_list")]
public List<DegreeIdentifier> UserDegrees { get; set; } = new();
/// <summary>
/// Wire is an array; parser iterates by index (LoadDetail.cs:348-356).
/// </summary>
[JsonPropertyName("user_leader_skin_list")]
[Key("user_leader_skin_list")]
public List<UserLeaderSkin> LeaderSkins { get; set; } = new();
/// <summary>
/// Wire is string[]; parser calls .ToString() on each element (LoadDetail.cs:387-392).
/// </summary>
[JsonPropertyName("user_mypage_list")]
[Key("user_mypage_list")]
public List<string> MyPageBackgrounds { get; set; } = new();
@@ -92,15 +115,19 @@ public class IndexResponse
/// Wire is an array of 5 entries; parser uses deck_format as discriminator
/// (LoadDetail.cs:527-538).
/// </summary>
[JsonPropertyName("user_rank")]
[Key("user_rank")]
public List<UserRankInfo> UserRankInfo { get; set; } = new();
[JsonPropertyName("user_rank_match_list")]
[Key("user_rank_match_list")]
public List<UserRankedMatches> UserRankedMatches { get; set; } = new();
[JsonPropertyName("daily_login_bonus")]
[Key("daily_login_bonus")]
public DailyLoginBonus DailyLoginBonus { get; set; } = new();
[JsonPropertyName("challenge_config")]
[Key("challenge_config")]
public ArenaConfig ArenaConfig { get; set; } = new();
@@ -108,6 +135,7 @@ public class IndexResponse
#region Global Data
[JsonPropertyName("red_ether_overwrite_list")]
[Key("red_ether_overwrite_list")]
public List<RedEtherOverride> RedEtherOverrides { get; set; } = new();
@@ -115,71 +143,94 @@ public class IndexResponse
/// Wire is a flat number[]; parser passes it straight to SetMaintenanceCardIds
/// (LoadDetail.cs:165).
/// </summary>
[JsonPropertyName("maintenance_card_list")]
[Key("maintenance_card_list")]
public List<long> MaintenanceCards { get; set; } = new();
/// <summary>
/// Client's ArenaData(JsonData) ctor reads data[0] unconditionally inside the
/// LoadDetail.cs:261 Keys.Contains("arena_info") branch — an empty list crashes
/// /load/index with ArgumentOutOfRangeException. Send null (omitted on wire) when
/// there's no arena to advertise.
/// </summary>
[JsonPropertyName("arena_info")]
[Key("arena_info")]
public List<ArenaInfo> ArenaInfos { get; set; } = new();
public List<ArenaInfo>? ArenaInfos { get; set; }
/// <summary>
/// Wire is an array; client uses POSITIONAL logic (index >= 24 = master ranks,
/// LoadDetail.cs:417-422). Order must match repository's ordering.
/// </summary>
[JsonPropertyName("rank_info")]
[Key("rank_info")]
public List<RankInfo> RankInfo { get; set; } = new();
/// <summary>
/// Wire is an array; parser iterates by index (LoadDetail.cs:425-434).
/// </summary>
[JsonPropertyName("class_exp")]
[Key("class_exp")]
public List<ClassExp> ClassExp { get; set; } = new();
[JsonPropertyName("loading_exclusion_card_list")]
[Key("loading_exclusion_card_list")]
public List<long> LoadingTipCardExclusions { get; set; } = new();
[JsonPropertyName("default_setting")]
[Key("default_setting")]
public DefaultSettings DefaultSettings { get; set; } = new();
[JsonPropertyName("unlimited_restricted_base_card_id_list")]
[Key("unlimited_restricted_base_card_id_list")]
public Dictionary<string, int> UnlimitedBanList { get; set; } = new();
/// <summary>
/// Client unconditionally accesses [1] and [Count-1] (LoadDetail.cs:184) list MUST
/// Client unconditionally accesses [1] and [Count-1] (LoadDetail.cs:184) 窶・list MUST
/// have at least 2 entries or the client crashes.
/// </summary>
[JsonPropertyName("rotation_card_set_id_list")]
[Key("rotation_card_set_id_list")]
public List<CardSetIdentifier> RotationSets { get; set; } = new();
/// <summary>
/// Wire is a flat number[]; parser iterates and reads .ToInt() (LoadDetail.cs:463-468).
/// </summary>
[JsonPropertyName("reprinted_base_card_ids")]
[Key("reprinted_base_card_ids")]
public List<long> ReprintedCards { get; set; } = new();
[JsonPropertyName("spot_cards")]
[Key("spot_cards")]
public Dictionary<string, int> SpotCards { get; set; } = new();
[JsonPropertyName("pre_release_info")]
[Key("pre_release_info")]
public PreReleaseInfo? PreReleaseInfo { get; set; }
[JsonPropertyName("my_rotation_info")]
[Key("my_rotation_info")]
public MyRotationInfo? MyRotationInfo { get; set; }
[JsonPropertyName("avatar_info")]
[Key("avatar_info")]
public MyRotationInfo? AvatarRotationInfo { get; set; }
[JsonPropertyName("feature_maintenance_list")]
[Key("feature_maintenance_list")]
public List<FeatureMaintenance> FeatureMaintenances { get; set; } = new();
[JsonPropertyName("special_crystal_info")]
[Key("special_crystal_info")]
public List<SpecialCrystalInfo> SpecialCrystalInfos { get; set; } = new();
[JsonPropertyName("battle_pass_level_info")]
[Key("battle_pass_level_info")]
public Dictionary<string, BattlePassLevel>? BattlePassLevelInfo { get; set; }
/// <summary>
/// Wire is string[]; parser calls .ToString() on each element (LoadDetail.cs:493-499).
/// </summary>
[JsonPropertyName("open_battle_field_id_list")]
[Key("open_battle_field_id_list")]
public List<string> OpenBattlefieldIds { get; set; } = new();
@@ -187,9 +238,11 @@ public class IndexResponse
#region Misc Data
[JsonPropertyName("loot_box_regulation")]
[Key("loot_box_regulation")]
public LootBoxRegulations LootBoxRegulations { get; set; } = new();
[JsonPropertyName("gathering_info")]
[Key("gathering_info")]
public GatheringInfo GatheringInfo { get; set; } = new();
@@ -197,6 +250,7 @@ public class IndexResponse
/// Spec is unclear whether this is returned at /load/index or only at /config/* endpoints
/// (load-index.md line 390). Pending live-capture confirmation; harmless extra.
/// </summary>
[JsonPropertyName("user_config")]
[Key("user_config")]
public UserConfig UserConfig { get; set; } = new();

View File

@@ -1,21 +1,25 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
/// <summary>
/// Same shape consumed by DeckGroupListData(jsonData, Format.All). Per-format keys are
/// conditional omit (don't send empty arrays) for formats the server doesn't enable.
/// conditional 窶・omit (don't send empty arrays) for formats the server doesn't enable.
/// </summary>
[MessagePackObject]
public class PracticeDeckListResponse
{
/// <summary>Card ids currently disabled for maintenance (client unions with global list).</summary>
[JsonPropertyName("maintenance_card_list")]
[Key("maintenance_card_list")] public List<long> MaintenanceCardList { get; set; } = new();
[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; }
// The remaining format keys (pre_rotation, crossover, my_rotation, avatar, default_deck_list,
// trial_deck_list, crossover_trial_deck_list, build_deck_list, user_leader_skin_setting_list)
// are all conditional added when those formats are enabled.
// are all conditional 窶・added when those formats are enabled.
}

View File

@@ -1,5 +1,6 @@
using MessagePack;
using SVSim.EmulatedEntrypoint.Models.Dtos.Common;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
@@ -7,20 +8,25 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
public class PracticeFinishResponse
{
/// <summary>Class XP gained this match.</summary>
[JsonPropertyName("get_class_experience")]
[Key("get_class_experience")] public int GetClassExperience { get; set; }
/// <summary>Total accumulated class XP for the played class after this match.</summary>
[JsonPropertyName("class_experience")]
[Key("class_experience")] public int ClassExperience { get; set; }
/// <summary>Class level after this match (post-promotion if XP rolled over).</summary>
[JsonPropertyName("class_level")]
[Key("class_level")] public int ClassLevel { get; set; } = 1;
/// <summary>
/// Missions / achievements / rewards rollup. Empty dict means "nothing accumulated"
/// (spec: parser tolerates empty object).
/// </summary>
[JsonPropertyName("achieved_info")]
[Key("achieved_info")] public Dictionary<string, object> AchievedInfo { get; set; } = new();
/// <summary>Standard reward grants applied to user's inventory. Empty by default.</summary>
[JsonPropertyName("reward_list")]
[Key("reward_list")] public List<Reward> RewardList { get; set; } = new();
}

View File

@@ -1,4 +1,5 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
@@ -6,38 +7,49 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
public class PracticeOpponent
{
/// <summary>Practice slot id (unique per entry; AI opponent identifier).</summary>
[JsonPropertyName("practice_id")]
[Key("practice_id")] public int PracticeId { get; set; }
/// <summary>
/// Text-table id resolved client-side via Data.Master.GetPracticeText(text_id).
/// Stringified int client calls .ToString() before lookup. Sent as string to be safe.
/// Stringified int 窶・client calls .ToString() before lookup. Sent as string to be safe.
/// </summary>
[JsonPropertyName("text_id")]
[Key("text_id")] public string TextId { get; set; } = string.Empty;
/// <summary>Class (leader) id the AI plays.</summary>
[JsonPropertyName("class_id")]
[Key("class_id")] public int ClassId { get; set; }
/// <summary>Portrait / character id (which leader art the AI uses).</summary>
[JsonPropertyName("chara_id")]
[Key("chara_id")] public int CharaId { get; set; }
/// <summary>Title-degree id shown next to the AI's name.</summary>
[JsonPropertyName("degree_id")]
[Key("degree_id")] public int DegreeId { get; set; }
/// <summary>AI deck-strength tier (drives which preset deck the AI uses).</summary>
[JsonPropertyName("ai_deck_level")]
[Key("ai_deck_level")] public int AiDeckLevel { get; set; }
/// <summary>AI decision-making tier.</summary>
[JsonPropertyName("ai_logic_level")]
[Key("ai_logic_level")] public int AiLogicLevel { get; set; }
/// <summary>Starting HP for the AI side (often 20).</summary>
[JsonPropertyName("ai_max_life")]
[Key("ai_max_life")] public int AiMaxLife { get; set; } = 20;
/// <summary>3D battle-field asset id (string on the wire; client int.TryParse's it).</summary>
[JsonPropertyName("battle3dfield_id")]
[Key("battle3dfield_id")] public string Battle3dFieldId { get; set; } = "1";
/// <summary>Optional. true => entry disabled, client prepends maintenance suffix.</summary>
[JsonPropertyName("is_maintenance")]
[Key("is_maintenance")] public bool? IsMaintenance { get; set; }
/// <summary>true => entry is a special "campaign" practice (event-tied).</summary>
[JsonPropertyName("is_campaign_practice")]
[Key("is_campaign_practice")] public bool IsCampaignPractice { get; set; }
}

View File

@@ -1,4 +1,5 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
@@ -8,7 +9,8 @@ public class PracticeStartResponse
/// <summary>
/// Optional mission/achievement evaluation snapshot. Spec: safe to omit entirely;
/// client tolerates absence (defensive `Keys.Contains` check). Always null in our
/// minimal impl we don't model missions.
/// minimal impl 窶・we don't model missions.
/// </summary>
[JsonPropertyName("mission_parameter")]
[Key("mission_parameter")] public object? MissionParameter { get; set; }
}

View File

@@ -1,4 +1,5 @@
using MessagePack;
using System.Text.Json.Serialization;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
@@ -9,6 +10,7 @@ public class SpecialTitleCheckResponse
/// Numeric string. "0"/"1" are the built-in default title screens; any other value
/// is treated as an asset-bundle id. When omitted, the client defaults to "0".
/// </summary>
[JsonPropertyName("title_image_id")]
[Key("title_image_id")]
public string? TitleImageId { get; set; }
}