@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.5

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.
Files changed (138) hide show
  1. package/.cspell.json +48 -0
  2. package/.eslintignore +8 -0
  3. package/.eslintrc.js +8 -0
  4. package/.prettierignore +0 -0
  5. package/.prettierrc.json +6 -0
  6. package/dist/lib-pixelstreamingfrontend.esm.js +1 -0
  7. package/dist/lib-pixelstreamingfrontend.js +1 -0
  8. package/jest.config.js +18 -0
  9. package/package.json +48 -0
  10. package/readme.md +15 -0
  11. package/src/AFK/AFKController.test.ts +162 -0
  12. package/src/AFK/AFKController.ts +158 -0
  13. package/src/Config/Config.test.ts +222 -0
  14. package/src/Config/Config.ts +970 -0
  15. package/src/Config/SettingBase.ts +65 -0
  16. package/src/Config/SettingFlag.ts +99 -0
  17. package/src/Config/SettingNumber.ts +111 -0
  18. package/src/Config/SettingOption.ts +124 -0
  19. package/src/Config/SettingText.ts +82 -0
  20. package/src/DataChannel/DataChannelController.ts +138 -0
  21. package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
  22. package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
  23. package/src/DataChannel/DataChannelSender.ts +59 -0
  24. package/src/DataChannel/InitialSettings.ts +61 -0
  25. package/src/DataChannel/LatencyTestResults.ts +76 -0
  26. package/src/FreezeFrame/FreezeFrame.ts +114 -0
  27. package/src/FreezeFrame/FreezeFrameController.ts +114 -0
  28. package/src/Inputs/FakeTouchController.ts +199 -0
  29. package/src/Inputs/GamepadController.ts +314 -0
  30. package/src/Inputs/GamepadTypes.ts +10 -0
  31. package/src/Inputs/HoveringMouseEvents.ts +192 -0
  32. package/src/Inputs/IMouseEvents.ts +64 -0
  33. package/src/Inputs/ITouchController.ts +29 -0
  34. package/src/Inputs/InputClassesFactory.ts +140 -0
  35. package/src/Inputs/KeyboardController.ts +354 -0
  36. package/src/Inputs/LockedMouseEvents.ts +287 -0
  37. package/src/Inputs/MouseButtons.ts +25 -0
  38. package/src/Inputs/MouseController.ts +362 -0
  39. package/src/Inputs/SpecialKeyCodes.ts +16 -0
  40. package/src/Inputs/TouchController.ts +208 -0
  41. package/src/Inputs/XRGamepadController.ts +126 -0
  42. package/src/PeerConnectionController/AggregatedStats.ts +311 -0
  43. package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
  44. package/src/PeerConnectionController/CandidateStat.ts +13 -0
  45. package/src/PeerConnectionController/CodecStats.ts +19 -0
  46. package/src/PeerConnectionController/DataChannelStats.ts +17 -0
  47. package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
  48. package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
  49. package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
  50. package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
  51. package/src/PeerConnectionController/SessionStats.ts +10 -0
  52. package/src/PeerConnectionController/StreamStats.ts +11 -0
  53. package/src/PixelStreaming/PixelStreaming.test.ts +626 -0
  54. package/src/PixelStreaming/PixelStreaming.ts +851 -0
  55. package/src/UI/OnScreenKeyboard.ts +97 -0
  56. package/src/UeInstanceMessage/ResponseController.ts +47 -0
  57. package/src/UeInstanceMessage/SendMessageController.ts +154 -0
  58. package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
  59. package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
  60. package/src/Util/CoordinateConverter.ts +289 -0
  61. package/src/Util/EventEmitter.ts +611 -0
  62. package/src/Util/EventListenerTracker.ts +29 -0
  63. package/src/Util/FileUtil.ts +140 -0
  64. package/src/Util/RTCUtils.ts +41 -0
  65. package/src/Util/WebGLUtils.ts +49 -0
  66. package/src/Util/WebXRUtils.ts +25 -0
  67. package/src/VideoPlayer/StreamController.ts +89 -0
  68. package/src/VideoPlayer/VideoPlayer.ts +246 -0
  69. package/src/WebRtcPlayer/WebRtcPlayerController.ts +2158 -0
  70. package/src/WebXR/WebXRController.ts +319 -0
  71. package/src/__test__/mockMediaStream.ts +124 -0
  72. package/src/__test__/mockRTCPeerConnection.ts +347 -0
  73. package/src/__test__/mockRTCRtpReceiver.ts +22 -0
  74. package/src/__test__/mockWebSocket.ts +136 -0
  75. package/src/pixelstreamingfrontend.ts +46 -0
  76. package/tsconfig.jest.json +8 -0
  77. package/tsconfig.json +24 -0
  78. package/types/AFK/AFKController.d.ts +39 -0
  79. package/types/Config/Config.d.ts +218 -0
  80. package/types/Config/SettingBase.d.ts +30 -0
  81. package/types/Config/SettingFlag.d.ts +33 -0
  82. package/types/Config/SettingNumber.d.ts +45 -0
  83. package/types/Config/SettingOption.d.ts +43 -0
  84. package/types/Config/SettingText.d.ts +29 -0
  85. package/types/DataChannel/DataChannelController.d.ts +59 -0
  86. package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
  87. package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
  88. package/types/DataChannel/DataChannelSender.d.ts +21 -0
  89. package/types/DataChannel/InitialSettings.d.ts +44 -0
  90. package/types/DataChannel/LatencyTestResults.d.ts +31 -0
  91. package/types/FreezeFrame/FreezeFrame.d.ts +36 -0
  92. package/types/FreezeFrame/FreezeFrameController.d.ts +37 -0
  93. package/types/Inputs/FakeTouchController.d.ts +61 -0
  94. package/types/Inputs/GamepadController.d.ts +85 -0
  95. package/types/Inputs/GamepadTypes.d.ts +8 -0
  96. package/types/Inputs/HoveringMouseEvents.d.ts +56 -0
  97. package/types/Inputs/IMouseEvents.d.ts +53 -0
  98. package/types/Inputs/ITouchController.d.ts +24 -0
  99. package/types/Inputs/InputClassesFactory.d.ts +54 -0
  100. package/types/Inputs/KeyboardController.d.ts +62 -0
  101. package/types/Inputs/LockedMouseEvents.d.ts +80 -0
  102. package/types/Inputs/MouseButtons.d.ts +22 -0
  103. package/types/Inputs/MouseController.d.ts +75 -0
  104. package/types/Inputs/SpecialKeyCodes.d.ts +14 -0
  105. package/types/Inputs/TouchController.d.ts +53 -0
  106. package/types/Inputs/XRGamepadController.d.ts +15 -0
  107. package/types/PeerConnectionController/AggregatedStats.d.ts +77 -0
  108. package/types/PeerConnectionController/CandidatePairStats.d.ts +15 -0
  109. package/types/PeerConnectionController/CandidateStat.d.ts +11 -0
  110. package/types/PeerConnectionController/CodecStats.d.ts +14 -0
  111. package/types/PeerConnectionController/DataChannelStats.d.ts +15 -0
  112. package/types/PeerConnectionController/InboundRTPStats.d.ts +141 -0
  113. package/types/PeerConnectionController/InboundTrackStats.d.ts +32 -0
  114. package/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -0
  115. package/types/PeerConnectionController/PeerConnectionController.d.ts +132 -0
  116. package/types/PeerConnectionController/SessionStats.d.ts +8 -0
  117. package/types/PeerConnectionController/StreamStats.d.ts +9 -0
  118. package/types/PixelStreaming/PixelStreaming.d.ts +259 -0
  119. package/types/UI/OnScreenKeyboard.d.ts +31 -0
  120. package/types/UeInstanceMessage/ResponseController.d.ts +19 -0
  121. package/types/UeInstanceMessage/SendMessageController.d.ts +18 -0
  122. package/types/UeInstanceMessage/StreamMessageController.d.ts +29 -0
  123. package/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -0
  124. package/types/Util/CoordinateConverter.d.ts +100 -0
  125. package/types/Util/EventEmitter.d.ts +422 -0
  126. package/types/Util/EventListenerTracker.d.ts +14 -0
  127. package/types/Util/FileUtil.d.ts +32 -0
  128. package/types/Util/RTCUtils.d.ts +8 -0
  129. package/types/Util/WebGLUtils.d.ts +4 -0
  130. package/types/Util/WebXRUtils.d.ts +9 -0
  131. package/types/VideoPlayer/StreamController.d.ts +24 -0
  132. package/types/VideoPlayer/VideoPlayer.d.ts +78 -0
  133. package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +377 -0
  134. package/types/WebXR/WebXRController.d.ts +26 -0
  135. package/types/pixelstreamingfrontend.d.ts +22 -0
  136. package/webpack.common.js +35 -0
  137. package/webpack.dev.js +35 -0
  138. package/webpack.prod.js +36 -0
