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,
|
logout,
|
||||||
onMatchData,
|
onMatchData,
|
||||||
joinMatchmaker,
|
joinMatchmaker,
|
||||||
|
exitMatchmaker,
|
||||||
} = useNakama();
|
} = useNakama();
|
||||||
|
|
||||||
const [username, setUsername] = useState(
|
const [username, setUsername] = useState(
|
||||||
localStorage.getItem("username") ?? ""
|
localStorage.getItem("username") ?? ""
|
||||||
);
|
);
|
||||||
|
const [selectedGame, setSelectedGame] = useState("tictactoe");
|
||||||
const [selectedMode, setSelectedMode] = useState("classic");
|
const [selectedMode, setSelectedMode] = useState("classic");
|
||||||
const [isQueueing, setIsQueueing] = useState(false);
|
const [isQueueing, setIsQueueing] = useState(false);
|
||||||
const isRegistered = localStorage.getItem("registered") === "yes";
|
const isRegistered = localStorage.getItem("registered") === "yes";
|
||||||
@@ -36,14 +38,19 @@ export default function Player({
|
|||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
// MATCHMAKING
|
// MATCHMAKING
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
async function startQueue(selectedMode: string) {
|
async function startQueue(
|
||||||
|
selectedGame: string,
|
||||||
|
selectedMode: string
|
||||||
|
) {
|
||||||
setIsQueueing(true);
|
setIsQueueing(true);
|
||||||
|
const gameMetadata = {
|
||||||
|
game: selectedGame,
|
||||||
|
mode: selectedMode,
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ticket = await joinMatchmaker({
|
await exitMatchmaker(gameMetadata)
|
||||||
game: 'tictactoe',
|
const ticket = await joinMatchmaker(gameMetadata);
|
||||||
mode: selectedMode,
|
|
||||||
});
|
|
||||||
console.log("Queued:", ticket);
|
console.log("Queued:", ticket);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Matchmaking failed:", err);
|
console.error("Matchmaking failed:", err);
|
||||||
@@ -148,6 +155,24 @@ export default function Player({
|
|||||||
Select Game Mode
|
Select Game Mode
|
||||||
</label>
|
</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
|
<select
|
||||||
value={selectedMode}
|
value={selectedMode}
|
||||||
disabled={isQueueing}
|
disabled={isQueueing}
|
||||||
@@ -169,7 +194,10 @@ export default function Player({
|
|||||||
{!isQueueing && (
|
{!isQueueing && (
|
||||||
<motion.button
|
<motion.button
|
||||||
whileTap={{ scale: 0.95 }}
|
whileTap={{ scale: 0.95 }}
|
||||||
onClick={() => startQueue(selectedMode)}
|
onClick={() => startQueue(
|
||||||
|
selectedGame,
|
||||||
|
selectedMode,
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
padding: "10px 20px",
|
padding: "10px 20px",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export interface NakamaContextType {
|
|||||||
loginOrRegister(username: string): Promise<void>;
|
loginOrRegister(username: string): Promise<void>;
|
||||||
logout(): Promise<void>;
|
logout(): Promise<void>;
|
||||||
joinMatchmaker(gameMetadata: GameMetadata): Promise<string>;
|
joinMatchmaker(gameMetadata: GameMetadata): Promise<string>;
|
||||||
|
exitMatchmaker(gameMetadata: GameMetadata): Promise<void>;
|
||||||
joinMatch(matchId: string): Promise<void>;
|
joinMatch(matchId: string): Promise<void>;
|
||||||
|
|
||||||
sendMatchData(matchId: string, op: number, data: object): 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 gameMetadataRef = React.useRef<GameMetadata | null>(null);
|
||||||
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 [matchmakerTicket, setMatchmakerTicket] = useState<string | null>(null);
|
||||||
const [matchId, setMatchId] = useState<string | null>(null);
|
const [matchId, setMatchId] = useState<string | null>(null);
|
||||||
const socketRef = React.useRef<Socket | null>(null);
|
const socketRef = React.useRef<Socket | null>(null);
|
||||||
|
|
||||||
@@ -212,10 +214,22 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
gameMetadataRef.current = { game, mode };
|
gameMetadataRef.current = { game, mode };
|
||||||
|
setMatchmakerTicket(ticket.ticket);
|
||||||
|
|
||||||
return 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
|
// EXPLICIT MATCH JOIN
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
@@ -284,6 +298,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) {
|
|||||||
loginOrRegister,
|
loginOrRegister,
|
||||||
logout,
|
logout,
|
||||||
joinMatchmaker,
|
joinMatchmaker,
|
||||||
|
exitMatchmaker,
|
||||||
joinMatch,
|
joinMatch,
|
||||||
sendMatchData,
|
sendMatchData,
|
||||||
onMatchData,
|
onMatchData,
|
||||||
|
|||||||
Reference in New Issue
Block a user