playsocketjs 1.0.6 → 1.0.7

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.
@@ -16,10 +16,12 @@ class PlaySocket {
16
16
 
17
17
  // Room properties
18
18
  #storage = {}; // Shared storage object
19
+ #storageTimestamps = {}; // Tracks update timestamps per key
19
20
  #isHost = false; // Whether this client is host
20
21
  #roomHost;
21
22
  #roomId; // ID of the host (client only)
22
23
  #connectionCount = 0;
24
+ #serverTimeOffset = 0; // Offset between server time and local time
23
25
 
24
26
  // Event handling
25
27
  #callbacks = new Map(); // Event callbacks
@@ -55,6 +57,14 @@ class PlaySocket {
55
57
  );
56
58
  }
57
59
 
60
+ /**
61
+ * Get synchronized timestamp
62
+ * @private
63
+ */
64
+ #getTimestamp() {
65
+ return Date.now() + this.#serverTimeOffset;
66
+ }
67
+
58
68
  /**
59
69
  * Register an event callback
60
70
  * @param {string} event - Event name
@@ -119,7 +129,8 @@ class PlaySocket {
119
129
  this.#sendToServer({
120
130
  type: 'register',
121
131
  id: this.#id,
122
- customData: this.#customData || undefined
132
+ customData: this.#customData || undefined,
133
+ clientTime: Date.now()
123
134
  });
124
135
  };
125
136
  });
@@ -140,6 +151,7 @@ class PlaySocket {
140
151
  // Connected to room
141
152
  if (this.#pendingJoin) {
142
153
  this.#storage = message.storage;
154
+ this.#storageTimestamps = message.storageTimestamps;
143
155
  this.#connectionCount = message.participantCount - 1;
144
156
  this.#triggerEvent("status", `Connected to room.`);
145
157
  this.#triggerEvent("storageUpdated", { ...this.#storage });
@@ -184,6 +196,9 @@ class PlaySocket {
184
196
 
185
197
  case 'registered':
186
198
  if (this.#pendingInit) {
199
+ const roundTripTime = Date.now() - message.clientTime;
200
+ const serverTimeAtClientNow = message.serverTime + (roundTripTime / 2);
201
+ this.#serverTimeOffset = serverTimeAtClientNow - Date.now();
187
202
  this.#pendingInit.resolve();
188
203
  this.#triggerEvent("status", "Connected to server.");
189
204
  this.#pendingInit = null;
@@ -191,9 +206,13 @@ class PlaySocket {
191
206
  break;
192
207
 
193
208
  case 'storage_sync':
194
- if (message.key && JSON.stringify(this.#storage[message.key]) !== JSON.stringify(message.value)) {
195
- this.#storage[message.key] = message.value;
196
- this.#triggerEvent("storageUpdated", { ...this.#storage });
209
+ if (message.key) {
210
+ const localTimestamp = this.#storageTimestamps[message.key] || 0;
211
+ if (message.timestamp > localTimestamp && JSON.stringify(this.#storage[message.key]) !== JSON.stringify(message.value)) {
212
+ this.#storage[message.key] = message.value;
213
+ this.#storageTimestamps[message.key] = message.timestamp;
214
+ this.#triggerEvent("storageUpdated", { ...this.#storage });
215
+ }
197
216
  }
198
217
  break;
199
218
 
@@ -333,12 +352,15 @@ class PlaySocket {
333
352
  */
334
353
  updateStorage(key, value) {
335
354
  if (JSON.stringify(this.#storage[key]) === JSON.stringify(value)) return; // Prevent updates without changes
355
+ const timestamp = this.#getTimestamp();
336
356
  this.#sendToServer({
337
357
  type: 'room_storage_update',
338
358
  key,
339
- value
359
+ value,
360
+ timestamp
340
361
  });
341
362
  this.#storage[key] = value;
363
+ this.#storageTimestamps[key] = timestamp;
342
364
  this.#triggerEvent("storageUpdated", { ...this.#storage });
343
365
  }
344
366
 
@@ -360,6 +382,7 @@ class PlaySocket {
360
382
  updateValue
361
383
  });
362
384
  this.#storage[key] = updatedArray;
385
+ this.#storageTimestamps[key] = this.#getTimestamp();
363
386
  this.#triggerEvent("storageUpdated", { ...this.#storage });
364
387
  }
