audit finding #3 (deck), after verifying the RNG stream is not involved:
addSpellsInRandomOrder shuffles the INITIAL order with a separate
Mersenne Twister, but the server is authoritative (ships the deck in the
snapshot; the client decodes it, never reshuffles), and the in-match
cycle is fully deterministic. so no RNG matching is needed.
play: the command's slot is the DECK index (LogicDoSpellCommand::execute
maps it to a hand slot via getSpellIndex, rejecting if it is not in
hand). play_from_hand finds the hand slot holding that index, moves the
card to the used pile and empties the slot - useSpellFromHand. spawn is
untouched; it already reads the card by the deck index.
draw: cycle_summoner_decks decrements spell_cooldown each tick and, when
it hits 0 with an empty hand slot, slides the front of the draw pile in
(refilling draw from used IN ORDER when it empties) and resets the timer
to NEXT_SPELL_COOLDOWN_MILLISECONDS (2000 / _BOOST / _OVERTIME).
field_240 (reshuffle) just counts down and stays 0 in normal play.
harness: playing hand slot 0 empties it and banks the card in used;
ticking past the cooldown refills the slot with a different card.