- Added automatic connect() invocation on page load. - Implemented robust login flow: register or auto-login based on local flags. - Added logout support with clean WebSocket disconnect + state reset. - Updated NakamaProvider with getSession(), autoLogin(), registerWithUsername(). - Connected logout button and integrated updated login behavior into UI.
158 lines
4.0 KiB
TypeScript
158 lines
4.0 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { useNakama } from "./providers/NakamaProvider";
|
|
import Board from "./Board";
|
|
import Leaderboard from "./Leaderboard";
|
|
// import { Match } from "@heroiclabs/nakama-js";
|
|
// import MatchList from "./MatchList";
|
|
|
|
export default function TicTacToe() {
|
|
const [username, setUsername] = useState(localStorage.getItem("username") ?? "");
|
|
const [selectedMode, setSelectedMode] = useState("classic");
|
|
const [board, setBoard] = useState<string[][]>([
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""]
|
|
]);
|
|
const [turn, setTurn] = useState<number>(0);
|
|
const [winner, setWinner] = useState<string | null>(null);
|
|
// const [openMatches, setOpenMatches] = useState<Match[]>([]);
|
|
const [players, setPlayers] = useState<string[]>([]);
|
|
|
|
function onMatchDataCallback(msg: { opCode: number; data: any }) {
|
|
console.log("[Match Data]", msg);
|
|
|
|
if (msg.opCode === 2) {
|
|
const state = msg.data;
|
|
console.log("Match state:", state);
|
|
|
|
setBoard(state.board);
|
|
setTurn(state.turn);
|
|
setWinner(state.winner || null);
|
|
|
|
// new:
|
|
setPlayers(state.players || []);
|
|
}
|
|
}
|
|
const {
|
|
loginOrRegister,
|
|
logout,
|
|
joinMatchmaker,
|
|
onMatchData,
|
|
sendMatchData,
|
|
listOpenMatches,
|
|
matchId,
|
|
session,
|
|
} = useNakama();
|
|
|
|
useEffect(() => {
|
|
onMatchData(onMatchDataCallback);
|
|
}, [onMatchData]);
|
|
|
|
// useEffect(() => {
|
|
// let active = true;
|
|
//
|
|
// async function refreshLoop() {
|
|
// while (active) {
|
|
// const matches = await listOpenMatches();
|
|
// setOpenMatches(matches);
|
|
//
|
|
// await new Promise(res => setTimeout(res, 500)); // 0.5s refresh
|
|
// }
|
|
// }
|
|
//
|
|
// refreshLoop();
|
|
//
|
|
// return () => {
|
|
// active = false;
|
|
// };
|
|
// }, [listOpenMatches]);
|
|
|
|
// ------------------------------------------
|
|
// CONNECT
|
|
// ------------------------------------------
|
|
async function connect() {
|
|
await loginOrRegister(username);
|
|
|
|
// Match data listener
|
|
onMatchData(onMatchDataCallback);
|
|
}
|
|
|
|
useEffect(() => {
|
|
connect();
|
|
}, []);
|
|
|
|
// ------------------------------------------
|
|
// SEND A MOVE
|
|
// ------------------------------------------
|
|
function handleCellClick(row: number, col: number) {
|
|
if (!matchId) return;
|
|
|
|
sendMatchData(matchId, 1, { row, col }); // OpMove=1
|
|
}
|
|
|
|
// ------------------------------------------
|
|
// MATCHMAKING
|
|
// ------------------------------------------
|
|
async function startQueue(selectedMode: string) {
|
|
const ticket = await joinMatchmaker(selectedMode);
|
|
console.log("Queued:", ticket);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<h1>Tic Tac Toe Multiplayer</h1>
|
|
|
|
{!session && (
|
|
<>
|
|
<input
|
|
placeholder="username"
|
|
value={username}
|
|
disabled={username.length > 0}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
/>
|
|
<button onClick={connect}>Connect</button>
|
|
</>
|
|
)}
|
|
|
|
{session && !matchId && (
|
|
<>
|
|
<h2>Hello, {session.username}</h2>
|
|
|
|
{/* Game mode selection */}
|
|
<label style={{ display: "block", marginTop: "10px" }}>
|
|
Select Game Mode:
|
|
</label>
|
|
|
|
<select
|
|
value={selectedMode}
|
|
onChange={(e) => setSelectedMode(e.target.value)}
|
|
style={{ padding: "6px", marginBottom: "10px" }}
|
|
>
|
|
<option value="classic">Classic</option>
|
|
<option value="blitz">Blitz</option>
|
|
</select>
|
|
|
|
{/* Join matchmaking */}
|
|
<button onClick={() => startQueue(selectedMode)}>Join Matchmaking</button>
|
|
<button onClick={() => logout()}>Logout</button>
|
|
|
|
{/*/!* List open matches *!/*/}
|
|
{/*<MatchList matches={openMatches} />*/}
|
|
<Leaderboard/>
|
|
</>
|
|
)}
|
|
|
|
{matchId && (
|
|
<Board
|
|
board={board}
|
|
turn={turn}
|
|
winner={winner}
|
|
players={players}
|
|
myUserId={session?.user_id ?? null}
|
|
onCellClick={handleCellClick}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|