svelte-realtime 0.6.0-next.77 → 0.6.0-next.78
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/package.json +1 -1
- package/src/server/smooth.js +22 -17
package/package.json
CHANGED
package/src/server/smooth.js
CHANGED
|
@@ -1096,17 +1096,18 @@ function _smoothJoinSnapshot(rec, identity) {
|
|
|
1096
1096
|
* `moved` carries the entities that changed this tick, each with the author identity
|
|
1097
1097
|
* to suppress for that frame (echo suppression): on the owner that is the commanded
|
|
1098
1098
|
* author under noEcho, on the non-owner it is the exclude identity the owner stamped
|
|
1099
|
-
* onto the relay. An entity in range that did NOT move is delivered
|
|
1100
|
-
* (first-sight catch-up
|
|
1099
|
+
* onto the relay. An entity in range that did NOT move is delivered via `lookup`
|
|
1100
|
+
* (first-sight catch-up: the owner reads the authority, the non-owner its shadow),
|
|
1101
|
+
* and a subscriber's own entity is never caught up to itself
|
|
1101
1102
|
* under noEcho. Only a remote frame advances the send-cadence estimator (a client
|
|
1102
1103
|
* discards its own entity frame before measuring its interpolation delay).
|
|
1103
1104
|
* @param {any} rec
|
|
1104
1105
|
* @param {Map<string, Set<string>>} relevancy identity -> entity keys to deliver
|
|
1105
|
-
* @param {
|
|
1106
|
+
* @param {(key: string) => any} lookup current per-entity state, for catch-up
|
|
1106
1107
|
* @param {Map<string, { state: any, exclude: string | undefined }>} moved entities that changed this tick
|
|
1107
1108
|
* @param {number} t wall stamp for the send-cadence estimator (hitTest only)
|
|
1108
1109
|
*/
|
|
1109
|
-
function _smoothDeliverCulled(rec, relevancy,
|
|
1110
|
+
function _smoothDeliverCulled(rec, relevancy, lookup, moved, t) {
|
|
1110
1111
|
for (const [identity, ws] of rec.registry) {
|
|
1111
1112
|
const relSet = relevancy.get(identity);
|
|
1112
1113
|
if (relSet === undefined) continue;
|
|
@@ -1123,7 +1124,7 @@ function _smoothDeliverCulled(rec, relevancy, catalogByKey, moved, t) {
|
|
|
1123
1124
|
exclude = m.exclude;
|
|
1124
1125
|
} else {
|
|
1125
1126
|
// First-sight catch-up: in range, no update of its own this tick.
|
|
1126
|
-
state =
|
|
1127
|
+
state = lookup(key);
|
|
1127
1128
|
exclude = rec.noEcho ? key : undefined;
|
|
1128
1129
|
}
|
|
1129
1130
|
if (exclude !== undefined && identity === exclude) continue;
|
|
@@ -1169,7 +1170,7 @@ function _smoothCullTick(rec) {
|
|
|
1169
1170
|
const catalog = [];
|
|
1170
1171
|
for (const [key, state] of rec.shadow) catalog.push({ key, state });
|
|
1171
1172
|
const relevancy = rec.interest.compute(catalog, rec.registry.keys(), rec.interestTick++, rec.budgetOf ?? undefined);
|
|
1172
|
-
_smoothDeliverCulled(rec, relevancy, rec.shadow, rec.pendingRelay, t);
|
|
1173
|
+
_smoothDeliverCulled(rec, relevancy, (key) => rec.shadow.get(key), rec.pendingRelay, t);
|
|
1173
1174
|
rec.pendingRelay.clear();
|
|
1174
1175
|
rec.shadowDirty = false;
|
|
1175
1176
|
rec.interestDirty = false;
|
|
@@ -1309,8 +1310,9 @@ function _smoothTick(rec) {
|
|
|
1309
1310
|
rec.interestDirty = false;
|
|
1310
1311
|
// Broadcast updates collect here and flush as ONE batched fan-out after the
|
|
1311
1312
|
// loop (per-entry author exclusion rides along); the relay and the cells
|
|
1312
|
-
// routing stay per-entity inside the loop.
|
|
1313
|
-
|
|
1313
|
+
// routing stay per-entity inside the loop. Allocated on first use: the
|
|
1314
|
+
// interest and cells paths never push (their delivery runs elsewhere).
|
|
1315
|
+
let broadcastUpdates = null;
|
|
1314
1316
|
for (let i = 0; i < wireUpdates.length; i++) {
|
|
1315
1317
|
const u = wireUpdates[i];
|
|
1316
1318
|
// Cells mode: route each changed entity to its cell topic (native shared
|
|
@@ -1343,7 +1345,7 @@ function _smoothTick(rec) {
|
|
|
1343
1345
|
// own subscribers all see the move).
|
|
1344
1346
|
if (!relevancy) {
|
|
1345
1347
|
const localAuthor = rec.noEcho && u.commanded ? rec.registry.get(u.key) : undefined;
|
|
1346
|
-
broadcastUpdates.push({ key: u.key, state: u.state, excludeWs: localAuthor });
|
|
1348
|
+
(broadcastUpdates ??= []).push({ key: u.key, state: u.state, excludeWs: localAuthor });
|
|
1347
1349
|
}
|
|
1348
1350
|
// Relay to other instances. Only a remote (surrogate) author needs a
|
|
1349
1351
|
// cross-instance exclude - a local author is already excluded (below, or
|
|
@@ -1357,13 +1359,13 @@ function _smoothTick(rec) {
|
|
|
1357
1359
|
// Interest-on: updates fan out per subscriber after this loop (the
|
|
1358
1360
|
// relevancy walk below), so nothing broadcasts here.
|
|
1359
1361
|
} else {
|
|
1360
|
-
broadcastUpdates.push({ key: u.key, state: u.state, excludeWs: rec.noEcho && u.commanded ? u.ws : undefined });
|
|
1362
|
+
(broadcastUpdates ??= []).push({ key: u.key, state: u.state, excludeWs: rec.noEcho && u.commanded ? u.ws : undefined });
|
|
1361
1363
|
}
|
|
1362
1364
|
}
|
|
1363
1365
|
// One tick, one fan-out: every collected update in a single batched frame
|
|
1364
1366
|
// per capable viewer (per-entity on an older platform). Acks, events, and
|
|
1365
1367
|
// removals keep their own frames below, exactly as before.
|
|
1366
|
-
_smoothPublishUpdates(rec, broadcastUpdates);
|
|
1368
|
+
if (broadcastUpdates !== null) _smoothPublishUpdates(rec, broadcastUpdates);
|
|
1367
1369
|
// Interest-on: deliver each LOCAL subscriber (single-instance, or the owner
|
|
1368
1370
|
// side of a cluster) the CURRENT state of every entity the relevancy pass marked
|
|
1369
1371
|
// for it this tick - the entities inside its area of interest that changed since
|
|
@@ -1377,19 +1379,22 @@ function _smoothTick(rec) {
|
|
|
1377
1379
|
// the cursor viewport cull does. Events and removals stay on the shared broadcast
|
|
1378
1380
|
// path (over-deliver rather than risk a ghost or a dropped one-shot).
|
|
1379
1381
|
if (relevancy) {
|
|
1380
|
-
const catalogByKey = new Map();
|
|
1381
|
-
for (let i = 0; i < catalog.length; i++) catalogByKey.set(catalog[i].key, catalog[i].state);
|
|
1382
1382
|
// `moved` carries each entity that changed this tick with the author to suppress
|
|
1383
1383
|
// for that frame (the commanded author under noEcho). The shared delivery helper
|
|
1384
|
-
// catches up in-range entities that did not move from
|
|
1385
|
-
//
|
|
1386
|
-
//
|
|
1384
|
+
// catches up in-range entities that did not move straight from the authority
|
|
1385
|
+
// (`get` is O(1) and hands back the same state object the catalog carried -
|
|
1386
|
+
// nothing mutates entries between the post-drain snapshot and this delivery).
|
|
1387
|
+
// Only a remote frame advances the lag-comp send-cadence estimator (the client
|
|
1388
|
+
// discards its own entity frame before measuring its interpolation delay).
|
|
1387
1389
|
const moved = new Map();
|
|
1388
1390
|
for (let i = 0; i < wireUpdates.length; i++) {
|
|
1389
1391
|
const u = wireUpdates[i];
|
|
1390
1392
|
moved.set(u.key, { state: u.state, exclude: rec.noEcho && u.commanded ? u.key : undefined });
|
|
1391
1393
|
}
|
|
1392
|
-
_smoothDeliverCulled(rec, relevancy,
|
|
1394
|
+
_smoothDeliverCulled(rec, relevancy, (key) => {
|
|
1395
|
+
const e = rec.authority.get(key);
|
|
1396
|
+
return e === undefined ? undefined : e.state;
|
|
1397
|
+
}, moved, t);
|
|
1393
1398
|
}
|
|
1394
1399
|
for (let i = 0; i < acks.length; i++) {
|
|
1395
1400
|
const a = acks[i];
|