playsocketjs 2.4.2 → 2.5.0
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 +2 -1
- package/dist/playsocket-client.js +3 -3
- package/dist/playsocket-server.js +39 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ new PlaySocket(id?: string, options: PlaySocketOptions)
|
|
|
96
96
|
### Methods
|
|
97
97
|
|
|
98
98
|
- `init()`: Initialize the WebSocket connection – Returns Promise (async) which resolves with the client's ID
|
|
99
|
-
- `createRoom(initialStorage?: object, maxSize?: number)`: Create a new room and become host – Returns Promise (async) which resolves with the room ID
|
|
99
|
+
- `createRoom(initialStorage?: object, maxSize?: number)`: Create a new room and become host – Returns Promise (async) which resolves with the room ID. The room participant maximum is 100
|
|
100
100
|
- `joinRoom(hostId: string)`: Join an existing room – Returns Promise (async)
|
|
101
101
|
- `destroy()`: Use this to leave a room and close the connection
|
|
102
102
|
- `updateStorage(key: string, type: 'set' | 'array-add' | 'array-add-unique' | 'array-remove-matching' | 'array-update-matching', value: any, updateValue?: any)`: Update the shared storage (max. 100 keys). Safely update arrays in storage by adding, removing, or updating items. UpdateValue is only required for the 'array-update-matching' operation type
|
|
@@ -203,6 +203,7 @@ Creates a new PlaySocket Server instance with configuration options.
|
|
|
203
203
|
- `port`: Port to listen on (default: 3000, used only if no server provided)
|
|
204
204
|
- `path`: WebSocket endpoint path (default: '/')
|
|
205
205
|
- `server`: Existing http server (optional)
|
|
206
|
+
- `rateLimit`: Adjust the messages/second rate limit (defaults to 20)
|
|
206
207
|
- `debug`: Set this property to true to enable extra logging
|
|
207
208
|
|
|
208
209
|
### Methods
|
|
@@ -2301,10 +2301,10 @@ class PlaySocket {
|
|
|
2301
2301
|
case 'room_created':
|
|
2302
2302
|
if (this.#pendingCreate) {
|
|
2303
2303
|
this.#inRoom = true;
|
|
2304
|
-
this.#triggerEvent("status", `Room created
|
|
2304
|
+
this.#triggerEvent("status", `Room created with max. size ${message.size}.`);
|
|
2305
2305
|
this.#crdtManager.importState(message.state);
|
|
2306
2306
|
this.#triggerEvent("storageUpdated", this.getStorage);
|
|
2307
|
-
this.#pendingCreate.resolve(
|
|
2307
|
+
this.#pendingCreate.resolve(message.roomId);
|
|
2308
2308
|
}
|
|
2309
2309
|
break;
|
|
2310
2310
|
|
|
@@ -2456,7 +2456,7 @@ class PlaySocket {
|
|
|
2456
2456
|
return Promise.race([
|
|
2457
2457
|
new Promise((resolve, reject) => {
|
|
2458
2458
|
this.#roomHost = this.#id;
|
|
2459
|
-
this.#pendingCreate = {
|
|
2459
|
+
this.#pendingCreate = { resolve, reject };
|
|
2460
2460
|
this.#sendToServer({
|
|
2461
2461
|
type: 'create_room',
|
|
2462
2462
|
initialStorage,
|
|
@@ -2213,6 +2213,7 @@ function requireServer () {
|
|
|
2213
2213
|
class PlaySocketServer {
|
|
2214
2214
|
#server;
|
|
2215
2215
|
#ownsServer = false;
|
|
2216
|
+
#rateLimitMaxPoints;
|
|
2216
2217
|
#wss;
|
|
2217
2218
|
#clients = new Map(); // ClientId -> WebSocket instance
|
|
2218
2219
|
#rooms = {};
|
|
@@ -2235,10 +2236,12 @@ function requireServer () {
|
|
|
2235
2236
|
* @param {string} [options.path='/socket'] - WebSocket endpoint path
|
|
2236
2237
|
*/
|
|
2237
2238
|
constructor(options = {}) {
|
|
2238
|
-
const { server, port = 3000, path = '/', debug = false } = options;
|
|
2239
|
+
const { server, port = 3000, path = '/', debug = false, rateLimit = 20 } = options;
|
|
2239
2240
|
|
|
2240
2241
|
if (debug) this.#debug = true; // Enable extra logging
|
|
2241
2242
|
|
|
2243
|
+
this.#rateLimitMaxPoints = rateLimit; // Set rate limit
|
|
2244
|
+
|
|
2242
2245
|
// Handle server creation / usage
|
|
2243
2246
|
if (server) {
|
|
2244
2247
|
this.#server = server; // Use provided HTTP server
|
|
@@ -2311,10 +2314,8 @@ function requireServer () {
|
|
|
2311
2314
|
|
|
2312
2315
|
// Generate client ID if none provided
|
|
2313
2316
|
if (!data.id) {
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
2317
|
-
const id = Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
2317
|
+
for (let i = 0; i < 50; i++) {
|
|
2318
|
+
const id = this.#generateId();
|
|
2318
2319
|
if (!this.#clients.get(id)) {
|
|
2319
2320
|
data.id = id;
|
|
2320
2321
|
break;
|
|
@@ -2384,16 +2385,31 @@ function requireServer () {
|
|
|
2384
2385
|
|
|
2385
2386
|
case 'create_room':
|
|
2386
2387
|
if (!ws.clientId) return;
|
|
2387
|
-
const newRoomId = ws.clientId;
|
|
2388
2388
|
|
|
2389
|
-
|
|
2389
|
+
let newRoomId;
|
|
2390
|
+
|
|
2391
|
+
if (this.#clientRooms.get(ws.clientId)) {
|
|
2390
2392
|
ws.send(encode({
|
|
2391
2393
|
type: 'room_creation_failed',
|
|
2392
|
-
reason:
|
|
2394
|
+
reason: 'Already in a room.'
|
|
2393
2395
|
}), { binary: true });
|
|
2394
2396
|
return;
|
|
2395
2397
|
}
|
|
2396
2398
|
|
|
2399
|
+
// Generate room ID
|
|
2400
|
+
for (let i = 0; i < 50; i++) {
|
|
2401
|
+
const id = this.#generateId();
|
|
2402
|
+
if (!this.#rooms[id]) {
|
|
2403
|
+
newRoomId = id;
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
if (!newRoomId) {
|
|
2409
|
+
ws.send(encode({ type: 'room_creation_failed', reason: 'No available ID found.' }), { binary: true });
|
|
2410
|
+
throw new Error('Failed to generate unique room ID!');
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2397
2413
|
const roomCrdtManager = new CRDTManager(this.#debug); // Create the room's crdt manager
|
|
2398
2414
|
|
|
2399
2415
|
// Event callback with potential initial storage modifications
|
|
@@ -2421,18 +2437,19 @@ function requireServer () {
|
|
|
2421
2437
|
}
|
|
2422
2438
|
|
|
2423
2439
|
// Create room
|
|
2440
|
+
const maxSize = Math.min(Number(data.size), 100) || 100; // Max. limit is 100 clients / room
|
|
2424
2441
|
this.#roomVersions.set(newRoomId, 0); // Start with version 0
|
|
2425
2442
|
this.#rooms[newRoomId] = {
|
|
2426
2443
|
participants: [ws.clientId],
|
|
2427
2444
|
host: ws.clientId,
|
|
2428
|
-
maxSize
|
|
2445
|
+
maxSize,
|
|
2429
2446
|
crdtManager: roomCrdtManager
|
|
2430
2447
|
};
|
|
2431
2448
|
this.#clientRooms.set(ws.clientId, newRoomId); // Add client to the room
|
|
2432
2449
|
|
|
2433
|
-
ws.send(encode({ type: 'room_created', state: roomCrdtManager.getState }), { binary: true });
|
|
2450
|
+
ws.send(encode({ type: 'room_created', state: roomCrdtManager.getState, roomId: newRoomId, size: maxSize }), { binary: true });
|
|
2434
2451
|
this.#triggerEvent("roomCreated", newRoomId);
|
|
2435
|
-
if (this.#debug) console.log(
|
|
2452
|
+
if (this.#debug) console.log(`Room ${newRoomId} created with initial storage:`, data.initialStorage);
|
|
2436
2453
|
break;
|
|
2437
2454
|
|
|
2438
2455
|
case 'join_room':
|
|
@@ -2518,6 +2535,7 @@ function requireServer () {
|
|
|
2518
2535
|
break;
|
|
2519
2536
|
|
|
2520
2537
|
case 'request':
|
|
2538
|
+
if (!ws.clientId) return;
|
|
2521
2539
|
const requestorRoomId = this.#clientRooms.get(ws.clientId) || null;
|
|
2522
2540
|
this.#triggerEvent("requestReceived", { roomId: requestorRoomId, clientId: ws.clientId, name: data.request.name, data: data.request.data });
|
|
2523
2541
|
break;
|
|
@@ -2532,6 +2550,14 @@ function requireServer () {
|
|
|
2532
2550
|
}
|
|
2533
2551
|
}
|
|
2534
2552
|
|
|
2553
|
+
/**
|
|
2554
|
+
* Generate a readable, 6 digit ID
|
|
2555
|
+
*/
|
|
2556
|
+
#generateId() {
|
|
2557
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
|
|
2558
|
+
return Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2535
2561
|
/**
|
|
2536
2562
|
* If the client that disconnected (and is now in the reconnection-phase) was the room host,
|
|
2537
2563
|
* pick a new room host immediately to avoid host-less phase (in case important logic is attached to the host)
|
|
@@ -2570,11 +2596,10 @@ function requireServer () {
|
|
|
2570
2596
|
* @private
|
|
2571
2597
|
*/
|
|
2572
2598
|
#checkRateLimit(connectionId, operationType) {
|
|
2573
|
-
const MAX_POINTS = 20;
|
|
2574
2599
|
const now = Date.now();
|
|
2575
2600
|
|
|
2576
2601
|
if (!this.#rateLimits.has(connectionId)) {
|
|
2577
|
-
this.#rateLimits.set(connectionId, { points:
|
|
2602
|
+
this.#rateLimits.set(connectionId, { points: this.#rateLimitMaxPoints, lastReset: now });
|
|
2578
2603
|
return true;
|
|
2579
2604
|
}
|
|
2580
2605
|
|
|
@@ -2582,7 +2607,7 @@ function requireServer () {
|
|
|
2582
2607
|
|
|
2583
2608
|
// Reset points if interval has passed (1s)
|
|
2584
2609
|
if (now - limit.lastReset > 1000) {
|
|
2585
|
-
limit.points =
|
|
2610
|
+
limit.points = this.#rateLimitMaxPoints;
|
|
2586
2611
|
limit.lastReset = now;
|
|
2587
2612
|
}
|
|
2588
2613
|
|