Server Setup
Configure and run the Web Engine multiplayer server with Colyseus for real-time networked gameplay.
The Web Engine multiplayer system uses Colyseus as its networking backend, providing robust room management, state synchronization, and scalability features.
Server Architecture#
Dedicated Server
Authoritative game server runs game logic and validates all client inputs to prevent cheating.
WebSocket Transport
Real-time bidirectional communication using WebSockets with automatic reconnection support.
Installation#
The multiplayer server is included in the monorepo under apps/server. Start it with:
# Start the multiplayer serverpnpm server # Server will start on ws://localhost:2567Development Mode
In development, the server automatically reloads when you make changes to server-side code. The server runs independently from the editor/client.
Server Configuration#
Configure the server via environment variables or the configuration file:
# Server configurationPORT=2567NODE_ENV=development # Colyseus configurationCOLYSEUS_MONITOR_PASSWORD=admin123COLYSEUS_PRESENCE_TYPE=local # Room configurationMAX_ROOM_CAPACITY=10CONNECTION_TIMEOUT=30000HEARTBEAT_INTERVAL=5000Creating Rooms#
Rooms are the core concept in Colyseus. Each room manages a game session with its own state.
import { Room, Client } from 'colyseus';import { GameRoomState } from './schema/GameRoomState'; export class GameRoom extends Room<GameRoomState> { maxClients = 10; onCreate(options: any) { this.setState(new GameRoomState()); // Set up room logic this.setSimulationInterval((deltaTime) => { this.state.update(deltaTime); }); // Set up message handlers this.onMessage('input', (client, message) => { this.handlePlayerInput(client, message); }); console.log('GameRoom created!', options); } onJoin(client: Client, options: any) { console.log(client.sessionId, 'joined!'); // Create player entity this.state.createPlayer(client.sessionId, options); } onLeave(client: Client, consented: boolean) { console.log(client.sessionId, 'left!'); // Remove player entity this.state.removePlayer(client.sessionId); } onDispose() { console.log('GameRoom disposed!'); } private handlePlayerInput(client: Client, message: any) { // Validate and process player input const player = this.state.players.get(client.sessionId); if (player) { player.processInput(message); } }}Defining State Schema#
Use Colyseus schemas to define synchronized state. Schemas enable automatic state tracking and delta compression.
import { Schema, MapSchema, type } from '@colyseus/schema'; export class Player extends Schema { @type('number') x = 0; @type('number') y = 0; @type('number') z = 0; @type('number') rotX = 0; @type('number') rotY = 0; @type('number') rotZ = 0; @type('number') rotW = 1; @type('string') name = ''; @type('string') avatarId = '';} export class GameRoomState extends Schema { @type({ map: Player }) players = new MapSchema<Player>(); @type('number') gameTime = 0; createPlayer(sessionId: string, options: any) { const player = new Player(); player.name = options.name || 'Player'; player.avatarId = options.avatarId || 'default'; this.players.set(sessionId, player); } removePlayer(sessionId: string) { this.players.delete(sessionId); } update(deltaTime: number) { this.gameTime += deltaTime; // Update game logic this.players.forEach((player) => { // Physics, movement, etc. }); }}Schema Benefits
Colyseus automatically tracks changes to schema properties and sends only deltas to clients, significantly reducing bandwidth usage. The schema is also type-safe on both client and server.
Connection Handling#
The server includes robust connection management with heartbeat monitoring, timeout handling, and automatic reconnection support:
import { getConnectionManager } from './ConnectionManager'; // Initialize connection managerconst connectionManager = getConnectionManager({ heartbeatInterval: 5000, // 5 seconds connectionTimeout: 30000, // 30 seconds maxReconnectionAttempts: 5, autoReconnect: true,}); // Register connectionconnectionManager.registerConnection(clientId); // Track connection qualityconnectionManager.updateQuality(clientId, rtt, packetLoss, jitter); // Handle heartbeatconnectionManager.updateHeartbeat(clientId);Connection Quality Monitoring#
The connection manager automatically monitors connection quality and categorizes it:
- Excellent — RTT < 50ms, Packet loss < 1%, Jitter < 10ms
- Good — RTT < 100ms, Packet loss < 5%, Jitter < 20ms
- Fair — RTT < 200ms, Packet loss < 10%, Jitter < 50ms
- Poor — RTT < 500ms, Packet loss < 20%, Jitter < 100ms
- Critical — Anything worse than poor
Environment Setup#
Configure different environments for development, staging, and production:
export const environment = { development: { port: 2567, wsUrl: 'ws://localhost:2567', corsOrigins: ['http://localhost:3000', 'http://localhost:5173'], logLevel: 'debug', }, production: { port: process.env.PORT || 8080, wsUrl: process.env.WS_URL || 'wss://game.example.com', corsOrigins: ['https://game.example.com'], logLevel: 'info', },}; export const config = environment[process.env.NODE_ENV || 'development'];Connecting from Client#
Connect to the server from your game client:
import { NetworkManager } from '@web-engine/core/network'; const network = NetworkManager.getInstance(); // Connect to serverawait network.connectToColyseus( 'ws://localhost:2567', 'game_room', { name: 'Player1', avatarId: 'avatar-001' }); // Create private roomawait network.createPrivateRoom('game_room', { name: 'Player1', avatarId: 'avatar-001'}); // Join by invite codeawait network.joinPrivateRoom('ABC123', { name: 'Player2', avatarId: 'avatar-002'});Server Monitoring#
Colyseus includes a built-in monitoring dashboard accessible at:
http://localhost:2567/colyseus Login credentials (configure in .env):Username: adminPassword: admin123Production Security
Always change the default monitoring password in production environments. Consider disabling the monitor entirely or restricting access via firewall rules.
Best Practices#
- Validate inputs — Never trust client data. Validate all inputs on the server.
- Rate limiting — Prevent spam and DoS attacks with rate limiting on message handlers.
- Graceful degradation — Handle disconnections gracefully with reconnection logic.
- Monitor performance — Track room count, player count, and server metrics.
- Secure production — Use WSS (secure WebSocket), change default passwords, enable CORS properly.