From 153873e0341198296b7438c5a64e51db9c0191f3 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:16:07 +0700 Subject: [PATCH] Reduce Virtual Controller's input latency --- dist/better-xcloud.lite.user.js | 155 +++++++++++------- dist/better-xcloud.user.js | 151 +++++++++-------- src/modules/mkb/mkb-handler.ts | 85 +++++----- src/modules/mkb/native-mkb-handler.ts | 20 +-- src/modules/patcher/patcher.ts | 16 +- .../shortcuts/virtual-controller-shortcut.ts | 4 +- src/modules/ui/dialog/settings/mkb-extra.ts | 2 +- src/types/global.d.ts | 3 +- src/utils/gamepad.ts | 61 ++++++- src/utils/stream-settings.ts | 32 +--- 10 files changed, 300 insertions(+), 229 deletions(-) diff --git a/dist/better-xcloud.lite.user.js b/dist/better-xcloud.lite.user.js index 16d4eff..c12f66a 100755 --- a/dist/better-xcloud.lite.user.js +++ b/dist/better-xcloud.lite.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Better xCloud (Lite) // @namespace https://github.com/redphx -// @version 6.2.0 +// @version 6.2.1-beta // @description Improve Xbox Cloud Gaming (xCloud) experience // @author redphx // @license MIT @@ -105,7 +105,7 @@ class UserAgent { }); } } -var SCRIPT_VERSION = "6.2.0", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface; +var SCRIPT_VERSION = "6.2.1-beta", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface; UserAgent.init(); var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, STATES = { supportedRegion: !0, @@ -2792,6 +2792,69 @@ function hasGamepad() { if (gamepad?.connected) return !0; return !1; } +function generateVirtualControllerMapping(index, override = {}) { + return Object.assign({}, { + GamepadIndex: index, + A: 0, + B: 0, + X: 0, + Y: 0, + LeftShoulder: 0, + RightShoulder: 0, + LeftTrigger: 0, + RightTrigger: 0, + View: 0, + Menu: 0, + LeftThumb: 0, + RightThumb: 0, + DPadUp: 0, + DPadDown: 0, + DPadLeft: 0, + DPadRight: 0, + Nexus: 0, + LeftThumbXAxis: 0, + LeftThumbYAxis: 0, + RightThumbXAxis: 0, + RightThumbYAxis: 0, + PhysicalPhysicality: 0, + VirtualPhysicality: 0, + Dirty: !1, + Virtual: !1 + }, override); +} +var XCLOUD_GAMEPAD_KEY_MAPPING = { + 0: "A", + 1: "B", + 2: "X", + 3: "Y", + 12: "DPadUp", + 15: "DPadRight", + 13: "DPadDown", + 14: "DPadLeft", + 4: "LeftShoulder", + 5: "RightShoulder", + 6: "LeftTrigger", + 7: "RightTrigger", + 10: "LeftThumb", + 11: "RightThumb", + 104: "LeftStickAxes", + 204: "RightStickAxes", + 8: "View", + 9: "Menu", + 16: "Nexus", + 17: "Share", + 102: "LeftThumbXAxis", + 103: "LeftThumbXAxis", + 100: "LeftThumbYAxis", + 101: "LeftThumbYAxis", + 202: "RightThumbXAxis", + 203: "RightThumbXAxis", + 200: "RightThumbYAxis", + 201: "RightThumbYAxis" +}; +function toXcloudGamepadKey(gamepadKey) { + return XCLOUD_GAMEPAD_KEY_MAPPING[gamepadKey]; +} class StreamSettings { static settings = { settings: {}, @@ -2802,27 +2865,6 @@ class StreamSettings { mkbPreset: null, keyboardShortcuts: {} }; - static CONTROLLER_CUSTOMIZATION_MAPPING = { - 0: "A", - 1: "B", - 2: "X", - 3: "Y", - 12: "DPadUp", - 15: "DPadRight", - 13: "DPadDown", - 14: "DPadLeft", - 4: "LeftShoulder", - 5: "RightShoulder", - 6: "LeftTrigger", - 7: "RightTrigger", - 10: "LeftThumb", - 11: "RightThumb", - 104: "LeftStickAxes", - 204: "RightStickAxes", - 8: "View", - 9: "Menu", - 17: "Share" - }; static getPref(key) { return getPref(key); } @@ -2853,10 +2895,10 @@ class StreamSettings { vibrationIntensity: 1 }, gamepadKey; for (gamepadKey in customization.mapping) { - let gamepadStr = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[gamepadKey]; + let gamepadStr = toXcloudGamepadKey(gamepadKey); if (!gamepadStr) continue; let mappedKey = customization.mapping[gamepadKey]; - if (typeof mappedKey === "number") converted.mapping[gamepadStr] = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[mappedKey]; + if (typeof mappedKey === "number") converted.mapping[gamepadStr] = toXcloudGamepadKey(mappedKey); else converted.mapping[gamepadStr] = !1; } return StreamSettings.preCalculateControllerRange(converted.ranges, "LeftTrigger", customization.settings.leftTriggerRange), StreamSettings.preCalculateControllerRange(converted.ranges, "RightTrigger", customization.settings.rightTriggerRange), StreamSettings.preCalculateControllerRange(converted.ranges, "LeftThumb", customization.settings.leftStickDeadzone), StreamSettings.preCalculateControllerRange(converted.ranges, "RightThumb", customization.settings.rightStickDeadzone), converted.vibrationIntensity = customization.settings.vibrationIntensity / 100, converted; @@ -2982,7 +3024,7 @@ class NativeMkbHandler extends MkbHandler { mouseButtonsPressed = 0; mouseVerticalMultiply = 0; mouseHorizontalMultiply = 0; - inputSink; + inputChannel; popup; constructor() { super(); @@ -3024,7 +3066,7 @@ class NativeMkbHandler extends MkbHandler { } } init() { - this.pointerClient = PointerClient.getInstance(), this.inputSink = window.BX_EXPOSED.inputSink, this.updateInputConfigurationAsync(!1); + this.pointerClient = PointerClient.getInstance(), this.inputChannel = window.BX_EXPOSED.inputChannel, this.updateInputConfigurationAsync(!1); try { this.pointerClient.start(STATES.pointerServerPort, this); } catch (e) { @@ -3109,7 +3151,7 @@ class NativeMkbHandler extends MkbHandler { return this.enabled; } sendMouseInput(data) { - data.Type = 0, this.inputSink?.onMouseInput(data); + data.Type = 0, this.inputChannel?.queueMouseInput(data); } resetMouseInput() { this.mouseButtonsPressed = 0, this.sendMouseInput({ @@ -3202,6 +3244,7 @@ class EmulatedMkbHandler extends MkbHandler { vibrationActuator: null }; nativeGetGamepads; + xCloudGamepad = generateVirtualControllerMapping(0); initialized = !1; enabled = !1; mouseDataProvider; @@ -3216,14 +3259,14 @@ class EmulatedMkbHandler extends MkbHandler { RIGHT_STICK_Y = []; popup; STICK_MAP = { - 102: [this.LEFT_STICK_X, 0, -1], - 103: [this.LEFT_STICK_X, 0, 1], - 100: [this.LEFT_STICK_Y, 1, -1], - 101: [this.LEFT_STICK_Y, 1, 1], - 202: [this.RIGHT_STICK_X, 2, -1], - 203: [this.RIGHT_STICK_X, 2, 1], - 200: [this.RIGHT_STICK_Y, 3, -1], - 201: [this.RIGHT_STICK_Y, 3, 1] + 102: [this.LEFT_STICK_X, -1], + 103: [this.LEFT_STICK_X, 1], + 100: [this.LEFT_STICK_Y, 1], + 101: [this.LEFT_STICK_Y, -1], + 202: [this.RIGHT_STICK_X, -1], + 203: [this.RIGHT_STICK_X, 1], + 200: [this.RIGHT_STICK_Y, 1], + 201: [this.RIGHT_STICK_Y, -1] }; constructor() { super(); @@ -3236,31 +3279,32 @@ class EmulatedMkbHandler extends MkbHandler { }; getVirtualGamepad = () => this.VIRTUAL_GAMEPAD; updateStick(stick, x, y) { - let virtualGamepad = this.getVirtualGamepad(); - virtualGamepad.axes[stick * 2] = x, virtualGamepad.axes[stick * 2 + 1] = y, virtualGamepad.timestamp = performance.now(); + let gamepad = this.xCloudGamepad; + if (stick === 0) gamepad.LeftThumbXAxis = x, gamepad.LeftThumbYAxis = -y; + else gamepad.RightThumbXAxis = x, gamepad.RightThumbYAxis = -y; + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } vectorLength = (x, y) => Math.sqrt(x ** 2 + y ** 2); - resetGamepad() { - let gamepad = this.getVirtualGamepad(); - gamepad.axes = [0, 0, 0, 0]; - for (let button of gamepad.buttons) - button.pressed = !1, button.value = 0; - gamepad.timestamp = performance.now(); + resetXcloudGamepads() { + let index = getPref("mkb.p1.slot") - 1; + this.xCloudGamepad = generateVirtualControllerMapping(0, { + GamepadIndex: getPref("localCoOp.enabled") ? index : 0, + Dirty: !0 + }), this.VIRTUAL_GAMEPAD.index = index; } pressButton(buttonIndex, pressed) { - let virtualGamepad = this.getVirtualGamepad(); + let xCloudKey = toXcloudGamepadKey(buttonIndex); if (buttonIndex >= 100) { - let [valueArr, axisIndex] = this.STICK_MAP[buttonIndex]; - valueArr = valueArr, axisIndex = axisIndex; + let [valueArr] = this.STICK_MAP[buttonIndex]; for (let i = valueArr.length - 1;i >= 0; i--) if (valueArr[i] === buttonIndex) valueArr.splice(i, 1); pressed && valueArr.push(buttonIndex); let value; - if (valueArr.length) value = this.STICK_MAP[valueArr[valueArr.length - 1]][2]; + if (valueArr.length) value = this.STICK_MAP[valueArr[valueArr.length - 1]][1]; else value = 0; - virtualGamepad.axes[axisIndex] = value; - } else virtualGamepad.buttons[buttonIndex].pressed = pressed, virtualGamepad.buttons[buttonIndex].value = pressed ? 1 : 0; - virtualGamepad.timestamp = performance.now(); + this.xCloudGamepad[xCloudKey] = value; + } else this.xCloudGamepad[xCloudKey] = pressed ? 1 : 0; + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } onKeyboardEvent = (e) => { let isKeyDown = e.type === "keydown"; @@ -3336,7 +3380,7 @@ class EmulatedMkbHandler extends MkbHandler { else document.pointerLockElement && document.exitPointerLock(); } refreshPresetData() { - this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset, this.resetGamepad(); + this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset, this.resetXcloudGamepads(); } waitForMouseData(showPopup) { this.popup.toggleVisibility(showPopup); @@ -3395,12 +3439,9 @@ class EmulatedMkbHandler extends MkbHandler { else document.removeEventListener("pointerlockchange", this.onPointerLockChange), document.removeEventListener("pointerlockerror", this.onPointerLockError); window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged), BxEventBus.Script.off("dialog.shown", this.onDialogShown), this.mouseDataProvider?.destroy(), window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged); } - updateGamepadSlots() { - this.VIRTUAL_GAMEPAD.index = getPref("mkb.p1.slot") - 1; - } start() { if (!this.enabled) this.enabled = !0, Toast.show(t("virtual-controller"), t("enabled"), { instant: !0 }); - this.isPolling = !0, this.escKeyDownTime = -1, this.resetGamepad(), this.updateGamepadSlots(), window.navigator.getGamepads = this.patchedGetGamepads, this.waitForMouseData(!1), this.mouseDataProvider?.start(); + this.isPolling = !0, this.escKeyDownTime = -1, window.BX_EXPOSED.toggleLocalCoOp(getPref("localCoOp.enabled")), this.resetXcloudGamepads(), window.navigator.getGamepads = this.patchedGetGamepads, this.waitForMouseData(!1), this.mouseDataProvider?.start(); let virtualGamepad = this.getVirtualGamepad(); virtualGamepad.connected = !0, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepadconnected", { gamepad: virtualGamepad @@ -3409,7 +3450,7 @@ class EmulatedMkbHandler extends MkbHandler { stop() { this.enabled = !1, this.isPolling = !1, this.escKeyDownTime = -1; let virtualGamepad = this.getVirtualGamepad(); - if (virtualGamepad.connected) this.resetGamepad(), virtualGamepad.connected = !1, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepaddisconnected", { + if (virtualGamepad.connected) this.resetXcloudGamepads(), virtualGamepad.connected = !1, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepaddisconnected", { gamepad: virtualGamepad }), window.navigator.getGamepads = this.nativeGetGamepads; this.waitForMouseData(!0), this.mouseDataProvider?.stop(); diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js index ff7945f..3f03eca 100755 --- a/dist/better-xcloud.user.js +++ b/dist/better-xcloud.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Better xCloud // @namespace https://github.com/redphx -// @version 6.2.0 +// @version 6.2.1-beta // @description Improve Xbox Cloud Gaming (xCloud) experience // @author redphx // @license MIT @@ -107,7 +107,7 @@ class UserAgent { }); } } -var SCRIPT_VERSION = "6.2.0", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface; +var SCRIPT_VERSION = "6.2.1-beta", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface; UserAgent.init(); var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, STATES = { supportedRegion: !0, @@ -2908,9 +2908,9 @@ function hasGamepad() { if (gamepad?.connected) return !0; return !1; } -function generateVirtualControllerMapping(override = {}) { +function generateVirtualControllerMapping(index, override = {}) { return Object.assign({}, { - GamepadIndex: 0, + GamepadIndex: index, A: 0, B: 0, X: 0, @@ -2941,6 +2941,39 @@ function generateVirtualControllerMapping(override = {}) { function getGamepadPrompt(gamepadKey) { return GamepadKeyName[gamepadKey][1]; } +var XCLOUD_GAMEPAD_KEY_MAPPING = { + 0: "A", + 1: "B", + 2: "X", + 3: "Y", + 12: "DPadUp", + 15: "DPadRight", + 13: "DPadDown", + 14: "DPadLeft", + 4: "LeftShoulder", + 5: "RightShoulder", + 6: "LeftTrigger", + 7: "RightTrigger", + 10: "LeftThumb", + 11: "RightThumb", + 104: "LeftStickAxes", + 204: "RightStickAxes", + 8: "View", + 9: "Menu", + 16: "Nexus", + 17: "Share", + 102: "LeftThumbXAxis", + 103: "LeftThumbXAxis", + 100: "LeftThumbYAxis", + 101: "LeftThumbYAxis", + 202: "RightThumbXAxis", + 203: "RightThumbXAxis", + 200: "RightThumbYAxis", + 201: "RightThumbYAxis" +}; +function toXcloudGamepadKey(gamepadKey) { + return XCLOUD_GAMEPAD_KEY_MAPPING[gamepadKey]; +} class StreamSettings { static settings = { settings: {}, @@ -2951,27 +2984,6 @@ class StreamSettings { mkbPreset: null, keyboardShortcuts: {} }; - static CONTROLLER_CUSTOMIZATION_MAPPING = { - 0: "A", - 1: "B", - 2: "X", - 3: "Y", - 12: "DPadUp", - 15: "DPadRight", - 13: "DPadDown", - 14: "DPadLeft", - 4: "LeftShoulder", - 5: "RightShoulder", - 6: "LeftTrigger", - 7: "RightTrigger", - 10: "LeftThumb", - 11: "RightThumb", - 104: "LeftStickAxes", - 204: "RightStickAxes", - 8: "View", - 9: "Menu", - 17: "Share" - }; static getPref(key) { return getPref(key); } @@ -3002,10 +3014,10 @@ class StreamSettings { vibrationIntensity: 1 }, gamepadKey; for (gamepadKey in customization.mapping) { - let gamepadStr = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[gamepadKey]; + let gamepadStr = toXcloudGamepadKey(gamepadKey); if (!gamepadStr) continue; let mappedKey = customization.mapping[gamepadKey]; - if (typeof mappedKey === "number") converted.mapping[gamepadStr] = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[mappedKey]; + if (typeof mappedKey === "number") converted.mapping[gamepadStr] = toXcloudGamepadKey(mappedKey); else converted.mapping[gamepadStr] = !1; } return StreamSettings.preCalculateControllerRange(converted.ranges, "LeftTrigger", customization.settings.leftTriggerRange), StreamSettings.preCalculateControllerRange(converted.ranges, "RightTrigger", customization.settings.rightTriggerRange), StreamSettings.preCalculateControllerRange(converted.ranges, "LeftThumb", customization.settings.leftStickDeadzone), StreamSettings.preCalculateControllerRange(converted.ranges, "RightThumb", customization.settings.rightStickDeadzone), converted.vibrationIntensity = customization.settings.vibrationIntensity / 100, converted; @@ -3131,7 +3143,7 @@ class NativeMkbHandler extends MkbHandler { mouseButtonsPressed = 0; mouseVerticalMultiply = 0; mouseHorizontalMultiply = 0; - inputSink; + inputChannel; popup; constructor() { super(); @@ -3173,7 +3185,7 @@ class NativeMkbHandler extends MkbHandler { } } init() { - this.pointerClient = PointerClient.getInstance(), this.inputSink = window.BX_EXPOSED.inputSink, this.updateInputConfigurationAsync(!1); + this.pointerClient = PointerClient.getInstance(), this.inputChannel = window.BX_EXPOSED.inputChannel, this.updateInputConfigurationAsync(!1); try { this.pointerClient.start(STATES.pointerServerPort, this); } catch (e) { @@ -3258,7 +3270,7 @@ class NativeMkbHandler extends MkbHandler { return this.enabled; } sendMouseInput(data) { - data.Type = 0, this.inputSink?.onMouseInput(data); + data.Type = 0, this.inputChannel?.queueMouseInput(data); } resetMouseInput() { this.mouseButtonsPressed = 0, this.sendMouseInput({ @@ -3351,6 +3363,7 @@ class EmulatedMkbHandler extends MkbHandler { vibrationActuator: null }; nativeGetGamepads; + xCloudGamepad = generateVirtualControllerMapping(0); initialized = !1; enabled = !1; mouseDataProvider; @@ -3365,14 +3378,14 @@ class EmulatedMkbHandler extends MkbHandler { RIGHT_STICK_Y = []; popup; STICK_MAP = { - 102: [this.LEFT_STICK_X, 0, -1], - 103: [this.LEFT_STICK_X, 0, 1], - 100: [this.LEFT_STICK_Y, 1, -1], - 101: [this.LEFT_STICK_Y, 1, 1], - 202: [this.RIGHT_STICK_X, 2, -1], - 203: [this.RIGHT_STICK_X, 2, 1], - 200: [this.RIGHT_STICK_Y, 3, -1], - 201: [this.RIGHT_STICK_Y, 3, 1] + 102: [this.LEFT_STICK_X, -1], + 103: [this.LEFT_STICK_X, 1], + 100: [this.LEFT_STICK_Y, 1], + 101: [this.LEFT_STICK_Y, -1], + 202: [this.RIGHT_STICK_X, -1], + 203: [this.RIGHT_STICK_X, 1], + 200: [this.RIGHT_STICK_Y, 1], + 201: [this.RIGHT_STICK_Y, -1] }; constructor() { super(); @@ -3385,31 +3398,32 @@ class EmulatedMkbHandler extends MkbHandler { }; getVirtualGamepad = () => this.VIRTUAL_GAMEPAD; updateStick(stick, x, y) { - let virtualGamepad = this.getVirtualGamepad(); - virtualGamepad.axes[stick * 2] = x, virtualGamepad.axes[stick * 2 + 1] = y, virtualGamepad.timestamp = performance.now(); + let gamepad = this.xCloudGamepad; + if (stick === 0) gamepad.LeftThumbXAxis = x, gamepad.LeftThumbYAxis = -y; + else gamepad.RightThumbXAxis = x, gamepad.RightThumbYAxis = -y; + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } vectorLength = (x, y) => Math.sqrt(x ** 2 + y ** 2); - resetGamepad() { - let gamepad = this.getVirtualGamepad(); - gamepad.axes = [0, 0, 0, 0]; - for (let button of gamepad.buttons) - button.pressed = !1, button.value = 0; - gamepad.timestamp = performance.now(); + resetXcloudGamepads() { + let index = getPref("mkb.p1.slot") - 1; + this.xCloudGamepad = generateVirtualControllerMapping(0, { + GamepadIndex: getPref("localCoOp.enabled") ? index : 0, + Dirty: !0 + }), this.VIRTUAL_GAMEPAD.index = index; } pressButton(buttonIndex, pressed) { - let virtualGamepad = this.getVirtualGamepad(); + let xCloudKey = toXcloudGamepadKey(buttonIndex); if (buttonIndex >= 100) { - let [valueArr, axisIndex] = this.STICK_MAP[buttonIndex]; - valueArr = valueArr, axisIndex = axisIndex; + let [valueArr] = this.STICK_MAP[buttonIndex]; for (let i = valueArr.length - 1;i >= 0; i--) if (valueArr[i] === buttonIndex) valueArr.splice(i, 1); pressed && valueArr.push(buttonIndex); let value; - if (valueArr.length) value = this.STICK_MAP[valueArr[valueArr.length - 1]][2]; + if (valueArr.length) value = this.STICK_MAP[valueArr[valueArr.length - 1]][1]; else value = 0; - virtualGamepad.axes[axisIndex] = value; - } else virtualGamepad.buttons[buttonIndex].pressed = pressed, virtualGamepad.buttons[buttonIndex].value = pressed ? 1 : 0; - virtualGamepad.timestamp = performance.now(); + this.xCloudGamepad[xCloudKey] = value; + } else this.xCloudGamepad[xCloudKey] = pressed ? 1 : 0; + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } onKeyboardEvent = (e) => { let isKeyDown = e.type === "keydown"; @@ -3485,7 +3499,7 @@ class EmulatedMkbHandler extends MkbHandler { else document.pointerLockElement && document.exitPointerLock(); } refreshPresetData() { - this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset, this.resetGamepad(); + this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset, this.resetXcloudGamepads(); } waitForMouseData(showPopup) { this.popup.toggleVisibility(showPopup); @@ -3544,12 +3558,9 @@ class EmulatedMkbHandler extends MkbHandler { else document.removeEventListener("pointerlockchange", this.onPointerLockChange), document.removeEventListener("pointerlockerror", this.onPointerLockError); window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged), BxEventBus.Script.off("dialog.shown", this.onDialogShown), this.mouseDataProvider?.destroy(), window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged); } - updateGamepadSlots() { - this.VIRTUAL_GAMEPAD.index = getPref("mkb.p1.slot") - 1; - } start() { if (!this.enabled) this.enabled = !0, Toast.show(t("virtual-controller"), t("enabled"), { instant: !0 }); - this.isPolling = !0, this.escKeyDownTime = -1, this.resetGamepad(), this.updateGamepadSlots(), window.navigator.getGamepads = this.patchedGetGamepads, this.waitForMouseData(!1), this.mouseDataProvider?.start(); + this.isPolling = !0, this.escKeyDownTime = -1, window.BX_EXPOSED.toggleLocalCoOp(getPref("localCoOp.enabled")), this.resetXcloudGamepads(), window.navigator.getGamepads = this.patchedGetGamepads, this.waitForMouseData(!1), this.mouseDataProvider?.start(); let virtualGamepad = this.getVirtualGamepad(); virtualGamepad.connected = !0, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepadconnected", { gamepad: virtualGamepad @@ -3558,7 +3569,7 @@ class EmulatedMkbHandler extends MkbHandler { stop() { this.enabled = !1, this.isPolling = !1, this.escKeyDownTime = -1; let virtualGamepad = this.getVirtualGamepad(); - if (virtualGamepad.connected) this.resetGamepad(), virtualGamepad.connected = !1, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepaddisconnected", { + if (virtualGamepad.connected) this.resetXcloudGamepads(), virtualGamepad.connected = !1, virtualGamepad.timestamp = performance.now(), BxEvent.dispatch(window, "gamepaddisconnected", { gamepad: virtualGamepad }), window.navigator.getGamepads = this.nativeGetGamepads; this.waitForMouseData(!0), this.mouseDataProvider?.stop(); @@ -4690,11 +4701,11 @@ true` + text; if (!str.includes(text)) return !1; return str = str.replace(text, text + "return true;"), str; }, - exposeInputSink(str) { - let text = "this.controlChannel=null,this.inputChannel=null"; - if (!str.includes(text)) return !1; - let newCode = "window.BX_EXPOSED.inputSink = this;"; - return str = str.replace(text, newCode + text), str; + exposeInputChannel(str) { + let index = str.indexOf("this.flushData="); + if (index < 0) return !1; + let newCode = "window.BX_EXPOSED.inputChannel = this,"; + return str = PatcherUtils.insertAt(str, index, newCode), str; }, disableNativeRequestPointerLock(str) { let text = "async requestPointerLock(){"; @@ -4892,7 +4903,6 @@ ${subsVar} = subs; }, PATCH_ORDERS = PatcherUtils.filterPatches([ ...AppInterface && getPref("nativeMkb.mode") === "on" ? [ "enableNativeMkb", - "exposeInputSink", "disableAbsoluteMouse" ] : [], "exposeReactCreateComponent", @@ -4952,6 +4962,7 @@ ${subsVar} = subs; STATES.browser.capabilities.touch && hideSections.includes("touch") && "ignorePlayWithTouchSection", hideSections.some((value) => ["native-mkb", "most-popular"].includes(value)) && "ignoreSiglSections" ]), STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ + "exposeInputChannel", "patchXcloudTitleInfo", "disableGamepadDisconnectedScreen", "patchStreamHud", @@ -5024,11 +5035,15 @@ class Patcher { if (!tmpStr) continue; modified = !0, patchedFuncStr = tmpStr, BxLogger.info(LOG_TAG2, `✅ ${patchName}`), appliedPatches.push(patchName), patchesToCheck.splice(patchIndex, 1), patchIndex--, PATCH_ORDERS = PATCH_ORDERS.filter((item2) => item2 != patchName), BxLogger.info(LOG_TAG2, "Remaining patches", PATCH_ORDERS); } - if (modified) try { + if (modified) { + BX_FLAGS.Debug && console.time(LOG_TAG2); + try { chunkData[chunkId] = eval(patchedFuncStr); } catch (e) { if (e instanceof Error) BxLogger.error(LOG_TAG2, "Error", appliedPatches, e.message, patchedFuncStr); } + BX_FLAGS.Debug && console.timeEnd(LOG_TAG2); + } if (appliedPatches.length) patchesMap[chunkId] = appliedPatches; } if (Object.keys(patchesMap).length) patcherCache.saveToCache(patchesMap); @@ -6341,7 +6356,7 @@ class MkbExtraSettings extends HTMLElement { }) })), { multiLines: !0 }), createSettingRow(t("virtual-controller-slot"), SettingElement.fromPref("mkb.p1.slot", STORAGE.Global, () => { - EmulatedMkbHandler.getInstance()?.updateGamepadSlots(); + EmulatedMkbHandler.getInstance()?.resetXcloudGamepads(); })) ] : [], createSettingRow(t("in-game-keyboard-shortcuts"), CE("div", { class: "bx-preset-row", @@ -7385,7 +7400,7 @@ class VirtualControllerShortcut { static pressXboxButton() { let streamSession = window.BX_EXPOSED.streamSession; if (!streamSession) return; - let released = generateVirtualControllerMapping(), pressed = generateVirtualControllerMapping({ + let released = generateVirtualControllerMapping(0), pressed = generateVirtualControllerMapping(0, { Nexus: 1, VirtualPhysicality: 1024 }); diff --git a/src/modules/mkb/mkb-handler.ts b/src/modules/mkb/mkb-handler.ts index 9211256..9ba3857 100755 --- a/src/modules/mkb/mkb-handler.ts +++ b/src/modules/mkb/mkb-handler.ts @@ -19,6 +19,7 @@ import type { MkbConvertedPresetData } from "@/types/presets"; import { StreamSettings } from "@/utils/stream-settings"; import { ShortcutAction } from "@/enums/shortcut-actions"; import { BxEventBus } from "@/utils/bx-event-bus"; +import { generateVirtualControllerMapping, toXcloudGamepadKey } from "@/utils/gamepad"; const PointerToMouseButton = { 1: 0, @@ -152,6 +153,8 @@ export class EmulatedMkbHandler extends MkbHandler { }; private nativeGetGamepads: Navigator['getGamepads']; + private xCloudGamepad: XcloudGamepad = generateVirtualControllerMapping(0); + private initialized = false; private enabled = false; private mouseDataProvider: MouseDataProvider | undefined; @@ -171,16 +174,16 @@ export class EmulatedMkbHandler extends MkbHandler { private popup: MkbPopup; - private STICK_MAP: { [key in GamepadKey]?: [GamepadKey[], number, number] } = { - [GamepadKey.LS_LEFT]: [this.LEFT_STICK_X, 0, -1], - [GamepadKey.LS_RIGHT]: [this.LEFT_STICK_X, 0, 1], - [GamepadKey.LS_UP]: [this.LEFT_STICK_Y, 1, -1], - [GamepadKey.LS_DOWN]: [this.LEFT_STICK_Y, 1, 1], + private STICK_MAP: { [key in GamepadKey]?: [GamepadKey[], number] } = { + [GamepadKey.LS_LEFT]: [this.LEFT_STICK_X, -1], + [GamepadKey.LS_RIGHT]: [this.LEFT_STICK_X, 1], + [GamepadKey.LS_UP]: [this.LEFT_STICK_Y, 1], + [GamepadKey.LS_DOWN]: [this.LEFT_STICK_Y, -1], - [GamepadKey.RS_LEFT]: [this.RIGHT_STICK_X, 2, -1], - [GamepadKey.RS_RIGHT]: [this.RIGHT_STICK_X, 2, 1], - [GamepadKey.RS_UP]: [this.RIGHT_STICK_Y, 3, -1], - [GamepadKey.RS_DOWN]: [this.RIGHT_STICK_Y, 3, 1], + [GamepadKey.RS_LEFT]: [this.RIGHT_STICK_X, -1], + [GamepadKey.RS_RIGHT]: [this.RIGHT_STICK_X, 1], + [GamepadKey.RS_UP]: [this.RIGHT_STICK_Y, 1], + [GamepadKey.RS_DOWN]: [this.RIGHT_STICK_Y, -1], }; private constructor() { @@ -205,11 +208,16 @@ export class EmulatedMkbHandler extends MkbHandler { private getVirtualGamepad = () => this.VIRTUAL_GAMEPAD; private updateStick(stick: GamepadStick, x: number, y: number) { - const virtualGamepad = this.getVirtualGamepad(); - virtualGamepad.axes[stick * 2] = x; - virtualGamepad.axes[stick * 2 + 1] = y; + const gamepad = this.xCloudGamepad; + if (stick === GamepadStick.LEFT) { + gamepad.LeftThumbXAxis = x; + gamepad.LeftThumbYAxis = -y; + } else { + gamepad.RightThumbXAxis = x; + gamepad.RightThumbYAxis = -y; + } - virtualGamepad.timestamp = performance.now(); + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } /* @@ -224,29 +232,20 @@ export class EmulatedMkbHandler extends MkbHandler { private vectorLength = (x: number, y: number): number => Math.sqrt(x ** 2 + y ** 2); - private resetGamepad() { - const gamepad = this.getVirtualGamepad(); + resetXcloudGamepads() { + const index = getPref(PrefKey.MKB_P1_SLOT) - 1; - // Reset axes - gamepad.axes = [0, 0, 0, 0]; - - // Reset buttons - for (const button of gamepad.buttons) { - button.pressed = false; - button.value = 0; - } - - gamepad.timestamp = performance.now(); + this.xCloudGamepad = generateVirtualControllerMapping(0, { + GamepadIndex: getPref(PrefKey.LOCAL_CO_OP_ENABLED) ? index : 0, + Dirty: true, + }); + this.VIRTUAL_GAMEPAD.index = index; } private pressButton(buttonIndex: GamepadKey, pressed: boolean) { - const virtualGamepad = this.getVirtualGamepad(); - + const xCloudKey = toXcloudGamepadKey(buttonIndex)!; if (buttonIndex >= 100) { - let [valueArr, axisIndex] = this.STICK_MAP[buttonIndex]!; - valueArr = valueArr as number[]; - axisIndex = axisIndex as number; - + let [valueArr]: [GamepadKey[], number] = this.STICK_MAP[buttonIndex]!; // Remove old index of the array for (let i = valueArr.length - 1; i >= 0; i--) { if (valueArr[i] === buttonIndex) { @@ -259,18 +258,19 @@ export class EmulatedMkbHandler extends MkbHandler { let value; if (valueArr.length) { // Get value of the last key of the axis - value = this.STICK_MAP[valueArr[valueArr.length - 1]]![2] as number; + value = this.STICK_MAP[valueArr[valueArr.length - 1]]![1] as number; } else { value = 0; } - virtualGamepad.axes[axisIndex] = value; + // @ts-ignore + this.xCloudGamepad[xCloudKey] = value; } else { - virtualGamepad.buttons[buttonIndex].pressed = pressed; - virtualGamepad.buttons[buttonIndex].value = pressed ? 1 : 0; + // @ts-ignore + this.xCloudGamepad[xCloudKey] = pressed ? 1 : 0; } - virtualGamepad.timestamp = performance.now(); + window.BX_EXPOSED.inputChannel?.sendGamepadInput(performance.now(), [this.xCloudGamepad]); } private onKeyboardEvent = (e: KeyboardEvent) => { @@ -453,7 +453,7 @@ export class EmulatedMkbHandler extends MkbHandler { refreshPresetData() { this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset; - this.resetGamepad(); + this.resetXcloudGamepads(); } waitForMouseData(showPopup: boolean) { @@ -581,11 +581,6 @@ export class EmulatedMkbHandler extends MkbHandler { window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged); } - updateGamepadSlots() { - // Set gamepad slot - this.VIRTUAL_GAMEPAD.index = getPref(PrefKey.MKB_P1_SLOT) - 1; - } - start() { if (!this.enabled) { this.enabled = true; @@ -595,8 +590,8 @@ export class EmulatedMkbHandler extends MkbHandler { this.isPolling = true; this.escKeyDownTime = -1; - this.resetGamepad(); - this.updateGamepadSlots(); + window.BX_EXPOSED.toggleLocalCoOp(getPref(PrefKey.LOCAL_CO_OP_ENABLED)); + this.resetXcloudGamepads(); window.navigator.getGamepads = this.patchedGetGamepads; this.waitForMouseData(false); @@ -625,7 +620,7 @@ export class EmulatedMkbHandler extends MkbHandler { const virtualGamepad = this.getVirtualGamepad(); if (virtualGamepad.connected) { // Dispatch "gamepaddisconnected" event - this.resetGamepad(); + this.resetXcloudGamepads(); virtualGamepad.connected = false; virtualGamepad.timestamp = performance.now(); diff --git a/src/modules/mkb/native-mkb-handler.ts b/src/modules/mkb/native-mkb-handler.ts index ced5bdb..97d7821 100755 --- a/src/modules/mkb/native-mkb-handler.ts +++ b/src/modules/mkb/native-mkb-handler.ts @@ -13,19 +13,7 @@ import { StreamSettings } from "@/utils/stream-settings"; import { ShortcutAction } from "@/enums/shortcut-actions"; import { NativeMkbMode } from "@/enums/pref-values"; import { BxEventBus } from "@/utils/bx-event-bus"; - -type NativeMouseData = { - X: number, - Y: number, - Buttons: number, - WheelX: number, - WheelY: number, - Type?: 0, // 0: Relative, 1: Absolute -} - -type XcloudInputSink = { - onMouseInput: (data: NativeMouseData) => void; -} +import type { NativeMouseData, XcloudInputChannel } from "@/utils/gamepad"; export class NativeMkbHandler extends MkbHandler { private static instance: NativeMkbHandler | null | undefined; @@ -54,7 +42,7 @@ export class NativeMkbHandler extends MkbHandler { private mouseVerticalMultiply = 0; private mouseHorizontalMultiply = 0; - private inputSink: XcloudInputSink | undefined; + private inputChannel: XcloudInputChannel | undefined; private popup!: MkbPopup; @@ -114,7 +102,7 @@ export class NativeMkbHandler extends MkbHandler { init() { this.pointerClient = PointerClient.getInstance(); - this.inputSink = window.BX_EXPOSED.inputSink; + this.inputChannel = window.BX_EXPOSED.inputChannel; // Stop keyboard input at startup this.updateInputConfigurationAsync(false); @@ -274,7 +262,7 @@ export class NativeMkbHandler extends MkbHandler { private sendMouseInput(data: NativeMouseData) { data.Type = 0; // Relative - this.inputSink?.onMouseInput(data); + this.inputChannel?.queueMouseInput(data); } private resetMouseInput() { diff --git a/src/modules/patcher/patcher.ts b/src/modules/patcher/patcher.ts index 71c2d7c..966df75 100755 --- a/src/modules/patcher/patcher.ts +++ b/src/modules/patcher/patcher.ts @@ -643,15 +643,14 @@ true` + text; return str; }, - exposeInputSink(str: string) { - let text = 'this.controlChannel=null,this.inputChannel=null'; - if (!str.includes(text)) { + exposeInputChannel(str: string) { + let index = str.indexOf('this.flushData='); + if (index < 0) { return false; } - const newCode = 'window.BX_EXPOSED.inputSink = this;'; - - str = str.replace(text, newCode + text); + const newCode = 'window.BX_EXPOSED.inputChannel = this,'; + str = PatcherUtils.insertAt(str, index, newCode); return str; }, @@ -1120,7 +1119,6 @@ ${subsVar} = subs; let PATCH_ORDERS = PatcherUtils.filterPatches([ ...(AppInterface && getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [ 'enableNativeMkb', - 'exposeInputSink', 'disableAbsoluteMouse', ] : []), @@ -1208,6 +1206,8 @@ let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ // TODO: check this // @ts-ignore let STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ + 'exposeInputChannel', + 'patchXcloudTitleInfo', 'disableGamepadDisconnectedScreen', 'patchStreamHud', @@ -1377,6 +1377,7 @@ export class Patcher { // Apply patched functions if (modified) { + BX_FLAGS.Debug && console.time(LOG_TAG); try { chunkData[chunkId] = eval(patchedFuncStr); } catch (e: unknown) { @@ -1384,6 +1385,7 @@ export class Patcher { BxLogger.error(LOG_TAG, 'Error', appliedPatches, e.message, patchedFuncStr); } } + BX_FLAGS.Debug && console.timeEnd(LOG_TAG); } // Save to cache diff --git a/src/modules/shortcuts/virtual-controller-shortcut.ts b/src/modules/shortcuts/virtual-controller-shortcut.ts index ed1ca73..1aede39 100644 --- a/src/modules/shortcuts/virtual-controller-shortcut.ts +++ b/src/modules/shortcuts/virtual-controller-shortcut.ts @@ -7,8 +7,8 @@ export class VirtualControllerShortcut { return; } - const released = generateVirtualControllerMapping(); - const pressed = generateVirtualControllerMapping({ + const released = generateVirtualControllerMapping(0); + const pressed = generateVirtualControllerMapping(0, { Nexus: 1, VirtualPhysicality: 1024, // Home }); diff --git a/src/modules/ui/dialog/settings/mkb-extra.ts b/src/modules/ui/dialog/settings/mkb-extra.ts index 0f2c962..e17c863 100755 --- a/src/modules/ui/dialog/settings/mkb-extra.ts +++ b/src/modules/ui/dialog/settings/mkb-extra.ts @@ -69,7 +69,7 @@ export class MkbExtraSettings extends HTMLElement { createSettingRow( t('virtual-controller-slot'), SettingElement.fromPref(PrefKey.MKB_P1_SLOT, STORAGE.Global, () => { - EmulatedMkbHandler.getInstance()?.updateGamepadSlots(); + EmulatedMkbHandler.getInstance()?.resetXcloudGamepads(); }), ), ] : []), diff --git a/src/types/global.d.ts b/src/types/global.d.ts index beae692..b27d2a9 100755 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -5,6 +5,7 @@ import type { StreamSettings, type StreamSettingsData } from "@/utils/stream-set import type { BxEvent } from "@/utils/bx-event"; import type { BxEventBus } from "@/utils/bx-event-bus"; import type { BxLogger } from "@/utils/bx-logger"; +import type { XcloudInputChannel } from "@/utils/gamepad"; export {}; @@ -20,7 +21,7 @@ declare global { closeAll: () => void; }; showStreamMenu: () => void; - inputSink: any; + inputChannel: XcloudInputChannel | undefined; streamSession: any; touchLayoutManager: any; }>; diff --git a/src/utils/gamepad.ts b/src/utils/gamepad.ts index 7859790..b8aecde 100755 --- a/src/utils/gamepad.ts +++ b/src/utils/gamepad.ts @@ -4,7 +4,21 @@ import { Toast } from "@utils/toast"; import { BxLogger } from "@utils/bx-logger"; import { PrefKey } from "@/enums/pref-keys"; import { getPref } from "./settings-storages/global-settings-storage"; -import { GamepadKeyName, type GamepadKey } from "@/enums/gamepad"; +import { GamepadKey, GamepadKeyName } from "@/enums/gamepad"; + +export type NativeMouseData = { + X: number, + Y: number, + Buttons: number, + WheelX: number, + WheelY: number, + Type?: 0, // 0: Relative, 1: Absolute +} + +export type XcloudInputChannel = { + sendGamepadInput: (timestamp: number, gamepads: XcloudGamepad[]) => void; + queueMouseInput: (data: NativeMouseData) => void; +} // Show a toast when connecting/disconecting controller export function showGamepadToast(gamepad: Gamepad) { @@ -59,9 +73,9 @@ export function hasGamepad() { return false; } -export function generateVirtualControllerMapping(override: {}={}) { +export function generateVirtualControllerMapping(index: number, override: Partial={}) { const mapping = { - GamepadIndex: 0, + GamepadIndex: index, A: 0, B: 0, X: 0, @@ -95,3 +109,44 @@ export function generateVirtualControllerMapping(override: {}={}) { export function getGamepadPrompt(gamepadKey: GamepadKey): string { return GamepadKeyName[gamepadKey][1]; } + +const XCLOUD_GAMEPAD_KEY_MAPPING: { [key in GamepadKey]?: keyof XcloudGamepad } = { + [GamepadKey.A]: 'A', + [GamepadKey.B]: 'B', + [GamepadKey.X]: 'X', + [GamepadKey.Y]: 'Y', + + [GamepadKey.UP]: 'DPadUp', + [GamepadKey.RIGHT]: 'DPadRight', + [GamepadKey.DOWN]: 'DPadDown', + [GamepadKey.LEFT]: 'DPadLeft', + + [GamepadKey.LB]: 'LeftShoulder', + [GamepadKey.RB]: 'RightShoulder', + [GamepadKey.LT]: 'LeftTrigger', + [GamepadKey.RT]: 'RightTrigger', + + [GamepadKey.L3]: 'LeftThumb', + [GamepadKey.R3]: 'RightThumb', + [GamepadKey.LS]: 'LeftStickAxes', + [GamepadKey.RS]: 'RightStickAxes', + + [GamepadKey.SELECT]: 'View', + [GamepadKey.START]: 'Menu', + [GamepadKey.HOME]: 'Nexus', + [GamepadKey.SHARE]: 'Share', + + [GamepadKey.LS_LEFT]: 'LeftThumbXAxis', + [GamepadKey.LS_RIGHT]: 'LeftThumbXAxis', + [GamepadKey.LS_UP]: 'LeftThumbYAxis', + [GamepadKey.LS_DOWN]: 'LeftThumbYAxis', + + [GamepadKey.RS_LEFT]: 'RightThumbXAxis', + [GamepadKey.RS_RIGHT]: 'RightThumbXAxis', + [GamepadKey.RS_UP]: 'RightThumbYAxis', + [GamepadKey.RS_DOWN]: 'RightThumbYAxis', +}; + +export function toXcloudGamepadKey(gamepadKey: GamepadKey) { + return XCLOUD_GAMEPAD_KEY_MAPPING[gamepadKey]; +} diff --git a/src/utils/stream-settings.ts b/src/utils/stream-settings.ts index 7ff41e6..ef7aa3d 100755 --- a/src/utils/stream-settings.ts +++ b/src/utils/stream-settings.ts @@ -6,7 +6,7 @@ import type { ControllerCustomizationConvertedPresetData, ControllerCustomizatio import { STATES } from "./global"; import { DeviceVibrationMode } from "@/enums/pref-values"; import { VIRTUAL_GAMEPAD_ID } from "@/modules/mkb/mkb-handler"; -import { hasGamepad } from "./gamepad"; +import { hasGamepad, toXcloudGamepadKey } from "./gamepad"; import { MkbMappingPresetsTable } from "./local-db/mkb-mapping-presets-table"; import { GamepadKey } from "@/enums/gamepad"; import { MkbPresetKey, MouseConstant } from "@/enums/mkb"; @@ -51,32 +51,6 @@ export class StreamSettings { keyboardShortcuts: {}, }; - private static CONTROLLER_CUSTOMIZATION_MAPPING: { [key in GamepadKey]?: keyof XcloudGamepad } = { - [GamepadKey.A]: 'A', - [GamepadKey.B]: 'B', - [GamepadKey.X]: 'X', - [GamepadKey.Y]: 'Y', - - [GamepadKey.UP]: 'DPadUp', - [GamepadKey.RIGHT]: 'DPadRight', - [GamepadKey.DOWN]: 'DPadDown', - [GamepadKey.LEFT]: 'DPadLeft', - - [GamepadKey.LB]: 'LeftShoulder', - [GamepadKey.RB]: 'RightShoulder', - [GamepadKey.LT]: 'LeftTrigger', - [GamepadKey.RT]: 'RightTrigger', - - [GamepadKey.L3]: 'LeftThumb', - [GamepadKey.R3]: 'RightThumb', - [GamepadKey.LS]: 'LeftStickAxes', - [GamepadKey.RS]: 'RightStickAxes', - - [GamepadKey.SELECT]: 'View', - [GamepadKey.START]: 'Menu', - [GamepadKey.SHARE]: 'Share', - }; - static getPref(key: T) { return getPref(key); } @@ -146,14 +120,14 @@ export class StreamSettings { // Swap GamepadKey.A with "A" let gamepadKey: unknown; for (gamepadKey in customization.mapping) { - const gamepadStr = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[gamepadKey as GamepadKey]; + const gamepadStr = toXcloudGamepadKey(gamepadKey as GamepadKey); if (!gamepadStr) { continue; } const mappedKey = customization.mapping[gamepadKey as GamepadKey]; if (typeof mappedKey === 'number') { - converted.mapping[gamepadStr] = StreamSettings.CONTROLLER_CUSTOMIZATION_MAPPING[mappedKey as GamepadKey]; + converted.mapping[gamepadStr] = toXcloudGamepadKey(mappedKey as GamepadKey); } else { converted.mapping[gamepadStr] = false; }