@3dsource/angular-unreal-module 0.0.156-dev.0 → 0.0.157

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
@@ -1,412 +1,422 @@
1
- # @3dsource/angular-unreal-module
2
-
3
- A set of standalone Angular components, services, and providers for integrating Unreal Engine (WebRTC) scenes into Angular applications. It facilitates communication between Angular and Unreal Engine and enables interactive 3D experiences.
4
-
5
- ## Overview
6
-
7
- This package provides:
8
-
9
- - Standalone Unreal scene component to embed UE stream
10
- - Communication bridge (commands, UI interactions, input data)
11
- - Callback listener for Unreal events and command responses
12
- - NgRx state and effects for 3D stream lifecycle
13
- - Config and utilities for telemetry, errors, and regions ping
14
- - Auto-reconnection support for WebRTC/DataChannel failures
15
- - File receiving from Unreal Engine
16
- - Analytics, FPS monitoring, and stream status telemetry
17
- - Playwright testing mode with mock services
18
-
19
- ## Installation
20
-
21
- ### Prerequisites
22
-
23
- - Angular `>=19.0.0 <23.0.0`
24
- - NgRx store and effects `>=19.0.0 <23.0.0`
25
- - Angular CDK `>=19.0.0 <23.0.0` — used for dialog overlays
26
- - RxJS `>=7.8.0 <8.0.0`
27
- - `provideHttpClient()` — required by internal services (telemetry, signalling, regions ping, error reporting)
28
-
29
- ### Peer Dependencies
30
-
31
- This library requires the following peer dependencies:
32
-
33
- ```json
34
- {
35
- "@3dsource/source-ui-native": ">=1.0.9",
36
- "@3dsource/types-unreal": ">=0.0.7",
37
- "@3dsource/utils": ">=1.0.21",
38
- "@angular/cdk": ">=19.0.0 <23.0.0",
39
- "@angular/common": ">=19.0.0 <23.0.0",
40
- "@angular/core": ">=19.0.0 <23.0.0",
41
- "@angular/forms": ">=19.0.0 <23.0.0",
42
- "@angular/platform-browser": ">=19.0.0 <23.0.0",
43
- "@ngrx/effects": ">=19.0.0 <23.0.0",
44
- "@ngrx/store": ">=19.0.0 <23.0.0",
45
- "rxjs": ">=7.8.0 <8.0.0"
46
- }
47
- ```
48
-
49
- ### Library Installation
50
-
51
- ```shell
52
- npm i @3dsource/angular-unreal-module
53
- ```
54
-
55
- ## Usage
56
-
57
- The API is fully standalone (no NgModule). Use providers and components as shown below.
58
-
59
- ### 1) Provide the state and config at the application root
60
-
61
- Add providers in your application bootstrap (e.g., `app.config.ts`):
62
-
63
- > **⚠️ Important:** `UNREAL_CONFIG` is **required** — multiple internal services inject it without `{ optional: true }`. Omitting it will cause a `NullInjectorError` at runtime. You can provide it with an empty object `{}` as a minimum.
64
-
65
- ```ts
66
- import { ApplicationConfig } from '@angular/core';
67
- import { provideRouter } from '@angular/router';
68
- import { provideHttpClient } from '@angular/common/http';
69
- import { provideStore } from '@ngrx/store';
70
- import { provideUnrealState, UNREAL_CONFIG } from '@3dsource/angular-unreal-module';
71
-
72
- export const appConfig: ApplicationConfig = {
73
- providers: [
74
- provideRouter([]),
75
- provideHttpClient(),
76
-
77
- // Root NgRx (if not already added in your app)
78
- provideStore(),
79
- provideUnrealState(),
80
-
81
- // Required: Unreal initial configuration
82
- {
83
- provide: UNREAL_CONFIG,
84
- useValue: {
85
- customErrorsEndpoint: '', // Endpoint for custom error reporting
86
- commandTelemetryReceiver: '', // Endpoint for command telemetry
87
- regionsPingUrl: '', // URL prefix for regions latency ping
88
- screenLockerContainerId: '', // DOM container id for screen locker overlay
89
- dataChannelConnectionTimeout: 8000, // Timeout in ms for data channel connection (default: 8000)
90
- streamTelemetryV2Url: '', // Endpoint for stream-status telemetry
91
- fpsMonitor: false, // Enable FPS monitoring
92
- autoHighResolution: false, // Raise resolution after the scene becomes idle
93
- playwright: false, // Mirrors the provider flag for services/effects
94
- },
95
- },
96
- ],
97
- };
98
- ```
99
-
100
- #### Minimal configuration
101
-
102
- If you don't need custom endpoints, provide `UNREAL_CONFIG` with an empty object:
103
-
104
- ```ts
105
- { provide: UNREAL_CONFIG, useValue: {} },
106
- provideUnrealState(),
107
- ```
108
-
109
- Every `UnrealInitialConfig` field is optional, but the `UNREAL_CONFIG` provider
110
- itself is required. Available fields are `playwright`,
111
- `customErrorsEndpoint`, `commandTelemetryReceiver`, `regionsPingUrl`,
112
- `screenLockerContainerId`, `dataChannelConnectionTimeout`,
113
- `streamTelemetryV2Url`, `fpsMonitor` and `autoHighResolution`.
114
-
115
- ### 2) Boot the engine in each lazy 3D route
116
-
117
- `provideUnrealModule()` registers the effects and initializes the streaming
118
- services. Put it on every lazy route that renders a 3D scene:
119
-
120
- ```ts
121
- import { Routes } from '@angular/router';
122
- import { provideUnrealModule } from '@3dsource/angular-unreal-module';
123
-
124
- export const STREAM_ROUTES: Routes = [
125
- {
126
- path: ':id',
127
- loadComponent: () => import('./stream.component').then((module) => module.StreamComponent),
128
- providers: [provideUnrealModule()],
129
- },
130
- ];
131
- ```
132
-
133
- Keep `provideUnrealState()` and `UNREAL_CONFIG` at the root. Keeping
134
- `provideUnrealModule()` in lazy routes also keeps the streaming engine out of
135
- the initial application bundle.
136
-
137
- ### 3) Use the Unreal scene component
138
-
139
- Import the component into a standalone component and use it in the template.
140
-
141
- ```ts
142
- import { Component } from '@angular/core';
143
- import { UnrealSceneComponent } from '@3dsource/angular-unreal-module';
144
-
145
- @Component({
146
- selector: 'app-root',
147
- standalone: true,
148
- imports: [UnrealSceneComponent],
149
- template: ` <app-unreal-scene [isStudio]="false" [useContainerAsSizeProvider]="true" [resolutionSize]="{ width: 1920, height: 1080 }" (changeMouseOverScene)="onHover($event)" /> `,
150
- })
151
- export class AppComponent {
152
- onHover(isOver: boolean) {
153
- // handle mouse over scene
154
- }
155
- }
156
- ```
157
-
158
- Component selector: `<app-unreal-scene>`
159
-
160
- Inputs:
161
-
162
- | Input | Type | Default |
163
- | ---------------------------- | ----------------------------------- | ------------------------------- |
164
- | `isStudio` | `boolean` | `false` |
165
- | `useContainerAsSizeProvider` | `boolean` | `true` |
166
- | `resolutionSize` | `{ width: number; height: number }` | `{ width: 1920, height: 1080 }` |
167
-
168
- Outputs:
169
-
170
- | Output | Type |
171
- | ---------------------- | --------------------------- |
172
- | `changeMouseOverScene` | `OutputEmitterRef<boolean>` |
173
-
174
- ### 4) Send commands / interactions to Unreal
175
-
176
- Inject `UnrealCommunicatorService` to send commands or UI interactions. Types for command packets are provided by `@3dsource/types-unreal`.
177
-
178
- There are three sending methods — choose based on your needs:
179
-
180
- | Method | Description |
181
- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
182
- | `sendCommandToUnreal` | Full pipeline: adds `correlationId`, records telemetry, dispatches NgRx `commandStarted` action. **Recommended for application commands.** |
183
- | `emitUIInteraction` | Sends the packet as a raw `UIInteraction` message. No telemetry or store dispatch. |
184
- | `emitCommand` | Sends the packet as a raw `Command` message (for console commands, resolution changes, etc.). |
185
-
186
- ```ts
187
- import { Component, inject } from '@angular/core';
188
- import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
189
- import { MetaBoxCommand } from '@3dsource/types-unreal';
190
- import type { MetaBoxCommandPacket } from '@3dsource/types-unreal';
191
-
192
- @Component({ standalone: true, template: '' })
193
- export class MyComponent {
194
- private unreal = inject(UnrealCommunicatorService);
195
-
196
- sendSomeCommand() {
197
- // Recommended: use sendCommandToUnreal for full telemetry + store tracking
198
- this.unreal.sendCommandToUnreal({
199
- command: MetaBoxCommand.FChangeResolutionCommand,
200
- payload: { resolution: { x: 1920, y: 1080 } },
201
- });
202
- }
203
-
204
- sendRawUIInteraction() {
205
- // Low-level: sends UIInteraction message without telemetry tracking
206
- const packet = {
207
- command: 'CustomCommand',
208
- payload: { key: 'value' },
209
- } as MetaBoxCommandPacket;
210
- this.unreal.emitUIInteraction(packet);
211
- }
212
- }
213
- ```
214
-
215
- ### 5) Store integration (actions & selectors)
216
-
217
- `provideUnrealState()` registers the NgRx feature state `unrealFeature`. You
218
- can dispatch actions and select state in your components:
219
-
220
- ```ts
221
- import { inject } from '@angular/core';
222
- import { Store } from '@ngrx/store';
223
- import { disconnectStream, selectShowLoader, selectTotalProgress, setConfig, setOrchestrationContext, startStream, unrealFeature } from '@3dsource/angular-unreal-module';
224
-
225
- // Dispatch actions
226
- const store = inject(Store);
227
- store.dispatch(startStream({ config: { autoStart: true, warnTimeout: 120 } }));
228
-
229
- // Select state
230
- const progress = store.selectSignal(selectTotalProgress);
231
- const isVideoPlaying = store.selectSignal(unrealFeature.selectIsVideoPlaying);
232
- const dataChannelConnected = store.selectSignal(unrealFeature.selectDataChannelConnected);
233
- ```
234
-
235
- **Key selectors:**
236
-
237
- - `selectTotalProgress` — Scene load progress (0–1 float)
238
- - `selectShowLoader` — Whether loader screen should be visible
239
- - `selectShowReconnectPopup` — Whether reconnect popup should be shown
240
- - `selectIsVideoPlayingAndDataChannelConnected` — Combined readiness check
241
- - `selectStreamConfig` — Current stream configuration
242
- - `unrealFeature.selectCirrusConnected` — Signalling server connection status
243
- - `unrealFeature.selectDataChannelConnected` — Data channel status
244
- - `unrealFeature.selectViewportReady` — Viewport readiness
245
-
246
- **Key actions:**
247
-
248
- - `startStream` — Start streaming with config
249
- - `setConfig` — Update stream configuration
250
- - `setOrchestrationContext` — Set orchestration URLs and environment
251
- - `disconnectStream` — Disconnect with reason
252
- - `destroyUnrealScene` — Full teardown
253
- - `reconnectPeer` — Trigger peer reconnection
254
-
255
- ### 6) Listen for Unreal callbacks
256
-
257
- Inject `UnrealCallbackService` to listen for callback events from Unreal Engine and to observe command responses.
258
-
259
- There are two methods:
260
-
261
- | Method | Description |
262
- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
- | `fromUnrealCallback` | Listens for callbacks matching a command or event key. Supports MetaBox commands and custom Unreal callback events. Returns `Observable<UnrealCallbackDescriptor[K]>`. |
264
- | `observeCommandResponse` | Sends a command and waits for its matching response by `correlationId`. Returns `Observable<MetaBoxCommandList[K]>`. Includes timeout and error handling. |
265
-
266
- **Custom Unreal callback events** (defined in `UnrealCallbackEventMap`):
267
-
268
- | Event | Payload Type |
269
- | -------------------------- | --------------------------------------------------- |
270
- | `onSceneState` | `FSceneState` |
271
- | `onFocusObject` | `FProductPayload` |
272
- | `cameraChanged` | `FCameraChangedPayload` |
273
- | `onObjectTransformChanged` | `{ objectName: string; transform: FTransformJson }` |
274
- | `onChangeSequence` | `unknown` |
275
- | `onFinishedSequence` | `unknown` |
276
-
277
- ```ts
278
- import { Component, inject } from '@angular/core';
279
- import { UnrealCallbackService } from '@3dsource/angular-unreal-module';
280
- import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
281
- import { MetaBoxCommand } from '@3dsource/types-unreal';
282
-
283
- @Component({ standalone: true, template: '' })
284
- export class MyComponent {
285
- private callbackService = inject(UnrealCallbackService);
286
- private communicator = inject(UnrealCommunicatorService);
287
-
288
- listenForCallbacks() {
289
- // Listen for a MetaBox command callback
290
- this.callbackService.fromUnrealCallback(MetaBoxCommand.FLoadProductCommand).subscribe((data) => console.log('Product loaded:', data));
291
-
292
- // Listen for a custom Unreal event
293
- this.callbackService.fromUnrealCallback('cameraChanged').subscribe((data) => console.log('Camera changed:', data));
294
- }
295
-
296
- sendAndObserve() {
297
- // Send a command and observe its response (with correlationId matching)
298
- this.callbackService
299
- .observeCommandResponse(
300
- { command: MetaBoxCommand.FLoopBackCommand },
301
- (data) => this.communicator.sendCommandToUnreal(data),
302
- 60000, // timeout in ms (default: 60000)
303
- true, // emit on timeout (default: true)
304
- )
305
- .subscribe((response) => console.log('Response:', response));
306
- }
307
- }
308
- ```
309
-
310
- ## Exported API
311
-
312
- ### Components
313
-
314
- | Component | Selector | Description |
315
- | ------------------------------- | ------------------ | -------------------------------------- |
316
- | `UnrealSceneComponent` | `app-unreal-scene` | Main scene container with video stream |
317
- | `AfkTimeoutModalComponent` | — | AFK timeout warning modal |
318
- | `FreezeFrameComponent` | — | Freeze frame overlay |
319
- | `LowBandwidthModalComponent` | — | Low bandwidth warning modal |
320
- | `LowBandwidthDetectorComponent` | — | Low bandwidth detector |
321
- | `ImageLoadingSrcComponent` | — | Loading image overlay |
322
- | `IntroSrcComponent` | — | Intro image/video overlay |
323
- | `VideoStatsComponent` | — | Video statistics display |
324
- | `StatGraphComponent` | — | Statistics graph |
325
- | `WebrtcErrorModalComponent` | — | WebRTC error modal |
326
-
327
- ### Services
328
-
329
- | Service | Description |
330
- | ------------------------------ | --------------------------------------------------------------- |
331
- | `UnrealCommunicatorService` | Send commands and UI interactions to Unreal |
332
- | `UnrealCallbackService` | Listen for Unreal callback events and observe command responses |
333
- | `AggregatorService` | Aggregates data channel messages from Unreal |
334
- | `SignallingService` | Manages WebSocket signalling connection |
335
- | `VideoService` | Manages video element and stats |
336
- | `WebRtcPlayerService` | Manages WebRTC peer connection |
337
- | `FreezeFrameService` | Handles freeze frame images |
338
- | `AFKService` | AFK (away from keyboard) detection and timeout |
339
- | `DevModeService` | Toggle dev mode for debugging |
340
- | `FileReceiverService` | Receives files from Unreal via data channel |
341
- | `FileHandlerService` | Processes received files |
342
- | `RegionsPingService` | Pings regions to determine latency |
343
- | `CommandTelemetryService` | Records command telemetry |
344
- | `StreamStatusTelemetryService` | Reports stream status telemetry |
345
- | `AnalyticsService` | Analytics event tracking |
346
- | `FpsMonitorService` | FPS monitoring |
347
-
348
- ### Pipes
349
-
350
- | Pipe | Description |
351
- | ---------- | ------------------------------------------------------------ |
352
- | `SafePipe` | Sanitizes a value for the requested Angular security context |
353
-
354
- ### Interfaces
355
-
356
- | Interface | Description |
357
- | -------------------------- | ---------------------------------------------------------------------------- |
358
- | `UnrealInitialConfig` | Shape for `UNREAL_CONFIG` injection token |
359
- | `StreamConfig` | Stream configuration (autoStart, warnTimeout) |
360
- | `StreamResolutionProps` | Stream resolution width/height |
361
- | `UnrealCallbackEventMap` | Custom Unreal callback events pushed from Unreal via data channel |
362
- | `UnrealCallbackDescriptor` | Combined map of MetaBoxCommandList and UnrealCallbackEventMap callback types |
363
-
364
- ## Features
365
-
366
- - Standalone Unreal Scene Component
367
- - Command and UI Interaction API via `UnrealCommunicatorService`
368
- - Callback listener and command response observer via `UnrealCallbackService`
369
- - Event-driven status UI (freeze frame, video stats, play overlay, AFK, low bandwidth)
370
- - NgRx-powered state management and effects
371
- - **Required** initial configuration via `UNREAL_CONFIG` injection token
372
- - Auto-reconnection on WebRTC/DataChannel failures (configurable)
373
- - File receiving from Unreal Engine via data channel
374
- - Analytics and FPS monitoring
375
- - Playwright testing mode with mock service substitution
376
-
377
- ## Examples
378
-
379
- Check the demo application for complete usage examples:
380
-
381
- ```shell
382
- pnpm demo:start
383
- ```
384
-
385
- See also: `projects/demo/src/app/demo-layout/info-pages/unreal-scene-demo/constants/unreal.routes.ts` for a real-world provider configuration example.
386
-
387
- ## Repository development
388
-
389
- Run package commands from the repository root:
390
-
391
- ```shell
392
- pnpm --filter @3dsource/angular-unreal-module build
393
- pnpm --filter @3dsource/angular-unreal-module build:watch
394
- pnpm --filter @3dsource/angular-unreal-module test:signalling
395
- ```
396
-
397
- To version and publish:
398
-
399
- ```shell
400
- pnpm release:package -- angular-unreal-module patch latest
401
- ```
402
-
403
- The release command publishes to npm. After verifying the npm release, purge
404
- the matching jsDelivr tag explicitly:
405
-
406
- ```shell
407
- pnpm --filter @3dsource/angular-unreal-module purge-cdn -- latest
408
- ```
409
-
410
- ## Engine requirements
411
-
412
- - Node.js: >=20
1
+ # @3dsource/angular-unreal-module
2
+
3
+ A set of standalone Angular components, services, and providers for integrating Unreal Engine (WebRTC) scenes into Angular applications. It facilitates communication between Angular and Unreal Engine and enables interactive 3D experiences.
4
+
5
+ ## Overview
6
+
7
+ This package provides:
8
+
9
+ - Standalone Unreal scene component to embed UE stream
10
+ - Communication bridge (commands, UI interactions, input data)
11
+ - Callback listener for Unreal events and command responses
12
+ - NgRx state and effects for 3D stream lifecycle
13
+ - Config and utilities for telemetry, errors, and regions ping
14
+ - Auto-reconnection support for WebRTC/DataChannel failures
15
+ - File receiving from Unreal Engine
16
+ - Analytics, FPS monitoring, and stream status telemetry
17
+ - Playwright testing mode with mock services
18
+
19
+ ## Installation
20
+
21
+ ### Prerequisites
22
+
23
+ - Angular `>=19.0.0 <23.0.0`
24
+ - NgRx store and effects `>=19.0.0 <23.0.0`
25
+ - Angular CDK `>=19.0.0 <23.0.0` — used for dialog overlays
26
+ - RxJS `>=7.8.0 <8.0.0`
27
+ - `provideHttpClient()` — required by internal services (telemetry, signalling, regions ping, error reporting)
28
+
29
+ ### Peer Dependencies
30
+
31
+ This library requires the following peer dependencies:
32
+
33
+ ```json
34
+ {
35
+ "@3dsource/source-ui-native": ">=1.0.9",
36
+ "@3dsource/types-unreal": ">=0.0.7",
37
+ "@3dsource/utils": ">=1.0.21",
38
+ "@angular/cdk": ">=19.0.0 <23.0.0",
39
+ "@angular/common": ">=19.0.0 <23.0.0",
40
+ "@angular/core": ">=19.0.0 <23.0.0",
41
+ "@angular/forms": ">=19.0.0 <23.0.0",
42
+ "@angular/platform-browser": ">=19.0.0 <23.0.0",
43
+ "@ngrx/effects": ">=19.0.0 <23.0.0",
44
+ "@ngrx/store": ">=19.0.0 <23.0.0",
45
+ "rxjs": ">=7.8.0 <8.0.0"
46
+ }
47
+ ```
48
+
49
+ ### Library Installation
50
+
51
+ ```shell
52
+ npm i @3dsource/angular-unreal-module
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ The API is fully standalone (no NgModule). Use providers and components as shown below.
58
+
59
+ ### 1) Provide the state and config at the application root
60
+
61
+ Add providers in your application bootstrap (e.g., `app.config.ts`):
62
+
63
+ > **⚠️ Important:** `UNREAL_CONFIG` is **required** — multiple internal services inject it without `{ optional: true }`. Omitting it will cause a `NullInjectorError` at runtime. You can provide it with an empty object `{}` as a minimum.
64
+
65
+ ```ts
66
+ import { ApplicationConfig } from '@angular/core';
67
+ import { provideRouter } from '@angular/router';
68
+ import { provideHttpClient } from '@angular/common/http';
69
+ import { provideStore } from '@ngrx/store';
70
+ import { provideUnrealState, UNREAL_CONFIG } from '@3dsource/angular-unreal-module';
71
+
72
+ export const appConfig: ApplicationConfig = {
73
+ providers: [
74
+ provideRouter([]),
75
+ provideHttpClient(),
76
+
77
+ // Root NgRx (if not already added in your app)
78
+ provideStore(),
79
+ provideUnrealState(),
80
+
81
+ // Required: Unreal initial configuration
82
+ {
83
+ provide: UNREAL_CONFIG,
84
+ useValue: {
85
+ customErrorsEndpoint: '', // Endpoint for custom error reporting
86
+ commandTelemetryReceiver: '', // Endpoint for command telemetry
87
+ regionsPingUrl: '', // URL prefix for regions latency ping
88
+ screenLockerContainerId: '', // DOM container id for screen locker overlay
89
+ dataChannelConnectionTimeout: 8000, // Timeout in ms for data channel connection (default: 8000)
90
+ streamTelemetryV2Url: '', // Endpoint for stream-status telemetry
91
+ fpsMonitor: false, // Enable FPS monitoring
92
+ autoHighResolution: false, // Raise resolution after the scene becomes idle
93
+ playwright: false, // Mirrors the provider flag for services/effects
94
+ },
95
+ },
96
+ ],
97
+ };
98
+ ```
99
+
100
+ #### Minimal configuration
101
+
102
+ If you don't need custom endpoints, provide `UNREAL_CONFIG` with an empty object:
103
+
104
+ ```ts
105
+ { provide: UNREAL_CONFIG, useValue: {} },
106
+ provideUnrealState(),
107
+ ```
108
+
109
+ Every `UnrealInitialConfig` field is optional, but the `UNREAL_CONFIG` provider
110
+ itself is required. Available fields are `playwright`,
111
+ `customErrorsEndpoint`, `commandTelemetryReceiver`, `regionsPingUrl`,
112
+ `screenLockerContainerId`, `dataChannelConnectionTimeout`,
113
+ `streamTelemetryV2Url`, `fpsMonitor` and `autoHighResolution`.
114
+
115
+ ### 2) Boot the engine in each lazy 3D route
116
+
117
+ `provideUnrealModule()` registers the effects and initializes the streaming
118
+ services. Put it on every lazy route that renders a 3D scene:
119
+
120
+ ```ts
121
+ import { Routes } from '@angular/router';
122
+ import { provideUnrealModule } from '@3dsource/angular-unreal-module';
123
+
124
+ export const STREAM_ROUTES: Routes = [
125
+ {
126
+ path: ':id',
127
+ loadComponent: () => import('./stream.component').then((module) => module.StreamComponent),
128
+ providers: [provideUnrealModule()],
129
+ },
130
+ ];
131
+ ```
132
+
133
+ Keep `provideUnrealState()` and `UNREAL_CONFIG` at the root. Keeping
134
+ `provideUnrealModule()` in lazy routes also keeps the streaming engine out of
135
+ the initial application bundle.
136
+
137
+ ### 3) Use the Unreal scene component
138
+
139
+ Import the component into a standalone component and use it in the template.
140
+
141
+ ```ts
142
+ import { Component } from '@angular/core';
143
+ import { UnrealSceneComponent } from '@3dsource/angular-unreal-module';
144
+
145
+ @Component({
146
+ selector: 'app-root',
147
+ standalone: true,
148
+ imports: [UnrealSceneComponent],
149
+ template: ` <app-unreal-scene [isStudio]="false" [useContainerAsSizeProvider]="true" [resolutionSize]="{ width: 1920, height: 1080 }" (changeMouseOverScene)="onHover($event)" /> `,
150
+ })
151
+ export class AppComponent {
152
+ onHover(isOver: boolean) {
153
+ // handle mouse over scene
154
+ }
155
+ }
156
+ ```
157
+
158
+ Component selector: `<app-unreal-scene>`
159
+
160
+ Inputs:
161
+
162
+ | Input | Type | Default |
163
+ | ---------------------------- | ----------------------------------- | ------------------------------- |
164
+ | `isStudio` | `boolean` | `false` |
165
+ | `useContainerAsSizeProvider` | `boolean` | `true` |
166
+ | `resolutionSize` | `{ width: number; height: number }` | `{ width: 1920, height: 1080 }` |
167
+
168
+ Outputs:
169
+
170
+ | Output | Type |
171
+ | ---------------------- | --------------------------- |
172
+ | `changeMouseOverScene` | `OutputEmitterRef<boolean>` |
173
+
174
+ ### 4) Send commands / interactions to Unreal
175
+
176
+ Inject `UnrealCommunicatorService` to send commands or UI interactions. Types for command packets are provided by `@3dsource/types-unreal`.
177
+
178
+ There are three sending methods — choose based on your needs:
179
+
180
+ | Method | Description |
181
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
182
+ | `sendCommandToUnreal` | Full pipeline: adds `correlationId`, records telemetry, dispatches NgRx `commandStarted` action. **Recommended for application commands.** |
183
+ | `emitUIInteraction` | Sends the packet as a raw `UIInteraction` message. No telemetry or store dispatch. |
184
+ | `emitCommand` | Sends the packet as a raw `Command` message (for console commands, resolution changes, etc.). |
185
+
186
+ ```ts
187
+ import { Component, inject } from '@angular/core';
188
+ import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
189
+ import { MetaBoxCommand } from '@3dsource/types-unreal';
190
+ import type { MetaBoxCommandPacket } from '@3dsource/types-unreal';
191
+
192
+ @Component({ standalone: true, template: '' })
193
+ export class MyComponent {
194
+ private unreal = inject(UnrealCommunicatorService);
195
+
196
+ sendSomeCommand() {
197
+ // Recommended: use sendCommandToUnreal for full telemetry + store tracking
198
+ this.unreal.sendCommandToUnreal({
199
+ command: MetaBoxCommand.FChangeResolutionCommand,
200
+ payload: { resolution: { x: 1920, y: 1080 } },
201
+ });
202
+ }
203
+
204
+ sendRawUIInteraction() {
205
+ // Low-level: sends UIInteraction message without telemetry tracking
206
+ const packet = {
207
+ command: 'CustomCommand',
208
+ payload: { key: 'value' },
209
+ } as MetaBoxCommandPacket;
210
+ this.unreal.emitUIInteraction(packet);
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### 5) Store integration (actions & selectors)
216
+
217
+ `provideUnrealState()` registers the NgRx feature state `unrealFeature`. You
218
+ can dispatch actions and select state in your components:
219
+
220
+ ```ts
221
+ import { inject } from '@angular/core';
222
+ import { Store } from '@ngrx/store';
223
+ import { disconnectStream, selectShowLoader, selectTotalProgress, setConfig, setOrchestrationContext, startStream, unrealFeature } from '@3dsource/angular-unreal-module';
224
+
225
+ // Dispatch actions
226
+ const store = inject(Store);
227
+ store.dispatch(startStream({ config: { autoStart: true, warnTimeout: 120 } }));
228
+
229
+ // Select state
230
+ const progress = store.selectSignal(selectTotalProgress);
231
+ const isVideoPlaying = store.selectSignal(unrealFeature.selectIsVideoPlaying);
232
+ const dataChannelConnected = store.selectSignal(unrealFeature.selectDataChannelConnected);
233
+ ```
234
+
235
+ **Key selectors:**
236
+
237
+ - `selectTotalProgress` — Scene load progress (0–1 float)
238
+ - `selectShowLoader` — Whether loader screen should be visible
239
+ - `selectShowReconnectPopup` — Whether reconnect popup should be shown
240
+ - `selectIsVideoPlayingAndDataChannelConnected` — Combined readiness check
241
+ - `selectStreamConfig` — Current stream configuration
242
+ - `unrealFeature.selectCirrusConnected` — Signalling server connection status
243
+ - `unrealFeature.selectDataChannelConnected` — Data channel status
244
+ - `unrealFeature.selectViewportReady` — Viewport readiness
245
+
246
+ **Key actions:**
247
+
248
+ - `startStream` — Start streaming with config
249
+ - `setConfig` — Update stream configuration
250
+ - `setOrchestrationContext` — Set orchestration URLs and environment
251
+ - `disconnectStream` — Disconnect with reason
252
+ - `destroyUnrealScene` — Full teardown
253
+ - `reconnectPeer` — Trigger peer reconnection
254
+
255
+ ### 6) Listen for Unreal callbacks
256
+
257
+ Inject `UnrealCallbackService` to listen for callback events from Unreal Engine and to observe command responses.
258
+
259
+ There are two methods:
260
+
261
+ | Method | Description |
262
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
+ | `fromUnrealCallback` | Listens for callbacks matching a command or event key. Supports MetaBox commands and custom Unreal callback events. Returns `Observable<UnrealCallbackDescriptor[K]>`. |
264
+ | `observeCommandResponse` | Sends a command and waits for its matching response by `correlationId`. Returns `Observable<MetaBoxCommandList[K]>`. Includes timeout and error handling. |
265
+
266
+ **Custom Unreal callback events** (defined in `UnrealCallbackEventMap`):
267
+
268
+ | Event | Payload Type |
269
+ | -------------------------- | --------------------------------------------------- |
270
+ | `onSceneState` | `FSceneState` |
271
+ | `onFocusObject` | `FProductPayload` |
272
+ | `cameraChanged` | `FCameraChangedPayload` |
273
+ | `onObjectTransformChanged` | `{ objectName: string; transform: FTransformJson }` |
274
+ | `onChangeSequence` | `unknown` |
275
+ | `onFinishedSequence` | `unknown` |
276
+
277
+ ```ts
278
+ import { Component, inject } from '@angular/core';
279
+ import { UnrealCallbackService } from '@3dsource/angular-unreal-module';
280
+ import { UnrealCommunicatorService } from '@3dsource/angular-unreal-module';
281
+ import { MetaBoxCommand } from '@3dsource/types-unreal';
282
+
283
+ @Component({ standalone: true, template: '' })
284
+ export class MyComponent {
285
+ private callbackService = inject(UnrealCallbackService);
286
+ private communicator = inject(UnrealCommunicatorService);
287
+
288
+ listenForCallbacks() {
289
+ // Listen for a MetaBox command callback
290
+ this.callbackService.fromUnrealCallback(MetaBoxCommand.FLoadProductCommand).subscribe((data) => console.log('Product loaded:', data));
291
+
292
+ // Listen for a custom Unreal event
293
+ this.callbackService.fromUnrealCallback('cameraChanged').subscribe((data) => console.log('Camera changed:', data));
294
+ }
295
+
296
+ sendAndObserve() {
297
+ // Send a command and observe its response (with correlationId matching)
298
+ this.callbackService
299
+ .observeCommandResponse(
300
+ { command: MetaBoxCommand.FLoopBackCommand },
301
+ (data) => this.communicator.sendCommandToUnreal(data),
302
+ 60000, // timeout in ms (default: 60000)
303
+ true, // emit on timeout (default: true)
304
+ )
305
+ .subscribe((response) => console.log('Response:', response));
306
+ }
307
+ }
308
+ ```
309
+
310
+ ## Exported API
311
+
312
+ ### Components
313
+
314
+ | Component | Selector | Description |
315
+ | ------------------------------- | ------------------ | -------------------------------------- |
316
+ | `UnrealSceneComponent` | `app-unreal-scene` | Main scene container with video stream |
317
+ | `AfkTimeoutModalComponent` | — | AFK timeout warning modal |
318
+ | `FreezeFrameComponent` | — | Freeze frame overlay |
319
+ | `LowBandwidthModalComponent` | — | Low bandwidth warning modal |
320
+ | `LowBandwidthDetectorComponent` | — | Low bandwidth detector |
321
+ | `ImageLoadingSrcComponent` | — | Loading image overlay |
322
+ | `IntroSrcComponent` | — | Intro image/video overlay |
323
+ | `VideoStatsComponent` | — | Video statistics display |
324
+ | `StatGraphComponent` | — | Statistics graph |
325
+ | `WebrtcErrorModalComponent` | — | WebRTC error modal |
326
+
327
+ ### Services
328
+
329
+ | Service | Description |
330
+ | ------------------------------ | --------------------------------------------------------------- |
331
+ | `UnrealCommunicatorService` | Send commands and UI interactions to Unreal |
332
+ | `UnrealCallbackService` | Listen for Unreal callback events and observe command responses |
333
+ | `AggregatorService` | Aggregates data channel messages from Unreal |
334
+ | `SignallingService` | Manages WebSocket signalling connection |
335
+ | `VideoService` | Manages video element and stats |
336
+ | `WebRtcPlayerService` | Manages WebRTC peer connection |
337
+ | `FreezeFrameService` | Handles freeze frame images |
338
+ | `AFKService` | AFK (away from keyboard) detection and timeout |
339
+ | `DevModeService` | Toggle dev mode for debugging |
340
+ | `FileReceiverService` | Receives files from Unreal via data channel |
341
+ | `FileHandlerService` | Processes received files |
342
+ | `RegionsPingService` | Pings regions to determine latency |
343
+ | `CommandTelemetryService` | Records command telemetry |
344
+ | `StreamStatusTelemetryService` | Reports stream status telemetry |
345
+ | `AnalyticsService` | Analytics event tracking |
346
+ | `FpsMonitorService` | FPS monitoring |
347
+
348
+ ### Pipes
349
+
350
+ | Pipe | Description |
351
+ | ---------- | ------------------------------------------------------------ |
352
+ | `SafePipe` | Sanitizes a value for the requested Angular security context |
353
+
354
+ ### Interfaces
355
+
356
+ | Interface | Description |
357
+ | -------------------------- | ---------------------------------------------------------------------------- |
358
+ | `UnrealInitialConfig` | Shape for `UNREAL_CONFIG` injection token |
359
+ | `StreamConfig` | Stream configuration (autoStart, warnTimeout) |
360
+ | `StreamResolutionProps` | Stream resolution width/height |
361
+ | `UnrealCallbackEventMap` | Custom Unreal callback events pushed from Unreal via data channel |
362
+ | `UnrealCallbackDescriptor` | Combined map of MetaBoxCommandList and UnrealCallbackEventMap callback types |
363
+
364
+ ## Features
365
+
366
+ - Standalone Unreal Scene Component
367
+ - Command and UI Interaction API via `UnrealCommunicatorService`
368
+ - Callback listener and command response observer via `UnrealCallbackService`
369
+ - Event-driven status UI (freeze frame, video stats, play overlay, AFK, low bandwidth)
370
+ - NgRx-powered state management and effects
371
+ - **Required** initial configuration via `UNREAL_CONFIG` injection token
372
+ - Auto-reconnection on WebRTC/DataChannel failures (configurable)
373
+ - File receiving from Unreal Engine via data channel
374
+ - Analytics and FPS monitoring
375
+ - Playwright testing mode with mock service substitution
376
+
377
+ ## Examples
378
+
379
+ Check the demo application for complete usage examples:
380
+
381
+ ```shell
382
+ pnpm demo:start
383
+ ```
384
+
385
+ See also: `projects/demo/src/app/demo-layout/info-pages/unreal-scene-demo/constants/unreal.routes.ts` for a real-world provider configuration example.
386
+
387
+ ## Repository development
388
+
389
+ Run package commands from the repository root:
390
+
391
+ ```shell
392
+ pnpm unreal:build
393
+ pnpm unreal:build:watch
394
+ pnpm unreal:lint
395
+ pnpm unreal:test
396
+ pnpm unreal:test:watch
397
+ pnpm unreal:test:signalling
398
+ pnpm unreal:test:signalling:leaks
399
+ ```
400
+
401
+ `unreal:build` builds `source-ui-native`, `types-unreal` and `utils` before this
402
+ package. `unreal:build:watch` performs that initial build automatically and then
403
+ watches `angular-unreal-module`.
404
+
405
+ To version and publish:
406
+
407
+ ```shell
408
+ pnpm unreal:release:patch
409
+ pnpm unreal:release:dev
410
+ ```
411
+
412
+ The release command publishes to npm. After verifying the npm release, purge
413
+ the matching jsDelivr tag explicitly:
414
+
415
+ ```shell
416
+ pnpm unreal:purge-cdn -- latest
417
+ ```
418
+
419
+ ## Engine requirements
420
+
421
+ - Node.js: >=24.16.0
422
+ - pnpm: 11.17.0 (activated from the root `packageManager` field via Corepack)