@3dsource/angular-unreal-module 0.0.163 → 0.0.165

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.
@@ -157,7 +157,6 @@ export interface RequestReservation extends InstanceMessageBase {
157
157
  }
158
158
  */
159
159
  const OrchestrationMessageTypes = {
160
- ping: 'ping',
161
160
  error: 'error',
162
161
  offer: 'offer',
163
162
  answer: 'answer',
@@ -171,8 +170,6 @@ const OrchestrationMessageTypes = {
171
170
  instanceReady: 'instanceReady',
172
171
  p2pEstablished: 'p2pEstablished',
173
172
  instanceReserved: 'instanceReserved',
174
- playerDisconnected: 'playerDisconnected',
175
- streamerDisconnected: 'streamerDisconnected',
176
173
  interruptClientStream: 'interruptClientStream',
177
174
  };
178
175
 
@@ -225,6 +222,14 @@ const OUTER_RETRY_DELAY = POLLING_TIME * 2;
225
222
  // blip, brief unavailability) without burning a second reservation.
226
223
  // Total WS attempts before URL rotation = 1 + WS_RETRY_PER_INSTANCE.
227
224
  const WS_RETRY_PER_INSTANCE = 1;
225
+ // Bound on `requestCirrusViaSocket`: how long the orchestration control
226
+ // socket may stay silent between `requestStream` and `instanceReady`.
227
+ // The orchestration itself reports an `eta` (default 150 s in
228
+ // `InstanceReservedHandler`) for a cold machine, so `WS_TIMEOUT` (6 s) is
229
+ // far too short here — this is a provisioning wait, not a handshake.
230
+ // On expiry the request fails like any other, so `connectAttempt`'s
231
+ // URL-rotating tier asks a different orchestration URL for a new machine.
232
+ const INSTANCE_READY_TIMEOUT = 180_000;
228
233
  const WS_OPEN_STATE = 1;
229
234
  const DEFAULT_AFK_TIMEOUT_PERIOD = 15;
230
235
  const DEFAULT_AFK_TIMEOUT = 300; // 5 minutes
@@ -479,7 +484,7 @@ const initialState = {
479
484
  };
480
485
  const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
481
486
  return { ...state, isVideoPlaying };
482
- }), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl, streamRequestId }) => {
487
+ }), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl, streamRequestId, kind }) => {
483
488
  return {
484
489
  ...state,
485
490
  awsInstance: {
@@ -488,7 +493,11 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
488
493
  wsUrl,
489
494
  pollingUrl,
490
495
  streamRequestId,
491
- progressComplete: 0,
496
+ kind: kind ?? state.awsInstance.kind,
497
+ // In the socket flow this action lands only once `instanceReady`
498
+ // arrived, i.e. the machine IS provisioned — resetting to 0 here would
499
+ // throw the bar back to the start right at the end of that phase.
500
+ progressComplete: kind === 'socket' ? 1 : 0,
492
501
  },
493
502
  };
494
503
  }), on(setViewportReady, (state) => {
@@ -594,6 +603,10 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
594
603
  instanceName,
595
604
  streamRequestId,
596
605
  eta,
606
+ // `instanceReserved` exists only in the socket flow, and it arrives
607
+ // BEFORE `setAwsInstance` — so this is where the flow becomes known,
608
+ // early enough for `selectTotalProgress` to use the eta-driven bar.
609
+ kind: 'socket',
597
610
  },
598
611
  };
599
612
  }), on(setOrchestrationProgress, (state, { progressComplete }) => {
@@ -643,7 +656,7 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
643
656
  return { ...state, analyticsEvent: event };
644
657
  }), on(setCirrusConnected, (state) => {
645
658
  return { ...state, cirrusConnected: true };
646
- }), on(streamAdopted, (state, { machineId, ssData, streamRequestId }) => {
659
+ }), on(streamAdopted, (state, { machineId, pollingUrl, ssData, streamRequestId }) => {
647
660
  return {
648
661
  ...state,
649
662
  cirrusConnected: true,
@@ -652,7 +665,10 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
652
665
  awsInstance: {
653
666
  ...state.awsInstance,
654
667
  wsUrl: machineId,
655
- pollingUrl: null,
668
+ // The endpoint the prefetch polled. It is the only record of where the
669
+ // adopted stream came from — `connectToSignaling` never ran, so no
670
+ // `setAwsInstance` will fill this in later.
671
+ pollingUrl: pollingUrl ?? state.awsInstance.pollingUrl,
656
672
  instanceName: 'Adopted Connection',
657
673
  streamRequestId: streamRequestId ?? state.awsInstance.streamRequestId,
658
674
  progressComplete: 0,
@@ -780,13 +796,16 @@ function InstanceReadyHandler(msg) {
780
796
  Logger.warn('instanceReady received without payload — ignoring');
781
797
  return;
782
798
  }
799
+ if (!payload.originInstanceUri) {
800
+ Logger.warn('instanceReady without originInstanceUri — ignoring');
801
+ return;
802
+ }
783
803
  // Emit for observability (telemetry) and store the stream-machine coords.
784
804
  this.store.dispatch(instanceReady(payload));
785
- // Open the SECOND socket directly to the stream machine and route the whole
786
- // WebRTC signalling through it. The orchestration socket stays open for
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);
805
+ // This is the answer `requestCirrusViaSocket` is waiting for: the Cirrus
806
+ // address (`originInstanceUri`) plus the connection id to hand it. The socket
807
+ // itself is opened by the shared `connectToCirrus`, not here.
808
+ this.onInstanceReady$.next(payload);
790
809
  }
791
810
 
792
811
  function InstanceReservedHandler(msg) {
@@ -797,15 +816,6 @@ function InstanceReservedHandler(msg) {
797
816
  }));
798
817
  }
799
818
 
800
- function PingHandler() {
801
- // Reply pong on the active signalling socket (stream socket in the new flow,
802
- // relay socket in the old polling flow).
803
- this.sendPlayerSignal(JSON.stringify({
804
- type: 'pong',
805
- source: 'front',
806
- }));
807
- }
808
-
809
819
  function PlayerCountHandler(msg) {
810
820
  Logger.log(msg);
811
821
  }
