feat(matchmaking): add selectedGame support and implement exitMatchmaker to clear active tickets
Added selectedGame state and UI dropdown
Updated startQueue() to pass { game, mode } metadata
Added exitMatchmaker() to remove existing ticket
Stored active matchmaker ticket in context
Prevents duplicate matchmaker ticket errors
This commit is contained in:
@@ -14,11 +14,13 @@ export default function Player({
|
||||
logout,
|
||||
onMatchData,
|
||||
joinMatchmaker,
|
||||
exitMatchmaker,
|
||||
} = useNakama();
|
||||
|
||||
const [username, setUsername] = useState(
|
||||
localStorage.getItem("username") ?? ""
|
||||
);
|
||||
const [selectedGame, setSelectedGame] = useState("tictactoe");
|
||||
const [selectedMode, setSelectedMode] = useState("classic");
|
||||
const [isQueueing, setIsQueueing] = useState(false);
|
||||
const isRegistered = localStorage.getItem("registered") === "yes";
|
||||
@@ -36,14 +38,19 @@ export default function Player({
|
||||
// ------------------------------------------
|
||||
// MATCHMAKING
|
||||
// ------------------------------------------
|
||||
async function startQueue(selectedMode: string) {
|
||||
async function startQueue(
|
||||
selectedGame: string,
|
||||
selectedMode: string
|
||||
) {
|
||||
setIsQueueing(true);
|
||||
const gameMetadata = {
|
||||
game: selectedGame,
|
||||
mode: selectedMode,
|
||||
}
|
||||
|
||||
try {
|
||||
const ticket = await joinMatchmaker({
|
||||
game: 'tictactoe',
|
||||
mode: selectedMode,
|
||||
});
|
||||
await exitMatchmaker(gameMetadata)
|
||||
const ticket = await joinMatchmaker(gameMetadata);
|
||||
console.log("Queued:", ticket);
|
||||
} catch (err) {
|
||||
console.error("Matchmaking failed:", err);
|
||||
@@ -148,6 +155,24 @@ export default function Player({
|
||||
Select Game Mode
|
||||
</label>
|
||||
|
||||
<select
|
||||
value={selectedGame}
|
||||
disabled={isQueueing}
|
||||
onChange={(e) => setSelectedGame(e.target.value)}
|
||||
style={{
|
||||
padding: "8px",
|
||||
margin: "10px 0 16px",
|
||||
width: "60%",
|
||||
borderRadius: "10px",
|
||||
background: "#222",
|
||||
color: "white",
|
||||
border: "1px solid #333",
|
||||
}}
|
||||
>
|
||||
<option value="tictactoe">Tic Tac Toe</option>
|
||||
<option value="battleship">Battleship</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedMode}
|
||||
disabled={isQueueing}
|
||||
@@ -169,7 +194,10 @@ export default function Player({
|
||||
{!isQueueing && (
|
||||
<motion.button
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => startQueue(selectedMode)}
|
||||
onClick={() => startQueue(
|
||||
selectedGame,
|
||||
selectedMode,
|
||||
)}
|
||||
style={{
|
||||
padding: "10px 20px",
|
||||
borderRadius: "12px",
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface NakamaContextType {
|
||||
loginOrRegister(username: string): Promise<void>;
|
||||
logout(): Promise<void>;
|
||||
joinMatchmaker(gameMetadata: GameMetadata): Promise<string>;
|
||||
exitMatchmaker(gameMetadata: GameMetadata): Promise<void>;
|
||||
joinMatch(matchId: string): Promise<void>;
|
||||
|
||||
sendMatchData(matchId: string, op: number, data: object): void;
|
||||
@@ -70,6 +71,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
||||
const gameMetadataRef = React.useRef<GameMetadata | null>(null);
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [matchmakerTicket, setMatchmakerTicket] = useState<string | null>(null);
|
||||
const [matchId, setMatchId] = useState<string | null>(null);
|
||||
const socketRef = React.useRef<Socket | null>(null);
|
||||
|
||||
@@ -212,10 +214,22 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
|
||||
gameMetadataRef.current = { game, mode };
|
||||
setMatchmakerTicket(ticket.ticket);
|
||||
|
||||
return ticket.ticket;
|
||||
}
|
||||
|
||||
async function exitMatchmaker(gameMetadata: GameMetadata) {
|
||||
const socket = socketRef.current;
|
||||
const game = gameMetadata.game;
|
||||
const mode = gameMetadata.mode;
|
||||
if (!socket) throw new Error("socket missing");
|
||||
|
||||
console.log(`[Nakama] Exiting Matchmaking... game="${game}" mode="${mode}"`);
|
||||
if (matchmakerTicket) await socket.removeMatchmaker(matchmakerTicket);
|
||||
setMatchmakerTicket(null);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// EXPLICIT MATCH JOIN
|
||||
// ----------------------------------------------------
|
||||
@@ -284,6 +298,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
||||
loginOrRegister,
|
||||
logout,
|
||||
joinMatchmaker,
|
||||
exitMatchmaker,
|
||||
joinMatch,
|
||||
sendMatchData,
|
||||
onMatchData,
|
||||
|
||||
Reference in New Issue
Block a user