using Microsoft.AspNetCore.Mvc; using SVSim.Database.Enums; using SVSim.Database.Models; using SVSim.Database.Repositories.Card; using SVSim.Database.Repositories.Collectibles; using SVSim.Database.Repositories.Globals; using SVSim.Database.Repositories.Viewer; using SVSim.EmulatedEntrypoint.Constants; using SVSim.EmulatedEntrypoint.Models.Dtos; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests; using SVSim.EmulatedEntrypoint.Models.Dtos.Responses; namespace SVSim.EmulatedEntrypoint.Controllers; public class LoadController : SVSimController { // Per-format rank entries the wire expects (5 entries, in deck_format discriminator order). // Hard-coded until viewer rank-state is persisted (see audit §6 #1). private static readonly Format[] RankFormats = { Format.Rotation, Format.Unlimited, Format.MyRotation, Format.Avatar, Format.Crossover }; // Until ShadowverseCardSetEntry is seeded by CardImport, hard-code a stub so the client // doesn't crash on RotationCardSetList[1] / [Count-1] (LoadDetail.cs:184). private static readonly List StubRotationSets = new() { new CardSetIdentifier { SetId = 10000 }, new CardSetIdentifier { SetId = 10005 }, new CardSetIdentifier { SetId = 10010 } }; private readonly IViewerRepository _viewerRepository; private readonly ICardRepository _cardRepository; private readonly ICollectionRepository _collectionRepository; private readonly IGlobalsRepository _globalsRepository; public LoadController(IViewerRepository viewerRepository, ICardRepository cardRepository, ICollectionRepository collectionRepository, IGlobalsRepository globalsRepository) { _viewerRepository = viewerRepository; _cardRepository = cardRepository; _collectionRepository = collectionRepository; _globalsRepository = globalsRepository; } [HttpPost("index")] public async Task> Index(IndexRequest request) { var shortUdidClaim = User.Claims.FirstOrDefault(c => c.Type == ShadowverseClaimTypes.ShortUdidClaim)?.Value; if (shortUdidClaim is null || !long.TryParse(shortUdidClaim, out long shortUdid)) { return Unauthorized(); } Viewer? viewer = await _viewerRepository.GetViewerByShortUdid(shortUdid); if (viewer is null) { return NotFound(); } // Cards. Empty until CardImport lands (audit §3 — user_card_list is blocked). List allCollectibleCards = await _cardRepository.GetAll(true); List allBasicCards = await _cardRepository.GetAllBasic(); List ownedCards = viewer.Cards; List allCardsAsOwned = allCollectibleCards.GroupJoin(ownedCards, card => card.Id, ownedCard => ownedCard.Card.Id, (card, foundOwnedCards) => foundOwnedCards.DefaultIfEmpty().FirstOrDefault() ?? new OwnedCardEntry { Card = card, Count = 0, IsProtected = false }).ToList(); allCardsAsOwned = allCardsAsOwned.Union(allBasicCards.Select(bc => new OwnedCardEntry { Card = bc, Count = 3, IsProtected = true })).ToList(); List allLeaderSkins = await _collectionRepository.GetLeaderSkins(); var classExpCurve = await _globalsRepository.GetClassExpCurve(); List classExps = new(); int accumulateExp = 0; int? prevNecessaryExp = null; foreach (var entry in classExpCurve) { accumulateExp += entry.NecessaryExp; classExps.Add(new ClassExp { Level = entry.Id, NecessaryExp = entry.NecessaryExp, DiffExp = prevNecessaryExp.HasValue ? entry.NecessaryExp - prevNecessaryExp.Value : entry.NecessaryExp, AccumulateExp = accumulateExp }); prevNecessaryExp = entry.NecessaryExp; } List rotationSets = (await _cardRepository.GetCardSets(true)) .Select(set => new CardSetIdentifier { SetId = set.Id }) .ToList(); if (rotationSets.Count < 2) { rotationSets = StubRotationSets; } var deviceHeader = Request.Headers["DEVICE"].FirstOrDefault(); int deviceType = int.TryParse(deviceHeader, out int parsed) ? parsed : 0; return new IndexResponse { UserTutorial = new UserTutorial { TutorialStep = viewer.MissionData.TutorialState }, UserInfo = new UserInfo(deviceType, viewer), UserCurrency = new UserCurrency(viewer), UserItems = viewer.Items.Select(item => new UserItem(item)).ToList(), UserRotationDecks = new UserFormatDeckInfo { UserDecks = viewer.Decks.Where(d => d.Format == Format.Rotation) .Select(d => new UserDeck(d)).ToList() }, UserUnlimitedDecks = new UserFormatDeckInfo { UserDecks = viewer.Decks.Where(d => d.Format == Format.Unlimited) .Select(d => new UserDeck(d)).ToList() }, UserMyRotationDecks = new UserFormatDeckInfo { UserDecks = viewer.Decks.Where(d => d.Format == Format.MyRotation) .Select(d => new UserDeck(d)).ToList() }, UserCards = allCardsAsOwned.Select(card => new UserCard(card)).ToList(), UserClasses = viewer.Classes.Select(vc => new UserClass(vc)).ToList(), Sleeves = viewer.Sleeves.Select(s => new SleeveIdentifier { SleeveId = s.Id }).ToList(), UserEmblems = viewer.Emblems.Select(e => new EmblemIdentifier { EmblemId = e.Id }).ToList(), UserDegrees = viewer.Degrees.Select(d => new DegreeIdentifier { DegreeId = d.Id }).ToList(), LeaderSkins = allLeaderSkins .Select(skin => new UserLeaderSkin(skin, viewer.LeaderSkins.Any(vs => vs.Id == skin.Id))) .ToList(), MyPageBackgrounds = viewer.MyPageBackgrounds.Select(mpbg => mpbg.Id.ToString()).ToList(), LootBoxRegulations = new LootBoxRegulations(), GatheringInfo = new GatheringInfo(), IsBattlePassPeriod = 0, BattlePassLevelInfo = null, SpecialCrystalInfos = new List(), AvatarRotationInfo = null, MyRotationInfo = null, FeatureMaintenances = new List(), PreReleaseInfo = null, SpotCards = new Dictionary(), ReprintedCards = new List(), UnlimitedBanList = new Dictionary(), LoadingTipCardExclusions = new List(), MaintenanceCards = new List(), RedEtherOverrides = new List(), DailyLoginBonus = new DailyLoginBonus(), UserRankedMatches = new List(), UserRankInfo = RankFormats.Select(f => new UserRankInfo { DeckFormat = (int)f, Rank = 1, BattlePoints = 0, WinStreak = 0, IsPromotion = 0, IsMasterRank = 0, IsGrandMasterRank = 0, MasterPoints = 0 }).ToList(), ArenaConfig = new ArenaConfig(), ArenaInfos = new List(), RotationSets = rotationSets, UserConfig = new UserConfig(), OpenBattlefieldIds = (await _globalsRepository.GetBattlefields(true)) .Select(bf => bf.Id.ToString()).ToList(), DefaultSettings = new DefaultSettings(await _globalsRepository.GetGameConfiguration("default")), ClassExp = classExps, RankInfo = (await _globalsRepository.GetRankInfo()).Select(ri => new RankInfo(ri)).ToList(), DeckFormat = 1, CardSetIdForResourceDlView = rotationSets.Last().SetId }; } }