wsmini
Version:
Minimalist WebSocket client and server for real-time applications with RPC, PubSub, Rooms and Game state synchronization.
586 lines (454 loc) • 18.2 kB
Markdown
# WSServerRoomManager API Documentation
The `WSServerRoomManager` class extends [`WSServerPubSub`](./WSServerPubSub.md) to provide room-based functionality for WebSocket communication. It allows clients to create, join, leave, and manage rooms with dedicated messaging and command systems. It's better to read the documentation for [`WSServerPubSub`](./WSServerPubSub.md) first to understand the PubSub and RPC features. You will find a complete example of a room-based WebSocket server in the [examples directory](../examples/index.md). It demonstrates how to create rooms, manage clients, and handle messages with custom hooks.
## Table of Contents
- [Constructor](#constructor)
- [Room Management](#room-management)
- [createRoom](#createroomroomname-withhook)
- [deleteRoom](#deleteroomroomname)
- [getClientsOfRoom](#getclientsofroomroomname)
- [isRoomFull](#isroomfullroomname)
- [getRoomMeta](#getroometaroomname)
- [Room Broadcasting](#room-broadcasting)
- [broadcastRoom](#broadcastroomroom-msg)
- [broadcastRoomName](#broadcastroomnamereoomname-msg)
- [broadcastRoomCmd](#broadcastroomcmdroom-cmd-data)
- [broadcastRoomNameCmd](#broadcastroomnamecmdroomname-cmd-data)
- [sendRoom](#sendroomroom-client-msg)
- [sendRoomName](#sendroomnamereoomname-client-msg)
- [sendRoomCmd](#sendroomcmdroom-client-cmd-data)
- [sendRoomNameCmd](#sendroomnamecmdroomname-client-cmd-data)
- [Server Control](#server-control)
- [start](#start)
- [close](#close)
- [Room Class Hooks](#room-class-hooks)
- [onCreate](#oncreate)
- [onJoin](#onjoin)
- [onMsg](#onmsg)
- [onLeave](#onleave)
- [onDispose](#ondispose)
- [onSendClient](#onsendclient)
- [onSendRoom](#onsendroom)
- [onSendRoomsList](#onsendroomslist)
- [Internal Methods](#internal-methods)
## Constructor
### `new WSServerRoomManager(options)`
Creates a new WebSocket room manager server instance.
The server will handle room creation, joining, leaving, and broadcasting messages to clients in rooms.
The server will automatically manage room lifecycle, including cleanup of empty rooms and broadcasting room updates to clients as well as room listing and user management.
You can disable some of these features by setting the corresponding options to `false` in the `options` object.
For the details of the `roomClass` option, see the documentation of the [Room Class Hooks](#room-class-hooks) below.
**Parameters:**
- `options` (object): Server configuration options (inherits from `WSServerPubSub`)
- `port` (number, optional): The port number to run the server on. Default: `443`
- `maxNbOfClients` (number, optional): Maximum number of allowed clients. Default: `1000`
- `maxInputSize` (number, optional): Maximum size of input messages in bytes. Default: `100000` (100KB)
- `origins` (string, optional): Allowed origins. Default: `'*'`
- `pingTimeout` (number, optional): The timeout in milliseconds for ping responses. Default: `30000`
- `authCallback` (function, optional): Authentication callback function. Default: `(token, request, wsServer) => ({})`
- `logLevel` (string, optional): Log level: 'none', 'error', 'warn', 'info', 'debug'. Default: `'info'`
- `logger` (object, optional): External logger instance for logging. Default: `null`
- `maxUsersByRoom` (number, optional): Maximum number of users per room. Default: `10`
- `usersCanCreateRoom` (boolean, optional): Whether users can create rooms. Default: `true`
- `usersCanNameRoom` (boolean, optional): Whether users can name rooms. Default: `true`
- `usersCanListRooms` (boolean, optional): Whether users can list rooms. Default: `true`
- `usersCanGetRoomUsers` (boolean, optional): Whether users can get room user lists. Default: `true`
- `roomClass` (class, optional): Custom room class extending `WSServerRoom`. Default: `class extends WSServerRoom {}`
- `autoJoinCreatedRoom` (boolean, optional): Whether room creators automatically join. Default: `true`
- `autoDeleteEmptyRoom` (boolean, optional): Whether empty rooms are automatically deleted. Default: `true`
- `autoSendRoomListOnUsersChange` (boolean, optional): Whether to send room list updates on user changes. Default: `true`
- `syncMode` (string, optional): Synchronization mode: 'immediate', 'immediate-other', 'patch'. Default: `'immediate'` (or `'patch'` for game rooms)
**Example:**
```javascript
import { WSServerRoomManager, WSServerRoom, WSServerError } from 'wsmini';
const wsServer = new WSServerRoomManager({
port: 8889,
origins: '*',
maxUsersByRoom: 10,
usersCanCreateRoom: true,
usersCanNameRoom: true,
usersCanListRooms: true,
roomClass: class extends WSServerRoom {
onMsg(msg, clientMeta, client) {
return {
time: Date.now(),
user: 'User-' + clientMeta.id.slice(0, 4),
message: msg
};
}
onCreate(name, msg, clientMeta, client) {
return { createdAt: Date.now() };
}
}
});
```
## Room Management
### `createRoom(roomName, withHook)`
Creates a new room on the server.
**Parameters:**
- `roomName` (string, optional): The room name. If `null`, generates a UUID. Default: `null`
- `withHook` (boolean, optional): Whether to call the `onCreate` hook. Default: `false`
**Returns:** `string|false` - The room name if successful, `false` if room already exists or creation failed
**Example:**
```javascript
// Create room with auto-generated name
const roomName = wsServer.createRoom();
// Create room with specific name
const roomName = wsServer.createRoom('game-lobby');
// Create room with onCreate hook
const roomName = wsServer.createRoom('custom-room', true);
```
### `deleteRoom(roomName)`
Deletes a room from the server. All clients in the room will be removed.
**Parameters:**
- `roomName` (string): The room name to delete
**Returns:** `boolean` - `true` if room was deleted successfully, `false` if room doesn't exist
**Example:**
```javascript
// Delete a room
wsServer.deleteRoom('old-room');
```
### `getClientsOfRoom(roomName)`
Gets all clients in a specific room.
**Parameters:**
- `roomName` (string): The room name
**Returns:** `array` - Array of client metadata objects
**Example:**
```javascript
const clients = wsServer.getClientsOfRoom('game-lobby');
console.log(`Room has ${clients.length} clients`);
```
### `isRoomFull(roomName)`
Checks if a room is full (at maximum capacity).
**Parameters:**
- `roomName` (string): The room name
**Returns:** `boolean` - `true` if room is full, `false` otherwise
**Example:**
```javascript
if (wsServer.isRoomFull('game-lobby')) {
console.log('Room is full');
}
```
### `getRoomMeta(roomName)`
Gets the metadata of a specific room.
**Parameters:**
- `roomName` (string): The room name
**Returns:** `object|false` - Room metadata object or `false` if room doesn't exist
**Example:**
```javascript
const meta = wsServer.getRoomMeta('game-lobby');
if (meta) {
console.log('Room created at:', meta.createdAt);
}
```
## Room Broadcasting
### `broadcastRoom(room, msg)`
Broadcasts a message to all clients in a room.
**Parameters:**
- `room` (object): The room object
- `msg` (any): The message to broadcast
**Returns:** `boolean` - `true` if successful
**Example:**
```javascript
const room = wsServer.rooms.get('game-lobby');
wsServer.broadcastRoom(room, {
type: 'announcement',
message: 'Game starting in 30 seconds'
});
```
### `broadcastRoomName(roomName, msg)`
Broadcasts a message to all clients in a room by room name.
**Parameters:**
- `roomName` (string): The room name
- `msg` (any): The message to broadcast
**Returns:** `boolean` - `true` if successful, `false` if room doesn't exist
**Example:**
```javascript
wsServer.broadcastRoomName('game-lobby', {
type: 'game-update',
score: { player1: 10, player2: 8 }
});
```
### `broadcastRoomCmd(room, cmd, data)`
Broadcasts a command to all clients in a room.
**Parameters:**
- `room` (object): The room object
- `cmd` (string): The command name
- `data` (object, optional): The command data. Default: `{}`
**Returns:** `boolean` - `true` if successful
**Example:**
```javascript
const room = wsServer.rooms.get('game-lobby');
wsServer.broadcastRoomCmd(room, 'game-start', {
mode: 'competitive',
duration: 300
});
```
### `broadcastRoomNameCmd(roomName, cmd, data)`
Broadcasts a command to all clients in a room by room name.
**Parameters:**
- `roomName` (string): The room name
- `cmd` (string): The command name
- `data` (object, optional): The command data. Default: `{}`
**Returns:** `boolean` - `true` if successful, `false` if room doesn't exist
**Example:**
```javascript
wsServer.broadcastRoomNameCmd('game-lobby', 'timer-update', {
timeLeft: 120
});
```
### `sendRoom(room, client, msg)`
Sends a message to a specific client in a room.
**Parameters:**
- `room` (object): The room object
- `client` (WebSocket): The client WebSocket connection
- `msg` (any): The message to send
**Returns:** `boolean` - `true` if successful, `false` if client not in room
**Example:**
```javascript
const room = wsServer.rooms.get('game-lobby');
wsServer.sendRoom(room, client, {
type: 'private-message',
message: 'You are the game moderator'
});
```
### `sendRoomName(roomName, client, msg)`
Sends a message to a specific client in a room by room name.
**Parameters:**
- `roomName` (string): The room name
- `client` (WebSocket): The client WebSocket connection
- `msg` (any): The message to send
**Returns:** `boolean` - `true` if successful, `false` if room doesn't exist or client not in room
**Example:**
```javascript
wsServer.sendRoomName('game-lobby', client, {
type: 'role-assignment',
role: 'spectator'
});
```
### `sendRoomCmd(room, client, cmd, data)`
Sends a command to a specific client in a room.
**Parameters:**
- `room` (object): The room object
- `client` (WebSocket): The client WebSocket connection
- `cmd` (string): The command name
- `data` (object, optional): The command data. Default: `{}`
**Returns:** `boolean` - `true` if successful, `false` if client not in room
**Example:**
```javascript
const room = wsServer.rooms.get('game-lobby');
wsServer.sendRoomCmd(room, client, 'turn-notification', {
isYourTurn: true
});
```
### `sendRoomNameCmd(roomName, client, cmd, data)`
Sends a command to a specific client in a room by room name.
**Parameters:**
- `roomName` (string): The room name
- `client` (WebSocket): The client WebSocket connection
- `cmd` (string): The command name
- `data` (object, optional): The command data. Default: `{}`
**Returns:** `boolean` - `true` if successful, `false` if room doesn't exist or client not in room
**Example:**
```javascript
wsServer.sendRoomNameCmd('game-lobby', client, 'game-over', {
winner: 'player1',
score: { player1: 15, player2: 10 }
});
```
## Server Control
### `start()`
Starts the WebSocket server. Inherited from `WSServerPubSub`.
**Example:**
```javascript
wsServer.start();
console.log('Room manager server started');
```
### `close()`
Closes the WebSocket server and cleans up all rooms.
**Example:**
```javascript
wsServer.close();
```
## Room Class Hooks
When extending `WSServerRoom`, you can override these methods to customize room behavior. For real-time multiplayer games with fixed timestep simulation and world state synchronization, see the [`WSServerGameRoom`](./WSServerGameRoom.md) documentation which provides specialized game room functionality.
### `onCreate(name, msg, clientMeta, client)`
Called when a room is created. Returns room metadata or false to abort creation.
If the user does provided a name, it will be used as the room name.
If you return a `name` prop in the metadata, it will be used as the room name instead.
If no name is provided, a UUID will be generated as the room name.
You can throw a `WSServerError` to abort creation with an error message. The promise will reject with the error message on the client side.
**Parameters:**
- `name` (string): The room name
- `msg` (any): Additional data sent by the client
- `clientMeta` (object): Client metadata (null if created by server)
- `client` (WebSocket): Client connection (null if created by server)
**Returns:** `object|false` - Room metadata object or `false` to abort creation
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onCreate(name, msg, clientMeta, client) {
if (name === 'forbidden') {
throw new WSServerError('Room name not allowed');
}
// do not forget to validate the input received from the client
if (msg?.gameMode && !['normal', 'hardcore'].includes(msg.gameMode)) {
throw new WSServerError('Invalid game mode');
}
return {
createdAt: Date.now(),
gameMode: msg.gameMode,
};
}
}
```
### `onJoin(msg, clientMeta, client)`
Called when a client joins the room.
**Parameters:**
- `msg` (any): Additional data sent by the client
- `clientMeta` (object): Client metadata
- `client` (WebSocket): Client connection
**Returns:** `object|false` - Additional client metadata or `false` to abort join
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onJoin(msg, clientMeta, client) {
if (msg?.team !== 'red' && msg?.team !== 'blue') {
throw new WSServerError('Invalid team selection');
}
return {
team: msg.team,
joinedAt: Date.now()
};
}
}
```
### `onMsg(msg, clientMeta, client)`
Called when a client sends a message to the room.
**Parameters:**
- `msg` (any): The message from the client
- `clientMeta` (object): Client metadata
- `client` (WebSocket): Client connection
**Returns:** `any` - The message to broadcast to all room clients
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onMsg(msg, clientMeta, client) {
// Validate and transform message
if (!msg.text || msg.text.length > 500) {
throw new WSServerError('Invalid message');
}
return {
time: Date.now(),
user: clientMeta.nickname,
team: clientMeta.team,
text: msg.text
};
}
}
```
### `onLeave(clientMeta, client)`
Called when a client leaves the room.
**Parameters:**
- `clientMeta` (object): Client metadata
- `client` (WebSocket): Client connection
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onLeave(clientMeta, client) {
// Broadcast to other clients
this.broadcastCmd('player-left', {
playerId: clientMeta.id,
nickname: clientMeta.nickname
});
}
}
```
### `onDispose()`
Called when the room is being deleted.
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onDispose() {
// Clean up timers, save game state, etc.
if (this.gameTimer) {
clearInterval(this.gameTimer);
}
}
}
```
### `onSendClient(clientMeta)`
Called when sending client metadata to clients.
You can use this to filter or transform the client metadata before the server sends it to other clients.
**Parameters:**
- `clientMeta` (object): The client metadata
**Returns:** `object` - The filtered client metadata to send
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onSendClient(clientMeta) {
// Hide sensitive information
return {
id: clientMeta.id,
nickname: clientMeta.nickname,
team: clientMeta.team,
isReady: clientMeta.isReady
};
}
}
```
### `onSendRoom()`
Called when sending room metadata to clients.
You can use this to filter or transform the room metadata before the server sends it to clients.
**Returns:** `object` - The room metadata to send
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
onSendRoom() {
return {
name: this.name,
gameMode: this.meta.gameMode,
maxScore: this.meta.maxScore,
status: this.meta.status || 'waiting'
};
}
}
```
### `onSendRoomsList(rooms)` (static)
Called when sending the room list to clients.
You can use this to filter or transform the room list before the server sends it to clients.
For example, you can hide full rooms, private rooms or running games.
**Parameters:**
- `rooms` (array): Array of room objects
**Returns:** `array` - The filtered room list to send
**Example:**
```javascript
class CustomRoom extends WSServerRoom {
static onSendRoomsList(rooms) {
// Hide full rooms or private rooms
return rooms.filter(room =>
room.nbUsers < room.maxUsers &&
!room.meta.isPrivate
);
}
}
```
## Internal Methods
The following methods are used internally by the server and typically don't need to be called directly:
### Client RPC Handlers
- `clientCreateRoom(data, clientMeta, client)` - Handles room creation requests
- `clientJoinRoom(data, clientMeta, client)` - Handles room join requests
- `clientCreateOrJoinRoom(data, clientMeta, client)` - Handles create-or-join requests
- `clientLeaveRoom(data, clientMeta, client)` - Handles room leave requests
- `clientListRooms(data, clientMeta, client)` - Handles room list requests
### Room Management
- `addClientToRoom(roomName, clientMeta, client)` - Adds a client to a room
- `removeClientFromRoom(roomName, client)` - Removes a client from a room
- `prepareRoomList()` - Prepares room list for client consumption
- `prepareRoomClients(room)` - Prepares client list for a room
- `pubRoomList()` - Publishes room list updates
- `pubRoomClients(room)` - Publishes client list updates for a room
### Message Processing
- `manageRoomActions(client, data)` - Handles room-specific actions
- `onMessage(client, message)` - Processes incoming messages
- `onClose(client)` - Handles client disconnections
- `isActionValid(action)` - Validates action types