playsocketjs 1.1.12 → 1.2.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 CHANGED
@@ -63,10 +63,11 @@ socket.destroy();
63
63
  ### Constructor
64
64
 
65
65
  ```javascript
66
- new PlaySocket(id: string, options: PlaySocketOptions)
66
+ new PlaySocket(id?: string, options: PlaySocketOptions)
67
67
  ```
68
68
 
69
69
  Creates a new PlaySocket instance with a specified ID and configuration options.
70
+ Note: With PlaySocket, the id can be set to `null` to let the server pick a unique one.
70
71
 
71
72
  #### Configuration options
72
73
  - `endpoint`: WebSocket server endpoint (e.g., 'wss://example.com/socket')
@@ -77,7 +78,7 @@ Creates a new PlaySocket instance with a specified ID and configuration options.
77
78
 
78
79
  #### Core
79
80
 
80
- - `init()`: Initialize the WebSocket connection – Returns Promise (async)
81
+ - `init()`: Initialize the WebSocket connection – Returns Promise (async) which resolves with the client's id
81
82
  - `createRoom(initialStorage?: object, maxSize?: number)`: Create a new room and become host – Returns Promise (async) which resolves with the room id (matches the creator's id)
82
83
  - `joinRoom(hostId: string)`: Join an existing room – Returns Promise (async)
83
84
  - `destroy()`: Use this to leave a room and close the connection
@@ -2061,7 +2061,7 @@ class PlaySocket {
2061
2061
 
2062
2062
  // Room properties
2063
2063
  #roomHost;
2064
- #roomId; // ID of the host (client only)
2064
+ #inRoom; // If the client is currently in a room or not
2065
2065
  #connectionCount = 0;
2066
2066
  #crdtManager;
2067
2067
  #roomVersion = 0; // Update version (used to compare local vs. remote state to detect package loss)
@@ -2143,15 +2143,14 @@ class PlaySocket {
2143
2143
  async init() {
2144
2144
  if (this.#initialized) return Promise.reject(new Error("Already initialized"));
2145
2145
  if (!this.#endpoint) return Promise.reject(new Error("No websocket endpoint provided"));
2146
- if (!this.#id) return Promise.reject(new Error("No id provided"));
2147
2146
  this.#triggerEvent("status", "Initializing...");
2148
2147
 
2149
2148
  // Connect to WS server
2150
2149
  await this.#connect();
2151
2150
 
2152
2151
  // Register with server
2153
- await Promise.race([
2154
- new Promise(async (resolve, reject) => {
2152
+ const id = await Promise.race([
2153
+ new Promise((resolve, reject) => {
2155
2154
  this.#pendingRegistration = { resolve, reject }; // Resolve or reject depending on answer to registration msg
2156
2155
 
2157
2156
  // Register with server
@@ -2165,6 +2164,10 @@ class PlaySocket {
2165
2164
  ]).finally(() => {
2166
2165
  this.#pendingRegistration = null;
2167
2166
  });
2167
+
2168
+ // If the server sent out an id, store and return that
2169
+ if (id) this.#id = id;
2170
+ return this.#id;
2168
2171
  }
2169
2172
 
2170
2173
  /**
@@ -2202,6 +2205,7 @@ class PlaySocket {
2202
2205
  // Connected to room
2203
2206
  if (this.#pendingJoin) {
2204
2207
  if (this.#debug) console.log(LOG_PREFIX + "State received for join:", message.state);
2208
+ this.#inRoom = true;
2205
2209
  this.#crdtManager.importState(message.state);
2206
2210
  this.#connectionCount = message.participantCount - 1; // Counted without the user themselves
2207
2211
  this.#roomHost = message.host;
@@ -2232,7 +2236,7 @@ class PlaySocket {
2232
2236
  this.#connectionCount = message.roomData.participantCount - 1; // Counted without the user themselves
2233
2237
  this.#setHost(message.roomData.host); // Set host before in case there are .isHost checks in the storageUpdate fallback
2234
2238
  this.#triggerEvent("storageUpdated", this.getStorage);
2235
- } else if (this.#roomId) {
2239
+ } else if (this.#inRoom) {
2236
2240
  // If no room data received, but client thinks they were in a room...
2237
2241
  this.#triggerEvent("error", "Reconnected, but room no longer exists.");
2238
2242
  return this.destroy();
@@ -2248,8 +2252,9 @@ class PlaySocket {
2248
2252
 
2249
2253
  case 'room_created':
2250
2254
  if (this.#pendingCreate) {
2255
+ this.#inRoom = true;
2251
2256
  this.#triggerEvent("status", `Room created${this.#pendingCreate.maxSize ? ` with max size ${this.#pendingCreate.maxSize}.` : '.'}`);
2252
- this.#pendingCreate.resolve(this.#roomId);
2257
+ this.#pendingCreate.resolve(this.#pendingCreate.id);
2253
2258
  }
2254
2259
  break;
2255
2260
 
@@ -2271,7 +2276,7 @@ class PlaySocket {
2271
2276
  if (this.#pendingRegistration) {
2272
2277
  this.#sessionToken = message.sessionToken;
2273
2278
  this.#initialized = true;
2274
- this.#pendingRegistration.resolve();
2279
+ this.#pendingRegistration.resolve(message.id);
2275
2280
  this.#triggerEvent("status", "Connected to server.");
2276
2281
  }
2277
2282
  break;
@@ -2408,9 +2413,8 @@ class PlaySocket {
2408
2413
  Object.entries(initialStorage)?.forEach(([key, value]) => {
2409
2414
  this.#crdtManager.updateProperty(key, "set", value);
2410
2415
  });
2411
- this.#roomId = this.#id; // Create room with your own ID as the room ID to mimic p2p
2412
2416
  this.#roomHost = this.#id;
2413
- this.#pendingCreate = { maxSize, resolve, reject };
2417
+ this.#pendingCreate = { maxSize, id: this.#id, resolve, reject };
2414
2418
  this.#sendToServer({
2415
2419
  type: 'create_room',
2416
2420
  state: this.#crdtManager.getState,
@@ -2437,7 +2441,6 @@ class PlaySocket {
2437
2441
 
2438
2442
  return Promise.race([
2439
2443
  new Promise((resolve, reject) => {
2440
- this.#roomId = roomId;
2441
2444
  this.#pendingJoin = { resolve, reject };
2442
2445
 
2443
2446
  // Send connection request
@@ -2508,7 +2511,7 @@ class PlaySocket {
2508
2511
  // Reset state
2509
2512
  clearTimeout(this.#reconnectTimeout);
2510
2513
  this.#roomHost = null;
2511
- this.#roomId = null;
2514
+ this.#inRoom = false;
2512
2515
  this.#connectionCount = 0;
2513
2516
  this.#isReconnecting = false;
2514
2517
  this.#reconnectCount = 0;
@@ -2273,17 +2273,35 @@ function requireServer () {
2273
2273
 
2274
2274
  switch (data.type) {
2275
2275
  case 'register':
2276
- // Register client ID
2277
- if (!data.id) return;
2278
- if (this.#clients.get(data.id)) {
2276
+ // Register client ID if provided & check for a duplicate
2277
+ if (data.id && this.#clients.get(data.id)) {
2279
2278
  ws.send(encode({ type: 'id_taken' }), { binary: true });
2280
2279
  return;
2281
2280
  }
2281
+
2282
+ // Generate client ID if none provided
2283
+ if (!data.id) {
2284
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
2285
+ const maxAttempts = 50;
2286
+ for (let i = 0; i < maxAttempts; i++) {
2287
+ const id = Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
2288
+ if (!this.#clients.get(id)) {
2289
+ data.id = id;
2290
+ break;
2291
+ }
2292
+ }
2293
+ if (!data.id) {
2294
+ ws.send(encode({ type: 'id_taken' }), { binary: true });
2295
+ throw new Error('Failed to generate unique ID!');
2296
+ }
2297
+ }
2298
+
2299
+
2282
2300
  ws.clientId = data.id; // Adds the provided id to the ws object as clientId
2283
2301
  this.#clients.set(data.id, ws);
2284
2302
  const sessionToken = this.#generateSessionToken();
2285
2303
  this.#clientTokens.set(data.id, sessionToken); // Token is used when reconnecting to prevent impersonation attacks
2286
- ws.send(encode({ type: 'registered', sessionToken }), { binary: true });
2304
+ ws.send(encode({ type: 'registered', id: data.id, sessionToken }), { binary: true });
2287
2305
  this.#triggerEvent("clientRegistered", data.id, data.customData);
2288
2306
  break;
2289
2307
 
@@ -2432,7 +2450,7 @@ function requireServer () {
2432
2450
  break;
2433
2451
  }
2434
2452
  } catch (error) {
2435
- console.error('Error processing message:', error);
2453
+ console.error('Error in message handler:', error);
2436
2454
  }
2437
2455
  }
2438
2456
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playsocketjs",
3
- "version": "1.1.12",
3
+ "version": "1.2.0",
4
4
  "description": "WebSocket wrapper for creating simple multiplayer systems with ease.",
5
5
  "module": "dist/playsocket-client.js",
6
6
  "exports": {