226 lines
4.4 KiB
Markdown
226 lines
4.4 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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** → `true` for wss://, `false` for ws://
|
|
|
|
---
|
|
|
|
## 🔌 Runtime Flow
|
|
|
|
### Authentication
|
|
|
|
* UI generates a device UUID
|
|
* Calls `client.authenticateDevice()`
|
|
* Stores session in React state
|
|
|
|
### Matchmaking
|
|
|
|
1. User selects mode (classic / blitz)
|
|
2. joins the matchmaking queue
|
|
3. Waits for matchmaker ticket
|
|
4. 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
|
|
|
|
---
|