playsocketjs 2.6.1 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -57
- package/dist/playsocket-server.js +16 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A reactive, optimistic WebSocket library that simplifies game & app development
|
|
|
7
7
|
PlaySocket eliminates the traditional complexity of collaborative experiences:
|
|
8
8
|
|
|
9
9
|
- **Streamlined architecture**: No additional backend code is required, but server-authoritative behavior supported
|
|
10
|
-
- **State synchronization**: Built-in storage system keeps the full state synchronized across all clients, always
|
|
10
|
+
- **State synchronization**: Built-in storage system keeps the full state synchronized across all clients, always conflict-free and in order
|
|
11
11
|
- **Resilient & secure connections**: Automatic reconnection handling & strict rate-limiting
|
|
12
12
|
- **Lightweight**: Uses WebSockets for efficient, predictable, reliable communication and has little dependencies
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ npm install playsocketjs
|
|
|
19
19
|
|
|
20
20
|
## Usage examples
|
|
21
21
|
|
|
22
|
-
Note that in production, you should **always try...catch** promises, such as socket.init() –
|
|
22
|
+
Note that in production, you should **always try...catch** promises, such as socket.init() – they can reject!
|
|
23
23
|
|
|
24
24
|
Initializing the client:
|
|
25
25
|
```javascript
|
|
@@ -77,7 +77,7 @@ Sending traditional requests to the server:
|
|
|
77
77
|
socket.sendRequest('chosen-request-name', { fact: "You can build server-authoritative logic using this!" })
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
## API
|
|
80
|
+
## API reference
|
|
81
81
|
|
|
82
82
|
### Constructor
|
|
83
83
|
|
|
@@ -89,36 +89,45 @@ new PlaySocket(id?: string, options: PlaySocketOptions)
|
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
#### Configuration options
|
|
92
|
-
- `endpoint`: WebSocket server endpoint (e.g., 'wss://example.com/socket')
|
|
93
|
-
- `customData`: You can pass arbitrary data to the "clientRegistered" server event (optional)
|
|
94
|
-
- `debug`: Set this property to true to enable extra logging
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- `destroy()`: Use this to leave a room and close the connection
|
|
102
|
-
- `updateStorage(key: string, type: 'set' | 'array-add' | 'array-add-unique' | 'array-remove-matching' | 'array-update-matching', value: any, updateValue?: any)`: Update a key in the shared storage (max. 100 keys). Array operation types allow for conflict-free simultaneous array updates. For '-matching' operations, value becomes the value to match, and updateValue the replacement.
|
|
103
|
-
- `sendRequest(name: string, data?: any)`: Send requests to the server with optional custom data (handle these in the `requestReceived` server event)
|
|
104
|
-
- `onEvent(event: string, callback: Function)`: Register an event callback
|
|
93
|
+
| Option | Type | Default | Description |
|
|
94
|
+
|--------|------|---------|-------------|
|
|
95
|
+
| `endpoint` | string | `none - set this!` | WebSocket server endpoint (e.g., 'wss://example.com/socket') |
|
|
96
|
+
| `customData` | object | `{}` | Arbitrary data to pass to the "clientRegistered" server event (optional) |
|
|
97
|
+
| `debug` | boolean | `false` | Set to true to enable extra logging |
|
|
105
98
|
|
|
106
|
-
|
|
99
|
+
### Methods
|
|
107
100
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- `
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
- `
|
|
114
|
-
|
|
101
|
+
| Method | Parameters | Return type | Description |
|
|
102
|
+
|--------|------------|-------------|-------------|
|
|
103
|
+
| `init()` | - | `Promise<string>` | Initialize the WebSocket connection – Returns Promise which resolves with the client's ID |
|
|
104
|
+
| `createRoom()` | `initialStorage?: object, size?: number` | `Promise<string>` | Create a new room and become host – Returns Promise which resolves with the room ID. The room participant maximum is 100 |
|
|
105
|
+
| `joinRoom()` | `roomId: string` | `Promise<void>` | Join an existing room |
|
|
106
|
+
| `destroy()` | - | `void` | Use this to leave a room and close the connection |
|
|
107
|
+
| `updateStorage()` | `key: string, type: 'set' \| 'array-add' \| 'array-add-unique' \| 'array-remove-matching' \| 'array-update-matching', value: any, updateValue?: any` | `void` | Update a key in the shared storage (max. 100 keys). Array operation types allow for conflict-free simultaneous array updates. For '-matching' operations, value becomes the value to match, and updateValue the replacement |
|
|
108
|
+
| `sendRequest()` | `name: string, data?: any` | `void` | Send requests to the server with optional custom data (handle these in the `requestReceived` server event) |
|
|
109
|
+
| `onEvent()` | `event: string, callback: Function` | `void` | Register an event callback |
|
|
110
|
+
|
|
111
|
+
### Event types
|
|
112
|
+
|
|
113
|
+
| Event | Callback parameter | Description |
|
|
114
|
+
|-------|-------------------|-------------|
|
|
115
|
+
| `status` | `status: string` | Connection status updates |
|
|
116
|
+
| `error` | `error: string` | Error events |
|
|
117
|
+
| `instanceDestroyed` | - | Destruction event - triggered by manual .destroy() method invocation or by fatal errors and disconnects |
|
|
118
|
+
| `storageUpdated` | `storage: object` | Storage state changes |
|
|
119
|
+
| `hostMigrated` | `roomId: string` | Host changes |
|
|
120
|
+
| `clientConnected` | `clientId: string` | New client connected to the room |
|
|
121
|
+
| `clientDisconnected` | `clientId: string, roomId?: string` | Client disconnected from the room |
|
|
115
122
|
|
|
116
123
|
### Properties (Read-only)
|
|
117
124
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
| Property | Type | Description |
|
|
126
|
+
|----------|------|-------------|
|
|
127
|
+
| `id` | string | Client's unique identifier on the WebSocket server |
|
|
128
|
+
| `isHost` | boolean | If this user is currently assigned the host role |
|
|
129
|
+
| `connectionCount` | number | Number of active client connections in room (without yourself) |
|
|
130
|
+
| `getStorage` | object | Retrieve storage object |
|
|
122
131
|
|
|
123
132
|
|
|
124
133
|
|
|
@@ -188,51 +197,59 @@ process.on('SIGINT', shutdown);
|
|
|
188
197
|
process.on('SIGTERM', shutdown);
|
|
189
198
|
```
|
|
190
199
|
|
|
191
|
-
## API
|
|
200
|
+
## API reference
|
|
192
201
|
|
|
193
202
|
### Constructor
|
|
194
203
|
|
|
204
|
+
Creates a new PlaySocket Server instance with configuration options.
|
|
205
|
+
|
|
195
206
|
```javascript
|
|
196
207
|
new PlaySocket(options: PlaySocketServerOptions)
|
|
197
208
|
```
|
|
198
209
|
|
|
199
|
-
Creates a new PlaySocket Server instance with configuration options.
|
|
200
|
-
|
|
201
210
|
### Configuration options
|
|
202
211
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
212
|
+
| Option | Type | Default | Description |
|
|
213
|
+
|--------|------|---------|-------------|
|
|
214
|
+
| `port` | number | 3000 | Port to listen on (used only if no server provided) |
|
|
215
|
+
| `path` | string | '/' | WebSocket endpoint path |
|
|
216
|
+
| `server` | http.Server | - | Existing http server (optional) |
|
|
217
|
+
| `rateLimit` | number | 20 | Adjust the messages/second rate limit |
|
|
218
|
+
| `debug` | boolean | false | Set to true to enable extra logging |
|
|
208
219
|
|
|
209
220
|
### Methods
|
|
210
221
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
222
|
+
| Method | Parameters | Return type | Description |
|
|
223
|
+
|--------|------------|-------------|-------------|
|
|
224
|
+
| `stop()` | - | `void` | Closes all active client connections, the websocket server and the underlying http server if it's standalone |
|
|
225
|
+
| `kick()` | `clientId: string, reason?: string` | `void` | Kick a client by their clientID – this will close their connection and set an error message |
|
|
226
|
+
| `onEvent()` | `event: string, callback: Function` | `void` | Register a server-side event callback |
|
|
227
|
+
| `getRoomStorage()` | `roomId: string` | `object` | Get a snapshot of the current room storage |
|
|
228
|
+
| `updateRoomStorage()` | `roomId: string, key: string, type: 'set' \| 'array-add' \| 'array-add-unique' \| 'array-remove-matching' \| 'array-update-matching', value: any, updateValue?: any` | `void` | Update a key in the shared room storage from the server |
|
|
229
|
+
| `createRoom()` | `initialStorage?: object, size?: number, host?: string` | `Promise<object>` | Create a room (returns object containing room ID and state) – Rooms created with a non-player host like "server" (default) will not be deleted when the last participant leaves |
|
|
230
|
+
| `destroyRoom()` | `roomId: string` | `void` | Destroy a room & kick all participants |
|
|
231
|
+
|
|
232
|
+
### Event types
|
|
233
|
+
|
|
234
|
+
| Event | Callback parameters | Description | Return for action |
|
|
235
|
+
|-------|-------------------|-------------|--------------|
|
|
236
|
+
| `clientRegistered` | `clientId: string, customData: object` | Client registered with the server | - |
|
|
237
|
+
| `clientRegistrationRequested` | `clientId: string, customData: object` | Client requests to register | Return `false` or rejection reason `string` to block |
|
|
238
|
+
| `clientDisconnected` | `clientId: string` | Client disconnected from the server | - |
|
|
239
|
+
| `clientJoinedRoom` | `clientId: string, roomId: string` | Client joined a room (clients can only leave by disconnecting) | - |
|
|
240
|
+
| `clientJoinRequested` | `clientId: string, roomId: string` | Client requests to join a room | Return `false` or rejection reason `string` to block |
|
|
241
|
+
| `roomCreated` | `roomId: string` | Client created a room | - |
|
|
242
|
+
| `roomDestroyed` | `roomId: string` | Room was destroyed (happens when all participants leave) | - |
|
|
243
|
+
| `roomCreationRequested` | `{clientId: string, initialStorage: object}` | Room creation requested by client | Return `object` to override initial storage, `false` to deny |
|
|
244
|
+
| `storageUpdated` | `{clientId: string, roomId: string, update: object, storage: object}` | Room storage property updated | - |
|
|
245
|
+
| `storageUpdateRequested` | `{clientId: string, roomId: string, update: object, storage: object}` | Room storage property update requested by client | Return `false` to block the update |
|
|
246
|
+
| `requestReceived` | `{clientId: string, roomId?: string, requestName: string, data?: any}` | Request from client was received by the server | - |
|
|
232
247
|
|
|
233
248
|
### Properties (Read-only)
|
|
234
249
|
|
|
235
|
-
|
|
250
|
+
| Property | Type | Description |
|
|
251
|
+
|----------|------|-------------|
|
|
252
|
+
| `getRooms` | object | Retrieve the rooms object |
|
|
236
253
|
|
|
237
254
|
# License
|
|
238
255
|
|
|
@@ -2284,25 +2284,16 @@ class PlaySocketServer {
|
|
|
2284
2284
|
return;
|
|
2285
2285
|
}
|
|
2286
2286
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
if (typeof reviewedStorage === 'object') data.initialStorage = reviewedStorage;
|
|
2290
|
-
if (reviewedStorage === false) {
|
|
2291
|
-
ws.send(encode({
|
|
2292
|
-
type: 'room_creation_failed',
|
|
2293
|
-
reason: 'Room creation denied.'
|
|
2294
|
-
}), { binary: true });
|
|
2295
|
-
return;
|
|
2296
|
-
}
|
|
2287
|
+
try {
|
|
2288
|
+
const newRoom = await this.createRoom(data.initialStorage, data.size, ws.clientId);
|
|
2297
2289
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2290
|
+
// Check if client/creator is still connected - abort if not
|
|
2291
|
+
if (!this.#clients.has(ws.clientId)) {
|
|
2292
|
+
this.destroyRoom(newRoom.id);
|
|
2293
|
+
if (this.#debug) console.log(`Room creation cancelled - client ${ws.clientId} disconnected during event callback.`);
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2303
2296
|
|
|
2304
|
-
try {
|
|
2305
|
-
const newRoom = this.createRoom(data.initialStorage, data.size, ws.clientId);
|
|
2306
2297
|
this.#rooms[newRoom.id].participants.push(ws.clientId); // Add client to the room
|
|
2307
2298
|
this.#clientRooms.set(ws.clientId, newRoom.id); // Add client to the client-room map
|
|
2308
2299
|
ws.send(encode({ type: 'room_created', state: newRoom.state, roomId: newRoom.id }), { binary: true });
|
|
@@ -2640,12 +2631,18 @@ class PlaySocketServer {
|
|
|
2640
2631
|
|
|
2641
2632
|
/**
|
|
2642
2633
|
* Create a room from the server
|
|
2634
|
+
* @async
|
|
2643
2635
|
* @param {object} [initialStorage] - Optional initial storage object
|
|
2644
2636
|
* @param {number} [size] - Max. room size, up to 500
|
|
2645
2637
|
* @param {string} [host] - Host ID, defaults to "server" (when set to "server", room will not be deleted if all clients leave)
|
|
2646
|
-
* @returns {object} Object containing room state and room ID
|
|
2638
|
+
* @returns {Promise<object>} Object containing room state and room ID
|
|
2647
2639
|
*/
|
|
2648
|
-
createRoom(initialStorage, size, host = "server") {
|
|
2640
|
+
async createRoom(initialStorage, size, host = "server") {
|
|
2641
|
+
// Event callback with potential initial storage modifications
|
|
2642
|
+
const reviewedStorage = await this.#triggerEvent("roomCreationRequested", { clientId: host, initialStorage: structuredClone({ ...initialStorage }) });
|
|
2643
|
+
if (typeof reviewedStorage === 'object') initialStorage = reviewedStorage;
|
|
2644
|
+
if (reviewedStorage === false) throw new Error("Room creation denied.");
|
|
2645
|
+
|
|
2649
2646
|
let newRoomId;
|
|
2650
2647
|
|
|
2651
2648
|
for (let i = 0; i < 100; i++) {
|