@signe/room 2.6.0 → 2.7.1
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/dist/index.d.ts +5 -0
- package/dist/index.js +28 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +70 -0
- package/src/interfaces.ts +5 -0
- package/src/server.ts +62 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signe/room",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dset": "^3.1.3",
|
|
18
18
|
"partysocket": "^1.0.1",
|
|
19
19
|
"zod": "^3.23.8",
|
|
20
|
-
"@signe/sync": "2.
|
|
20
|
+
"@signe/sync": "2.7.1"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
package/readme.md
CHANGED
|
@@ -246,6 +246,76 @@ class GameRoom {
|
|
|
246
246
|
}
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
+
### Manual Synchronization
|
|
250
|
+
|
|
251
|
+
By default, state changes are automatically synchronized to all clients. However, you can disable automatic synchronization and manually control when to broadcast changes using the `autoSync` option and the `$applySync` method.
|
|
252
|
+
|
|
253
|
+
This is useful when you want to:
|
|
254
|
+
- Batch multiple state changes before broadcasting
|
|
255
|
+
- Control synchronization timing for performance optimization
|
|
256
|
+
- Implement custom synchronization logic
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
@Room({
|
|
260
|
+
path: "game"
|
|
261
|
+
})
|
|
262
|
+
class GameRoom {
|
|
263
|
+
autoSync = false; // Disable automatic synchronization
|
|
264
|
+
|
|
265
|
+
@sync() count = signal(0);
|
|
266
|
+
@sync() score = signal(0);
|
|
267
|
+
@sync() level = signal(1);
|
|
268
|
+
|
|
269
|
+
@Action("updateGameState")
|
|
270
|
+
updateGameState(player: Player, data: { count: number, score: number, level: number }) {
|
|
271
|
+
// Make multiple changes without triggering sync
|
|
272
|
+
this.count.set(data.count);
|
|
273
|
+
this.score.set(data.score);
|
|
274
|
+
this.level.set(data.level);
|
|
275
|
+
|
|
276
|
+
// Manually trigger synchronization when ready
|
|
277
|
+
this.$applySync();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@Action("batchUpdate")
|
|
281
|
+
batchUpdate(player: Player, updates: any[]) {
|
|
282
|
+
// Apply multiple updates
|
|
283
|
+
updates.forEach(update => {
|
|
284
|
+
// ... apply updates
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Sync all changes at once
|
|
288
|
+
this.$applySync();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
You can also toggle `autoSync` at runtime:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
class GameRoom {
|
|
297
|
+
@sync() count = signal(0);
|
|
298
|
+
|
|
299
|
+
@Action("startBatchMode")
|
|
300
|
+
startBatchMode() {
|
|
301
|
+
this.$autoSync = false; // Disable auto sync
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@Action("endBatchMode")
|
|
305
|
+
endBatchMode() {
|
|
306
|
+
this.$applySync(); // Sync pending changes
|
|
307
|
+
this.$autoSync = true; // Re-enable auto sync
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Instance Properties:**
|
|
313
|
+
- `$autoSync` (boolean): Controls whether synchronization happens automatically (default: `true`)
|
|
314
|
+
- `$pendingSync` (Map): Stores pending synchronization changes when `autoSync` is disabled
|
|
315
|
+
- `$applySync()` (method): Manually broadcasts all pending changes to all clients
|
|
316
|
+
|
|
317
|
+
**Note:** When `autoSync` is disabled, changes are stored in `$pendingSync` until you call `$applySync()`. If you call `$applySync()` with no pending changes, it will broadcast the current state from `$memoryAll`, which is useful for forcing a full state synchronization.
|
|
318
|
+
|
|
249
319
|
### Connecting to World Service
|
|
250
320
|
|
|
251
321
|
The World Service provides optimal room and shard assignment for distributed applications. It handles load balancing and allows clients to connect to the most appropriate server.
|
package/src/interfaces.ts
CHANGED
|
@@ -15,4 +15,9 @@ export interface RoomOnLeave {
|
|
|
15
15
|
export interface RoomMethods {
|
|
16
16
|
$send: (conn: Party.Connection, obj: any) => void;
|
|
17
17
|
$broadcast: (obj: any) => void;
|
|
18
|
+
$applySync: () => void;
|
|
19
|
+
$sessionTransfer: (conn: Party.Connection, targetRoomId: string) => Promise<string | null>;
|
|
20
|
+
$pendingSync: Map<string, any>;
|
|
21
|
+
$memoryAll: Map<string, any>;
|
|
22
|
+
$autoSync: boolean;
|
|
18
23
|
}
|
package/src/server.ts
CHANGED
|
@@ -246,12 +246,57 @@ export class Server implements Party.Server {
|
|
|
246
246
|
};
|
|
247
247
|
|
|
248
248
|
instance.$memoryAll = {}
|
|
249
|
+
instance.$autoSync = instance["autoSync"] !== false; // Default to true
|
|
250
|
+
instance.$pendingSync = new Map<string, any>();
|
|
249
251
|
instance.$send = (conn: Party.Connection, obj: any) => {
|
|
250
252
|
return this.send(conn, obj, instance)
|
|
251
253
|
}
|
|
252
254
|
instance.$broadcast = (obj: any) => {
|
|
253
255
|
return this.broadcast(obj, instance)
|
|
254
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Applies pending synchronization changes by broadcasting them to all clients.
|
|
259
|
+
* This method is useful when autoSync is disabled and you want to manually trigger synchronization.
|
|
260
|
+
*
|
|
261
|
+
* @method $applySync
|
|
262
|
+
* @description Broadcasts all pending synchronization changes and clears the pending queue.
|
|
263
|
+
* If there are pending changes, they are merged with $memoryAll and broadcast. If there are no
|
|
264
|
+
* pending changes, it broadcasts the current state from $memoryAll (useful for forcing a full sync).
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* // Disable auto sync
|
|
269
|
+
* instance.$autoSync = false;
|
|
270
|
+
*
|
|
271
|
+
* // Make some changes
|
|
272
|
+
* instance.count.set(10);
|
|
273
|
+
* instance.text.set('hello');
|
|
274
|
+
*
|
|
275
|
+
* // Manually apply sync when ready
|
|
276
|
+
* instance.$applySync();
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
instance.$applySync = () => {
|
|
280
|
+
let packet: any;
|
|
281
|
+
if (instance.$pendingSync.size > 0) {
|
|
282
|
+
if (options.getMemoryAll) {
|
|
283
|
+
buildObject(instance.$pendingSync, instance.$memoryAll);
|
|
284
|
+
}
|
|
285
|
+
packet = buildObject(instance.$pendingSync, instance.$memoryAll);
|
|
286
|
+
instance.$pendingSync.clear();
|
|
287
|
+
} else {
|
|
288
|
+
// No pending changes, broadcast current state from memory
|
|
289
|
+
packet = instance.$memoryAll;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this.broadcast(
|
|
293
|
+
{
|
|
294
|
+
type: "sync",
|
|
295
|
+
value: packet,
|
|
296
|
+
},
|
|
297
|
+
instance
|
|
298
|
+
);
|
|
299
|
+
}
|
|
255
300
|
instance.$sessionTransfer = async (conn: Party.Connection, targetRoomId: string) => {
|
|
256
301
|
let user: any;
|
|
257
302
|
|
|
@@ -326,15 +371,28 @@ export class Server implements Party.Server {
|
|
|
326
371
|
}
|
|
327
372
|
};
|
|
328
373
|
|
|
329
|
-
// Sync callback: Broadcast changes to all clients
|
|
374
|
+
// Sync callback: Broadcast changes to all clients or store them for manual sync
|
|
330
375
|
const syncCb = (values) => {
|
|
331
376
|
if (options.getMemoryAll) {
|
|
332
377
|
buildObject(values, instance.$memoryAll);
|
|
333
378
|
}
|
|
379
|
+
// During initialization in hibernate mode, skip entirely
|
|
334
380
|
if (init && this.isHibernate) {
|
|
335
381
|
init = false;
|
|
336
382
|
return;
|
|
337
383
|
}
|
|
384
|
+
|
|
385
|
+
// If autoSync is disabled, store changes in pendingSync instead of broadcasting
|
|
386
|
+
if (!instance.$autoSync) {
|
|
387
|
+
// Merge pending changes into $pendingSync
|
|
388
|
+
for (const [path, value] of values) {
|
|
389
|
+
instance.$pendingSync.set(path, value);
|
|
390
|
+
}
|
|
391
|
+
values.clear();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Auto sync: broadcast immediately (even during init if autoSync is enabled)
|
|
338
396
|
const packet = buildObject(values, instance.$memoryAll);
|
|
339
397
|
this.broadcast(
|
|
340
398
|
{
|
|
@@ -367,13 +425,14 @@ export class Server implements Party.Server {
|
|
|
367
425
|
|
|
368
426
|
// Set up syncing and persistence with throttling to optimize performance
|
|
369
427
|
syncClass(instance, {
|
|
370
|
-
onSync: throttle(syncCb, instance["throttleSync"]
|
|
371
|
-
onPersist: throttle(persistCb, instance["throttleStorage"]
|
|
428
|
+
onSync: instance["throttleSync"] ? throttle(syncCb, instance["throttleSync"]) : syncCb,
|
|
429
|
+
onPersist: instance["throttleStorage"] ? throttle(persistCb, instance["throttleStorage"]) : persistCb,
|
|
372
430
|
});
|
|
373
431
|
|
|
374
432
|
await loadMemory();
|
|
375
433
|
|
|
376
434
|
initPersist = false
|
|
435
|
+
init = false; // Allow syncs after initialization is complete
|
|
377
436
|
|
|
378
437
|
return instance
|
|
379
438
|
}
|