Commit Graph

5 Commits

Author SHA1 Message Date
gamer147
578d0a75ef refactor(battlenode): rename mode-id field off BattleType, add BattleModes (§D)
Behavior-preserving; 271 BattleNode/Matching/Services tests green, full solution builds.

"BattleType" meant two things: the Sessions.BattleType enum (Pvp/Bot) and an int
"mode id" field. Renamed the int field on MatchContext AND the BattleStartBody wire
DTO to BattleModeId (wire key stays "battleType" via JsonPropertyName), so BattleType
now means only the enum project-wide.

New Bridge/BattleModes.cs (TakeTwo = 11) replaces every 11 literal — both prod
MatchContextBuilder sites and the test fixtures/assertions. The arbitrary-passthrough
42 and bot 0 stay literal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 07:44:02 -04:00
gamer147
56652c7034 fix(battle-node): expand rank-battle deck by DeckCard.Count
BuildForRankBattleAsync projected deck.Cards.Select(c => c.Card.Id),
discarding Count. DeckCard is count-based (one row per unique card +
a Count), so a 3-copy card shipped to the node as a single in-battle
card -- matched decks showed 1 of each card instead of the real count.

Expand each row by its Count so SelfDeckCardIds carries one entry per
physical card. TwoPick path is unaffected (flat per-pick list).

Add a regression test seeding 3+2+1 copies (failed Expected 6/was 3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:09:14 -04:00
gamer147
898b872edd fix(rank-battle): route ai-start through the queue-time MatchContext
Live-smoke bug 2026-06-02: queued Bloodcraft (deck #5), wire showed
classId=2 (Swordcraft) for self_info on the /ai_unlimited_rank_battle/start
response — client rendered the wrong leader.

Two layers of the same bug:

1. MatchContextBuilder.BuildForRankBattleAsync hardcoded deckNo=1 instead
   of taking it from the do_matching request — verified against
   data_dumps/captures/traffic.ndjson L17 where deck_no=5 was on the wire.
   Signature changes to (viewerId, format, deckNo); DoMatchingInternal
   passes req.DeckNo.

2. AiStartInternal rebuilt MatchContext from scratch — but the /ai_*/start
   request body is BaseRequest only, no deck_no on the wire. The fix uses
   the MatchContext the bridge already stored at do_matching resolution time
   (in the Bot PendingBattle), so deck/cosmetic data is consistent end-to-end.
   New IBattleSessionStore.TryFindPendingForViewer(viewerId) finds the
   viewer's pending battle for lookup. The store entry persists across
   ai_start (idempotent reads are fine — the WS handler removes on connect).
   No-pending sentinel: ai_id=-1 surfaces the "no AI assigned" error in the
   client.

Tests: 936 → 939 passing.
- MatchContextBuilderTests.BuildForRankBattle_uses_the_caller_supplied_deck_number
  seeds deck #1 (class 1) and deck #5 (class 6) and asserts the deckNo
  argument picks the right one.
- RankBattleControllerTests.AiStart_self_info_class_matches_queued_deck_number
  is the end-to-end regression: register Bot battle with deck #5, hit
  /ai_unlimited_rank_battle/start, assert self_info.classId == 6.
- RankBattleControllerTests.AiStart_without_pending_battle_returns_neg1_sentinel
  locks the defensive ai_id=-1 path.
- Existing AiStart_* tests bypass do_matching, so adapted to call a new
  RegisterBotBattleAsync helper that mirrors what InProcessPairUp does on
  AI-fallback resolution.

SeedDeckAsync gains an optional classId so test cases can differentiate
decks by class (was always picking Classes.First()).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 12:28:42 -04:00
gamer147
7eaf13893e feat(matching): MatchContextBuilder.BuildForRankBattleAsync for rank battles
Sibling to BuildForTwoPickAsync. Routes through IDeckRepository.GetDeck
to pull the viewer's deck #1 for the requested format (avoiding the
viewer-graph nav-ref auto-load pitfall — DeckCard.Card silently ships
card_id=0 via the default include path). Throws if the viewer has no
deck for the format. Cosmetics fall back to DefaultLoadoutConfig
defaults when unequipped, same shape as TK2.

Used by RankBattleController in a later task to build self-context for
/ai_<fmt>_rank_battle/start and to pair-up under /<fmt>_rank_battle/do_matching.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 01:13:19 -04:00
gamer147
a0fdb0f3c5 feat(match-context): add IMatchContextBuilder TK2 implementation
Assembles MatchContext from ArenaTwoPickRun + viewer cosmetics + config.
Per-mode interface — future modes (rank/free/open-room/...) add one method
each. DI scoped registration. Four tests cover happy path, no-run, incomplete
draft, default-loadout fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 12:40:26 -04:00