@@ -861,9 +871,16 @@ function OnMessageHandler(e) {
861
871
  }
862
872
  }
863
873
 
874
+ /**
875
+ * Open handler of the CIRRUS socket only (`addWsHandlers`), so
876
+ * `setCirrusConnected` now means what it says. It used to also send
877
+ * `requestStream` here, which made the flag fire on the orchestration control
878
+ * socket — a whole provisioning wait (`eta`, ~150 s) before any Cirrus existed.
879
+ * `requestStream` belongs to the control socket and is sent by
880
+ * `requestCirrusViaSocket`.
881
+ */
864
882
  function OnOpenHandler() {
865
883
  this.store.dispatch(setCirrusConnected());
866
- this.sendRequestStream();
867
884
  }
868
885
 
869
886
  /**
@@ -1183,7 +1200,9 @@ const selectSignalingParameters = createSelector(unrealFeature.selectSsData, unr
1183
1200
  info.push(`Unreal App Version: ${data.streamerVersion}`);
1184
1201
  info.push(`APP: ${data.streamPath}${data.streamApp}`);
1185
1202
  }
1186
- info.push(`Polling: ${instance.pollingUrl}`);
1203
+ // The endpoint that handed us the Cirrus address: an HTTP polling URL in one
1204
+ // flow, a `wss://` orchestration URL in the other.
1205
+ info.push(`Orchestration: ${instance.pollingUrl}`);
1187
1206
  info.push(`WS: ${instance.wsUrl}`);
1188
1207
  return info.join('<br>');
1189
1208
  });
@@ -1203,13 +1222,15 @@ const selectIsLoaderScreenVisible = createSelector(selectIsFullyConnected, (full
1203
1222
  */
1204
1223
  const selectTotalProgress = createSelector(unrealFeature.selectStatusPercentSignallingServer, selectCommandProgress, unrealFeature.selectAwsInstance, unrealFeature.selectDataChannelConnected, (statusPercentSignallingServer, commandProgress, awsInstance, dataChannelConnected) => {
1205
1224
  const SIGNALLING_MAX = SIGNALLING_PERCENT_VALUE / 100;
1206
- const ORCHESTRATION_WS_PATTERN = /^wss:\/\/orchestration-websocket/i;
1207
- const isNewOrchestration = ORCHESTRATION_WS_PATTERN.test(awsInstance?.wsUrl ?? '');
1225
+ // Stated by the service (`AwsInstance.kind`), not sniffed from the hostname.
1226
+ // In the socket flow it is set by `instanceReserved` — i.e. before
1227
+ // `setAwsInstance` — so the eta-driven bar is picked up from the start of
1228
+ // the provisioning wait.
1229
+ const isNewOrchestration = awsInstance?.kind === 'socket';
1208
1230
  // Once the data channel is connected, signaling is fully complete (1.0) —
1209
1231
  // regardless of orchestration type. This is what makes an ADOPTED stream
1210
- // (whose wsUrl is the machine host, not the orchestration-websocket URL,
1211
- // and which never set `statusPercentSignallingServer`) correctly show the
1212
- // whole first phase done instead of stalling near the command-only value.
1232
+ // (which never set `statusPercentSignallingServer`) correctly show the whole
1233
+ // first phase done instead of stalling near the command-only value.
1213
1234
  // Otherwise: new orchestration reports 0–1 progress; legacy uses the
1214
1235
  // polling percent.
1215
1236
  const signalingProgress = dataChannelConnected
@@ -1832,10 +1853,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1832
1853
  }], ctorParameters: () => [] });
1833
1854
 
