@roomkit/worker
v1.3.1
Published
Game logic worker for managing rooms and state in multiplayer games
Downloads
794
Maintainers
Readme
@roomkit/worker
Worker server for the RoomKit framework - manages room state and game logic.
Installation
npm install @roomkit/worker @roomkit/coreFeatures
- 🎮 Room Management - Create and manage game rooms with state synchronization
- 🔄 Message Handling - Type-safe message handling with RoomKit protocol
- 💾 State Persistence - Optional room state persistence to Redis
- 🔌 Hot Reload - Seamless room migration between workers
- ��️ Circuit Breaker - Protection against overload with graceful degradation
- 📊 Metrics - Built-in Prometheus metrics
- ⚡ High Performance - Optimized for low-latency multiplayer games
Quick Start
1. Define Your Room
import { Room } from '@roomkit/worker';
interface GameState {
players: Map<string, Player>;
score: number;
}
class GameRoom extends Room<GameState> {
maxClients = 4;
onCreate(options: any) {
this.setState({
players: new Map(),
score: 0,
});
}
onJoin(client: Client, options?: any) {
this.state.players.set(client.sessionId, {
name: options.name,
x: 0,
y: 0,
});
this.broadcast('player-joined', { id: client.sessionId });
}
onMessage(client: Client, type: string, payload: any) {
if (type === 'move') {
const player = this.state.players.get(client.sessionId);
if (player) {
player.x = payload.x;
player.y = payload.y;
this.broadcast('player-moved', { id: client.sessionId, ...payload });
}
}
}
onLeave(client: Client, consented: boolean) {
this.state.players.delete(client.sessionId);
this.broadcast('player-left', { id: client.sessionId });
}
}2. Start the Worker
import { createWorker, Room } from '@roomkit/worker';
class GameRoom extends Room<GameState> { /* ... */ }
const worker = await createWorker({
redis: 'redis://localhost:6379',
rooms: [
{ name: 'game', room: GameRoom }
],
admin: {
port: 28200
},
});
await worker.listen();
console.log(\`Worker listening on admin port \${worker.adminPort}\`);Configuration
Redis Configuration
// Option 1: URL string
redis: 'redis://:password@localhost:6379/0'
// Option 2: Object
redis: {
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0
}Room Registration
rooms: [
{
name: 'lobby', // Room type name
room: LobbyRoom // Room class
},
{
name: 'game',
room: GameRoom
},
]Circuit Breaker
circuitBreaker: {
maxRooms: 1000, // Max rooms per worker (default: 1000)
maxPendingRequests: 100, // Max pending room creation requests (default: 100)
enabled: true, // Enable circuit breaker (default: true)
failureThreshold: 5, // Failures before opening (default: 5)
successThreshold: 2, // Successes before closing (default: 2)
timeout: 10000, // Timeout in ms (default: 10000)
resetTimeout: 30000, // Reset timeout in ms (default: 30000)
}State Persistence
persistence: {
enabled: true, // Enable persistence (default: false)
saveInterval: 5000, // Auto-save interval in ms (default: 5000)
loadOnCreate: true, // Load state on room creation (default: true)
}Admin API
admin: {
port: 28200, // Admin API port (default: 28200)
enabled: true // Enable admin API (default: true)
}Complete Example
import { createWorker, Room } from '@roomkit/worker';
import { LobbyRoom } from './rooms/LobbyRoom';
class GameRoom extends Room<GameState> { /* ... */ }
const worker = await createWorker({
// Optional: Custom worker ID (auto-generated if not provided)
id: 'worker-1',
// Redis configuration (required)
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
password: process.env.REDIS_PASSWORD,
},
// Room definitions (required)
rooms: [
{ name: 'lobby', room: LobbyRoom },
{ name: 'game', room: GameRoom },
],
// Circuit breaker configuration
circuitBreaker: {
maxRooms: 1000,
maxPendingRequests: 100,
enabled: true,
failureThreshold: 5,
successThreshold: 2,
timeout: 10000,
resetTimeout: 30000,
},
// State persistence
persistence: {
enabled: true,
saveInterval: 5000,
loadOnCreate: true,
},
// Admin API
admin: {
port: 28200,
enabled: true,
},
});
// Start the worker
await worker.listen();
// Graceful shutdown
process.on('SIGTERM', async () => {
await worker.close();
process.exit(0);
});Room Lifecycle
class MyRoom extends Room<MyState> {
// Called when room is created
onCreate(options: any) {
this.setState({ ... });
}
// Called when a client joins
onJoin(client: Client, options?: any) {
// Add player to state
}
// Called when client sends a message
onMessage(client: Client, type: string, payload: any) {
// Handle game logic
}
// Called when a client leaves
onLeave(client: Client, consented: boolean) {
// Remove player from state
}
// Called before room is destroyed
onDispose() {
// Cleanup resources
}
}Room API
Broadcasting
// Broadcast to all clients
this.broadcast('event-type', { data: 'value' });
// Broadcast to all except one
this.broadcast('event-type', { data: 'value' }, { except: client });
// Send to specific client
client.send('event-type', { data: 'value' });State Management
// Set initial state
this.setState({ score: 0, players: new Map() });
// Update state (triggers automatic sync to clients)
this.state.score += 10;
this.state.players.set(id, player);
// Lock for atomic updates
this.lock.acquire('update-score', async () => {
this.state.score += 10;
});Room Metadata
// Set custom metadata (visible in lobby)
this.setMetadata({
mapName: 'Forest',
difficulty: 'Hard'
});
// Set if room is private
this.setPrivate(true);Environment Variables
The worker supports basic environment variables for infrastructure configuration:
# Redis (infrastructure)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your-password
# Note: Application-level configs (circuit breaker, persistence, etc.)
# should be passed via code parameters for better type safety.Admin API
Health Check
GET http://localhost:28200/healthMetrics
GET http://localhost:28200/metricsRoom Stats
GET http://localhost:28200/admin/roomsDocker
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 28200
CMD ["node", "worker.js"]docker run -p 28200:28200 \
-e REDIS_HOST=redis \
-e REDIS_PORT=6379 \
my-workerProduction Deployment
Horizontal Scaling
Run multiple worker instances:
# Worker 1
node worker.js --id worker-1
# Worker 2
node worker.js --id worker-2
# Workers register automatically via RedisMonitoring
- Monitor room count, client count, message rate
- Set up alerts for circuit breaker triggers
- Track room creation/disposal rates
Best Practices
- Set maxRooms based on your server capacity
- Enable persistence for important game state
- Use circuit breaker to prevent overload
- Implement proper cleanup in onDispose()
- Handle errors gracefully in onMessage()
License
MIT
