A cloud-native, horizontally scalable real-time multiplayer WebSocket server implementation built with Node.js, TypeScript, Redis Pub/Sub, and Redis Queues. It supports room-based matchmaking, low-latency state synchronization, and dynamic scaling via Kubernetes’ Horizontal Pod Autoscaler (HPA). Deployed on AWS EKS with an AWS Load Balancer, Redis (for Pub/Sub and task queues), and Docker.
-
WebSocket Service Layer
- Manages client connections
- Provides real-time messaging across rooms
- Handles matchmaking and state updates
-
Redis Manager
- Guarantees event delivery for all connected clients
- Single Redis connection pool (Singleton)
- Handles Pub/Sub and Queues operations
-
Matchmaking Service
- Handles queue operation to add players to the queue
- Handles players matchmaking
- Tracks player session and room allocation of matched players
-
GameState Service
- Synchronizes game state between clients during a match
- Broadcasts client game state to other clients in the same room
- Real-time, bidirectional multiplayer communication
- Room/Matchmaking support with fast state sync
- Horizontally scalable with HPA on Kubernetes
- Redis Pub/Sub for event distribution
- Redis Queues for matchmaking task
- Built with TypeScript for strong typing and maintainability
- Dockerized for easy CI/CD and portability
- Cloud-native deployment using Kubernetes
- Clone the repository
git clone https://github.com/TanishValesha/Scalable-Realtime-Multiplayer-System.git
cd Scalable-Realtime-Multiplayer-System- Install dependencies
npm install- Configure Redis
cd redis-docker
docker-compose up -d- Start the server
npm startsrc/
├── manager/
│ ├── RedisManager.ts # Singleton Redis connection
├── services/
│ ├── WebSocketService.ts # Handles WebSocket connections
│ ├── MatchmakingService.ts # Matchmaking and room logic
│ ├── RoomService.ts # Room Creation service
│ └── GameStateService.ts # Game state broadcasting
├── types/
│ └── index.ts # Shared TypeScript types
├── utils/
│ └── logger.ts # Logging helper function
└── index.ts # Application entry point
const ws = new WebSocket('ws://localhost:8080');ws.send(JSON.stringify({
type: 'MATCH_START'
}));ws.send(JSON.stringify({
type: 'JOIN',
payload: { room: 'match-12345' }
}));ws.send(JSON.stringify({
type: 'PLAYER_ACTION',
payload: {
room: 'match-12345',
action: { type: 'move', dx: 3, dy: -1 }
}
}));ws.send(JSON.stringify({
type: 'CHAT',
payload: { room: 'match-12345', message: 'Hello team!' }
}));ws.send(JSON.stringify({
type: 'PLAYER_ACTION',
payload: {
room: 'match-12345',
action: { type: 'move', dx: 3, dy: -1 }
}
}));| Type | Description | Payload |
|---|---|---|
MATCH_START |
Request to enter matchmaking queue | – |
MATCH_START (server → client) |
Notifies players of a new match and provides room info | { room: string, players: string[] } |
JOIN |
Join a specific room | { room: string } |
LEAVE |
Leave a room | { room: string } |
CHAT |
Send a chat message to everyone in the room | { room: string, message: string } |
PLAYER_ACTION |
Send a gameplay action (move / attack / heal) | { room: string, action: { type: "move"/"attack"/"heal", dx?, dy?, targetId?, damage? } } |
STATE_UPDATE (server → client) |
Broadcast with the current state of the room | { players: PlayerState[] } |
ECHO |
Debug echo test | { any } |
The application supports horizontal scaling by deploying multiple instances behind a load balancer. Using Redis Pub/Sub ensures that messages are broadcast and delivered reliably across all instances.
# Start multiple instances
PORT=8080 npm start
PORT=8081 npm start
PORT=8082 npm startnpm run devnpm run build