diff --git a/src/tictactoe/TicTacToe.tsx b/src/tictactoe/TicTacToe.tsx index 91dc3c6..7b2fefb 100644 --- a/src/tictactoe/TicTacToe.tsx +++ b/src/tictactoe/TicTacToe.tsx @@ -6,7 +6,7 @@ import Leaderboard from "./Leaderboard"; // import MatchList from "./MatchList"; export default function TicTacToe() { - const [username, setUsername] = useState(""); + const [username, setUsername] = useState(localStorage.getItem("username") ?? ""); const [selectedMode, setSelectedMode] = useState("classic"); const [board, setBoard] = useState([ ["", "", ""], @@ -35,6 +35,7 @@ export default function TicTacToe() { } const { loginOrRegister, + logout, joinMatchmaker, onMatchData, sendMatchData, @@ -76,6 +77,10 @@ export default function TicTacToe() { onMatchData(onMatchDataCallback); } + useEffect(() => { + connect(); + }, []); + // ------------------------------------------ // SEND A MOVE // ------------------------------------------ @@ -102,6 +107,7 @@ export default function TicTacToe() { 0} onChange={(e) => setUsername(e.target.value)} /> @@ -128,6 +134,7 @@ export default function TicTacToe() { {/* Join matchmaking */} + {/*/!* List open matches *!/*/} {/**/} diff --git a/src/tictactoe/providers/NakamaProvider.tsx b/src/tictactoe/providers/NakamaProvider.tsx index 3cf8cda..62f2b25 100644 --- a/src/tictactoe/providers/NakamaProvider.tsx +++ b/src/tictactoe/providers/NakamaProvider.tsx @@ -32,6 +32,7 @@ export interface NakamaContextType { matchId: string | null; loginOrRegister(username: string): Promise; + logout(): Promise; joinMatchmaker(mode: string): Promise; joinMatch(matchId: string): Promise; @@ -55,17 +56,63 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) { const lastModeRef = React.useRef(null); const socketRef = React.useRef(null); + async function autoLogin() { + const deviceId = getOrCreateDeviceId(); + + try { + return await client.authenticateDevice( + deviceId, + false + ); + } catch (e) { + // fallback: treat as new user + localStorage.removeItem("registered"); + throw e; + } + } + + async function registerWithUsername(username: string) { + const deviceId = getOrCreateDeviceId(); + + // create + set username + const session = await client.authenticateDevice( + deviceId, + true, + username + ); + + // mark an account as registered + localStorage.setItem("registered", "yes"); + localStorage.setItem("username", username); + + return session; + } + + async function getSession(username?: string) { + const isRegistered = localStorage.getItem("registered") === "yes"; + if (!username && !isRegistered) { + throw new Error("No username provided and not registered"); + } + + let newSession; + if (!isRegistered) { + newSession = await registerWithUsername(username ?? ""); + } else { + newSession = await autoLogin(); + } + + return newSession; + } + // ---------------------------------------------------- // LOGIN // ---------------------------------------------------- - async function loginOrRegister(username: string) { - const deviceId = getOrCreateDeviceId(); - + async function loginOrRegister(username?: string) { // authenticate user - const newSession = await client.authenticateDevice(deviceId, true, username); + const newSession = await getSession(username); setSession(newSession); - // create socket (new Nakama 3.x signature) + // create a socket (new Nakama 3.x signature) const s = client.createSocket(undefined, undefined); // no SSL on localhost await s.connect(newSession, true); setSocket(s); @@ -102,6 +149,25 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) { }; } + async function logout() { + try { + // 1) Disconnect socket if present + if (socketRef.current) { + socketRef.current.disconnect(true); + console.log("[Nakama] WebSocket disconnected"); + } + } catch (err) { + console.warn("[Nakama] Error while disconnecting socket:", err); + } + + // 2) Clear state + setSocket(null); + socketRef.current = null; + setSession(null); + + console.log("[Nakama] Clean logout completed"); + } + // ---------------------------------------------------- // MATCHMAKING // ---------------------------------------------------- @@ -187,6 +253,7 @@ export function NakamaProvider({ children }: { children: React.ReactNode }) { socket, matchId, loginOrRegister, + logout, joinMatchmaker, joinMatch, sendMatchData,