playsocketjs 1.1.9 → 1.1.11

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.
@@ -2333,7 +2333,8 @@ class PlaySocket {
2333
2333
  * @private
2334
2334
  */
2335
2335
  async #attemptReconnect() {
2336
- if (this.#reconnectCount++ >= 3) {
2336
+ this.#reconnectCount++;
2337
+ if (this.#reconnectCount > 9) {
2337
2338
  this.#triggerEvent("error", "Disconnected from server.");
2338
2339
  return this.destroy();
2339
2340
  }
@@ -2226,6 +2226,7 @@ function requireServer () {
2226
2226
 
2227
2227
  // Set up ws event handlers
2228
2228
  this.#wss.on('connection', ws => {
2229
+ ws.connectionId = crypto.randomUUID();
2229
2230
  ws.isAlive = true;
2230
2231
  ws.on('pong', () => { ws.isAlive = true; });
2231
2232
  ws.on('message', msg => this.#handleMessage(ws, msg));
@@ -2255,12 +2256,19 @@ function requireServer () {
2255
2256
  * @private
2256
2257
  */
2257
2258
  #handleMessage(ws, message) {
2259
+ if (ws.isTerminating) return;
2258
2260
  try {
2259
2261
  const data = decode(message);
2260
2262
 
2261
- // Apply rate limiting after client is registered
2262
- if (ws.clientId && !this.#checkRateLimit(ws.clientId, data.type)) {
2263
- return console.error(`Client ${ws.clientId} rate limit exceeded.`);
2263
+ // Apply rate limiting to all connections (including unregistered)
2264
+ if (!this.#checkRateLimit(ws.connectionId, data.type)) {
2265
+ ws.rateLimitViolations = (ws.rateLimitViolations || 0) + 1;
2266
+ if (ws.rateLimitViolations > 5 && !ws.isTerminating) {
2267
+ ws.isTerminating = true; // Prevent multiple terminate calls (it is async)
2268
+ ws.terminate();
2269
+ return console.error(`Connection ${ws.connectionId} terminated due to repeated rate limit violations.`);
2270
+ }
2271
+ return console.error(`Connection ${ws.connectionId} rate limit exceeded.`);
2264
2272
  }
2265
2273
 
2266
2274
  switch (data.type) {
@@ -2465,24 +2473,24 @@ function requireServer () {
2465
2473
  * Check rate limit using token bucket algorithm
2466
2474
  * @private
2467
2475
  */
2468
- #checkRateLimit(clientId, operationType) {
2469
- const MAX_POINTS = 100;
2476
+ #checkRateLimit(connectionId, operationType) {
2477
+ const MAX_POINTS = 20;
2470
2478
  const now = Date.now();
2471
2479
 
2472
- if (!this.#rateLimits.has(clientId)) {
2473
- this.#rateLimits.set(clientId, { points: MAX_POINTS, lastReset: now });
2480
+ if (!this.#rateLimits.has(connectionId)) {
2481
+ this.#rateLimits.set(connectionId, { points: MAX_POINTS, lastReset: now });
2474
2482
  return true;
2475
2483
  }
2476
2484
 
2477
- const limit = this.#rateLimits.get(clientId);
2485
+ const limit = this.#rateLimits.get(connectionId);
2478
2486
 
2479
- // Reset points if interval has passed (5s)
2480
- if (now - limit.lastReset > 5000) {
2487
+ // Reset points if interval has passed (1s)
2488
+ if (now - limit.lastReset > 1000) {
2481
2489
  limit.points = MAX_POINTS;
2482
2490
  limit.lastReset = now;
2483
2491
  }
2484
2492
 
2485
- const pointCost = ['create_room', 'join_room'].includes(operationType) ? 20 : 1;
2493
+ const pointCost = ['create_room', 'join_room'].includes(operationType) ? 5 : 1;
2486
2494
  if (limit.points < pointCost) return false;
2487
2495
 
2488
2496
  limit.points -= pointCost;
@@ -2494,22 +2502,25 @@ function requireServer () {
2494
2502
  * @private
2495
2503
  */
2496
2504
  #handleDisconnection(ws) {
2497
- if (!ws.clientId) return;
2498
- this.#clients.delete(ws.clientId); // Immediately remove from active clients (otherwise, server would try to message this client)
2505
+ this.#rateLimits.delete(ws.connectionId);
2499
2506
 
2500
- // If client was in a room, check if they were the host & migrate
2501
- const roomId = this.#clientRooms.get(ws.clientId);
2502
- if (roomId != null) this.#changeHostIfDisconnected(roomId, ws.clientId);
2507
+ if (ws.clientId) {
2508
+ this.#clients.delete(ws.clientId); // Immediately remove from active clients (otherwise, server would try to message this client)
2503
2509
 
2504
- if (ws.willfulDisconnect) {
2505
- this.#disconnectClient(ws); // Immediate disconnection
2506
- } else {
2507
- // Pending complete disconnection with 2s grace period to allow for reconnections
2508
- this.#pendingDisconnects.set(ws.clientId, {
2509
- timeout: setTimeout(() => {
2510
- this.#disconnectClient(ws);
2511
- }, 2000)
2512
- });
2510
+ // If client was in a room, check if they were the host & migrate
2511
+ const roomId = this.#clientRooms.get(ws.clientId);
2512
+ if (roomId != null) this.#changeHostIfDisconnected(roomId, ws.clientId);
2513
+
2514
+ if (ws.willfulDisconnect) {
2515
+ this.#disconnectClient(ws); // Immediate disconnection
2516
+ } else {
2517
+ // Pending complete disconnection with 5s grace period to allow for reconnections
2518
+ this.#pendingDisconnects.set(ws.clientId, {
2519
+ timeout: setTimeout(() => {
2520
+ this.#disconnectClient(ws);
2521
+ }, 5000)
2522
+ });
2523
+ }
2513
2524
  }
2514
2525
  }
2515
2526
 
@@ -2520,7 +2531,6 @@ function requireServer () {
2520
2531
  */
2521
2532
  #disconnectClient(ws) {
2522
2533
  this.#pendingDisconnects.delete(ws.clientId);
2523
- this.#rateLimits.delete(ws.clientId);
2524
2534
  this.#clientTokens.delete(ws.clientId);
2525
2535
  const roomId = this.#clientRooms.get(ws.clientId);
2526
2536
  const room = this.#rooms[roomId];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playsocketjs",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
4
4
  "description": "WebSocket wrapper for creating simple multiplayer systems with ease.",
5
5
  "module": "dist/playsocket-client.js",
6
6
  "exports": {