npm package: @roomkit/worker
Description: Game logic worker for managing rooms and state in multiplayer games
Latest version: 1.3.1

# @roomkit/worker

Worker server for the RoomKit framework - manages room state and game logic.

## Installation

```bash
npm install @roomkit/worker @roomkit/core
```

## Features

- 🎮 **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

```typescript
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

```typescript
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

```typescript
// 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

```typescript
rooms: [
  { 
    name: 'lobby',      // Room type name
    room: LobbyRoom     // Room class
  },
  { 
    name: 'game', 
    room: GameRoom 
  },
]
```

### Circuit Breaker

```typescript
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

```typescript
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

```typescript
admin: {
  port: 28200,       // Admin API port (default: 28200)
  enabled: true      // Enable admin API (default: true)
}
```

## Complete Example

```typescript
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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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:

```bash
# 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
```bash
GET http://localhost:28200/health
```

### Metrics
```bash
GET http://localhost:28200/metrics
```

### Room Stats
```bash
GET http://localhost:28200/admin/rooms
```

## Docker

```dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 28200
CMD ["node", "worker.js"]
```

```bash
docker run -p 28200:28200 \
  -e REDIS_HOST=redis \
  -e REDIS_PORT=6379 \
  my-worker
```

## Production Deployment

### Horizontal Scaling

Run multiple worker instances:

```bash
# Worker 1
node worker.js --id worker-1

# Worker 2
node worker.js --id worker-2

# Workers register automatically via Redis
```

### Monitoring

- Monitor room count, client count, message rate
- Set up alerts for circuit breaker triggers
- Track room creation/disposal rates

### Best Practices

1. **Set maxRooms** based on your server capacity
2. **Enable persistence** for important game state
3. **Use circuit breaker** to prevent overload
4. **Implement proper cleanup** in onDispose()
5. **Handle errors gracefully** in onMessage()

## License

MIT