@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.
- package/.cspell.json +48 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +8 -0
- package/.prettierignore +0 -0
- package/.prettierrc.json +6 -0
- package/dist/lib-pixelstreamingfrontend.esm.js +1 -0
- package/dist/lib-pixelstreamingfrontend.js +1 -0
- package/jest.config.js +18 -0
- package/package.json +48 -0
- package/readme.md +15 -0
- package/src/AFK/AFKController.test.ts +162 -0
- package/src/AFK/AFKController.ts +158 -0
- package/src/Config/Config.test.ts +222 -0
- package/src/Config/Config.ts +970 -0
- package/src/Config/SettingBase.ts +65 -0
- package/src/Config/SettingFlag.ts +99 -0
- package/src/Config/SettingNumber.ts +111 -0
- package/src/Config/SettingOption.ts +124 -0
- package/src/Config/SettingText.ts +82 -0
- package/src/DataChannel/DataChannelController.ts +138 -0
- package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
- package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
- package/src/DataChannel/DataChannelSender.ts +59 -0
- package/src/DataChannel/InitialSettings.ts +61 -0
- package/src/DataChannel/LatencyTestResults.ts +76 -0
- package/src/FreezeFrame/FreezeFrame.ts +114 -0
- package/src/FreezeFrame/FreezeFrameController.ts +114 -0
- package/src/Inputs/FakeTouchController.ts +199 -0
- package/src/Inputs/GamepadController.ts +314 -0
- package/src/Inputs/GamepadTypes.ts +10 -0
- package/src/Inputs/HoveringMouseEvents.ts +192 -0
- package/src/Inputs/IMouseEvents.ts +64 -0
- package/src/Inputs/ITouchController.ts +29 -0
- package/src/Inputs/InputClassesFactory.ts +140 -0
- package/src/Inputs/KeyboardController.ts +354 -0
- package/src/Inputs/LockedMouseEvents.ts +287 -0
- package/src/Inputs/MouseButtons.ts +25 -0
- package/src/Inputs/MouseController.ts +362 -0
- package/src/Inputs/SpecialKeyCodes.ts +16 -0
- package/src/Inputs/TouchController.ts +208 -0
- package/src/Inputs/XRGamepadController.ts +126 -0
- package/src/PeerConnectionController/AggregatedStats.ts +311 -0
- package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
- package/src/PeerConnectionController/CandidateStat.ts +13 -0
- package/src/PeerConnectionController/CodecStats.ts +19 -0
- package/src/PeerConnectionController/DataChannelStats.ts +17 -0
- package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
- package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
- package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
- package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
- package/src/PeerConnectionController/SessionStats.ts +10 -0
- package/src/PeerConnectionController/StreamStats.ts +11 -0
- package/src/PixelStreaming/PixelStreaming.test.ts +626 -0
- package/src/PixelStreaming/PixelStreaming.ts +851 -0
- package/src/UI/OnScreenKeyboard.ts +97 -0
- package/src/UeInstanceMessage/ResponseController.ts +47 -0
- package/src/UeInstanceMessage/SendMessageController.ts +154 -0
- package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
- package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
- package/src/Util/CoordinateConverter.ts +289 -0
- package/src/Util/EventEmitter.ts +611 -0
- package/src/Util/EventListenerTracker.ts +29 -0
- package/src/Util/FileUtil.ts +140 -0
- package/src/Util/RTCUtils.ts +41 -0
- package/src/Util/WebGLUtils.ts +49 -0
- package/src/Util/WebXRUtils.ts +25 -0
- package/src/VideoPlayer/StreamController.ts +89 -0
- package/src/VideoPlayer/VideoPlayer.ts +246 -0
- package/src/WebRtcPlayer/WebRtcPlayerController.ts +2158 -0
- package/src/WebXR/WebXRController.ts +319 -0
- package/src/__test__/mockMediaStream.ts +124 -0
- package/src/__test__/mockRTCPeerConnection.ts +347 -0
- package/src/__test__/mockRTCRtpReceiver.ts +22 -0
- package/src/__test__/mockWebSocket.ts +136 -0
- package/src/pixelstreamingfrontend.ts +46 -0
- package/tsconfig.jest.json +8 -0
- package/tsconfig.json +24 -0
- package/types/AFK/AFKController.d.ts +39 -0
- package/types/Config/Config.d.ts +218 -0
- package/types/Config/SettingBase.d.ts +30 -0
- package/types/Config/SettingFlag.d.ts +33 -0
- package/types/Config/SettingNumber.d.ts +45 -0
- package/types/Config/SettingOption.d.ts +43 -0
- package/types/Config/SettingText.d.ts +29 -0
- package/types/DataChannel/DataChannelController.d.ts +59 -0
- package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
- package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
- package/types/DataChannel/DataChannelSender.d.ts +21 -0
- package/types/DataChannel/InitialSettings.d.ts +44 -0
- package/types/DataChannel/LatencyTestResults.d.ts +31 -0
- package/types/FreezeFrame/FreezeFrame.d.ts +36 -0
- package/types/FreezeFrame/FreezeFrameController.d.ts +37 -0
- package/types/Inputs/FakeTouchController.d.ts +61 -0
- package/types/Inputs/GamepadController.d.ts +85 -0
- package/types/Inputs/GamepadTypes.d.ts +8 -0
- package/types/Inputs/HoveringMouseEvents.d.ts +56 -0
- package/types/Inputs/IMouseEvents.d.ts +53 -0
- package/types/Inputs/ITouchController.d.ts +24 -0
- package/types/Inputs/InputClassesFactory.d.ts +54 -0
- package/types/Inputs/KeyboardController.d.ts +62 -0
- package/types/Inputs/LockedMouseEvents.d.ts +80 -0
- package/types/Inputs/MouseButtons.d.ts +22 -0
- package/types/Inputs/MouseController.d.ts +75 -0
- package/types/Inputs/SpecialKeyCodes.d.ts +14 -0
- package/types/Inputs/TouchController.d.ts +53 -0
- package/types/Inputs/XRGamepadController.d.ts +15 -0
- package/types/PeerConnectionController/AggregatedStats.d.ts +77 -0
- package/types/PeerConnectionController/CandidatePairStats.d.ts +15 -0
- package/types/PeerConnectionController/CandidateStat.d.ts +11 -0
- package/types/PeerConnectionController/CodecStats.d.ts +14 -0
- package/types/PeerConnectionController/DataChannelStats.d.ts +15 -0
- package/types/PeerConnectionController/InboundRTPStats.d.ts +141 -0
- package/types/PeerConnectionController/InboundTrackStats.d.ts +32 -0
- package/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -0
- package/types/PeerConnectionController/PeerConnectionController.d.ts +132 -0
- package/types/PeerConnectionController/SessionStats.d.ts +8 -0
- package/types/PeerConnectionController/StreamStats.d.ts +9 -0
- package/types/PixelStreaming/PixelStreaming.d.ts +259 -0
- package/types/UI/OnScreenKeyboard.d.ts +31 -0
- package/types/UeInstanceMessage/ResponseController.d.ts +19 -0
- package/types/UeInstanceMessage/SendMessageController.d.ts +18 -0
- package/types/UeInstanceMessage/StreamMessageController.d.ts +29 -0
- package/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -0
- package/types/Util/CoordinateConverter.d.ts +100 -0
- package/types/Util/EventEmitter.d.ts +422 -0
- package/types/Util/EventListenerTracker.d.ts +14 -0
- package/types/Util/FileUtil.d.ts +32 -0
- package/types/Util/RTCUtils.d.ts +8 -0
- package/types/Util/WebGLUtils.d.ts +4 -0
- package/types/Util/WebXRUtils.d.ts +9 -0
- package/types/VideoPlayer/StreamController.d.ts +24 -0
- package/types/VideoPlayer/VideoPlayer.d.ts +78 -0
- package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +377 -0
- package/types/WebXR/WebXRController.d.ts +26 -0
- package/types/pixelstreamingfrontend.d.ts +22 -0
- package/webpack.common.js +35 -0
- package/webpack.dev.js +35 -0
- 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
|
+
}
|