🎮 Beta Gamer

Tic-tac-toe — React SDK @beta-gamer/react

Wrap your game UI in BetaGamerProvider with the session token. The provider connects to the /tictactoe namespace automatically.

Install

npm install @beta-gamer/react

Minimal setup

import { BetaGamerProvider } from '@beta-gamer/react';
import { TictactoeGame } from './TictactoeGame';

export default function Page({ sessionToken }: { sessionToken: string }) {
  return (
    <BetaGamerProvider
      token={sessionToken}
      serverUrl="https://api.beta-gamer.com"
    >
      <TictactoeGame />
    </BetaGamerProvider>
  );
}

Hooks

HookReturnsDescription
useSocket()Socket | nullRaw Socket.IO socket for the /tictactoe namespace.
useSession()SessionStateDecoded token payload — players[], game, sessionId.
useGameState()GameStatestatus, winner, reason — updated on game:over.

Full example

import { useEffect, useRef, useState } from 'react';
import { BetaGamerProvider, useSocket, useSession } from '@beta-gamer/react';

function TictactoeGame() {
  const socket  = useSocket();
  const session = useSession();
  const myPlayerId = session.players[0]?.id ?? '';

  const roomIdRef = useRef('');
  const [board, setBoard]         = useState<('X'|'O'|null)[]>(Array(9).fill(null));
  const [currentTurn, setCurrentTurn] = useState(0);
  const [players, setPlayers]     = useState<any[]>([]);
  const [myMark, setMyMark]       = useState<'X'|'O'>('X');
  const [winningLine, setWinningLine] = useState<number[]|null>(null);
  const [gameOver, setGameOver]   = useState(false);

  useEffect(() => {
    if (!socket) return;

    const join = () => socket.emit('matchmaking:join', {
      username: session.players[0]?.displayName,
      playerId: myPlayerId,
    });
    if (socket.connected) join(); else socket.once('connect', join);

    socket.on('game:started', ({ roomId, playerId, mark, board: b, players: p, currentTurn: t }) => {
      roomIdRef.current = roomId;
      setMyMark(mark);
      setBoard(b);
      setPlayers(p);
      setCurrentTurn(t);
    });

    socket.on('game:move:made', ({ board: b, currentTurn: t, winningLine: wl }) => {
      setBoard(b);
      setCurrentTurn(t);
      if (wl) setWinningLine(wl);
    });

    socket.on('game:over', ({ winner, reason, winningLine: wl }) => {
      if (wl) setWinningLine(wl);
      setGameOver(true);
    });

    return () => {
      socket.off('connect', join);
      socket.off('game:started');
      socket.off('game:move:made');
      socket.off('game:over');
    };
  }, [socket]);

  const isMyTurn = players[currentTurn]?.id === myPlayerId;

  const move = (position: number) => {
    if (!isMyTurn || board[position] || gameOver) return;
    socket?.emit('game:move', { roomId: roomIdRef.current, playerId: myPlayerId, position });
  };

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 80px)', gap: 8 }}>
      {board.map((cell, i) => (
        <button key={i} onClick={() => move(i)}
          style={{ width: 80, height: 80, fontSize: 32,
            background: winningLine?.includes(i) ? '#fef08a' : '#1e293b' }}>
          {cell}
        </button>
      ))}
    </div>
  );
}

export default function Page({ sessionToken }: { sessionToken: string }) {
  return (
    <BetaGamerProvider token={sessionToken} serverUrl="https://api.beta-gamer.com">
      <TictactoeGame />
    </BetaGamerProvider>
  );
}
Beta Gamer GaaS API — questions? support@beta-gamer.com