fix(mm): pro players also wait the 15s queue; compact post-match roster
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 4m45s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m10s
CI/CD / Deploy - local stack (db + server + web) (push) Successful in 1m33s

- server: remove pro instant-start so all players queue for up to 15s,
  giving real players a chance to seat together before bot-fill
- post-match: render the 4 seats as a horizontal strip so every player
  is visible at once without scrolling

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-16 21:31:18 +03:30
parent 60d44100a2
commit 6aa4f37642
2 changed files with 32 additions and 53 deletions
+3 -7
View File
@@ -54,13 +54,9 @@ public sealed partial class GameManager
return; return;
} }
// Pro players skip the queue entirely. // Everyone — including pro — waits in the queue so we always try to seat
if (p.Plan == "pro") // real players together first. The 15s timer (NextQueueWaitMs) then fills
{ // any empty seats with bots, and a full group of 4 forms instantly.
StartMatch(new List<Player> { p });
return;
}
lock (_mmLock) lock (_mmLock)
{ {
if (_waiting.Any(w => w.player.UserId == p.UserId)) return; if (_waiting.Any(w => w.player.UserId == p.UserId)) return;
+29 -46
View File
@@ -32,64 +32,47 @@ export function MatchPlayersList() {
return ( return (
<div className="mt-3 sm:mt-4 text-start"> <div className="mt-3 sm:mt-4 text-start">
<div className="text-[11px] font-bold text-cream/55 mb-1.5">{t("match.players")}</div> <div className="text-[11px] font-bold text-cream/55 mb-1.5">{t("match.players")}</div>
<div className="space-y-1"> {/* Horizontal strip: all four seats are visible at once — no scrolling. */}
<div className="flex items-start justify-between gap-1.5">
{seatPlayers.map((p, i) => { {seatPlayers.map((p, i) => {
const isMe = p.id ? p.id === myId : !p.isBot; const isMe = p.id ? p.id === myId : !p.isBot;
const canAdd = !!p.id && !p.isBot && p.id !== myId; const canAdd = !!p.id && !p.isBot && p.id !== myId;
const tag = isMe ? t("match.you") : p.isBot ? t("match.bot") : null;
return ( return (
<div key={i} className="flex items-center gap-2.5 glass rounded-xl px-2.5 py-1"> <button
{canAdd ? ( key={i}
<button onClick={() => canAdd && viewProfile(p.id!)}
onClick={() => viewProfile(p.id!)} className={
className="flex items-center gap-2.5 flex-1 min-w-0 text-start active:scale-[0.99] transition" "flex-1 min-w-0 glass rounded-xl px-1 py-2 flex flex-col items-center gap-0.5 text-center " +
> (canAdd ? "active:scale-[0.97] transition" : "cursor-default")
<span className="grid size-8 shrink-0 place-items-center rounded-lg bg-navy-900 text-lg"> }
{p.avatar} >
</span> <span className="grid size-9 shrink-0 place-items-center rounded-lg bg-navy-900 text-lg">
<span className="flex-1 min-w-0"> {p.avatar}
<span className="block text-sm font-semibold text-cream truncate">{p.name}</span> </span>
{p.level > 0 && ( <span className="w-full truncate text-[11px] font-semibold text-cream">{p.name}</span>
<span className="block text-[10px] text-cream/45"> <span className="text-[9px] text-cream/45 leading-tight">
{t("common.level")} {p.level} {tag ?? (p.level > 0 ? `${t("common.level")} ${p.level}` : "")}
</span> </span>
)}
</span>
</button>
) : (
<>
<span className="grid size-8 shrink-0 place-items-center rounded-lg bg-navy-900 text-lg">
{p.avatar}
</span>
<span className="flex-1 min-w-0">
<span className="block text-sm font-semibold text-cream truncate">
{p.name}
{isMe && <span className="text-gold-300 font-normal"> ({t("match.you")})</span>}
{p.isBot && <span className="text-cream/35 font-normal"> ({t("match.bot")})</span>}
</span>
{p.level > 0 && (
<span className="block text-[10px] text-cream/45">
{t("common.level")} {p.level}
</span>
)}
</span>
</>
)}
{canAdd && {canAdd &&
(sent[p.id!] ? ( (sent[p.id!] ? (
<span className="text-[11px] text-teal-300 flex items-center gap-1 shrink-0"> <span className="mt-0.5 text-[10px] text-teal-300 flex items-center gap-0.5">
<Check className="size-3.5" /> <Check className="size-3" />
{t("match.sent")} {t("match.sent")}
</span> </span>
) : ( ) : (
<button <span
onClick={() => add(p.id!)} onClick={(e) => {
className="press-3d btn-gold rounded-lg px-2.5 py-1.5 text-[11px] font-bold flex items-center gap-1 shrink-0" e.stopPropagation();
add(p.id!);
}}
className="press-3d btn-gold mt-0.5 rounded-md px-2 py-0.5 text-[10px] font-bold flex items-center gap-0.5"
> >
<UserPlus className="size-3.5" /> <UserPlus className="size-3" />
{t("match.addFriend")} {t("match.addFriend")}
</button> </span>
))} ))}
</div> </button>
); );
})} })}
</div> </div>