🎮 Beta Gamer

Checkers — AFK timeout

The AFK system prevents games from stalling when a player goes idle. Each player has 90 seconds to make a move. The timer resets on every move — it only counts idle time, not total game time.

Timeline

AtEventPayloadWhat to do
0s(move made)AFK timer resets to 0.
60s idlecheckers:afk_warning{ playerId, secondsRemaining: 30 }Show a countdown banner. If playerId === your playerId, you'll forfeit in 30s.
any movecheckers:afk_warning_cleared{}Dismiss the banner immediately.
90s idlegame:over{ winner: opponentId, reason: "afk_timeout" }Idle player is forfeited. Opponent wins.

checkers:afk_warning — field reference

FieldTypeDescription
playerIdstringThe player who is idle. Compare to your own playerId to know if it's you or the opponent.
secondsRemainingnumberSeconds until forfeit. Starts at 30. The event fires once — you must count down locally.

Disabling AFK timeout

// Option A — server-side (session creation)
body: JSON.stringify({ game: 'checkers', afkTimeoutEnabled: false, … })

// Option B — client-side (matchmaking:join)
socket.emit('matchmaking:join', { username: 'Alex', afkTimeoutEnabled: false })

Banner implementation

The server fires checkers:afk_warning once with secondsRemaining: 30. Run a local countdown interval to update the UI each second.

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

function AfkBanner({ myPlayerId }: { myPlayerId: string }) {
  const socket = useSocket();
  const [warning, setWarning] = useState<{ isSelf: boolean; seconds: number } | null>(null);
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

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

    socket.on('checkers:afk_warning', ({ playerId, secondsRemaining }) => {
      setWarning({ isSelf: playerId === myPlayerId, seconds: secondsRemaining });

      intervalRef.current = setInterval(() => {
        setWarning(prev => {
          if (!prev || prev.seconds <= 1) {
            clearInterval(intervalRef.current!);
            return null;
          }
          return { ...prev, seconds: prev.seconds - 1 };
        });
      }, 1000);
    });

    socket.on('checkers:afk_warning_cleared', () => {
      clearInterval(intervalRef.current!);
      setWarning(null);
    });

    return () => {
      socket.off('checkers:afk_warning');
      socket.off('checkers:afk_warning_cleared');
      clearInterval(intervalRef.current!);
    };
  }, [socket, myPlayerId]);

  if (!warning) return null;

  return (
    <div
      role="alert"
      className={`fixed top-4 left-1/2 -translate-x-1/2 z-50 px-6 py-3 rounded-xl font-semibold text-white shadow-lg ${
        warning.isSelf ? 'bg-red-600' : 'bg-yellow-600'
      }`}
    >
      {warning.isSelf
        ? `⚠️ Make a move in ${warning.seconds}s or you'll forfeit!`
        : `⏳ Opponent has ${warning.seconds}s to move…`}
    </div>
  );
}
Using the SDK? The CheckersBoard component from @beta-gamer/react, @beta-gamer/react-native, and @beta-gamer/angular includes a built-in AFK banner by default. Pass showAfkWarning={false} to disable it and use your own implementation below.
⚠️ Always show the AFK banner. Players should never be forfeited without a visible countdown.
Beta Gamer GaaS API — questions? support@beta-gamer.com