- Added metadata state to App and wired incoming match metadata. - Added Fleet + FLEET_ORDER in BattleShipBoard to drive ship placement order. - Added nextShip + nextShipSize calculation for guided placement. - Updated handlePlace and handleShoot to send structured payloads (action + data). - Added lobby/placement/battle status messages. - Updated grids to use shipBoard + shipName/shipSize props instead of generic grid. - Fixed metadata access (state.Metadata vs state.metadata). - Consolidated PlacementGrid usage and disabled it during battle phase. - Added logging for debugging incoming battleship boards.
tic-tac-toe-ui — Multiplayer Game Client (React + TypeScript + Vite)
A fully functional multiplayer Tic-Tac-Toe game client built using React + TypeScript, powered by Nakama WebSocket real-time networking, and delivered as a tiny production-optimized Vite build (served via BusyBox/Docker).
This UI communicates with the authoritative backend (tic-tac-toe) to deliver a secure, synced, cheat-proof multiplayer experience.
🎮 Overview
This repository contains the front-end implementation of the Tic-Tac-Toe multiplayer platform.
The client supports:
- Device-based authentication
- Full matchmaking lifecycle
- Real-time gameplay with WebSockets
- Authoritative state rendering
- Leaderboard browsing
- Game result screens
- A Vite-powered environment system for dynamic host/SSL selection
This UI is production-ready and deployable to any server or container environment.
⭐ Features
- React + TypeScript UI
- WebSocket real-time gameplay using Nakama JS
- Matchmaking flow: queue → ticket → match → gameplay
- Authoritative state updates (OpCode 2)
- Secure device authentication (device UUID → session)
- Leaderboard view over Nakama's leaderboard API
- Production Docker image: Node → Vite → BusyBox
- Environment-based configuration for host/SSL
🧩 Architecture
Frontend System Diagram
flowchart LR
User[Browser] --> UI[React + TS + Vite]
UI -->|WebSocket| Nakama
UI -->|HTTP| Nakama
UI --> Leaderboard[Leaderboard API]
UI --> Matchmaking[Matchmaker API]
🛠 Tech Stack
- React 18 (TypeScript)
- Vite.js (build system)
- Nakama JavaScript Client
- Plain CSS for styling
- WebSockets (SSL / non-SSL selectable)
- Docker (multi-stage build)
🔧 Environment Variables (Vite)
These are injected at build time:
VITE_WS_HOST=nakama.aetoskia.com
VITE_WS_PORT=443
VITE_WS_SKEY=secret
VITE_WS_SSL=true
Meaning:
- VITE_WS_HOST → Nakama host (domain or IP)
- VITE_WS_PORT → Port for WebSocket/API
- VITE_WS_SKEY → Nakama server key
- VITE_WS_SSL →
truefor wss://,falsefor ws://
🔌 Runtime Flow
Authentication
- UI generates a device UUID
- Calls
client.authenticateDevice() - Stores session in React state
Matchmaking
- User selects mode (classic / blitz)
- joins the matchmaking queue
- Waits for matchmaker ticket
- Auto-joins the match when assigned
Gameplay
- User sends moves via OpCode 1
- Server validates + broadcasts authoritative board via OpCode 2
- UI re-renders board state from server packets
End of Game
- Player sees win/lose/draw
- Can return to home or matchmaking
🎨 Styling
Styling uses plain CSS via a single styles.css file.
Simple, responsive layout using Flexbox.
🐳 Docker (Production Build)
Dockerfile Overview
# Stage 1: Build
FROM node:20-alpine AS builder
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json (or yarn.lock)
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy the rest of the app
COPY . .
# Build arguments
ARG VITE_WS_HOST
ARG VITE_WS_PORT
ARG VITE_WS_SKEY
ARG VITE_WS_SSL
# Export them as actual environment variables (Vite needs ENV)
ENV VITE_WS_HOST=${VITE_WS_HOST}
ENV VITE_WS_PORT=${VITE_WS_PORT}
ENV VITE_WS_SSL=${VITE_WS_SSL}
# Build
RUN npm run build
# Stage 2: Static file server (BusyBox)
FROM busybox:latest
WORKDIR /app
# Copy only build frontend files
COPY --from=builder /app/dist /app
# Expose port
EXPOSE 3000
# Default command
CMD ["busybox", "httpd", "-f", "-p", "3000"]
Produces an extremely lightweight production image.
🧪 Testing
Manual testing validated:
- Full matchmaking loop
- Game state sync
- Invalid move handling (server rejections)
- Disconnect behaviour
- Leaderboard retrieval
Pending:
- Stress tests
- Mobile responsiveness
- Reconnect logic
📈 Deployment
Supported:
- Docker on any Linux host
- Raspberry Pi (ARM)
- Google Cloud Run / Compute Engine
- Traefik reverse proxy via
games.aetoskia.com
Example Deployment via Docker
docker run -d \
-p 3003:3003 \
--restart always \
tic-tac-toe-ui:latest
Traefik HTTPS routes:
- games.aetoskia.com → UI
🗺️ Roadmap
- Rematch flow
- Reconnect/resume after refresh
- Improved animations
- Mobile UI redesign
- Centralized error handling