From d7e5557d61b245c4f4180103f513a24e871ea6e1 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Fri, 29 May 2026 18:33:11 -0400 Subject: [PATCH] feat(import): import consumable item inventory --- .../Controllers/AdminController.cs | 19 +++++++++++++ .../Requests/Admin/ImportViewerRequest.cs | 8 ++++++ .../Controllers/AdminControllerTests.cs | 28 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/SVSim.EmulatedEntrypoint/Controllers/AdminController.cs b/SVSim.EmulatedEntrypoint/Controllers/AdminController.cs index ae17d63..dc0e32a 100644 --- a/SVSim.EmulatedEntrypoint/Controllers/AdminController.cs +++ b/SVSim.EmulatedEntrypoint/Controllers/AdminController.cs @@ -163,6 +163,25 @@ public class AdminController : SVSimController } } + if (request.Items is not null) + { + var wanted = request.Items + .GroupBy(i => i.ItemId) + .Select(g => g.First()) + .ToList(); + var ids = wanted.Select(i => i.ItemId).ToList(); + var itemMaster = await _dbContext.Items + .Where(i => ids.Contains(i.Id)) + .ToDictionaryAsync(i => i.Id); + + viewer.Items.Clear(); + foreach (var i in wanted) + { + if (!itemMaster.TryGetValue(i.ItemId, out var item)) continue; // unknown master id + viewer.Items.Add(new OwnedItemEntry { Item = item, Count = i.Count, Viewer = viewer }); + } + } + // Clone the 8 starter decks into the viewer when freshly created — workaround for a // client-side NRE in the deck-edit menu (DeckListUI.IsVisibleCreateNewButton at // decompile Wizard/DeckListUI.cs:316 unconditionally reads `_deckGroup.DeckFormat`, but diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/Admin/ImportViewerRequest.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/Admin/ImportViewerRequest.cs index e909a42..f096bdd 100644 --- a/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/Admin/ImportViewerRequest.cs +++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/Admin/ImportViewerRequest.cs @@ -28,6 +28,8 @@ public class ImportViewerRequest [JsonPropertyName("classes")] public List? Classes { get; set; } [JsonPropertyName("owned_cards")] public List? OwnedCards { get; set; } + + [JsonPropertyName("items")] public List? Items { get; set; } } public class ImportCurrency @@ -50,3 +52,9 @@ public class ImportCard [JsonPropertyName("count")] public int Count { get; set; } [JsonPropertyName("is_protected")] public bool IsProtected { get; set; } } + +public class ImportItem +{ + [JsonPropertyName("item_id")] public int ItemId { get; set; } + [JsonPropertyName("count")] public int Count { get; set; } +} diff --git a/SVSim.UnitTests/Controllers/AdminControllerTests.cs b/SVSim.UnitTests/Controllers/AdminControllerTests.cs index 93bd57e..6ed3453 100644 --- a/SVSim.UnitTests/Controllers/AdminControllerTests.cs +++ b/SVSim.UnitTests/Controllers/AdminControllerTests.cs @@ -195,4 +195,32 @@ public class AdminControllerTests Assert.That(stored.Cards.Select(c => c.Card.Id), Is.EquivalentTo(new[] { 10001002L }), "Full replace: the pre-seeded 10001001 must be gone, only 10001002 present."); } + + [Test] + public async Task ImportViewer_imports_items_and_replaces_existing() + { + using var factory = new SVSimTestFactory(); + const ulong steamId = 76_561_198_111_222_336UL; + long viewerId = await factory.SeedViewerAsync(steamId: steamId); + // Registers the ItemEntry master row (70001) and gives an initial owned count to be replaced. + await factory.SeedOwnedItemAsync(viewerId, itemId: 70001, count: 1); + + using var client = factory.CreateClient(); + var response = await client.PostAsJsonAsync("/admin/import_viewer", new ImportViewerRequest + { + SteamId = steamId, + Items = new List + { + new() { ItemId = 70001, Count = 5 }, + new() { ItemId = 88888, Count = 9 }, // unknown master id -> skipped silently + } + }); + + Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), + await response.Content.ReadAsStringAsync()); + Assert.That(await factory.GetOwnedItemCountAsync(viewerId, 70001), Is.EqualTo(5), + "Full replace: 70001 count updated to 5."); + Assert.That(await factory.GetOwnedItemCountAsync(viewerId, 88888), Is.EqualTo(0), + "Unknown item master id must not be inserted."); + } }