Prebuilt deck purchasing and fixes

This commit is contained in:
gamer147
2026-05-26 09:16:21 -04:00
parent fa0901b776
commit b6966ece6e
39 changed files with 7392 additions and 15 deletions

View File

@@ -14,14 +14,26 @@ namespace SVSim.Database.Services;
public sealed record GrantedReward(int RewardType, long RewardId, int RewardNum);
/// <summary>
/// Single canonical grant primitive. Switch on <see cref="UserGoodsType"/>, mutate the
/// appropriate viewer collection / <see cref="ViewerCurrency"/> field, and return the
/// wire-shape entries the caller should embed in its response's reward_list.
/// Single canonical grant primitive for every <see cref="UserGoodsType"/> the server hands to a
/// viewer. Switch on the type, mutate the appropriate viewer collection / <see cref="ViewerCurrency"/>
/// field, return the wire-shape entries to embed in the response's <c>reward_list</c>.
///
/// <para>
/// <b>DO NOT reimplement reward dispatch in a controller or new helper.</b> This service handles
/// RedEther, Crystal, Item, Card (with <see cref="CardCosmeticReward"/> cascade), Sleeve, Emblem,
/// Degree, Rupy, Skin, MyPageBG — everything except SpotCard (TODO). Endpoint code that takes a
/// list of <c>(type, id, num)</c> tuples should iterate and call <see cref="ApplyAsync"/>
/// per tuple — never switch on type yourself, never filter to "only card-typed rewards", never
/// build a second dispatch table. Past duplicate implementations (ICardAcquisitionService in the
/// EmulatedEntrypoint project, the first pass at /build_deck/buy) all silently dropped subsets of
/// types and produced the same bug: wire reward visible but viewer's collection unchanged. When a
/// new reward type comes up, add a case here. See <c>feedback_reward_grant_service</c> memory.
/// </para>
///
/// Card grants additionally run the <see cref="CardCosmeticReward"/> cascade: any cosmetic
/// associated with the granted card that the viewer doesn't yet own is granted too, and
/// produces an additional entry in the returned list. That's why the return type is a list:
/// most types produce one entry, Card produces 1 + N.
/// associated with the granted card that the viewer doesn't yet own is granted too, and produces
/// an additional entry in the returned list. That's why the return type is a list: most types
/// produce one entry, Card produces 1 + N.
///
/// Caller is responsible for <see cref="SVSimDbContext.SaveChangesAsync(System.Threading.CancellationToken)"/> —
/// this service only mutates the in-memory graph so a controller can stack several grants in