feat(online): live queue count — friends see each other waiting
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 1m24s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m10s
CI/CD / Deploy - local stack (db + server + web) (push) Successful in 1m52s

The server only sent the queue size to the player who just joined, and the
client dropped the count entirely (emitMM ignored s.players). So two friends
queuing together never saw each other, even though the server does seat 2+
waiting humans together within ~25s.

- Server: BroadcastQueueLocked() pushes the current queue size to EVERY waiting
  player on join/cancel (not just the joiner).
- Client: thread the count through emitMM → MatchmakingState.waiting.
- MatchmakingScreen shows "N players in queue" (mm.inQueue) when ≥2 humans wait,
  so friends can tell they're queued together before bots fill the empty seats.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-19 19:26:13 +03:30
parent fe3bedc631
commit 940e2af6d2
5 changed files with 36 additions and 6 deletions
+18 -3
View File
@@ -67,9 +67,10 @@ public sealed partial class GameManager
if (_waiting.Any(w => w.player.UserId == p.UserId)) return;
var timer = new Timer(_ => FlushTicket(p.UserId), null, QueueWaitMs, Timeout.Infinite);
_waiting.Add((p, timer, DateTime.UtcNow));
_ = _hub.Clients.User(p.UserId).SendAsync("matchmaking",
new MatchmakingStateDto("searching", _waiting.Count, null));
if (_waiting.Count >= 4) FormGroupLocked(4);
// Tell EVERYONE still waiting the new count (so friends queuing together
// see each other join), not just the player who just joined.
BroadcastQueueLocked();
}
}
@@ -78,10 +79,24 @@ public sealed partial class GameManager
lock (_mmLock)
{
var idx = _waiting.FindIndex(w => w.player.UserId == userId);
if (idx >= 0) { _waiting[idx].timer.Dispose(); _waiting.RemoveAt(idx); }
if (idx >= 0)
{
_waiting[idx].timer.Dispose();
_waiting.RemoveAt(idx);
BroadcastQueueLocked();
}
}
}
/// <summary>Push the current queue size to every waiting player (call inside _mmLock).</summary>
private void BroadcastQueueLocked()
{
int n = _waiting.Count;
foreach (var w in _waiting)
_ = _hub.Clients.User(w.player.UserId).SendAsync("matchmaking",
new MatchmakingStateDto("searching", n, null));
}
/// <summary>Client safety net: re-send the current game state to a player who
/// may have missed the initial broadcast (green-felt freeze guard).</summary>
public void Resync(string userId)