RegisterPending → TryRegisterPending (TryAdd instead of indexer) so battle-id collisions return false instead of silently evicting a live battle. MatchingBridge retries with fresh IDs on collision (max 5). Before registering, EvictStaleForViewer removes any stale pending battle the viewer left behind, enforcing the one-pending-per-viewer invariant that was previously comment-asserted. Store tests switched to per-test local stores to fix a race under the assembly-wide ParallelScope.All. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
26 lines
1.2 KiB
C#
26 lines
1.2 KiB
C#
namespace SVSim.BattleNode.Sessions;
|
|
|
|
public interface IBattleSessionStore
|
|
{
|
|
/// <summary>Register a battle minted by the matching bridge, awaiting a WS connect.
|
|
/// Returns false if a battle with the same id is already pending (caller should retry
|
|
/// with a fresh id).</summary>
|
|
bool TryRegisterPending(PendingBattle battle);
|
|
|
|
/// <summary>Look up the pending battle. Returns null if not present.</summary>
|
|
PendingBattle? TryGetPending(string battleId);
|
|
|
|
/// <summary>
|
|
/// Find a pending battle this viewer is a participant in (P1 or P2). Used by the
|
|
/// HTTP-side <c>/ai_<fmt>/start</c> endpoint to retrieve the deck/cosmetic
|
|
/// context the viewer registered at <c>do_matching</c> time — the <c>/start</c>
|
|
/// request body carries no <c>deck_no</c> of its own. Returns null if the viewer
|
|
/// has no pending battle (already consumed by WS connect, never registered, or
|
|
/// evicted by timeout).
|
|
/// </summary>
|
|
PendingBattle? TryFindPendingForViewer(long viewerId);
|
|
|
|
/// <summary>Mark a battle as no longer pending (e.g. on successful connect or explicit close).</summary>
|
|
bool RemovePending(string battleId);
|
|
}
|