feat(matchmaking): add automatic requeue on match rejection

- Detect Nakama error code 3 ("No match ID or token found") when a
  match is rejected due to incompatible match properties (e.g., mode mismatch).
- Preserve last selected game mode using a ref so client can requeue
  automatically without user interaction.
- Implement fallback logic to call joinMatchmaker() again after server
  rejection.
- Improve robustness of matchmaking flow by ensuring players remain
  in queue until a valid match is formed.
This commit is contained in:
2025-11-28 16:40:28 +05:30
parent 5b30ac8d83
commit 8284815337

View File

@@ -47,6 +47,8 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
const [session, setSession] = useState<Session | null>(null); const [session, setSession] = useState<Session | null>(null);
const [socket, setSocket] = useState<Socket | null>(null); const [socket, setSocket] = useState<Socket | null>(null);
const [matchId, setMatchId] = useState<string | null>(null); const [matchId, setMatchId] = useState<string | null>(null);
const lastModeRef = React.useRef<string | null>(null);
const socketRef = React.useRef<Socket | null>(null);
// ---------------------------------------------------- // ----------------------------------------------------
// LOGIN // LOGIN
@@ -62,17 +64,32 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
const s = client.createSocket(undefined, undefined); // no SSL on localhost const s = client.createSocket(undefined, undefined); // no SSL on localhost
await s.connect(newSession, true); await s.connect(newSession, true);
setSocket(s); setSocket(s);
socketRef.current = s;
console.log("[Nakama] WebSocket connected"); console.log("[Nakama] WebSocket connected");
// MATCHMAKER MATCHED CALLBACK // MATCHMAKER MATCHED CALLBACK
s.onmatchmakermatched = async (matched: MatchmakerMatched) => { s.onmatchmakermatched = async (matched: MatchmakerMatched) => {
// 1) If match_id is empty → server rejected the group.
if (!matched.match_id) {
console.warn("[Nakama] Match rejected by server. Auto-requeueing...");
if (lastModeRef.current) {
try {
await joinMatchmaker(lastModeRef.current);
} catch (e) {
console.error("[Nakama] Requeue failed:", e);
}
}
return;
}
// 2) Valid match: continue as usual.
console.log("[Nakama] MATCHED:", matched); console.log("[Nakama] MATCHED:", matched);
setMatchId(matched.match_id);
try { try {
await s.joinMatch(matched.match_id); await s.joinMatch(matched.match_id);
setMatchId(matched.match_id);
console.log("[Nakama] Auto-joined match:", matched.match_id); console.log("[Nakama] Auto-joined match:", matched.match_id);
} catch (err) { } catch (err) {
console.error("[Nakama] Failed to join match:", err); console.error("[Nakama] Failed to join match:", err);
@@ -84,6 +101,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
// MATCHMAKING // MATCHMAKING
// ---------------------------------------------------- // ----------------------------------------------------
async function joinMatchmaker(mode: string) { async function joinMatchmaker(mode: string) {
const socket = socketRef.current;
if (!socket) throw new Error("socket missing"); if (!socket) throw new Error("socket missing");
console.log(`[Nakama] Matchmaking... with +mode:"${mode}"`); console.log(`[Nakama] Matchmaking... with +mode:"${mode}"`);
@@ -94,6 +112,8 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
{ mode } // stringProperties { mode } // stringProperties
); );
lastModeRef.current = mode;
return ticket.ticket; return ticket.ticket;
} }