365
388
 
@@ -416,6 +439,7 @@ class PlaySocket {
416
439
  this.#isHost = false;
417
440
  this.#roomHost = null;
418
441
  this.#storage = {};
442
+ this.#storageTimestamps = {};
419
443
  this.#roomId = null;
420
444
  this.#connectionCount = 0;
421
445
  clearTimeout(this.#reconnectTimeout);
@@ -98,7 +98,7 @@ function requireServer () {
98
98
  }
99
99
  ws.clientId = data.id; // Adds the provided id to the ws object as clientId
100
100
  this.#clients.set(data.id, ws);
101
- ws.send(JSON.stringify({ type: 'registered' }));
101
+ ws.send(JSON.stringify({ type: 'registered', serverTime: Date.now(), clientTime: data.clientTime }));
102
102
  this.#triggerEvent("clientRegistered", data.id, data.customData);
103
103
  break;
104
104
 
@@ -117,6 +117,7 @@ function requireServer () {
117
117
  participants: [ws.clientId],
118
118
  maxSize: data.size || null,
119
119
  storage: data.storage || {},
120
+ storageTimestamps: {},
120
121
  };
121
122
 
122
123
  this.#clientRooms.set(ws.clientId, newRoomId);
@@ -148,6 +149,7 @@ function requireServer () {
148
149
  ws.send(JSON.stringify({
149
150
  type: 'connection_accepted',
150
151
  storage: room.storage,
152
+ storageTimestamps: room.storageTimestamps,
151
153
  participantCount: room.participants.length
152
154
  }));
153
155
 
@@ -169,9 +171,13 @@ function requireServer () {
169
171
  case 'room_storage_update':
170
172
  const updateRoomId = this.#clientRooms.get(ws.clientId);
171
173
  const updateRoom = updateRoomId ? this.#rooms[updateRoomId] : null;
172
- if (updateRoom && data.key && JSON.stringify(updateRoom.storage[data.key]) !== JSON.stringify(data.value)) {
173
- updateRoom.storage[data.key] = data.value;
174
- this.#syncRoomStorageKey(updateRoomId, data.key);
174
+ if (updateRoom && data.key) {
175
+ const existingTimestamp = updateRoom.storageTimestamps[data.key] || 0;
176
+ if (data.timestamp > existingTimestamp && JSON.stringify(updateRoom.storage[data.key]) !== JSON.stringify(data.value)) {
177
+ updateRoom.storage[data.key] = data.value;
178
+ updateRoom.storageTimestamps[data.key] = data.timestamp;
179
+ this.#syncRoomStorageKey(updateRoomId, data.key, data.timestamp);
180
+ }
175
181
  }
176
182
  break;
177
183
 
@@ -182,7 +188,8 @@ function requireServer () {
182
188
  const updatedArray = this.#arrayUpdate(arrayRoom.storage, data.key, data.operation, data.value, data.updateValue);
183
189
  if (JSON.stringify(arrayRoom.storage[data.key]) !== JSON.stringify(updatedArray)) {
184
190
  arrayRoom.storage[data.key] = updatedArray;
185
- this.#syncRoomStorageKey(arrayRoomId, data.key);
191
+ arrayRoom.storageTimestamps[data.key] = Date.now(); // New date to ensure that they are applied regardless of age (they don't overwrite – they transform)
192
+ this.#syncRoomStorageKey(arrayRoomId, data.key, Date.now());
186
193
  }
187
194
  }
188
195
  break;
@@ -224,7 +231,7 @@ function requireServer () {
224
231
  * Sync storage key to all clients in room
225
232
  * @private
226
233
  */
227
- #syncRoomStorageKey(roomId, key) {
234
+ #syncRoomStorageKey(roomId, key, timestamp) {
228
235
  const room = this.#rooms[roomId];
229
236
  if (!room) return;
230
237
  room.participants.forEach(p => {
@@ -233,7 +240,8 @@ function requireServer () {
233
240
  client.send(JSON.stringify({
234
241
  type: 'storage_sync',
235
242
  key,
236
- value: room.storage[key]
243
+ value: room.storage[key],
244
+ timestamp
237
245
  }));
238
246
  }
239
247
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playsocketjs",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "WebSocket wrapper for creating simple multiplayer systems with ease.",
5
5
  "module": "dist/playsocket-client.js",
6
6
  "exports": {