Phase 05 — sync infrastructure and documentation with the typed-protobuf refactor: - docker-compose.yml: drop 1024/1025 mappings, single "1999:1999" - server/Dockerfile: EXPOSE 1999, -p 1999 entrypoint - README.md: rewrite transport description, architecture diagram, protocol section, server options, project structure, add proto:gen script note - docs/project-overview.md: update transport + dependencies sections - docs/system-architecture.md: rewrite diagrams + pipeline + file inventory for the WebSocket-only typed-dispatch path - docs/codebase-summary.md: refresh file tree, java package inventory, gradle deps, vite deps, networking and game-flow sections - docs/deployment-guide.md: single-port walkthrough for local / docker / systemd / nginx; remove all 1024/1025 firewall and troubleshooting - docs/code-standards.md: replace dead ServerEventListener_CODE_* class-name example, fix sample ws:// URL to port 1999
8.7 KiB
Deployment Guide
Prerequisites
Required
- Java 25 (LTS) — build + runtime
- Download: https://adoptium.net/temurin/releases/?version=25
- Verify:
java --versionshould show25.x
- Gradle — NOT required. The project commits the Gradle wrapper (
./server/gradlew) which pins Gradle 9.2.1 and auto-downloads if needed. - Node.js 22+ — for the client (development only; not needed if using Docker)
- Verify:
node --version
- Verify:
Optional
- Git — for cloning repository
- Docker + Docker Compose — recommended for quick start and production
- Nginx/Apache — reverse proxy (optional)
Quick Start (Docker Compose)
Fastest path — single command:
docker compose up --build -d
Then:
- Client:
http://localhost:8080/ - Server WebSocket:
ws://localhost:1999/ratel(binary typed protobuf)
Stop:
docker compose down
What's running:
- caro-server — Java 25 server, listens on port
1999(WebSocket only at/ratel) - caro-client — Nginx serving the built Vite bundle on host port
8080
Storage: in-memory only; games are not persisted. Restarting clears all rooms.
Logs:
docker compose logs -f caro-server
docker compose logs -f caro-client
Local Development Setup
1. Clone Repository
git clone https://github.com/tiennm99/caro.git
cd caro
2. Build the Server
./server/gradlew -p server clean build
On Windows: server\gradlew.bat -p server clean build.
Output:
server/build/libs/caro-server-0.0.1.jar— shaded fat jar (~20 MB)
Skip tests:
./server/gradlew -p server build -x test
Run only tests:
./server/gradlew -p server test
Tests: 29 GomokuHelperTest + 8 GomokuAITest (JUnit 5). All must pass.
3. Run the Server
java -jar server/build/libs/caro-server-0.0.1.jar
Output:
[INFO] Server listening on port 1999 (WebSocket)
[INFO] WebSocket endpoint: /ratel
What's running:
- WebSocket port 1999 — Binary typed protobuf at
/ratel
Connect via: ws://localhost:1999/ratel
4. Run the Client (Vite dev server)
In a new terminal:
cd client
npm install
npm run dev
Output:
VITE v6.3.1 ready in 234 ms
➜ Local: http://localhost:5173/
Open http://localhost:5173/ — Phaser 3 client with full game UI.
Production Deployment
Option A: Standalone JAR
java -jar caro-server-0.0.1.jar
Server defaults to port 1999 (WebSocket only).
For production:
- Run in background:
nohup java -jar caro-server-0.0.1.jar & - Or use systemd (see below)
- Or container (Docker)
System requirements:
- 512 MB RAM (minimum)
- 1 GB RAM (recommended)
- 100 MB disk
- Java 25 runtime
Option B: Docker Container
The included server/Dockerfile is multi-stage:
- Build stage:
eclipse-temurin:25-jdk+ committed Gradle wrapper (./server/gradlew) - Runtime stage:
eclipse-temurin:25-jre-alpine
Build from repo root (context = .):
docker build -f server/Dockerfile -t caro-server:0.0.1 .
Run:
docker run -d --name caro-server \
-p 1999:1999/tcp \
caro-server:0.0.1
Option C: Linux Systemd Service
Create /etc/systemd/system/caro-server.service:
[Unit]
Description=Caro Gomoku Server
After=network.target
[Service]
Type=simple
User=gameserver
WorkingDirectory=/opt/caro
ExecStart=/usr/bin/java -jar /opt/caro/caro-server-0.0.1.jar
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable caro-server
sudo systemctl start caro-server
sudo systemctl status caro-server
sudo journalctl -u caro-server -f
Client Deployment
Option A: Docker Compose (Recommended)
The client service in docker-compose.yml builds client/dist/ and serves it via Nginx on host port 8080. Already covered in the Quick Start above.
Option B: Static Hosting (Netlify, Vercel, AWS S3)
Build:
cd client
npm ci
npm run build
Output: client/dist/ (ready to deploy)
Netlify:
netlify deploy --prod --dir client/dist
Vercel:
vercel --prod client
AWS S3:
aws s3 sync client/dist/ s3://my-bucket/caro/ --delete
Option C: Nginx Reverse Proxy
Serve client + proxy WebSocket to server:
server {
listen 80;
server_name caro.example.com;
# Static files (client)
location / {
root /var/www/caro;
try_files $uri /index.html;
}
# WebSocket proxy to server
location /ratel {
proxy_pass http://localhost:1999;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Deploy:
cd client
npm run build
sudo cp -r dist/* /var/www/caro/
sudo systemctl restart nginx
Configuration & Options
Server Options
Server uses defaults. No command-line arguments to configure port. Default port is 1999.
To change port, modify SimpleServer.java and rebuild:
// Default: 1999
private static final int DEFAULT_PORT = 1999;
Client Configuration
Connection endpoint is in client/src/services/connection-service.js. By default the client connects to WS on window.location.hostname:1999/ratel. To override, edit that file and rebuild.
Running Tests
Java Unit Tests (JUnit 5)
./server/gradlew -p server clean test
Expected output:
GomokuHelperTest > testHorizontalWin() PASSED
... (29 tests)
GomokuAITest > testEasyAIReturnsValidMove() PASSED
... (8 tests)
BUILD SUCCESSFUL
Run a single test class:
./server/gradlew -p server test --tests GomokuHelperTest
Test report: server/build/reports/tests/test/index.html
Client Tests
No automated tests currently. Manual testing:
- Start server:
java -jar server/build/libs/caro-server-0.0.1.jar - Start client:
cd client && npm run dev - Open
http://localhost:5173 - Create game, make moves, test AI, spectate
Monitoring & Maintenance
Server Health Check
# Check WebSocket port
netstat -an | grep 1999
# or
lsof -i :1999
# Test WebSocket
websocat ws://localhost:1999/ratel
Logs
- Foreground: stdout
- systemd:
journalctl -u caro-server -f - Docker:
docker compose logs -f caro-server
Performance Monitoring
top -p $(pgrep -f caro-server)
netstat -an | grep -c ESTABLISHED
Cleanup
- Finished rooms are auto-cleaned by
RoomClearTaskafter timeout - No manual cleanup required
Restart:
kill $(pgrep -f caro-server)
java -jar server/build/libs/caro-server-0.0.1.jar
Troubleshooting
Port Already in Use
lsof -i :1999
kill -9 <PID>
To use a different port, modify SimpleServer.java and rebuild.
Connection Refused
- Verify server is running:
ps aux | grep caro-server - Check firewall:
sudo ufw allow 1999/tcp - Test connectivity:
nc -zv localhost 1999 - Docker:
docker compose psanddocker compose port server
WebSocket Connection Fails
- WebSocket listens on port 1999 at path
/ratel - Ensure the client URL is correct:
ws://host:1999/ratel - Test directly:
websocat ws://localhost:1999/ratel - Verify server logs for request decoding errors
High Memory Usage
# Check concurrent clients
netstat -an | grep ESTABLISHED | wc -l
# Increase JVM heap
java -Xmx2g -jar caro-server-0.0.1.jar
# Restart to reclaim memory
Deployment Checklist
Before going live:
- Java 25 installed and verified
./server/gradlew -p server clean buildpasses (37 tests)- Server boots without errors
- WebSocket port 1999 open (firewall)
- Client built:
npm --prefix client ci && npm --prefix client run build - Client connects to correct server endpoint
- Manual test: create game, make moves, join room, spectate
- Monitoring configured (logs, memory, CPU)
- Backup JAR kept on hand
Updating & Patching
Update Server
- Back up current jar
- Pull + rebuild:
git pull && ./server/gradlew -p server clean build - Stop:
kill $(pgrep -f caro-server) - Start new jar:
java -jar server/build/libs/caro-server-0.0.1.jar
Update Client
git pull && npm --prefix client ci && npm --prefix client run build- Deploy
client/dist/to hosting (or rebuild Docker image withdocker compose build client)
Zero-Downtime Update
- Run new server on a different port (e.g.
-p 9090) - Migrate clients gradually (or wait for natural disconnect)
- Stop the old server
Performance Tuning
JVM Tuning
For production servers with 4+ GB RAM:
java -Xmx4g -XX:+UseG1GC -jar caro-server-0.0.1.jar
Java 25 defaults are already sensible; tune only if needed.