A multiplayer browser-based real-time strategy game. 1v1 RTS with lobby system, worker-based building, resource gathering, unit combat, and server-authoritative gameplay.
CrystalFront-RTS/
├── client/ # React + Vite frontend
├── server/ # Node.js + Express + ws backend
├── shared/ # Shared TypeScript types and constants
├── tests/ # Automated tests
├── package.json # Root workspace config
└── tsconfig.json # Root TypeScript config
- Node.js 20+
- npm
npm installnpm run devThis starts both the client (Vite dev server on port 5173) and server (on port 3777) concurrently. The client proxies API requests to the server.
npm run testnpm run buildThis builds the client and server. The server output goes to server/dist/.
npm startThe server serves the built frontend and WebSocket API on port 3777.
The server port is configurable via the PORT environment variable:
PORT=8080 npm startDefaults to 3777 if not set.
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3777 |
The production server is designed to run under a systemd service on a Debian/Ubuntu server.
- Update system packages:
sudo apt update && sudo apt upgrade -y- Install Node.js (if not already installed):
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs- Create a dedicated user (optional but recommended):
sudo useradd -m -s /bin/bash crystalfront
sudo usermod -aG www-data crystalfront- Copy the project to the server (e.g.,
/opt/crystalfront-rts):
# On local machine
scp -r . user@your-server:/opt/crystalfront-rts
# Or clone from git
ssh user@your-server
git clone https://github.com/Tom1tk/CrystalFront.git /opt/crystalfront-rts
cd /opt/crystalfront-rts- Install dependencies and build:
cd /opt/crystalfront-rts
npm install --production
npm run build- Set ownership:
sudo chown -R crystalfront:crystalfront /opt/crystalfront-rtsCreate a systemd service file at /etc/systemd/system/crystalfront-rts.service:
[Unit]
Description=CrystalFront RTS Server
After=network.target
[Service]
Type=simple
User=crystalfront
Group=crystalfront
WorkingDirectory=/opt/crystalfront-rts
Environment=NODE_ENV=production
Environment=PORT=3777
ExecStart=/usr/local/bin/node server/dist/index.js
Restart=on-failure
RestartSec=5
# Restart if process uses more than 512MB memory
MemoryMax=1G
[Install]
WantedBy=multi-user.target# Reload systemd to recognize the new service
sudo systemctl daemon-reload
# Enable the service to start on boot
sudo systemctl enable crystalfront-rts
# Start the service
sudo systemctl start crystalfront-rts
# Check status
sudo systemctl status crystalfront-rts- Check if the service is running:
sudo systemctl is-active crystalfront-rts- Check logs:
sudo journalctl -u crystalfront-rts -f- Test the server:
curl http://localhost:3777# Start
sudo systemctl start crystalfront-rts
# Stop
sudo systemctl stop crystalfront-rts
# Restart
sudo systemctl restart crystalfront-rts
# View logs
sudo journalctl -u crystalfront-rts -f --since "1 hour ago"
# Check resource usage
systemctl status crystalfront-rtsFor production deployment with HTTPS and domain name access:
sudo apt install nginx -yCreate /etc/nginx/sites-available/crystalfront:
server {
listen 80;
server_name your-domain.com www.your-domain.com;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# WebSocket support
location / {
proxy_pass http://localhost:3777;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://localhost:3777;
expires 30d;
add_header Cache-Control "public, immutable";
}
}# Create symlink
sudo ln -s /etc/nginx/sites-available/crystalfront /etc/nginx/sites-enabled/
# Remove default site (optional)
sudo rm /etc/nginx/sites-enabled/default
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
# Auto-renewal is configured automatically by Certbot
# Test renewal
sudo certbot renew --dry-runFROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY client/package.json ./client/
COPY server/package.json ./server/
COPY shared/package.json ./shared/
# Install dependencies
RUN npm install --production
# Copy source
COPY . .
# Build
RUN npm run build
# Expose port
EXPOSE 3777
# Start server
CMD ["node", "server/dist/index.js"]version: '3.8'
services:
crystalfront:
build: .
ports:
- "3777:3777"
environment:
- NODE_ENV=production
- PORT=3777
restart: unless-stopped
# Optional: Add volume for logs
# volumes:
# - ./logs:/app/logs# Build and run
docker-compose up -d
# View logs
docker-compose logs -f
# Update
docker-compose pull
docker-compose up -d --build# Allow SSH (important!)
sudo ufw allow ssh
# Allow HTTP/HTTPS if using Nginx
sudo ufw allow http
sudo ufw allow https
# Or allow direct port access
sudo ufw allow 3777/tcp
# Enable firewall
sudo ufw enable# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Or allow direct port
sudo iptables -A INPUT -p tcp --dport 3777 -j ACCEPT# Navigate to project directory
cd /opt/crystalfront-rts
# Pull latest changes
git pull origin main
# Install dependencies and rebuild
npm install --production
npm run build
# Restart the service
sudo systemctl restart crystalfront-rts
# Check for errors
sudo journalctl -u crystalfront-rts -n 50docker-compose pull
docker-compose up -d --buildCreate a simple health check script:
#!/bin/bash
# /usr/local/bin/crystalfront-health-check.sh
if ! curl -s --max-time 5 http://localhost:3777 > /dev/null; then
echo "CrystalFront server is down!"
sudo systemctl restart crystalfront-rts
fiAdd to crontab for periodic checks:
crontab -e
# Add: */5 * * * * /usr/local/bin/crystalfront-health-check.shThe server logs to the systemd journal, not to individual log files. Log rotation is handled automatically by journald. To control journal log retention and size, configure /etc/systemd/journald.conf:
# Limit journal to 500MB
SystemMaxUse=500M
# Keep logs for 2 weeks
MaxRetentionSec=2weekThen restart journald:
sudo systemctl restart systemd-journaldTo view server logs:
sudo journalctl -u crystalfront-rts -f# Check memory usage
systemctl status crystalfront-rts | grep Memory
# Check CPU usage
top -p $(pgrep -f "server/dist/index.js")
# View real-time logs
sudo journalctl -u crystalfront-rts -f- Port already in use
# Find what's using the port
sudo lsof -i :3777
# Kill the process
sudo kill -9 <PID>- Permission denied
# Fix ownership
sudo chown -R crystalfront:crystalfront /opt/crystalfront-rts- Service won't start
# Check detailed logs
sudo journalctl -u crystalfront-rts -xe
# Test running manually
cd /opt/crystalfront-rts
node server/dist/index.js- WebSocket connection fails
- Ensure Nginx is configured with WebSocket upgrade headers
- Check firewall rules
- Verify CORS settings if accessing from different domain
To run in debug mode with verbose logging:
# Edit systemd service
sudo nano /etc/systemd/system/crystalfront-rts.service
# Add environment variable
Environment=DEBUG=*
# Restart
sudo systemctl restart crystalfront-rts- Main Menu: Username input, host game, join by lobby code
- Lobby System: Create/join lobbies with 6-character codes, ready-up flow
- RTS Gameplay:
- Worker-based building system
- Multi-worker construction (workers can help build together)
- Resource gathering with range limits
- Unit movement and selection
- Combat system with range-based attacks
- Building placement validation
- Match Flow: Both players ready → game shell → gameplay → match end screen
- Rematch: Same lobby persists, score tracks across rematches
- WebSocket Sync: Real-time state synchronization between clients and server
- Server-Authoritative: Server is source of truth for all game state
- Fog of war
- Unit upgrades and technology tree
- More unit types and buildings
- Reconnect on refresh
- Chat system
- Replay system
- External matchmaking
- Workers construct buildings by traveling to the placement location
- Multiple workers can contribute to the same building to speed up construction
- Construction progress only increases when workers are within range (60 units)
- Workers are occupied for the entire construction duration
- Building placement is restricted to the player's half of the map
- Workers gather resources from nodes within range (60 units)
- Gathering is shown with visual indicators on the game map
- Workers can be reassigned from gathering to building or combat
- Units attack enemies within their attack range
- Ranged units can attack from a distance
- Melee units must be close to enemies
- HP bars show current health above units
Private project.
This is a private project. For questions or collaboration, please contact the repository owner.