@@ -0,0 +1,199 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { CoordinateConverter } from '../Util/CoordinateConverter';
4
+ import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
5
+ import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
6
+ import { ITouchController } from './ITouchController';
7
+ import { MouseButton } from './MouseButtons';
8
+ import { EventListenerTracker } from '../Util/EventListenerTracker';
9
+
10
+ /**
11
+ * Allows for the usage of fake touch events and implements ITouchController
12
+ * @param dataChannelController - The controller for the Data channel
13
+ * @param videoElementParent - The video player DOM element
14
+ */
15
+ export class FakeTouchController implements ITouchController {
16
+ fakeTouchFinger: FakeTouchFinger;
17
+ toStreamerMessagesProvider: StreamMessageController;
18
+ videoElementProvider: VideoPlayer;
19
+ coordinateConverter: CoordinateConverter;
20
+ videoElementParentClientRect: DOMRect;
21
+
22
+ // Utility for keeping track of event handlers and unregistering them
23
+ private touchEventListenerTracker = new EventListenerTracker();
24
+
25
+ /**
26
+ * @param toStreamerMessagesProvider - Stream message instance
27
+ * @param videoElementProvider - Video element instance
28
+ * @param coordinateConverter - A coordinate converter instance
29
+ */
30
+ constructor(
31
+ toStreamerMessagesProvider: StreamMessageController,
32
+ videoElementProvider: VideoPlayer,
33
+ coordinateConverter: CoordinateConverter
34
+ ) {
35
+ this.toStreamerMessagesProvider = toStreamerMessagesProvider;
36
+ this.videoElementProvider = videoElementProvider;
37
+ this.coordinateConverter = coordinateConverter;
38
+ const ontouchstart = (ev: TouchEvent) => this.onTouchStart(ev);
39
+ const ontouchend = (ev: TouchEvent) => this.onTouchEnd(ev);
40
+ const ontouchmove = (ev: TouchEvent) => this.onTouchMove(ev);
41
+ document.addEventListener('touchstart', ontouchstart, { passive: false });
42
+ document.addEventListener('touchend', ontouchend, { passive: false });
43
+ document.addEventListener('touchmove', ontouchmove, { passive: false });
44
+ this.touchEventListenerTracker.addUnregisterCallback(
45
+ () => document.removeEventListener('touchstart', ontouchstart)
46
+ );
47
+ this.touchEventListenerTracker.addUnregisterCallback(
48
+ () => document.removeEventListener('touchend', ontouchend)
49
+ );
50
+ this.touchEventListenerTracker.addUnregisterCallback(
51
+ () => document.removeEventListener('touchmove', ontouchmove)
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Unregister all touch events
57
+ */
58
+ unregisterTouchEvents() {
59
+ this.touchEventListenerTracker.unregisterAll();
60
+ }
61
+
62
+ /**
63
+ * Sets the video Element Parent Client Rect numbers for this class
64
+ * @param videoElementParentClientRect - a html ElementParentClientRect object
65
+ */
66
+ setVideoElementParentClientRect(videoElementParentClientRect: DOMRect) {
67
+ this.videoElementParentClientRect = videoElementParentClientRect;
68
+ }
69
+
70
+ /**
71
+ * When a touch event begins
72
+ * @param touch - the activating touch event
73
+ */
74
+ onTouchStart(touch: TouchEvent): void {
75
+ if (!this.videoElementProvider.isVideoReady() || touch.target !== this.videoElementProvider.getVideoElement()) {
76
+ return;
77
+ }
78
+ if (this.fakeTouchFinger == null) {
79
+ const first_touch = touch.changedTouches[0];
80
+ this.fakeTouchFinger = new FakeTouchFinger(
81
+ first_touch.identifier,
82
+ first_touch.clientX - this.videoElementParentClientRect.left,
83
+ first_touch.clientY - this.videoElementParentClientRect.top
84
+ );
85
+
86
+ const videoElementParent =
87
+ this.videoElementProvider.getVideoParentElement() as HTMLDivElement;
88
+ const mouseEvent = new MouseEvent('mouseenter', first_touch);
89
+ videoElementParent.dispatchEvent(mouseEvent);
90
+
91
+ const coord = this.coordinateConverter.normalizeAndQuantizeUnsigned(
92
+ this.fakeTouchFinger.x,
93
+ this.fakeTouchFinger.y
94
+ );
95
+ const toStreamerHandlers =
96
+ this.toStreamerMessagesProvider.toStreamerHandlers;
97
+ toStreamerHandlers.get('MouseDown')([
98
+ MouseButton.mainButton,
99
+ coord.x,
100
+ coord.y
101
+ ]);
102
+ }
103
+ touch.preventDefault();
104
+ }
105
+
106
+ /**
107
+ * When a touch event ends
108
+ * @param touchEvent - the activating touch event
109
+ */
110
+ onTouchEnd(touchEvent: TouchEvent): void {
111
+ if (!this.videoElementProvider.isVideoReady() || this.fakeTouchFinger == null) {
112
+ return;
113
+ }
114
+ const videoElementParent =
115
+ this.videoElementProvider.getVideoParentElement();
116
+ const toStreamerHandlers =
117
+ this.toStreamerMessagesProvider.toStreamerHandlers;
118
+
119
+ for (let t = 0; t < touchEvent.changedTouches.length; t++) {
120
+ const touch = touchEvent.changedTouches[t];
121
+ if (touch.identifier === this.fakeTouchFinger.id) {
122
+ const x =
123
+ touch.clientX - this.videoElementParentClientRect.left;
124
+ const y = touch.clientY - this.videoElementParentClientRect.top;
125
+ const coord =
126
+ this.coordinateConverter.normalizeAndQuantizeUnsigned(x, y);
127
+ toStreamerHandlers.get('MouseUp')([
128
+ MouseButton.mainButton,
129
+ coord.x,
130
+ coord.y
131
+ ]);
132
+
133
+ const mouseEvent = new MouseEvent('mouseleave', touch);
134
+ videoElementParent.dispatchEvent(mouseEvent);
135
+ this.fakeTouchFinger = null;
136
+ break;
137
+ }
138
+ }
139
+ touchEvent.preventDefault();
140
+ }
141
+
142
+ /**
143
+ * On a Move touch event
144
+ * @param touchEvent - the activating touch event
145
+ */
146
+ onTouchMove(touchEvent: TouchEvent): void {
147
+ if (!this.videoElementProvider.isVideoReady() || this.fakeTouchFinger == null) {
148
+ return;
149
+ }
150
+ const toStreamerHandlers =
151
+ this.toStreamerMessagesProvider.toStreamerHandlers;
152
+
153
+ for (let t = 0; t < touchEvent.touches.length; t++) {
154
+ const touch = touchEvent.touches[t];
155
+ if (touch.identifier === this.fakeTouchFinger.id) {
156
+ const x =
157
+ touch.clientX - this.videoElementParentClientRect.left;
158
+ const y = touch.clientY - this.videoElementParentClientRect.top;
159
+ const coord =
160
+ this.coordinateConverter.normalizeAndQuantizeUnsigned(x, y);
161
+ const delta =
162
+ this.coordinateConverter.normalizeAndQuantizeSigned(
163
+ x - this.fakeTouchFinger.x,
164
+ y - this.fakeTouchFinger.y
165
+ );
166
+ toStreamerHandlers.get('MouseMove')([
167
+ coord.x,
168
+ coord.y,
169
+ delta.x,
170
+ delta.y
171
+ ]);
172
+ this.fakeTouchFinger.x = x;
173
+ this.fakeTouchFinger.y = y;
174
+ break;
175
+ }
176
+ }
177
+ touchEvent.preventDefault();
178
+ }
179
+ }
180
+
181
+ /**
182
+ * The interface for finger position mapping
183
+ */
184
+ export class FakeTouchFinger {
185
+ id: number;
186
+ x: number;
187
+ y: number;
188
+
189
+ /**
190
+ * @param id - the button id
191
+ * @param x - the x axis value
192
+ * @param y - the y axis value
193
+ */
194
+ constructor(id: number, x: number, y: number) {
195
+ this.id = id;
196
+ this.x = x;
197
+ this.y = y;
198
+ }
199
+ }
@@ -0,0 +1,314 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
4
+ import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
5
+ import { EventListenerTracker } from '../Util/EventListenerTracker';
6
+ import { Controller } from './GamepadTypes';
7
+
8
+ /**
9
+ * The class that handles the functionality of gamepads and controllers
10
+ */
11
+ export class GamePadController {
12
+ controllers: Array<Controller>;
13
+ requestAnimationFrame: (callback: FrameRequestCallback) => number;
14
+ toStreamerMessagesProvider: StreamMessageController;
15
+
16
+ // Utility for keeping track of event handlers and unregistering them
17
+ private gamePadEventListenerTracker = new EventListenerTracker();
18
+
19
+ /**
20
+ * @param toStreamerMessagesProvider - Stream message instance
21
+ */
22
+ constructor(toStreamerMessagesProvider: StreamMessageController) {
23
+ this.toStreamerMessagesProvider = toStreamerMessagesProvider;
24
+
25
+ this.requestAnimationFrame = (
26
+ window.mozRequestAnimationFrame ||
27
+ window.webkitRequestAnimationFrame ||
28
+ window.requestAnimationFrame
29
+ ).bind(window);
30
+ const browserWindow = window as Window;
31
+
32
+ const onBeforeUnload = (ev: Event) =>
33
+ this.onBeforeUnload(ev);
34
+ window.addEventListener('beforeunload', onBeforeUnload);
35
+
36
+ if ('GamepadEvent' in browserWindow) {
37
+ const onGamePadConnected = (ev: GamepadEvent) =>
38
+ this.gamePadConnectHandler(ev);
39
+ const onGamePadDisconnected = (ev: GamepadEvent) =>
40
+ this.gamePadDisconnectHandler(ev);
41
+ window.addEventListener('gamepadconnected', onGamePadConnected);
42
+ window.addEventListener('gamepaddisconnected', onGamePadDisconnected);
43
+ this.gamePadEventListenerTracker.addUnregisterCallback(
44
+ () => window.removeEventListener('gamepadconnected', onGamePadConnected)
45
+ );
46
+ this.gamePadEventListenerTracker.addUnregisterCallback(
47
+ () => window.removeEventListener('gamepaddisconnected', onGamePadDisconnected)
48
+ );
49
+ } else if ('WebKitGamepadEvent' in browserWindow) {
50
+ const onWebkitGamePadConnected = (ev: GamepadEvent) => this.gamePadConnectHandler(ev);
51
+ const onWebkitGamePadDisconnected = (ev: GamepadEvent) => this.gamePadDisconnectHandler(ev);
52
+ window.addEventListener('webkitgamepadconnected', onWebkitGamePadConnected);
53
+ window.addEventListener('webkitgamepaddisconnected', onWebkitGamePadDisconnected);
54
+ this.gamePadEventListenerTracker.addUnregisterCallback(
55
+ () => window.removeEventListener('webkitgamepadconnected', onWebkitGamePadConnected)
56
+ );
57
+ this.gamePadEventListenerTracker.addUnregisterCallback(
58
+ () => window.removeEventListener('webkitgamepaddisconnected', onWebkitGamePadDisconnected)
59
+ );
60
+ }
61
+ this.controllers = [];
62
+ if (navigator.getGamepads) {
63
+ for (const gamepad of navigator.getGamepads()) {
64
+ if (gamepad) {
65
+ this.gamePadConnectHandler(new GamepadEvent('gamepadconnected', { gamepad }));
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Unregisters all event handlers
73
+ */
74
+ unregisterGamePadEvents() {
75
+ this.gamePadEventListenerTracker.unregisterAll();
76
+ for(const controller of this.controllers) {
77
+ if(controller.id !== undefined) {
78
+ this.onGamepadDisconnected(controller.id);
79
+ }
80
+ }
81
+ this.controllers = [];
82
+ this.onGamepadConnected = () => { /* */ };
83
+ this.onGamepadDisconnected = () => { /* */ };
84
+ }
85
+
86
+ /**
87
+ * Connects the gamepad handler
88
+ * @param gamePadEvent - the activating gamepad event
89
+ */
90
+ gamePadConnectHandler(gamePadEvent: GamepadEvent) {
91
+ Logger.Log(Logger.GetStackTrace(), 'Gamepad connect handler', 6);
92
+ const gamepad = gamePadEvent.gamepad;
93
+
94
+ const temp: Controller = {
95
+ currentState: gamepad,
96
+ prevState: gamepad,
97
+ id: undefined
98
+ };
99
+
100
+ this.controllers.push(temp);
101
+ this.controllers[gamepad.index].currentState = gamepad;
102
+ this.controllers[gamepad.index].prevState = gamepad;
103
+ Logger.Log(
104
+ Logger.GetStackTrace(),
105
+ 'gamepad: ' + gamepad.id + ' connected',
106
+ 6
107
+ );
108
+ window.requestAnimationFrame(() => this.updateStatus());
109
+ this.onGamepadConnected();
110
+ }
111
+
112
+ /**
113
+ * Disconnects the gamepad handler
114
+ * @param gamePadEvent - the activating gamepad event
115
+ */
116
+ gamePadDisconnectHandler(gamePadEvent: GamepadEvent) {
117
+ Logger.Log(Logger.GetStackTrace(), 'Gamepad disconnect handler', 6);
118
+ Logger.Log(
119
+ Logger.GetStackTrace(),
120
+ 'gamepad: ' + gamePadEvent.gamepad.id + ' disconnected',
121
+ 6
122
+ );
123
+ const deletedController = this.controllers[gamePadEvent.gamepad.index];
124
+ delete this.controllers[gamePadEvent.gamepad.index];
125
+ this.controllers = this.controllers.filter(
126
+ (controller) => controller !== undefined
127
+ );
128
+ this.onGamepadDisconnected(deletedController.id);
129
+ }
130
+
131
+ /**
132
+ * Scan for connected gamepads
133
+ */
134
+ scanGamePads() {
135
+ const gamepads = navigator.getGamepads
136
+ ? navigator.getGamepads()
137
+ : navigator.webkitGetGamepads
138
+ ? navigator.webkitGetGamepads()
139
+ : [];
140
+ for (let i = 0; i < gamepads.length; i++) {
141
+ if (gamepads[i] && gamepads[i].index in this.controllers) {
142
+ this.controllers[gamepads[i].index].currentState = gamepads[i];
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Updates the status of the gamepad and sends the inputs
149
+ */
150
+ updateStatus() {
151
+ this.scanGamePads();
152
+ const toStreamerHandlers =
153
+ this.toStreamerMessagesProvider.toStreamerHandlers;
154
+
155
+ // Iterate over multiple controllers in the case the multiple gamepads are connected
156
+ for (const controller of this.controllers) {
157
+ // If we haven't received an id (possible if using an older version of UE), return to original functionality
158
+ const controllerIndex = (controller.id === undefined) ? this.controllers.indexOf(controller) : controller.id;
159
+ const currentState = controller.currentState;
160
+ for (let i = 0; i < controller.currentState.buttons.length; i++) {
161
+ const currentButton = controller.currentState.buttons[i];
162
+ const previousButton = controller.prevState.buttons[i];
163
+ if (currentButton.pressed) {
164
+ // press
165
+ if (i == gamepadLayout.LeftTrigger) {
166
+ // UEs left analog has a button index of 5
167
+ toStreamerHandlers.get('GamepadAnalog')([
168
+ controllerIndex,
169
+ 5,
170
+ currentButton.value
171
+ ]);
172
+ } else if (i == gamepadLayout.RightTrigger) {
173
+ // UEs right analog has a button index of 6
174
+ toStreamerHandlers.get('GamepadAnalog')([
175
+ controllerIndex,
176
+ 6,
177
+ currentButton.value
178
+ ]);
179
+ } else {
180
+ toStreamerHandlers.get('GamepadButtonPressed')([
181
+ controllerIndex,
182
+ i,
183
+ previousButton.pressed ? 1 : 0
184
+ ]);
185
+ }
186
+ } else if (!currentButton.pressed && previousButton.pressed) {
187
+ // release
188
+ if (i == gamepadLayout.LeftTrigger) {
189
+ // UEs left analog has a button index of 5
190
+ toStreamerHandlers.get('GamepadAnalog')([
191
+ controllerIndex,
192
+ 5,
193
+ 0
194
+ ]);
195
+ } else if (i == gamepadLayout.RightTrigger) {
196
+ // UEs right analog has a button index of 6
197
+ toStreamerHandlers.get('GamepadAnalog')([
198
+ controllerIndex,
199
+ 6,
200
+ 0
201
+ ]);
202
+ } else {
203
+ toStreamerHandlers.get('GamepadButtonReleased')([
204
+ controllerIndex,
205
+ i,
206
+ 0
207
+ ]);
208
+ }
209
+ }
210
+ }
211
+ // Iterate over gamepad axes (we will increment in lots of 2 as there is 2 axes per stick)
212
+ for (let i = 0; i < currentState.axes.length; i += 2) {
213
+ // Horizontal axes are even numbered
214
+ const x = parseFloat(currentState.axes[i].toFixed(4));
215
+
216
+ // Vertical axes are odd numbered
217
+ // https://w3c.github.io/gamepad/#remapping Gamepad browser side standard mapping has positive down, negative up. This is downright disgusting. So we fix it.
218
+ const y = -parseFloat(currentState.axes[i + 1].toFixed(4));
219
+
220
+ // UE's analog axes follow the same order as the browsers, but start at index 1 so we will offset as such
221
+ toStreamerHandlers.get('GamepadAnalog')([
222
+ controllerIndex,
223
+ i + 1,
224
+ x
225
+ ]); // Horizontal axes, only offset by 1
226
+ toStreamerHandlers.get('GamepadAnalog')([
227
+ controllerIndex,
228
+ i + 2,
229
+ y
230
+ ]); // Vertical axes, offset by two (1 to match UEs axes convention and then another 1 for the vertical axes)
231
+ }
232
+ this.controllers[controllerIndex].prevState = currentState;
233
+ }
234
+ if (this.controllers.length > 0) {
235
+ this.requestAnimationFrame(() => this.updateStatus());
236
+ }
237
+ }
238
+
239
+ onGamepadResponseReceived(gamepadId: number) {
240
+ for(const controller of this.controllers) {
241
+ if(controller.id === undefined) {
242
+ controller.id = gamepadId;
243
+ break;
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Event to send the gamepadconnected message to the application
250
+ */
251
+ onGamepadConnected() {
252
+ // Default Functionality: Do Nothing
253
+ }
254
+
255
+ /**
256
+ * Event to send the gamepaddisconnected message to the application
257
+ */
258
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
259
+ onGamepadDisconnected(controllerIdx: number) {
260
+ // Default Functionality: Do Nothing
261
+ }
262
+
263
+ onBeforeUnload(ev: Event) {
264
+ // When a user navigates away from the page, we need to inform UE of all the disconnecting
265
+ // controllers
266
+ for(const controller of this.controllers) {
267
+ this.onGamepadDisconnected(controller.id);
268
+ }
269
+ }
270
+ }
271
+
272
+
273
+
274
+ /**
275
+ * Additional types for Window and Navigator
276
+ */
277
+ declare global {
278
+ interface Window {
279
+ mozRequestAnimationFrame(callback: FrameRequestCallback): number;
280
+ webkitRequestAnimationFrame(callback: FrameRequestCallback): number;
281
+ }
282
+
283
+ interface Navigator {
284
+ webkitGetGamepads(): Gamepad[];
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Gamepad layout codes enum
290
+ */
291
+ export enum gamepadLayout {
292
+ RightClusterBottomButton = 0,
293
+ RightClusterRightButton = 1,
294
+ RightClusterLeftButton = 2,
295
+ RightClusterTopButton = 3,
296
+ LeftShoulder = 4,
297
+ RightShoulder = 5,
298
+ LeftTrigger = 6,
299
+ RightTrigger = 7,
300
+ SelectOrBack = 8,
301
+ StartOrForward = 9,
302
+ LeftAnalogPress = 10,
303
+ RightAnalogPress = 11,
304
+ LeftClusterTopButton = 12,
305
+ LeftClusterBottomButton = 13,
306
+ LeftClusterLeftButton = 14,
307
+ LeftClusterRightButton = 15,
308
+ CentreButton = 16,
309
+ // Axes
310
+ LeftStickHorizontal = 0,
311
+ LeftStickVertical = 1,
312
+ RightStickHorizontal = 2,
313
+ RightStickVertical = 3
314
+ }
@@ -0,0 +1,10 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ /**
4
+ * The interface for controllers
5
+ */
6
+ export interface Controller {
7
+ currentState: Gamepad;
8
+ prevState: Gamepad;
9
+ id: number | undefined;
10
+ }