@3dsource/angular-unreal-module 0.0.162 → 0.0.163
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
|
@@ -183,6 +183,9 @@ The package publishes two dependency-free scripts for use in the document
|
|
|
183
183
|
`stream-prefetch.js` runs by default only on
|
|
184
184
|
`metabox-configurator/{modular|basic}/...` routes. It reads the same-origin
|
|
185
185
|
`assets/config.json`; override that path with `data-config-url` when needed.
|
|
186
|
+
It also forwards the orchestration-issued `streamRequestId` from the polling
|
|
187
|
+
response to Cirrus on the WebSocket URL (the session `connectionId`) and parks it
|
|
188
|
+
for the Angular side to adopt.
|
|
186
189
|
Pin an exact package version in production when deterministic CDN assets are
|
|
187
190
|
required.
|
|
188
191
|
|
|
@@ -479,7 +479,7 @@ const initialState = {
|
|
|
479
479
|
};
|
|
480
480
|
const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
|
|
481
481
|
return { ...state, isVideoPlaying };
|
|
482
|
-
}), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl }) => {
|
|
482
|
+
}), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl, streamRequestId }) => {
|
|
483
483
|
return {
|
|
484
484
|
...state,
|
|
485
485
|
awsInstance: {
|
|
@@ -487,6 +487,7 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
|
|
|
487
487
|
instanceName,
|
|
488
488
|
wsUrl,
|
|
489
489
|
pollingUrl,
|
|
490
|
+
streamRequestId,
|
|
490
491
|
progressComplete: 0,
|
|
491
492
|
},
|
|
492
493
|
};
|
|
@@ -642,7 +643,7 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
|
|
|
642
643
|
return { ...state, analyticsEvent: event };
|
|
643
644
|
}), on(setCirrusConnected, (state) => {
|
|
644
645
|
return { ...state, cirrusConnected: true };
|
|
645
|
-
}), on(streamAdopted, (state, { machineId, ssData }) => {
|
|
646
|
+
}), on(streamAdopted, (state, { machineId, ssData, streamRequestId }) => {
|
|
646
647
|
return {
|
|
647
648
|
...state,
|
|
648
649
|
cirrusConnected: true,
|
|
@@ -653,6 +654,7 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
|
|
|
653
654
|
wsUrl: machineId,
|
|
654
655
|
pollingUrl: null,
|
|
655
656
|
instanceName: 'Adopted Connection',
|
|
657
|
+
streamRequestId: streamRequestId ?? state.awsInstance.streamRequestId,
|
|
656
658
|
progressComplete: 0,
|
|
657
659
|
},
|
|
658
660
|
};
|
|
@@ -782,8 +784,9 @@ function InstanceReadyHandler(msg) {
|
|
|
782
784
|
this.store.dispatch(instanceReady(payload));
|
|
783
785
|
// Open the SECOND socket directly to the stream machine and route the whole
|
|
784
786
|
// WebRTC signalling through it. The orchestration socket stays open for
|
|
785
|
-
// control/lifecycle.
|
|
786
|
-
|
|
787
|
+
// control/lifecycle. `streamRequestId` is handed over so the stream machine's
|
|
788
|
+
// Cirrus reuses the orchestration-issued connection id.
|
|
789
|
+
this.connectToStreamMachine(payload.originInstanceUri, payload.streamRequestId);
|
|
787
790
|
}
|
|
788
791
|
|
|
789
792
|
function InstanceReservedHandler(msg) {
|
|
@@ -1223,8 +1226,19 @@ const selectTotalProgress = createSelector(unrealFeature.selectStatusPercentSign
|
|
|
1223
1226
|
const progressRest = lerp(0, 1 - splitPoint, commandProgress);
|
|
1224
1227
|
return clampf(0, 1, progressSignaling + progressRest);
|
|
1225
1228
|
});
|
|
1226
|
-
/**
|
|
1227
|
-
|
|
1229
|
+
/**
|
|
1230
|
+
* The connection id of the current stream session.
|
|
1231
|
+
*
|
|
1232
|
+
* Primary source is the orchestration, which issues it as `streamRequestId` —
|
|
1233
|
+
* in the polling response (legacy flow) or in `instanceReserved`/`instanceReady`
|
|
1234
|
+
* (new orchestration) — and the browser hands it to Cirrus on the WS upgrade
|
|
1235
|
+
* URL. `ssData.connectionId` (the `ssInfo` message) is the fallback for
|
|
1236
|
+
* backends that don't issue one yet: Cirrus then generates it itself and
|
|
1237
|
+
* reports it back here.
|
|
1238
|
+
*
|
|
1239
|
+
* Used by external consumers (e.g., metabox). Do not remove.
|
|
1240
|
+
*/
|
|
1241
|
+
const selectCirrusConnectionId = createSelector(unrealFeature.selectAwsInstance, unrealFeature.selectSsData, (awsInstance, ssData) => awsInstance?.streamRequestId ?? ssData?.connectionId);
|
|
1228
1242
|
/** Used by external consumers (e.g., metabox). Do not remove. */
|
|
1229
1243
|
const selectUserInactivityDetected = createSelector(unrealFeature.selectAfkCountdown, unrealFeature.selectIsAfkTimerVisible, (remainingSecondsToDisconnect, countdownVisible) => ({
|
|
1230
1244
|
remainingSecondsToDisconnect,
|
|
@@ -1835,6 +1849,27 @@ function httpUrlToWs(url) {
|
|
|
1835
1849
|
return url.replace('http://', 'ws://').replace('https://', 'wss://');
|
|
1836
1850
|
}
|
|
1837
1851
|
|
|
1852
|
+
/**
|
|
1853
|
+
* Append the orchestration-issued connection id to a signalling WS URL as
|
|
1854
|
+
* `?streamRequestId=…`.
|
|
1855
|
+
*
|
|
1856
|
+
* Cirrus reads it in its `onConnection` upgrade handler and uses it as the
|
|
1857
|
+
* player's `connectionId` instead of generating one (both the legacy Cirrus and
|
|
1858
|
+
* the new Cirrus-WS keep their `guid()` generator as a fallback). A missing id
|
|
1859
|
+
* is a no-op, so the URL stays byte-identical for backends that don't issue one.
|
|
1860
|
+
*
|
|
1861
|
+
* Only used at the moment of connecting — the URL kept in the store stays clean
|
|
1862
|
+
* (it is rendered in the debug overlay and pattern-matched by
|
|
1863
|
+
* `selectTotalProgress`).
|
|
1864
|
+
*/
|
|
1865
|
+
function withStreamRequestId(wsUrl, streamRequestId) {
|
|
1866
|
+
if (!wsUrl || !streamRequestId) {
|
|
1867
|
+
return wsUrl;
|
|
1868
|
+
}
|
|
1869
|
+
const separator = wsUrl.includes('?') ? '&' : '?';
|
|
1870
|
+
return `${wsUrl}${separator}streamRequestId=${encodeURIComponent(streamRequestId)}`;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1838
1873
|
/**
|
|
1839
1874
|
* Factory that creates a WebSocket connection and wraps it in an RxJS Observable.
|
|
1840
1875
|
*
|
|
@@ -2169,7 +2204,7 @@ class SignallingService extends SubService {
|
|
|
2169
2204
|
*/
|
|
2170
2205
|
connectAttempt(urls) {
|
|
2171
2206
|
const urlGen = getActiveUrl(urls);
|
|
2172
|
-
return defer(() => this.getAwsInstance(`${urlGen.next().value}`)).pipe(tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap(({ wsUrl }) =>
|
|
2207
|
+
return defer(() => this.getAwsInstance(`${urlGen.next().value}`)).pipe(tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap(({ wsUrl, streamRequestId }) =>
|
|
2173
2208
|
// ── WS-only retry tier ─────────────────────────────────────────────
|
|
2174
2209
|
// Tries the SAME `wsUrl` up to `WS_RETRY_PER_INSTANCE` extra times
|
|
2175
2210
|
// before letting the error bubble up to the outer (URL-rotating)
|
|
@@ -2183,7 +2218,14 @@ class SignallingService extends SubService {
|
|
|
2183
2218
|
// burning a second reservation. If WS still fails after these
|
|
2184
2219
|
// extra attempts, the instance is genuinely unusable and we
|
|
2185
2220
|
// accept the cost of rotating.
|
|
2186
|
-
|
|
2221
|
+
// POLLING flow only: the polled `signallingServer` IS Cirrus (single
|
|
2222
|
+
// socket), so the orchestration-issued connection id rides on this
|
|
2223
|
+
// upgrade URL. In the new WS flow this socket is orchestration-only
|
|
2224
|
+
// control and `streamRequestId` is still unknown here (it arrives later
|
|
2225
|
+
// in `instanceReserved`/`instanceReady`) → no-op, URL unchanged; the id
|
|
2226
|
+
// is attached to the machine address instead, see
|
|
2227
|
+
// `connectToStreamMachine`.
|
|
2228
|
+
this.connectToCirrus(withStreamRequestId(wsUrl ?? '', streamRequestId)).pipe(filter(Truthy), take(1), retry({
|
|
2187
2229
|
count: WS_RETRY_PER_INSTANCE,
|
|
2188
2230
|
delay: (err, attempt) => {
|
|
2189
2231
|
this.store.dispatch(signalingRetryAttempted({
|
|
@@ -2279,6 +2321,9 @@ class SignallingService extends SubService {
|
|
|
2279
2321
|
wsUrl: httpUrlToWs(`${location.protocol}//${data.signallingServer}`),
|
|
2280
2322
|
pollingUrl: url,
|
|
2281
2323
|
instanceName: data.signallingServer ?? '',
|
|
2324
|
+
// The orchestration issues the connection id here; it is handed to
|
|
2325
|
+
// Cirrus on the WS upgrade URL (see `connectAttempt`).
|
|
2326
|
+
streamRequestId: data.streamRequestId,
|
|
2282
2327
|
})), finalize(() => {
|
|
2283
2328
|
this.commandTelemetryService.trackStopCommand('EXT-getSignaling', true, { ...(lastResp ?? {}), multi: true });
|
|
2284
2329
|
this.store.dispatch(pollingEnded());
|
|
@@ -2396,8 +2441,15 @@ class SignallingService extends SubService {
|
|
|
2396
2441
|
*
|
|
2397
2442
|
* "Just connect" — nothing is sent on open (the machine is already reserved
|
|
2398
2443
|
* for this session): Cirrus pushes `config` and Unreal sends the `offer`.
|
|
2444
|
+
*
|
|
2445
|
+
* The orchestration-issued `streamRequestId` (from the same `instanceReady`
|
|
2446
|
+
* payload) rides on this upgrade URL: in the new flow THIS is the Cirrus
|
|
2447
|
+
* socket, so it is the one that must carry the id (the first socket is
|
|
2448
|
+
* orchestration-only control and needs nothing). Without it the stream
|
|
2449
|
+
* machine's Cirrus falls back to its own `guid()` and the session can no
|
|
2450
|
+
* longer be correlated. Absent id → URL unchanged.
|
|
2399
2451
|
*/
|
|
2400
|
-
connectToStreamMachine(originInstanceUri) {
|
|
2452
|
+
connectToStreamMachine(originInstanceUri, streamRequestId) {
|
|
2401
2453
|
if (!originInstanceUri) {
|
|
2402
2454
|
Logger.warn('instanceReady without originInstanceUri — stream socket not opened');
|
|
2403
2455
|
return;
|
|
@@ -2412,7 +2464,7 @@ class SignallingService extends SubService {
|
|
|
2412
2464
|
// before a fresh attempt — e.g. a retry that resolved to a different machine
|
|
2413
2465
|
// or two instanceReady messages before the first connect resolves.
|
|
2414
2466
|
this.teardownStreamWs();
|
|
2415
|
-
this.streamWsSub = createWebSocket(originInstanceUri)
|
|
2467
|
+
this.streamWsSub = createWebSocket(withStreamRequestId(originInstanceUri, streamRequestId))
|
|
2416
2468
|
.pipe(takeUntil(this.abort$), takeUntil(this.destroy$), take(1))
|
|
2417
2469
|
.subscribe({
|
|
2418
2470
|
next: (ws) => {
|
|
@@ -3788,6 +3840,7 @@ class StreamAdoptionService {
|
|
|
3788
3840
|
this.store.dispatch(streamAdopted({
|
|
3789
3841
|
machineId: parked.machineId ?? null,
|
|
3790
3842
|
ssData: parked.ssData ?? null,
|
|
3843
|
+
streamRequestId: parked.ids?.streamRequestId || null,
|
|
3791
3844
|
timings: parked.timings ?? null,
|
|
3792
3845
|
}));
|
|
3793
3846
|
// Hand the live socket over without re-sending `requestStream`, reusing the
|
|
@@ -4089,9 +4142,12 @@ class UnrealEffects {
|
|
|
4089
4142
|
return this.actions$.pipe(ofType(setAwsInstance),
|
|
4090
4143
|
// require wsUrl present on the action payload
|
|
4091
4144
|
filter((action) => !!action.wsUrl),
|
|
4092
|
-
// for each awsInstance action capture one
|
|
4145
|
+
// for each awsInstance action capture one connectionId snapshot, then wait
|
|
4146
|
+
// for dataChannelConnected. The gate stays on `ssInfo` (unchanged timing);
|
|
4147
|
+
// the id itself now comes from the orchestration when it issued one, and
|
|
4148
|
+
// falls back to the `ssInfo` value otherwise.
|
|
4093
4149
|
switchMap((awsInstance) => {
|
|
4094
|
-
return this.actions$.pipe(ofType(updateCirrusInfo), take(1), switchMap((
|
|
4150
|
+
return this.actions$.pipe(ofType(updateCirrusInfo), take(1), withLatestFrom(this.store.select(selectCirrusConnectionId)), switchMap(([, connectionId]) => {
|
|
4095
4151
|
const abort$ = this.actions$.pipe(ofType(setCirrusDisconnected, // ← "Cirrus closed" action — direct,
|
|
4096
4152
|
// no fan-in tick delay
|
|
4097
4153
|
abortEstablishingConnection), take(1));
|
|
@@ -4108,7 +4164,7 @@ class UnrealEffects {
|
|
|
4108
4164
|
Logger.error(error);
|
|
4109
4165
|
const body = {
|
|
4110
4166
|
awsInstance,
|
|
4111
|
-
connectionId
|
|
4167
|
+
connectionId,
|
|
4112
4168
|
href: location.href,
|
|
4113
4169
|
userAgent: navigator.userAgent,
|
|
4114
4170
|
error,
|