@3dsource/angular-unreal-module 0.0.146-dev.4 → 0.0.146-dev.6
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.
|
@@ -1670,6 +1670,10 @@ class RegionsPingService {
|
|
|
1670
1670
|
* entry is well-formed and still within {@link REGION_PING_CACHE_TTL_MS}.
|
|
1671
1671
|
* All access is guarded — `localStorage` can throw (private mode, disabled
|
|
1672
1672
|
* storage) or be absent (non-browser).
|
|
1673
|
+
*
|
|
1674
|
+
* Public so adopted connections (which skip `getFastest`) can replay the
|
|
1675
|
+
* region-resolving telemetry phase from the same cached winner — see
|
|
1676
|
+
* `UnrealEffects.regionTelemetryOnAdopt$`.
|
|
1673
1677
|
*/
|
|
1674
1678
|
readCachedWinner() {
|
|
1675
1679
|
try {
|
|
@@ -3598,26 +3602,41 @@ class StreamAdoptionService {
|
|
|
3598
3602
|
*
|
|
3599
3603
|
* - nothing parked → emit `false` at once (caller connects normally).
|
|
3600
3604
|
* - already settled (`ready`/`failed`/`adopted`) → adopt-if-ready at once.
|
|
3601
|
-
* - still `connecting` → poll until
|
|
3602
|
-
*
|
|
3605
|
+
* - still `connecting` → poll until the script settles it, then adopt-if-ready.
|
|
3606
|
+
*
|
|
3607
|
+
* Deliberately NO time budget: for DIRECT orchestration the prefetch holds
|
|
3608
|
+
* the socket open while the orchestrator provisions a machine, which can take
|
|
3609
|
+
* minutes (150–200s+). A fixed deadline would make Angular give up and open a
|
|
3610
|
+
* second connection mid-provisioning — exactly the race/orphan this avoids.
|
|
3611
|
+
* The wait is bounded by liveness instead: the script flips the parked state
|
|
3612
|
+
* to `failed` on a dead socket (`ws.onclose`/`onerror`), and the caller wraps
|
|
3613
|
+
* this in `takeUntil(abortEstablishingConnection)`, so a route change / teardown
|
|
3614
|
+
* cancels it. Emits exactly once. Cancelling runs no adoption — which is what
|
|
3615
|
+
* stops a fast route-switch from starting overlapping connections.
|
|
3603
3616
|
*
|
|
3604
|
-
*
|
|
3605
|
-
*
|
|
3606
|
-
*
|
|
3617
|
+
* Wrapped in `defer` so the `tryAdopt()` side effect (which dispatches
|
|
3618
|
+
* `streamAdopted`) runs at SUBSCRIPTION time, not when the observable is
|
|
3619
|
+
* built. The effect prepends `startWith(setEstablishingConnection(true))`,
|
|
3620
|
+
* which starts the telemetry session and subscribes its phase catalog; that
|
|
3621
|
+
* must happen BEFORE `streamAdopted` → `regionTelemetryOnAdopt$` replays
|
|
3622
|
+
* `regionResolved`, or the region phase fires into a session that doesn't
|
|
3623
|
+
* exist yet and is lost. `defer` guarantees that ordering.
|
|
3607
3624
|
*/
|
|
3608
|
-
awaitAndAdopt$(
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3625
|
+
awaitAndAdopt$() {
|
|
3626
|
+
return defer(() => {
|
|
3627
|
+
const parked = window.__stream;
|
|
3628
|
+
if (!parked) {
|
|
3629
|
+
return of(false);
|
|
3630
|
+
}
|
|
3631
|
+
if (parked.state !== 'connecting') {
|
|
3632
|
+
return of(this.tryAdopt());
|
|
3633
|
+
}
|
|
3634
|
+
Logger.info('[Adoption] Prefetch in progress — waiting before connecting');
|
|
3635
|
+
return timer(0, ADOPT_POLL_MS).pipe(map$1(() => window.__stream?.state ?? 'failed'),
|
|
3636
|
+
// Keep polling while still connecting; the `true` (inclusive) emits the
|
|
3637
|
+
// settling value (`ready`/`failed`) that ends the wait.
|
|
3638
|
+
takeWhile((state) => state === 'connecting', true), last(), map$1(() => this.tryAdopt()));
|
|
3639
|
+
});
|
|
3621
3640
|
}
|
|
3622
3641
|
/**
|
|
3623
3642
|
* @returns `true` if a ready parked stream was adopted (caller must NOT
|
|
@@ -3638,7 +3657,7 @@ class StreamAdoptionService {
|
|
|
3638
3657
|
return false;
|
|
3639
3658
|
}
|
|
3640
3659
|
Logger.info('[Adoption] Adopting prefetched stream', parked.timings);
|
|
3641
|
-
//
|
|
3660
|
+
// Clear the script's timers and mark the handoff complete.
|
|
3642
3661
|
parked.adopt();
|
|
3643
3662
|
// Flag cirrus connected (so a later `initSignalling` won't open a 2nd
|
|
3644
3663
|
// connection), record the adopted machine, and hydrate the streamer info
|
|
@@ -4197,9 +4216,7 @@ class UnrealEffects {
|
|
|
4197
4216
|
// connection; otherwise fall back to a fresh signalling connection.
|
|
4198
4217
|
// `takeUntil(abort)` cancels the wait on teardown / route change so a
|
|
4199
4218
|
// fast switch can't leave overlapping connections.
|
|
4200
|
-
this.streamAdoptionService
|
|
4201
|
-
.awaitAndAdopt$(this.dataChannelConnectionTimeout)
|
|
4202
|
-
.pipe(switchMap((adopted) => adopted ? EMPTY : this.signallingService.connectToSignaling(urls)), takeUntil(this.actions$.pipe(ofType(abortEstablishingConnection))),
|
|
4219
|
+
this.streamAdoptionService.awaitAndAdopt$().pipe(switchMap((adopted) => adopted ? EMPTY : this.signallingService.connectToSignaling(urls)), takeUntil(this.actions$.pipe(ofType(abortEstablishingConnection))),
|
|
4203
4220
|
// The adopt/connect stream is side-effect-only (its values aren't
|
|
4204
4221
|
// actions) — swallow them, and emit `setEstablishingConnection`
|
|
4205
4222
|
// first so the reconnect/resume popup stays hidden during the
|
|
@@ -4207,6 +4224,24 @@ class UnrealEffects {
|
|
|
4207
4224
|
// defers `connectToSignaling`, which is what normally sets this.
|
|
4208
4225
|
ignoreElements(), startWith(setEstablishingConnection({ value: true })))));
|
|
4209
4226
|
});
|
|
4227
|
+
// Adoption skips `connectToSignaling → getRegion`, so the region-resolving
|
|
4228
|
+
// telemetry phase never fires. Replay the region waterfall (start → end →
|
|
4229
|
+
// resolved) from the cached winner the prefetch reused — a near-zero-length
|
|
4230
|
+
// phase that still carries the ping samples. No live ping sweep; if there is
|
|
4231
|
+
// no cache the resolved event just carries empty values.
|
|
4232
|
+
this.regionTelemetryOnAdopt$ = createEffect(() => {
|
|
4233
|
+
return this.actions$.pipe(ofType(streamAdopted), mergeMap(() => {
|
|
4234
|
+
const cached = this.regionsPingService.readCachedWinner();
|
|
4235
|
+
return [
|
|
4236
|
+
regionPingFetchStart(),
|
|
4237
|
+
regionPingFetchEnd(),
|
|
4238
|
+
regionResolved({
|
|
4239
|
+
region: cached?.region_code ?? '',
|
|
4240
|
+
pingsMs: cached?.result ?? [],
|
|
4241
|
+
}),
|
|
4242
|
+
];
|
|
4243
|
+
}));
|
|
4244
|
+
});
|
|
4210
4245
|
}
|
|
4211
4246
|
canPeerReconnect() {
|
|
4212
4247
|
return RECONNECT_CONFIG.enabled && this.signallingService.isConnected();
|
|
@@ -5615,17 +5650,13 @@ class StreamStatusTelemetryService2 {
|
|
|
5615
5650
|
// CLOSER — the `regionResolved` action carries the raw ping
|
|
5616
5651
|
// samples (one per probe) from `RegionsPingService`; the
|
|
5617
5652
|
// catalog flattens them to a comma-separated string since
|
|
5618
|
-
// `toPhaseParams` only accepts primitives.
|
|
5619
|
-
//
|
|
5620
|
-
//
|
|
5621
|
-
//
|
|
5622
|
-
// using the cached ping result the prefetch reused, so the region
|
|
5623
|
-
// phase still appears (with its ping samples). Because this fires at
|
|
5624
|
-
// adoption time it also re-anchors `lastMilestonePerf`, which keeps the
|
|
5625
|
-
// following signaling / webrtc phases at ~0 for adopted sessions.
|
|
5653
|
+
// `toPhaseParams` only accepts primitives. Adopted connections never
|
|
5654
|
+
// run `getRegion`, but `UnrealEffects.regionTelemetryOnAdopt$` replays
|
|
5655
|
+
// the region waterfall (start → end → resolved) from the cached winner,
|
|
5656
|
+
// so this phase still appears with its ping samples.
|
|
5626
5657
|
{
|
|
5627
5658
|
id: 'region-resolved',
|
|
5628
|
-
event:
|
|
5659
|
+
event: this.actions$.pipe(ofType(regionResolved), take(1), map(({ region, pingsMs }) => ({
|
|
5629
5660
|
region,
|
|
5630
5661
|
pingsMs: pingsMs.length ? `${pingsMs.join(', ')} ms` : null,
|
|
5631
5662
|
}))),
|
|
@@ -5786,29 +5817,6 @@ class StreamStatusTelemetryService2 {
|
|
|
5786
5817
|
return v4();
|
|
5787
5818
|
}
|
|
5788
5819
|
}
|
|
5789
|
-
/**
|
|
5790
|
-
* Region winner + ping samples cached in `localStorage` by
|
|
5791
|
-
* `region-ping-prefetch.js` / `RegionsPingService` (`REGION_PING_CACHE_KEY`).
|
|
5792
|
-
* Used to fill the region phase for ADOPTED connections, which never run
|
|
5793
|
-
* `getRegion()` and so never dispatch `regionResolved`. Empty when there is
|
|
5794
|
-
* no usable cache.
|
|
5795
|
-
*/
|
|
5796
|
-
cachedRegionResult() {
|
|
5797
|
-
try {
|
|
5798
|
-
const raw = localStorage.getItem(REGION_PING_CACHE_KEY);
|
|
5799
|
-
const parsed = raw ? JSON.parse(raw) : null;
|
|
5800
|
-
if (parsed && typeof parsed.region_code === 'string') {
|
|
5801
|
-
return {
|
|
5802
|
-
region: parsed.region_code,
|
|
5803
|
-
pingsMs: Array.isArray(parsed.result) ? parsed.result : [],
|
|
5804
|
-
};
|
|
5805
|
-
}
|
|
5806
|
-
}
|
|
5807
|
-
catch {
|
|
5808
|
-
/* ignore — best-effort */
|
|
5809
|
-
}
|
|
5810
|
-
return { region: '', pingsMs: [] };
|
|
5811
|
-
}
|
|
5812
5820
|
init() {
|
|
5813
5821
|
// Start a new session when a real connection attempt BEGINS, not on
|
|
5814
5822
|
// every `initSignalling` dispatch. `initSignalling` is fired multiple
|