@3dsource/angular-unreal-module 0.0.143 → 0.0.145

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.
@@ -3249,6 +3249,20 @@ class WebRtcPlayerService extends SubService {
3249
3249
  this.remoteDescriptionSet = false;
3250
3250
  this.iceCandidateBuffer.length = 0;
3251
3251
  }
3252
+ /**
3253
+ * Single source of truth for tearing down the remote connection: closes the
3254
+ * signaling WebSocket and then the PeerConnection/DataChannel.
3255
+ *
3256
+ * Called by the `destroyRemoteConnections$` effect and, as a
3257
+ * lifecycle-independent safeguard, by `UnrealSceneComponent.onDestroy` (the
3258
+ * route-scoped effects may already be torn down on navigation). Both close
3259
+ * steps are idempotent, so invoking this twice in one teardown is a harmless
3260
+ * no-op.
3261
+ */
3262
+ releaseRemoteConnections(reason) {
3263
+ this.signallingSrv.close({ code: WSCloseCode_FORCE_CIRRUS_CLOSE, reason });
3264
+ this.closePC();
3265
+ }
3252
3266
  canSend() {
3253
3267
  return this.dcClient && this.dcClient.readyState === 'open';
3254
3268
  }
@@ -3816,11 +3830,7 @@ class UnrealEffects {
3816
3830
  });
3817
3831
  this.destroyRemoteConnections$ = createEffect(() => {
3818
3832
  return this.actions$.pipe(ofType(destroyRemoteConnections), tap(({ reason }) => {
3819
- this.signallingService.close({
3820
- code: WSCloseCode_FORCE_CIRRUS_CLOSE,
3821
- reason,
3822
- });
3823
- this.webRtcPlayerService.closePC();
3833
+ this.webRtcPlayerService.releaseRemoteConnections(reason);
3824
3834
  }), filter(({ reason }) => reason === DisconnectReason.DropConnection), map(() => dropConnection()));
3825
3835
  });
3826
3836
  this.destroyConnectionAndRestart$ = createEffect(() => {
@@ -4732,15 +4742,6 @@ const RESIZE_DEBOUNCE_MS = 300;
4732
4742
  const FUNCTION_KEY_MIN = 112;
4733
4743
  const FUNCTION_KEY_MAX = 123;
4734
4744
  const TAB_KEY_CODE = 9;
4735
- // Browser-destructive / navigation keys (Backspace can navigate back, Enter and
4736
- // Delete leak into the stream). These are ignored in the viewport regardless of
4737
- // dev mode — unless an HTML input is focused — to match non-dev-mode behavior.
4738
- const BROWSER_DESTRUCTIVE_CODES = new Set([
4739
- 'Backspace',
4740
- 'Delete',
4741
- 'Enter',
4742
- 'NumpadEnter',
4743
- ]);
4744
4745
  class InputService extends SubService {
4745
4746
  constructor() {
4746
4747
  super();
@@ -5166,15 +5167,9 @@ class InputService extends SubService {
5166
5167
  };
5167
5168
  this.startListenKeys(keyBlock);
5168
5169
  }
5169
- /**
5170
- * Keys that must never reach the Unreal stream from the viewport — Backspace
5171
- * (browser back navigation), Enter and Delete — regardless of dev mode. An
5172
- * HTML input keeps them so text entry still works; only the viewport ignores
5173
- * them. Applied independently of the configurable key filter.
5174
- */
5175
5170
  isIgnoredViewportKey(event) {
5176
5171
  const isInputFieldFocused = event.target?.localName === 'input';
5177
- return !isInputFieldFocused && BROWSER_DESTRUCTIVE_CODES.has(event.code);
5172
+ return !isInputFieldFocused;
5178
5173
  }
5179
5174
  startListenKeys(_filter) {
5180
5175
  const filter = _filter ?? (() => false);
@@ -7186,26 +7181,19 @@ class UnrealSceneComponent {
7186
7181
  this.commandsSender = inject(UnrealCommunicatorService);
7187
7182
  this.videoService = inject(VideoService);
7188
7183
  this.webRtcPlayerService = inject(WebRtcPlayerService);
7189
- this.signallingService = inject(SignallingService);
7190
7184
  this.destroyRef = inject(DestroyRef);
7191
7185
  this.unrealConfig = inject(UNREAL_CONFIG);
7192
7186
  this.autoHighResolution = false;
7193
7187
  this.destroyRef.onDestroy(() => {
7194
- // Mirror of the `destroyRemoteConnections$` effect's teardown tap, run
7195
- // directly here as a safeguard. provideUnrealModule (effects) is
7196
- // registered per 3D route, so on navigation the route's environment
7197
- // injector — and its UnrealEffects — may be torn down before the
7188
+ // Release the remote connection synchronously, independent of the
7189
+ // route-scoped UnrealEffects. provideUnrealModule (effects) is registered
7190
+ // per 3D route, so on navigation the route's environment injector — and
7191
+ // its UnrealEffects — may be torn down before the
7198
7192
  // `destroyUnrealScene → destroyRemoteConnections` effect chain closes the
7199
- // WebSocket/PeerConnection, which would leak the stream session. The
7200
- // engine services are providedIn:'root' singletons, so we close them
7201
- // directly. Keep the SAME calls, args and order as the effect's tap
7202
- // (close → closePC); both are idempotent, so whichever runs second is a
7203
- // harmless no-op.
7204
- this.signallingService.close({
7205
- code: WSCloseCode_FORCE_CIRRUS_CLOSE,
7206
- reason: DisconnectReason.Destroy,
7207
- });
7208
- this.webRtcPlayerService.closePC();
7193
+ // WebSocket/PeerConnection, which would leak the stream session. We call
7194
+ // the same root-singleton method the effect uses, so the two can never
7195
+ // drift; it's idempotent, so whichever runs second is a harmless no-op.
7196
+ this.webRtcPlayerService.releaseRemoteConnections(DisconnectReason.Destroy);
7209
7197
  // State cleanup is reducer-driven, not effect-driven: the
7210
7198
  // `destroyUnrealScene` reducer resets the whole UnrealState to
7211
7199
  // initialState (cirrusConnected/viewportReady/… → false). Reducers run in