fix(battlenode): emit real spellboost count in played-card knownList

The node hardcoded knownList.spellboost=0 on every played card. Prod sends
the true accumulated count, which the client reads straight into the card's
cost model; with 0 the opponent computes the card at full price and silently
rejects the play in OperateReceiveChecker.IsPlayCard (PP-over -> ConductError
-> NullOperationCollection -> no render/echo), desyncing the board.

Mine spellboost-count changes from the sender''s orderList alter ops
(MineAlterSpellboosts: a/s/h ops), accumulate per-side idx->count in
BattleSessionState (RecordSpellboostFrom), and surface the current count on
the played card via BuildPlayedCard. Recorded from the authoritative
PlayActions only (never the Echo) and folded in AFTER the played card is
built, since a card''s cost is fixed as it leaves hand and a play that grants
spellboost targets the rest of the hand.

Also adds a [sio-in-body] full-body inbound log to RealParticipant to capture
both clients'' re-simulated responses for PvP RNG verification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-05 13:51:40 -04:00
parent 2d32051cc0
commit 13f902ce58
7 changed files with 211 additions and 5 deletions

View File

@@ -39,7 +39,12 @@ internal sealed class PlayActionsHandler : IFrameHandler
ctx.State.RecordCopyTokensFrom(ctx.From, ctx.Other, orderList);
var deckMap = ctx.State.GetOrSeedDeckMap(ctx.From);
var played = KnownListBuilder.BuildPlayedCard(deckMap, playIdx, orderList);
// Spellboost count rides the played card's knownList (prod-faithful; the client reads it into the
// card's cost model). Read the CURRENT map (state before this frame's grant) for the emit, then
// fold THIS frame's alter ops in afterwards — a card's cost is fixed as it leaves hand, and a play
// that grants spellboost (e.g. Fate's Hand) targets the REST of the hand, not the card just played.
var played = KnownListBuilder.BuildPlayedCard(deckMap, playIdx, orderList, ctx.State.GetSpellboostMap(ctx.From));
ctx.State.RecordSpellboostFrom(ctx.From, ctx.Other, orderList);
var oppoTargets = KnownListBuilder.RenameTargets(entries.GetValueOrDefault(WireKeys.TargetList));
// Deck-sourced movements (fetch / search / summon-from-deck) ride the uList — a verbatim,