1834
1855
  function StreamerListHandler(msg) {
1835
- // `subscribe` is player signalling — reply on the active signalling socket
1836
- // (stream socket in the new flow, relay socket in the old polling flow),
1837
- // matching the socket the `streamerList` arrived on.
1838
- this.sendPlayerSignal(JSON.stringify({
1856
+ // `subscribe` is player signalling → the Cirrus socket.
1857
+ this.send(JSON.stringify({
1839
1858
  type: 'subscribe',
1840
1859
  streamerId: msg.ids[0],
1841
1860
  }));
@@ -2042,19 +2061,6 @@ function createWebSocket(wsUrl, timeoutMs = WS_TIMEOUT) {
2042
2061
  }));
2043
2062
  }
2044
2063
 
2045
- function PlayerDisconnectHandler() {
2046
- this.store.dispatch(disconnectStream({
2047
- reason: DisconnectReason.OrchestrationPlayerDisconnected,
2048
- message: 'Orchestration: PlayerDisconnectHandler',
2049
- }));
2050
- }
2051
- function StreamerDisconnectHandler() {
2052
- this.store.dispatch(disconnectStream({
2053
- reason: DisconnectReason.OrchestrationStreamerDisconnected,
2054
- message: 'Orchestration: StreamerDisconnectHandler',
2055
- }));
2056
- }
2057
-
2058
2064
  // Structured constants holding code + description
2059
2065
  const ORCHESTRATION_ERROR_CODES = {
2060
2066
  NO_AVAILABLE_INSTANCES: {
@@ -2079,6 +2085,16 @@ function OrchestrationErrorHandler(msg) {
2079
2085
  if (RETRY_CODES.has(msg.payload.code)) {
2080
2086
  const errorInfo = Object.values(ORCHESTRATION_ERROR_CODES).find(({ code }) => code === msg.payload.code);
2081
2087
  this.store.dispatch(setOrchestrationMessage({ message: errorInfo?.description }));
2088
+ // Same telemetry channel as the WS/URL/region retry tiers — this branch used
2089
+ // to resend silently, so `kind: 'orchestration'` was declared but never seen.
2090
+ // No count cap here on purpose: the wait is bounded by
2091
+ // `INSTANCE_READY_TIMEOUT` in `requestCirrusViaSocket`, and on expiry the
2092
+ // URL-rotating tier takes over.
2093
+ this.store.dispatch(signalingRetryAttempted({
2094
+ attempt: ++this.orchestrationRetryCount,
2095
+ reason: errorInfo?.description ?? `code ${msg.payload.code}`,
2096
+ kind: 'orchestration',
2097
+ }));
2082
2098
  this.orchestrationRetrySub?.unsubscribe();
2083
2099
  this.orchestrationRetrySub = timer(POLLING_TIME)
2084
2100
  .pipe(takeUntil(this.destroy$))
@@ -2102,21 +2118,27 @@ class SignallingService extends SubService {
2102
2118
  this.onConfig$ = new Subject();
2103
2119
  this.onWebRtcIce$ = new Subject();
2104
2120
  this.onWebRtcAnswer$ = new Subject();
2121
+ // Fed by `InstanceReadyHandler`. `requestCirrusViaSocket` awaits it the same
2122
+ // way `requestCirrusViaPolling` awaits a ready polling response — it is the
2123
+ // socket flow's "here is your Cirrus URL" answer.
2124
+ this.onInstanceReady$ = new Subject();
2105
2125
  // Pure signal — no side-effects. The `close()` is wired ONCE in init()
2106
2126
  // below so a single abort event triggers exactly one close, regardless
2107
2127
  // of how many pipelines have `takeUntil(this.abort$)` attached.
2108
2128
  this.abort$ = this.actions$.pipe(ofType(abortEstablishingConnection));
2129
+ // The CIRRUS signalling socket — the only socket that speaks WebRTC
2130
+ // (config/offer/answer/iceCandidate/ping). Same field in all three flows:
2131
+ // the polled `signallingServer`, the stream machine (`originInstanceUri`), or
2132
+ // a direct dev URL.
2109
2133
  this.ws = null;
2110
- // Second socket, opened directly to the stream machine (originInstanceUri
2111
- // from `instanceReady`). Carries the whole WebRTC signalling
2112
- // (config/offer/answer/iceCandidate/ping); `this.ws` stays for control.
2113
- this.streamWs = null;
2114
- // True once the stream-machine socket is established. After this the
2115
- // orchestration server intentionally closes the browser's orchestration
2116
- // socket (instance is "occupied") — that close must NOT be treated as a drop.
2117
- this.streamMachineConnected = false;
2118
- // Bounded retry counter for the stream-machine connection (new-flow only).
2119
- this.streamMachineRetryCount = 0;
2134
+ // The orchestration CONTROL socket (socket flow only). Opened by
2135
+ // `requestCirrusViaSocket` to ask for a machine and closed the moment
2136
+ // `instanceReady` arrives — per the backend contract nothing uses it after the
2137
+ // handover, so it is never left open with dangling handlers.
2138
+ this.controlWs = null;
2139
+ // `requestStream` resends driven by `OrchestrationErrorHandler` within one
2140
+ // `requestCirrusViaSocket` wait. Reported as `kind: 'orchestration'` retries.
2141
+ this.orchestrationRetryCount = 0;
2120
2142
  this.wsMsgHandlers = {};
2121
2143
  this.init();
2122
2144
  }
@@ -2124,7 +2146,7 @@ class SignallingService extends SubService {
2124
2146
  if (this.unrealConfig?.playwright) {
2125
2147
  return;
2126
2148
  }
2127
- this.setHandlersFromStream();
2149
+ this.setMessageHandlers();
2128
2150
  // Single owner of the "close on abort" side-effect. Previously this
2129
2151
  // lived as a `tap` inside `abort$` itself, which made every
2130
2152
  // `takeUntil(this.abort$)` subscriber fire its own close().
@@ -2139,13 +2161,12 @@ class SignallingService extends SubService {
2139
2161
  // and `init()` runs once in the constructor. If these subs were torn
2140
2162
  // down by abort$, a second in-injector session (e.g. navigating between
2141
2163
  // two routes that both host <app-unreal-scene>) would never send
2142
- // `p2pEstablished`. `sendPlayerSignal()` is already a no-op on a closed WS,
2164
+ // `p2pEstablished`. `send()` is already a no-op on a closed WS,
2143
2165
  // so there's no risk of stray messages.
2144
2166
  this.actions$
2145
2167
  .pipe(ofType(dataChannelReady), takeUntil(this.destroy$))
2146
2168
  .subscribe(() => {
2147
- // Player signalling → active signalling socket (stream/relay).
2148
- this.sendPlayerSignal({
2169
+ this.send({
2149
2170
  type: 'p2pEstablished',
2150
2171
  source: 'front',
2151
2172
  sessionId: generateUuid(),
@@ -2196,7 +2217,7 @@ class SignallingService extends SubService {
2196
2217
  }
2197
2218
  /**
2198
2219
  * One inner cycle: rotate `urlGen` over the pool, resolve via
2199
- * {@link getAwsInstance}, open WS via {@link connectToCirrus},
2220
+ * {@link requestCirrus}, open WS via {@link connectToCirrus},
2200
2221
  * retry up to {@link INNER_RETRY_LIMIT} on any hard failure. The
2201
2222
  * urlGen lives in this closure so retries advance through the
2202
2223
  * pool. Exhaustion bubbles to {@link connectToSignaling}'s outer
@@ -2204,12 +2225,20 @@ class SignallingService extends SubService {
2204
2225
  */
2205
2226
  connectAttempt(urls) {
2206
2227
  const urlGen = getActiveUrl(urls);
2207
- return defer(() => this.getAwsInstance(`${urlGen.next().value}`)).pipe(tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap(({ wsUrl, streamRequestId }) =>
2228
+ return defer(() => {
2229
+ // One control socket per ATTEMPT, whatever transport this attempt picks —
2230
+ // a rotation from a `wss://` orchestration URL to an `http://` one would
2231
+ // otherwise leave the previous control socket behind. Deliberately NOT
2232
+ // inside the WS-only tier below: that tier reuses the same resolved
2233
+ // machine, so its control socket must survive.
2234
+ this.teardownControlWs();
2235
+ return this.requestCirrus(`${urlGen.next().value}`);
2236
+ }).pipe(tap((awsInstance) => this.store.dispatch(setAwsInstance(awsInstance))), switchMap(({ wsUrl, streamRequestId }) =>
2208
2237
  // ── WS-only retry tier ─────────────────────────────────────────────
2209
2238
  // Tries the SAME `wsUrl` up to `WS_RETRY_PER_INSTANCE` extra times
2210
2239
  // before letting the error bubble up to the outer (URL-rotating)
2211
2240
  // retry. This is important because the outer retry calls
2212
- // `getAwsInstance(url)` again, which polls orchestration again,
2241
+ // `requestCirrus(url)` again, which asks orchestration again,
2213
2242
  // which causes orchestrator to RESERVE A NEW instance — the
2214
2243
  // previous reservation is orphaned (server-side leak).
2215
2244
  //
@@ -2218,13 +2247,10 @@ class SignallingService extends SubService {
2218
2247
  // burning a second reservation. If WS still fails after these
2219
2248
  // extra attempts, the instance is genuinely unusable and we
2220
2249
  // accept the cost of rotating.
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`.
2250
+ // `wsUrl` is always a CIRRUS address here, whichever request method
2251
+ // produced it, so the orchestration-issued connection id always rides
2252
+ // on this upgrade URL and Cirrus adopts it instead of generating its
2253
+ // own. Absent id (older backend, direct dev URL) → URL unchanged.
2228
2254
  this.connectToCirrus(withStreamRequestId(wsUrl ?? '', streamRequestId)).pipe(filter(Truthy), take(1), retry({
2229
2255
  count: WS_RETRY_PER_INSTANCE,
2230
2256
  delay: (err, attempt) => {
@@ -2278,22 +2304,52 @@ class SignallingService extends SubService {
2278
2304
  });
2279
2305
  }
2280
2306
  /**
2281
- * Resolve one URL to an `AwsInstance`. Dispatches to
2282
- * {@link directWsInstance} for `ws://` / `wss://` URLs, otherwise
2283
- * to {@link pollOrchestration}.
2307
+ * Ask the orchestration for a Cirrus to connect to. All three
2308
+ * implementations answer the same question — "which Cirrus, and with which
2309
+ * `streamRequestId`?" — and emit ONE `AwsInstance` whose `wsUrl` is a Cirrus
2310
+ * address. What differs is only how the answer is obtained and therefore how
2311
+ * it can fail:
2312
+ *
2313
+ * | Method | Transport | Fails on |
2314
+ * | -------------------------- | -------------------- | ------------------------------ |
2315
+ * | {@link requestCirrusViaPolling} | HTTP poll loop | HTTP 4xx (dead URL) |
2316
+ * | {@link requestCirrusViaSocket} | control WS | socket drop / no `instanceReady` in {@link INSTANCE_READY_TIMEOUT} |
2317
+ * | {@link requestCirrusDirect} | none (dev) | never — nothing is requested |
2318
+ *
2319
+ * Whatever they return goes through the SAME downstream in
2320
+ * {@link connectAttempt}: `setAwsInstance` → `connectToCirrus` → the WS-only
2321
+ * and URL-rotating retry tiers. A failure inside any of them is just an
2322
+ * error on this observable, so those tiers handle it uniformly.
2284
2323
  */
2285
- getAwsInstance(url) {
2286
- return url.match(/^ws(s?):\/\//i)
2287
- ? this.directWsInstance(url)
2288
- : this.pollOrchestration(url);
2324
+ requestCirrus(url) {
2325
+ if (!url.match(/^ws(s?):\/\//i)) {
2326
+ return this.requestCirrusViaPolling(url);
2327
+ }
2328
+ // A `ws://` URL is the orchestration control endpoint when the app supplied
2329
+ // a stream-request context (the same precondition `sendRequestStream` needs);
2330
+ // without it there is nothing to negotiate and the URL is Cirrus itself.
2331
+ return this.hasOrchestrationContext()
2332
+ ? this.requestCirrusViaSocket(url)
2333
+ : this.requestCirrusDirect(url);
2334
+ }
2335
+ /** True when `requestStream` can be composed → new orchestration. */
2336
+ hasOrchestrationContext() {
2337
+ return !!this.streamRequestContext() && !!this.environmentId();
2338
+ }
2339
+ /**
2340
+ * Wait until `clientId` and `viewId` are known — every request method needs
2341
+ * them before it may talk to the backend.
2342
+ */
2343
+ waitForClientAndViewIds() {
2344
+ return this.store.select(selectClientAndViewIds).pipe(filter(({ clientId, viewId }) => !!clientId && !!viewId), take(1));
2289
2345
  }
2290
2346
  /**
2291
- * Direct `ws://` / `wss://` short-circuit — no HTTP orchestration.
2292
- * Waits for `clientId` and `viewId` to be truthy in the store, then
2293
- * emits a `Direct Connection` `AwsInstance` and completes.
2347
+ * Direct `ws://` / `wss://` short-circuit — no orchestration at all, the URL
2348
+ * already points at Cirrus. Dev/playwright convenience, hence no
2349
+ * `kind`: nothing was requested.
2294
2350
  */
2295
- directWsInstance(url) {
2296
- return this.store.select(selectClientAndViewIds).pipe(filter(({ clientId, viewId }) => !!clientId && !!viewId), take(1), map$1(() => ({
2351
+ requestCirrusDirect(url) {
2352
+ return this.waitForClientAndViewIds().pipe(map$1(() => ({
2297
2353
  wsUrl: url,
2298
2354
  pollingUrl: null,
2299
2355
  instanceName: 'Direct Connection',
@@ -2306,7 +2362,7 @@ class SignallingService extends SubService {
2306
2362
  * last response captured in the closure). Maps the final ready
2307
2363
  * response to an `AwsInstance`.
2308
2364
  */
2309
- pollOrchestration(url) {
2365
+ requestCirrusViaPolling(url) {
2310
2366
  return defer(() => {
2311
2367
  let lastResp;
2312
2368
  this.store.dispatch(pollingStarted());
@@ -2324,12 +2380,84 @@ class SignallingService extends SubService {
2324
2380
  // The orchestration issues the connection id here; it is handed to
2325
2381
  // Cirrus on the WS upgrade URL (see `connectAttempt`).
2326
2382
  streamRequestId: data.streamRequestId,
2383
+ kind: 'polling',
2327
2384
  })), finalize(() => {
2328
2385
  this.commandTelemetryService.trackStopCommand('EXT-getSignaling', true, { ...(lastResp ?? {}), multi: true });
2329
2386
  this.store.dispatch(pollingEnded());
2330
2387
  }));
2331
2388
  });
2332
2389
  }
2390
+ /**
2391
+ * WS-orchestration path — the socket-transport twin of
2392
+ * {@link requestCirrusViaPolling}. Opens the control socket, sends
2393
+ * `requestStream` and waits for `instanceReady`, whose `originInstanceUri`
2394
+ * IS the Cirrus address.
2395
+ *
2396
+ * Failure modes are handled here so they surface as a plain error and reach
2397
+ * `connectAttempt`'s retry tiers (previously this branch lived outside them
2398
+ * and a dead control socket silently hung the session):
2399
+ * - control socket closes/errors before `instanceReady` → error
2400
+ * - no `instanceReady` within {@link INSTANCE_READY_TIMEOUT} → error
2401
+ *
2402
+ * The control socket lives exactly as long as this wait — see
2403
+ * {@link awaitInstanceReady}.
2404
+ */
2405
+ requestCirrusViaSocket(url) {
2406
+ return defer(() => {
2407
+ let resolved;
2408
+ this.orchestrationRetryCount = 0;
2409
+ this.commandTelemetryService.trackStartCommand('EXT-getSignaling');
2410
+ return this.waitForClientAndViewIds().pipe(switchMap(() => createWebSocket(url)), switchMap((control) => this.awaitInstanceReady(control, url)), tap((instance) => {
2411
+ resolved = instance;
2412
+ }), finalize(() => this.commandTelemetryService.trackStopCommand('EXT-getSignaling', true, { ...(resolved ?? {}), multi: true })));
2413
+ });
2414
+ }
2415
+ /**
2416
+ * Send `requestStream` on the freshly opened control socket and resolve with
2417
+ * the machine coordinates from `instanceReady`.
2418
+ *
2419
+ * The socket lives exactly as long as this wait: its close/error are HARD
2420
+ * failures of the request, and on resolution it is closed with every handler
2421
+ * detached (the backend does not use it after the handover). So the control
2422
+ * socket has no post-handover state at all — nothing that could report a stale
2423
+ * "disconnect" against a live stream.
2424
+ */
2425
+ awaitInstanceReady(control, orchestrationUrl) {
2426
+ return new Observable((observer) => {
2427
+ control.onmessage = OnMessageHandler.bind(this);
2428
+ control.onclose = (e) => observer.error(new Error(`Orchestration socket closed before instanceReady (code ${e.code})`));
2429
+ control.onerror = () => observer.error(new Error('Orchestration socket error before instanceReady'));
2430
+ this.controlWs = control;
2431
+ Logger.log('ORCHESTRATION CONTROL WS OPEN');
2432
+ this.sendRequestStream();
2433
+ const sub = this.onInstanceReady$.pipe(take(1)).subscribe((payload) => {
2434
+ // The control socket has done its only job. Per the backend contract it
2435
+ // is not used after the handover, so it is closed here — with all its
2436
+ // handlers detached by `teardownSocket`, which also means the close
2437
+ // itself cannot come back as an error above.
2438
+ this.teardownControlWs();
2439
+ observer.next({
2440
+ wsUrl: payload.originInstanceUri,
2441
+ // The endpoint that answered — the orchestration URL here, the HTTP
2442
+ // polling URL in the other flow. Shown as `Orchestration:` in the overlay.
2443
+ pollingUrl: orchestrationUrl,
2444
+ instanceName: payload.originInstanceId,
2445
+ streamRequestId: payload.streamRequestId,
2446
+ kind: 'socket',
2447
+ });
2448
+ observer.complete();
2449
+ });
2450
+ // Errored, timed out or aborted before `instanceReady` — same teardown,
2451
+ // the socket must not be left behind either way.
2452
+ return () => {
2453
+ sub.unsubscribe();
2454
+ this.teardownControlWs();
2455
+ };
2456
+ }).pipe(timeout$1({
2457
+ first: INSTANCE_READY_TIMEOUT,
2458
+ with: () => throwError(() => new Error(`No instanceReady within ${INSTANCE_READY_TIMEOUT}ms — requesting another machine`)),
2459
+ }), takeUntil(this.abort$));
2460
+ }
2333
2461
  /**
2334
2462
  * Poll the SAME `url` until ready, or until HTTP 4xx says the URL
2335
2463
  * is dead.
@@ -2386,37 +2514,31 @@ class SignallingService extends SubService {
2386
2514
  // still create a new socket, but in tests would re-emit the same
2387
2515
  // (already-errored) mock observable.
2388
2516
  return defer(() => {
2389
- this.close({ code: 1000, reason: DisconnectReason.NewConnectionAttempt });
2517
+ // Only the Cirrus socket — the control socket belongs to
2518
+ // `requestCirrusViaSocket`, which already opened it to GET this `wsUrl`.
2519
+ this.closeCirrus({
2520
+ code: 1000,
2521
+ reason: DisconnectReason.NewConnectionAttempt,
2522
+ });
2390
2523
  return createWebSocket(wsUrl);
2391
2524
  }).pipe(takeUntil(this.abort$), tapLog('WS CREATED'), tap((ws) => {
2392
2525
  this.commandTelemetryService.trackStartCommand('EXT-iceCandidate');
2393
2526
  this.addWsHandlers(ws);
2394
2527
  }));
2395
2528
  }
2529
+ /**
2530
+ * Handlers for the CIRRUS socket — the only long-lived socket. Its close/error
2531
+ * are real stream events (`setCirrusDisconnected` / `dropConnection`), unlike
2532
+ * the control socket's, which never outlives {@link awaitInstanceReady}.
2533
+ */
2396
2534
  addWsHandlers(ws) {
2397
2535
  ws.onmessage = OnMessageHandler.bind(this);
2398
- // Orchestration socket close/error is benign ONCE the stream machine is
2399
- // connected (orchestration closes it after marking the instance occupied).
2400
- ws.onclose = (e) => this.onOrchestrationClose(e);
2401
- ws.onerror = (e) => this.onOrchestrationError(e);
2536
+ ws.onclose = OnCloseHandler.bind(this);
2537
+ ws.onerror = OnErrorHandler.bind(this);
2402
2538
  this.ws = ws;
2403
2539
  Logger.log('WS HANDLERS ADDED');
2404
2540
  OnOpenHandler.call(this);
2405
2541
  }
2406
- onOrchestrationClose(e) {
2407
- if (this.streamMachineConnected) {
2408
- Logger.log(`Orchestration socket closed after occupancy (benign): ${e.code}`);
2409
- return;
2410
- }
2411
- OnCloseHandler.call(this, e);
2412
- }
2413
- onOrchestrationError(e) {
2414
- if (this.streamMachineConnected) {
2415
- Logger.log('Orchestration socket error after occupancy (benign)');
2416
- return;
2417
- }
2418
- OnErrorHandler.call(this, e);
2419
- }
2420
2542
  /**
2421
2543
  * Adopt a live signalling socket opened by the `stream-prefetch.js`
2422
2544
  * head-script. The socket is already OPEN and `requestStream` was already
@@ -2433,104 +2555,32 @@ class SignallingService extends SubService {
2433
2555
  this.ws = ws;
2434
2556
  Logger.log('WS ADOPTED (handshake already done, requestStream already sent)');
2435
2557
  }
2436
- /**
2437
- * Open the SECOND socket, directly to the stream machine's Cirrus
2438
- * (`originInstanceUri` from `instanceReady`). The whole WebRTC signalling
2439
- * (config/offer/answer/iceCandidate/ping) flows over this socket; the
2440
- * orchestration socket (`this.ws`) stays open for control/lifecycle.
2441
- *
2442
- * "Just connect" — nothing is sent on open (the machine is already reserved
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.
2451
- */
2452
- connectToStreamMachine(originInstanceUri, streamRequestId) {
2453
- if (!originInstanceUri) {
2454
- Logger.warn('instanceReady without originInstanceUri — stream socket not opened');
2455
- return;
2456
- }
2457
- // Guard: a stream socket is already live. A duplicate / re-sent
2458
- // `instanceReady` must NOT tear down the active stream.
2459
- if (this.streamMachineConnected) {
2460
- Logger.warn('Stream machine already connected — ignoring duplicate instanceReady');
2461
- return;
2462
- }
2463
- // Drop any prior (half-open) stream socket AND cancel any in-flight connect
2464
- // before a fresh attempt — e.g. a retry that resolved to a different machine
2465
- // or two instanceReady messages before the first connect resolves.
2466
- this.teardownStreamWs();
2467
- this.streamWsSub = createWebSocket(withStreamRequestId(originInstanceUri, streamRequestId))
2468
- .pipe(takeUntil(this.abort$), takeUntil(this.destroy$), take(1))
2469
- .subscribe({
2470
- next: (ws) => {
2471
- this.streamMachineRetryCount = 0;
2472
- this.addStreamWsHandlers(ws);
2473
- },
2474
- error: (err) => {
2475
- Logger.error('Stream-machine WS failed to connect', err?.message);
2476
- this.retryStreamMachine();
2477
- },
2478
- });
2479
- }
2480
- /**
2481
- * The stream-machine (Cirrus) connection failed. Re-request a machine over
2482
- * the still-open orchestration socket: orchestration issues a new
2483
- * streamRequestId and a fresh `instanceReady(originInstanceUri)`, which calls
2484
- * {@link connectToStreamMachine} again on a different machine. Bounded by
2485
- * {@link INNER_RETRY_LIMIT}; on exhaustion the stream is dropped.
2486
- */
2487
- retryStreamMachine() {
2488
- if (this.streamMachineRetryCount >= INNER_RETRY_LIMIT) {
2489
- Logger.error('Stream-machine connection exhausted retries — dropping');
2490
- this.store.dispatch(disconnectStream({
2491
- reason: DisconnectReason.ReconnectFailed,
2492
- message: 'Could not connect to the stream machine',
2493
- }));
2494
- return;
2495
- }
2496
- this.streamMachineRetryCount++;
2497
- this.orchestrationRetrySub?.unsubscribe();
2498
- this.orchestrationRetrySub = timer(POLLING_TIME)
2499
- .pipe(takeUntil(this.abort$))
2500
- .subscribe(() => this.sendRequestStream());
2501
- }
2502
- addStreamWsHandlers(ws) {
2503
- ws.onmessage = OnMessageHandler.bind(this);
2504
- ws.onclose = OnCloseHandler.bind(this);
2505
- ws.onerror = OnErrorHandler.bind(this);
2506
- this.streamWs = ws;
2507
- this.streamMachineConnected = true;
2508
- Logger.log('STREAM-MACHINE WS CONNECTED');
2509
- }
2510
- teardownStreamWs() {
2511
- this.streamWsSub?.unsubscribe();
2512
- this.teardownSocket(this.streamWs, 1000, DisconnectReason.NewConnectionAttempt);
2513
- this.streamWs = null;
2514
- this.streamMachineConnected = false;
2515
- }
2516
2558
  showStatusMessage(statusMessage) {
2517
2559
  this.store.dispatch(setStatusMessage({ statusMessage }));
2518
2560
  }
2519
2561
  /**
2520
- * Close the connection with the signaling server
2521
- * @param data
2562
+ * Close everything: the Cirrus socket AND the orchestration control socket.
2563
+ * Used on abort/teardown. A new connection attempt inside a live cycle uses
2564
+ * {@link closeCirrus} instead, so it does not kill the control socket that
2565
+ * produced the address it is about to connect to.
2522
2566
  */
2523
2567
  close(data) {
2524
2568
  const { code, reason } = data;
2525
- this.onWebRtcAnswer$.next(null);
2526
2569
  this.orchestrationRetrySub?.unsubscribe();
2527
- this.streamWsSub?.unsubscribe();
2570
+ this.closeCirrus(data);
2571
+ this.teardownControlWs(code, reason);
2572
+ }
2573
+ /** Close only the Cirrus signalling socket. */
2574
+ closeCirrus(data) {
2575
+ const { code, reason } = data;
2576
+ this.onWebRtcAnswer$.next(null);
2528
2577
  this.teardownSocket(this.ws, code, reason);
2529
2578
  this.ws = null;
2530
- this.teardownSocket(this.streamWs, code, reason);
2531
- this.streamWs = null;
2532
- this.streamMachineConnected = false;
2533
- this.streamMachineRetryCount = 0;
2579
+ }
2580
+ teardownControlWs(code = 1000, reason = DisconnectReason.NewConnectionAttempt) {
2581
+ this.teardownSocket(this.controlWs, code, reason);
2582
+ this.controlWs = null;
2583
+ this.orchestrationRetryCount = 0;
2534
2584
  }
2535
2585
  teardownSocket(ws, code, reason) {
2536
2586
  if (!ws) {
@@ -2547,17 +2597,16 @@ class SignallingService extends SubService {
2547
2597
  ws.close(code, reason);
2548
2598
  }
2549
2599
  }
2600
+ /**
2601
+ * Player signalling (offer/answer/iceCandidate/pong/p2pEstablished/subscribe)
2602
+ * — always the Cirrus socket, in every flow.
2603
+ */
2550
2604
  send(data) {
2551
2605
  this.sendTo(this.ws, data);
2552
2606
  }
2553
- /**
2554
- * Send player signalling (offer/answer/iceCandidate/pong/p2pEstablished/
2555
- * subscribe) over the ACTIVE signalling socket: the stream-machine socket in
2556
- * the new flow (once connected), or the orchestration/relay socket in the old
2557
- * polling flow (where there is no separate stream socket).
2558
- */
2559
- sendPlayerSignal(data) {
2560
- this.sendTo(this.streamMachineConnected ? this.streamWs : this.ws, data);
2607
+ /** Orchestration control messages (`requestStream`) — the control socket. */
2608
+ sendToOrchestration(data) {
2609
+ this.sendTo(this.controlWs, data);
2561
2610
  }
2562
2611
  sendTo(ws, data) {
2563
2612
  let dataString = data;
@@ -2587,21 +2636,45 @@ class SignallingService extends SubService {
2587
2636
  Logger.colored(...COLOR_CODES.FROM_CIRRUS_ERROR, `Invalid CIRRUS message type: ${msg.type}`);
2588
2637
  }
2589
2638
  }
2590
- setHandlersFromStream() {
2591
- this.wsMsgHandlers.ping = PingHandler.bind(this);
2639
+ /**
2640
+ * One handler map shared by both sockets — `handleMessage` dispatches purely by
2641
+ * message type. Which socket a type can arrive on is fixed, so registration is
2642
+ * split by origin: {@link setOrchestrationHandlers} (control socket) and
2643
+ * {@link setStreamHandlers} (Cirrus).
2644
+ *
2645
+ * Keepalive is deliberately absent: both servers ping at the WS **protocol**
2646
+ * level (`ws.ping()`), which the browser answers on its own — the page never
2647
+ * sees a ping and must never reply with an application-level `pong`. An
2648
+ * unregistered type is dropped silently by `handleMessage`, so a `ping` from a
2649
+ * not-yet-updated backend is simply ignored.
2650
+ */
2651
+ setMessageHandlers() {
2652
+ this.setOrchestrationHandlers();
2653
+ this.setStreamHandlers();
2654
+ }
2655
+ /**
2656
+ * Messages from the ORCHESTRATION control socket. Only reachable during the
2657
+ * provisioning wait — `requestCirrusViaSocket` closes that socket the moment
2658
+ * `instanceReady` lands.
2659
+ */
2660
+ setOrchestrationHandlers() {
2661
+ // `instanceReserved` (eta/progress) and `instanceReady` (the Cirrus address)
2662
+ // ARE the request/response pair of `requestCirrusViaSocket`.
2663
+ this.wsMsgHandlers.instanceReserved = InstanceReservedHandler.bind(this);
2664
+ this.wsMsgHandlers.instanceReady = InstanceReadyHandler.bind(this);
2665
+ // Orchestration-side refusals (4001 no instances / 4002 limit reached) →
2666
+ // resend `requestStream`, bounded by `INSTANCE_READY_TIMEOUT`.
2592
2667
  this.wsMsgHandlers.error = OrchestrationErrorHandler.bind(this);
2668
+ }
2669
+ /** Messages from the CIRRUS socket — the actual stream signalling. */
2670
+ setStreamHandlers() {
2593
2671
  this.wsMsgHandlers.config = ConfigHandler.bind(this);
2672
+ this.wsMsgHandlers.offer = OfferHandler.bind(this);
2594
2673
  this.wsMsgHandlers.answer = AnswerHandler.bind(this);
2674
+ this.wsMsgHandlers.iceCandidate = IceCandidateHandler.bind(this);
2595
2675
  this.wsMsgHandlers.ssInfo = SSInfoHandler.bind(this);
2596
2676
  this.wsMsgHandlers.playerCount = PlayerCountHandler.bind(this);
2597
2677
  this.wsMsgHandlers.streamerList = StreamerListHandler.bind(this);
2598
- this.wsMsgHandlers.iceCandidate = IceCandidateHandler.bind(this);
2599
- this.wsMsgHandlers.instanceReady = InstanceReadyHandler.bind(this);
2600
- this.wsMsgHandlers.instanceReserved = InstanceReservedHandler.bind(this);
2601
- this.wsMsgHandlers.playerDisconnected = PlayerDisconnectHandler.bind(this);
2602
- this.wsMsgHandlers.streamerDisconnected =
2603
- StreamerDisconnectHandler.bind(this);
2604
- this.wsMsgHandlers.offer = OfferHandler.bind(this);
2605
2678
  }
2606
2679
  sendRequestStream() {
2607
2680
  const context = this.streamRequestContext();
@@ -2609,7 +2682,7 @@ class SignallingService extends SubService {
2609
2682
  if (!context || !environmentId) {
2610
2683
  return;
2611
2684
  }
2612
- this.send({
2685
+ this.sendToOrchestration({
2613
2686
  type: OrchestrationMessageTypes.requestStream,
2614
2687
  correlationId: this.correlationId,
2615
2688
  payload: {
@@ -2620,18 +2693,12 @@ class SignallingService extends SubService {
2620
2693
  });
2621
2694
  }
2622
2695
  /**
2623
- * Check if the ACTIVE player-signalling socket is open and ready. Mirrors
2624
- * {@link sendPlayerSignal}'s socket choice so peer-reconnect gating is correct
2625
- * in both flows:
2626
- * - NEW flow (`streamMachineConnected`): the stream-machine socket
2627
- * (`streamWs`). The orchestration socket (`this.ws`) is intentionally
2628
- * closed after occupancy, so checking it would wrongly report "not
2629
- * connected" and block reconnect.
2630
- * - OLD polling flow: the single relay socket (`this.ws`) — unchanged.
2696
+ * Is the player-signalling socket open? Always the Cirrus socket — the
2697
+ * control socket's state is irrelevant to the stream (orchestration closes it
2698
+ * once the instance is occupied), so it must not be part of this gate.
2631
2699
  */
2632
2700
  isConnected() {
2633
- const activeWs = this.streamMachineConnected ? this.streamWs : this.ws;
2634
- return !!(activeWs && activeWs.readyState === WS_OPEN_STATE);
2701
+ return !!(this.ws && this.ws.readyState === WS_OPEN_STATE);
2635
2702
  }
2636
2703
  getRegion() {
2637
2704
  Logger.log('Start Getting Region');
@@ -3550,7 +3617,7 @@ class WebRtcPlayerService extends SubService {
3550
3617
  });
3551
3618
  fromEvent(pcClient, 'icecandidate')
3552
3619
  .pipe(map$1((e) => e.candidate), filter(Truthy), tap$1((candidate) => Logger.info('[Browser ICE candidate]', '| Type=', candidate.type, '| Protocol=', candidate.protocol, '| Address=', candidate.address, '| Port=', candidate.port, '|')), takeUntil(merge(this.disconnect$, this.destroy$)))
3553
- .subscribe((candidate) => this.signallingSrv.sendPlayerSignal({
3620
+ .subscribe((candidate) => this.signallingSrv.send({
3554
3621
  type: 'iceCandidate',
3555
3622
  candidate,
3556
3623
  }));
@@ -3700,7 +3767,7 @@ class WebRtcPlayerService extends SubService {
3700
3767
  const offer = await pcClient.createOffer();
3701
3768
  offer.sdp = this.mungeSDP(offer.sdp ?? '');
3702
3769
  await pcClient.setLocalDescription(offer);
3703
- this.signallingSrv.sendPlayerSignal(offer);
3770
+ this.signallingSrv.send(offer);
3704
3771
  }
3705
3772
  catch {
3706
3773
  Logger.error("Couldn't create offer");
@@ -3715,7 +3782,7 @@ class WebRtcPlayerService extends SubService {
3715
3782
  const answer = await pcClient.createAnswer();
3716
3783
  answer.sdp = this.mungeSDP(answer.sdp ?? '');
3717
3784
  await pcClient.setLocalDescription(answer);
3718
- this.signallingSrv.sendPlayerSignal(answer);
3785
+ this.signallingSrv.send(answer);
3719
3786
  // Observability signal for the signaling waterfall — the handshake
3720
3787
  // went "answer sent" between `offer received` and `datachannel_ready`.
3721
3788
  this.store.dispatch(answerSent());
@@ -3839,6 +3906,7 @@ class StreamAdoptionService {
3839
3906
  // script's ids are only used internally to establish the connection.
3840
3907
  this.store.dispatch(streamAdopted({
3841
3908
  machineId: parked.machineId ?? null,
3909
+ pollingUrl: parked.pollingUrl ?? null,
3842
3910
  ssData: parked.ssData ?? null,
3843
3911
  streamRequestId: parked.ids?.streamRequestId || null,
3844
3912
  timings: parked.timings ?? null,
@@ -4169,7 +4237,15 @@ class UnrealEffects {
4169
4237
  userAgent: navigator.userAgent,
4170
4238
  error,
4171
4239
  };
4172
- return this.http.post(awsInstance.pollingUrl || '', body).pipe(catchError((postError) => {
4240
+ // Only the polling flow has an HTTP endpoint to report to;
4241
+ // `pollingUrl` is a `wss://` orchestration address otherwise.
4242
+ const reportUrl = awsInstance.pollingUrl?.startsWith('http')
4243
+ ? awsInstance.pollingUrl
4244
+ : null;
4245
+ const report$ = reportUrl
4246
+ ? this.http.post(reportUrl, body)
4247
+ : of(null);
4248
+ return report$.pipe(catchError((postError) => {
4173
4249
  Logger.error('Error sending timeout info:', postError);
4174
4250
  return of(null);
4175
4251
  }),
@@ -8100,5 +8176,5 @@ function provideUnrealModule() {
8100
8176
  * Generated bundle index. Do not edit.
8101
8177
  */
8102
8178
 
8103
- export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, smoothTransition, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
8179
+ export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, smoothTransition, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
8104
8180
  //# sourceMappingURL=3dsource-angular-unreal-module.mjs.map