import React, { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { useNakama } from "./providers/NakamaProvider"; import Player from "./Player"; import TicTacToeGame from "./games/tictactoe/TicTacToeGame"; import { TicTacToeGameProps } from "./games/tictactoe/props"; import BattleshipGame from "./games/battleship/BattleshipGame" import { BattleshipGameProps } from "./games/battleship/props"; import { GameState } from "./interfaces/states"; import { GameProps } from "./interfaces/props"; const INITIAL_GAME_STATE: GameState = { boards: {}, turn: 0, winner: null, gameOver: false, players: [], metadata: {}, }; export default function App() { // unified game state const [game, setGame] = useState(INITIAL_GAME_STATE); const { onMatchData, matchId, session } = useNakama(); const commonProps: GameProps = { boards: game.boards, turn: game.turn, winner: game.winner, gameOver: game.gameOver, players: game.players, myUserId: session?.user_id ?? null, }; const ticTacToeProps: TicTacToeGameProps = { ...commonProps, }; const battleshipProps: BattleshipGameProps = { ...commonProps, metadata: game.metadata, }; // --------------------------------------------------- // RENDER GAME BOARD // --------------------------------------------------- function renderGameBoard() { if (!matchId || !game.metadata?.game) return null; switch (game.metadata.game) { case "tictactoe": return ( ); case "battleship": return ( ); default: return
Unknown game: {game.metadata.game}
; } } // ------------------------------------------ // MATCH DATA CALLBACK (from Player component) // ------------------------------------------ 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); setGame({ boards: state.boards, turn: state.turn, gameOver: state.game_over, winner: state.winner >= 0 ? state.players[state.winner].username : null, players: state.players ?? [], metadata: state.metadata ?? {}, }); } } // --------------------------------------------------- // EFFECTS // --------------------------------------------------- useEffect(() => { document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = "auto"; }; }, []); useEffect(() => { onMatchData(onMatchDataCallback); }, [onMatchData]); // --------------------------------------------------- // UI LAYOUT // --------------------------------------------------- return (
{/* ---------------- HEADER (always fixed at top) ---------------- */}
Games
{/* ---------------- MAIN CONTENT (scrolls) ---------------- */}
{renderGameBoard()}
); }