From 277a830d99cebcb65f91238e60224d3514d5d195 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:28:04 +0700 Subject: [PATCH] Fix unable to reset Virtual controller's preset & Keyboard shortcuts' preset --- dist/better-xcloud.pretty.user.js | 10 +++++----- dist/better-xcloud.user.js | 2 +- src/utils/stream-settings.ts | 5 +---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dist/better-xcloud.pretty.user.js b/dist/better-xcloud.pretty.user.js index b533af9..424324c 100644 --- a/dist/better-xcloud.pretty.user.js +++ b/dist/better-xcloud.pretty.user.js @@ -3691,7 +3691,7 @@ class StreamSettings { StreamSettings.settings.deviceVibrationIntensity = intensity, BxEventBus.Stream.emit("deviceVibration.updated", {}); } static async refreshMkbSettings() { - let settings = StreamSettings.settings, presetId = getStreamPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = { + let settings = StreamSettings.settings, presetId = getStreamPref("mkb.p1.preset.mappingId"), orgPresetData = (await MkbMappingPresetsTable.getInstance().getPreset(presetId)).data, converted = { mapping: {}, mouse: Object.assign({}, orgPresetData.mouse) }, key; @@ -3702,20 +3702,20 @@ class StreamSettings { if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex; } let mouse = converted.mouse; - mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setStreamPref("mkb.p1.preset.mappingId", orgPreset.id, "direct"), BxEventBus.Stream.emit("mkb.setting.updated", {}); + mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, BxEventBus.Stream.emit("mkb.setting.updated", {}); } static async refreshKeyboardShortcuts() { let settings = StreamSettings.settings, presetId = getStreamPref("keyboardShortcuts.preset.inGameId"); if (presetId === 0) { - settings.keyboardShortcuts = null, setStreamPref("keyboardShortcuts.preset.inGameId", presetId, "direct"), BxEventBus.Stream.emit("keyboardShortcuts.updated", {}); + settings.keyboardShortcuts = null, BxEventBus.Stream.emit("keyboardShortcuts.updated", {}); return; } - let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action; + let orgPresetData = (await KeyboardShortcutsTable.getInstance().getPreset(presetId)).data.mapping, converted = {}, action; for (action in orgPresetData) { let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`; converted[key] = action; } - settings.keyboardShortcuts = converted, setStreamPref("keyboardShortcuts.preset.inGameId", orgPreset.id, "direct"), BxEventBus.Stream.emit("keyboardShortcuts.updated", {}); + settings.keyboardShortcuts = converted, BxEventBus.Stream.emit("keyboardShortcuts.updated", {}); } static async refreshAllSettings() { window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts(); diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js index a0a5a23..4d77629 100755 --- a/dist/better-xcloud.user.js +++ b/dist/better-xcloud.user.js @@ -122,7 +122,7 @@ var PointerToMouseButton = {1: 0,2: 2,4: 1}, VIRTUAL_GAMEPAD_ID = "Better xCloud class WebSocketMouseDataProvider extends MouseDataProvider {pointerClient;isConnected = !1;init() {this.pointerClient = PointerClient.getInstance(), this.isConnected = !1;try {this.pointerClient.start(STATES.pointerServerPort, this.mkbHandler), this.isConnected = !0;} catch (e) {Toast.show("Cannot enable Mouse & Keyboard feature");}}start() {this.isConnected && AppInterface.requestPointerCapture();}stop() {this.isConnected && AppInterface.releasePointerCapture();}destroy() {this.isConnected && this.pointerClient?.stop();}} class PointerLockMouseDataProvider extends MouseDataProvider {start() {window.addEventListener("mousemove", this.onMouseMoveEvent), window.addEventListener("mousedown", this.onMouseEvent), window.addEventListener("mouseup", this.onMouseEvent), window.addEventListener("wheel", this.onWheelEvent, { passive: !1 }), window.addEventListener("contextmenu", this.disableContextMenu);}stop() {document.pointerLockElement && document.exitPointerLock(), window.removeEventListener("mousemove", this.onMouseMoveEvent), window.removeEventListener("mousedown", this.onMouseEvent), window.removeEventListener("mouseup", this.onMouseEvent), window.removeEventListener("wheel", this.onWheelEvent), window.removeEventListener("contextmenu", this.disableContextMenu);}onMouseMoveEvent = (e) => {this.mkbHandler.handleMouseMove({movementX: e.movementX,movementY: e.movementY});};onMouseEvent = (e) => {e.preventDefault();let data = {mouseButton: e.button,pressed: e.type === "mousedown"};this.mkbHandler.handleMouseClick(data);};onWheelEvent = (e) => {if (!KeyHelper.getKeyFromEvent(e)) return;let data = {vertical: e.deltaY,horizontal: e.deltaX};if (this.mkbHandler.handleMouseWheel(data)) e.preventDefault();};disableContextMenu = (e) => e.preventDefault();} class EmulatedMkbHandler extends MkbHandler {static instance;static getInstance() {if (typeof EmulatedMkbHandler.instance === "undefined") if (EmulatedMkbHandler.isAllowed()) EmulatedMkbHandler.instance = new EmulatedMkbHandler;else EmulatedMkbHandler.instance = null;return EmulatedMkbHandler.instance;}static LOG_TAG = "EmulatedMkbHandler";static isAllowed() {return getGlobalPref("mkb.enabled") && (AppInterface || !UserAgent.isMobile());}PRESET;VIRTUAL_GAMEPAD = {id: VIRTUAL_GAMEPAD_ID,index: 0,connected: !1,hapticActuators: null,mapping: "standard",axes: [0, 0, 0, 0],buttons: new Array(17).fill(null).map(() => ({ pressed: !1, value: 0 })),timestamp: performance.now(),vibrationActuator: null};nativeGetGamepads;xCloudGamepad = generateVirtualControllerMapping(0);initialized = !1;enabled = !1;mouseDataProvider;isPolling = !1;prevWheelCode = null;wheelStoppedTimeoutId = null;detectMouseStoppedTimeoutId = null;escKeyDownTime = -1;LEFT_STICK_X = [];LEFT_STICK_Y = [];RIGHT_STICK_X = [];RIGHT_STICK_Y = [];popup;STICK_MAP = {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();BxLogger.info(EmulatedMkbHandler.LOG_TAG, "constructor()"), this.nativeGetGamepads = window.navigator.getGamepads.bind(window.navigator), this.popup = MkbPopup.getInstance(), this.popup.attachMkbHandler(this);}isEnabled = () => this.enabled;patchedGetGamepads = () => {let gamepads = this.nativeGetGamepads() || [];return gamepads[this.VIRTUAL_GAMEPAD.index] = this.VIRTUAL_GAMEPAD, gamepads;};getVirtualGamepad = () => this.VIRTUAL_GAMEPAD;updateStick(stick, x, y) {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);resetXcloudGamepads() {let index = getStreamPref("mkb.p1.slot") - 1;this.xCloudGamepad = generateVirtualControllerMapping(0, {GamepadIndex: getStreamPref("localCoOp.enabled") ? index : 0,Dirty: !0}), this.VIRTUAL_GAMEPAD.index = index;}pressButton(buttonIndex, pressed) {let xCloudKey = toXcloudGamepadKey(buttonIndex);if (buttonIndex >= 100) {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]][1];else value = 0;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";if (e.code === "Escape") {if (e.preventDefault(), this.enabled && isKeyDown) {if (this.escKeyDownTime === -1) this.escKeyDownTime = performance.now();else if (performance.now() - this.escKeyDownTime >= 1000) this.stop();} else this.escKeyDownTime = -1;return;}if (!this.isPolling || !this.PRESET) return;if (window.BX_STREAM_SETTINGS.xCloudPollingMode !== "none") return;let buttonIndex = this.PRESET.mapping[e.code || e.key];if (typeof buttonIndex === "undefined") return;if (e.repeat) return;e.preventDefault(), this.pressButton(buttonIndex, isKeyDown);};onMouseStopped = () => {if (this.detectMouseStoppedTimeoutId = null, !this.PRESET) return;let analog = this.PRESET.mouse["mapTo"] === 1 ? 0 : 1;this.updateStick(analog, 0, 0);};handleMouseClick(data) {let mouseButton;if (typeof data.mouseButton !== "undefined") mouseButton = data.mouseButton;else if (typeof data.pointerButton !== "undefined") mouseButton = PointerToMouseButton[data.pointerButton];let key = {code: "Mouse" + mouseButton};if (!this.PRESET) return;let buttonIndex = this.PRESET.mapping[key.code];if (typeof buttonIndex === "undefined") return;this.pressButton(buttonIndex, data.pressed);}handleMouseMove(data) {let preset = this.PRESET;if (!preset) return;let mouseMapTo = preset.mouse["mapTo"];if (mouseMapTo === 0) return;this.detectMouseStoppedTimeoutId && clearTimeout(this.detectMouseStoppedTimeoutId), this.detectMouseStoppedTimeoutId = window.setTimeout(this.onMouseStopped, 50);let deadzoneCounterweight = preset.mouse["deadzoneCounterweight"], x = data.movementX * preset.mouse["sensitivityX"], y = data.movementY * preset.mouse["sensitivityY"], length = this.vectorLength(x, y);if (length !== 0 && length < deadzoneCounterweight) x *= deadzoneCounterweight / length, y *= deadzoneCounterweight / length;else if (length > 1.1) x *= 1.1 / length, y *= 1.1 / length;let analog = mouseMapTo === 1 ? 0 : 1;this.updateStick(analog, x, y);}handleMouseWheel(data) {let code = "";if (data.vertical < 0) code = "ScrollUp";else if (data.vertical > 0) code = "ScrollDown";else if (data.horizontal < 0) code = "ScrollLeft";else if (data.horizontal > 0) code = "ScrollRight";if (!code) return !1;if (!this.PRESET) return !1;let key = {code}, buttonIndex = this.PRESET.mapping[key.code];if (typeof buttonIndex === "undefined") return !1;if (this.prevWheelCode === null || this.prevWheelCode === key.code) this.wheelStoppedTimeoutId && clearTimeout(this.wheelStoppedTimeoutId), this.pressButton(buttonIndex, !0);return this.wheelStoppedTimeoutId = window.setTimeout(() => {this.prevWheelCode = null, this.pressButton(buttonIndex, !1);}, 20), !0;}async toggle(force) {if (!this.initialized) return;if (typeof force !== "undefined") this.enabled = force;else this.enabled = !this.enabled;if (this.enabled) try {await document.body.requestPointerLock({ unadjustedMovement: !0 });} catch (e) {document.body.requestPointerLock(), console.log(e);}else document.pointerLockElement && document.exitPointerLock();}refreshPresetData() {this.PRESET = window.BX_STREAM_SETTINGS.mkbPreset, this.resetXcloudGamepads();}waitForMouseData(showPopup) {this.popup.toggleVisibility(showPopup);}onPollingModeChanged = (e) => {let move = window.BX_STREAM_SETTINGS.xCloudPollingMode !== "none";this.popup.moveOffscreen(move);};onDialogShown = () => {document.pointerLockElement && document.exitPointerLock();};onPointerLockChange = () => {if (document.pointerLockElement) this.start();else this.stop();};onPointerLockError = (e) => {console.log(e), this.stop();};onPointerLockRequested = () => {this.start();};onPointerLockExited = () => {this.mouseDataProvider?.stop();};handleEvent(event) {switch (event.type) {case BxEvent.POINTER_LOCK_REQUESTED:this.onPointerLockRequested();break;case BxEvent.POINTER_LOCK_EXITED:this.onPointerLockExited();break;}}init() {if (!STATES.browser.capabilities.mkb) {this.initialized = !1;return;}if (this.initialized = !0, this.refreshPresetData(), this.enabled = !1, AppInterface) this.mouseDataProvider = new WebSocketMouseDataProvider(this);else this.mouseDataProvider = new PointerLockMouseDataProvider(this);if (this.mouseDataProvider.init(), window.addEventListener("keydown", this.onKeyboardEvent), window.addEventListener("keyup", this.onKeyboardEvent), window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this.onPollingModeChanged), BxEventBus.Script.on("dialog.shown", this.onDialogShown), AppInterface) window.addEventListener(BxEvent.POINTER_LOCK_REQUESTED, this), window.addEventListener(BxEvent.POINTER_LOCK_EXITED, this);else document.addEventListener("pointerlockchange", this.onPointerLockChange), document.addEventListener("pointerlockerror", this.onPointerLockError);if (MkbPopup.getInstance().reset(), AppInterface) {let shortcutKey = StreamSettings.findKeyboardShortcut("mkb.toggle");if (shortcutKey) {let msg = t("press-key-to-toggle-mkb", { key: `${KeyHelper.codeToKeyName(shortcutKey)}` });Toast.show(msg, t("native-mkb"), { html: !0 });}this.waitForMouseData(!1);} else this.waitForMouseData(!0);}destroy() {if (!this.initialized) return;if (this.initialized = !1, this.isPolling = !1, this.enabled = !1, this.stop(), this.waitForMouseData(!1), document.exitPointerLock(), window.removeEventListener("keydown", this.onKeyboardEvent), window.removeEventListener("keyup", this.onKeyboardEvent), AppInterface) window.removeEventListener(BxEvent.POINTER_LOCK_REQUESTED, this), window.removeEventListener(BxEvent.POINTER_LOCK_EXITED, this);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);}start() {if (!this.enabled) this.enabled = !0, Toast.show(t("virtual-controller"), t("enabled"), { instant: !0 });this.isPolling = !0, this.escKeyDownTime = -1, window.BX_EXPOSED.toggleLocalCoOp(getStreamPref("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}), window.BX_EXPOSED.stopTakRendering = !0, Toast.show(t("virtual-controller"), t("enabled"), { instant: !0 });}stop() {this.enabled = !1, this.isPolling = !1, this.escKeyDownTime = -1;let virtualGamepad = this.getVirtualGamepad();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();}static setupEvents() {if (BxEventBus.Stream.on("state.playing", () => {if (STATES.currentStream.titleInfo?.details.hasMkbSupport) NativeMkbHandler.getInstance()?.init();else EmulatedMkbHandler.getInstance()?.init();}), EmulatedMkbHandler.isAllowed())BxEventBus.Stream.on("mkb.setting.updated", () => {EmulatedMkbHandler.getInstance()?.refreshPresetData();});}} -class StreamSettings {static settings = {settings: {},xCloudPollingMode: "all",deviceVibrationIntensity: 0,controllerPollingRate: 4,controllers: {},mkbPreset: null,keyboardShortcuts: {}};static async refreshControllerSettings() {let settings = StreamSettings.settings, controllers = {}, shortcutsTable = ControllerShortcutsTable.getInstance(), mappingTable = ControllerCustomizationsTable.getInstance(), gamepads = window.navigator.getGamepads();for (let gamepad of gamepads) {if (!gamepad?.connected) continue;if (gamepad.id === VIRTUAL_GAMEPAD_ID) continue;let controllerSetting = STORAGE.Stream.getControllerSetting(gamepad.id), shortcutsPreset = await shortcutsTable.getPreset(controllerSetting.shortcutPresetId), shortcutsMapping = !shortcutsPreset ? null : shortcutsPreset.data.mapping, customizationPreset = await mappingTable.getPreset(controllerSetting.customizationPresetId), customizationData = StreamSettings.convertControllerCustomization(customizationPreset?.data);controllers[gamepad.id] = {shortcuts: shortcutsMapping,customization: customizationData};}settings.controllers = controllers, settings.controllerPollingRate = getStreamPref("controller.pollingRate"), await StreamSettings.refreshDeviceVibration();}static preCalculateControllerRange(obj, target, values) {if (values && Array.isArray(values)) {let [from, to] = values;if (from > 1 || to < 100) obj[target] = [from / 100, to / 100];}}static convertControllerCustomization(customization) {if (!customization) return null;let converted = {mapping: {},ranges: {},vibrationIntensity: 1}, gamepadKey;for (gamepadKey in customization.mapping) {let gamepadStr = toXcloudGamepadKey(gamepadKey);if (!gamepadStr) continue;let mappedKey = customization.mapping[gamepadKey];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;}static async refreshDeviceVibration() {if (!STATES.browser.capabilities.deviceVibration) return;let mode = getStreamPref("deviceVibration.mode"), intensity = 0;if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = getStreamPref("deviceVibration.intensity") / 100;StreamSettings.settings.deviceVibrationIntensity = intensity, BxEventBus.Stream.emit("deviceVibration.updated", {});}static async refreshMkbSettings() {let settings = StreamSettings.settings, presetId = getStreamPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = {mapping: {},mouse: Object.assign({}, orgPresetData.mouse)}, key;for (key in orgPresetData.mapping) {let buttonIndex = parseInt(key);if (!orgPresetData.mapping[buttonIndex]) continue;for (let keyName of orgPresetData.mapping[buttonIndex])if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex;}let mouse = converted.mouse;mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setStreamPref("mkb.p1.preset.mappingId", orgPreset.id, "direct"), BxEventBus.Stream.emit("mkb.setting.updated", {});}static async refreshKeyboardShortcuts() {let settings = StreamSettings.settings, presetId = getStreamPref("keyboardShortcuts.preset.inGameId");if (presetId === 0) {settings.keyboardShortcuts = null, setStreamPref("keyboardShortcuts.preset.inGameId", presetId, "direct"), BxEventBus.Stream.emit("keyboardShortcuts.updated", {});return;}let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action;for (action in orgPresetData) {let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`;converted[key] = action;}settings.keyboardShortcuts = converted, setStreamPref("keyboardShortcuts.preset.inGameId", orgPreset.id, "direct"), BxEventBus.Stream.emit("keyboardShortcuts.updated", {});}static async refreshAllSettings() {window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts();}static findKeyboardShortcut(targetAction) {let shortcuts = StreamSettings.settings.keyboardShortcuts;for (let codeStr in shortcuts)if (shortcuts[codeStr] === targetAction) return KeyHelper.parseFullKeyCode(codeStr);return null;}static setup() {let listener = () => {StreamSettings.refreshControllerSettings();};window.addEventListener("gamepadconnected", listener), window.addEventListener("gamepaddisconnected", listener), StreamSettings.refreshAllSettings();}} +class StreamSettings {static settings = {settings: {},xCloudPollingMode: "all",deviceVibrationIntensity: 0,controllerPollingRate: 4,controllers: {},mkbPreset: null,keyboardShortcuts: {}};static async refreshControllerSettings() {let settings = StreamSettings.settings, controllers = {}, shortcutsTable = ControllerShortcutsTable.getInstance(), mappingTable = ControllerCustomizationsTable.getInstance(), gamepads = window.navigator.getGamepads();for (let gamepad of gamepads) {if (!gamepad?.connected) continue;if (gamepad.id === VIRTUAL_GAMEPAD_ID) continue;let controllerSetting = STORAGE.Stream.getControllerSetting(gamepad.id), shortcutsPreset = await shortcutsTable.getPreset(controllerSetting.shortcutPresetId), shortcutsMapping = !shortcutsPreset ? null : shortcutsPreset.data.mapping, customizationPreset = await mappingTable.getPreset(controllerSetting.customizationPresetId), customizationData = StreamSettings.convertControllerCustomization(customizationPreset?.data);controllers[gamepad.id] = {shortcuts: shortcutsMapping,customization: customizationData};}settings.controllers = controllers, settings.controllerPollingRate = getStreamPref("controller.pollingRate"), await StreamSettings.refreshDeviceVibration();}static preCalculateControllerRange(obj, target, values) {if (values && Array.isArray(values)) {let [from, to] = values;if (from > 1 || to < 100) obj[target] = [from / 100, to / 100];}}static convertControllerCustomization(customization) {if (!customization) return null;let converted = {mapping: {},ranges: {},vibrationIntensity: 1}, gamepadKey;for (gamepadKey in customization.mapping) {let gamepadStr = toXcloudGamepadKey(gamepadKey);if (!gamepadStr) continue;let mappedKey = customization.mapping[gamepadKey];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;}static async refreshDeviceVibration() {if (!STATES.browser.capabilities.deviceVibration) return;let mode = getStreamPref("deviceVibration.mode"), intensity = 0;if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = getStreamPref("deviceVibration.intensity") / 100;StreamSettings.settings.deviceVibrationIntensity = intensity, BxEventBus.Stream.emit("deviceVibration.updated", {});}static async refreshMkbSettings() {let settings = StreamSettings.settings, presetId = getStreamPref("mkb.p1.preset.mappingId"), orgPresetData = (await MkbMappingPresetsTable.getInstance().getPreset(presetId)).data, converted = {mapping: {},mouse: Object.assign({}, orgPresetData.mouse)}, key;for (key in orgPresetData.mapping) {let buttonIndex = parseInt(key);if (!orgPresetData.mapping[buttonIndex]) continue;for (let keyName of orgPresetData.mapping[buttonIndex])if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex;}let mouse = converted.mouse;mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, BxEventBus.Stream.emit("mkb.setting.updated", {});}static async refreshKeyboardShortcuts() {let settings = StreamSettings.settings, presetId = getStreamPref("keyboardShortcuts.preset.inGameId");if (presetId === 0) {settings.keyboardShortcuts = null, BxEventBus.Stream.emit("keyboardShortcuts.updated", {});return;}let orgPresetData = (await KeyboardShortcutsTable.getInstance().getPreset(presetId)).data.mapping, converted = {}, action;for (action in orgPresetData) {let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`;converted[key] = action;}settings.keyboardShortcuts = converted, BxEventBus.Stream.emit("keyboardShortcuts.updated", {});}static async refreshAllSettings() {window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts();}static findKeyboardShortcut(targetAction) {let shortcuts = StreamSettings.settings.keyboardShortcuts;for (let codeStr in shortcuts)if (shortcuts[codeStr] === targetAction) return KeyHelper.parseFullKeyCode(codeStr);return null;}static setup() {let listener = () => {StreamSettings.refreshControllerSettings();};window.addEventListener("gamepadconnected", listener), window.addEventListener("gamepaddisconnected", listener), StreamSettings.refreshAllSettings();}} class BxNumberStepper extends HTMLInputElement {intervalId = null;isHolding;controlValue;controlMin;controlMax;uiMin;uiMax;steps;options;onChange;$text;$btnInc;$btnDec;$range;onRangeInput;onClick;onPointerUp;onPointerDown;setValue;normalizeValue;static create(key, value, min, max, options = {}, onChange) {options = options || {}, options.suffix = options.suffix || "", options.disabled = !!options.disabled, options.hideSlider = !!options.hideSlider;let $text, $btnInc, $btnDec, $range, self = CE("div", {class: "bx-number-stepper",id: `bx_setting_${escapeCssSelector(key)}`}, CE("div", !1, $btnDec = CE("button", {_dataset: {type: "dec"},type: "button",class: options.hideSlider ? "bx-focusable" : "",tabindex: options.hideSlider ? 0 : -1}, "-"), $text = CE("span"), $btnInc = CE("button", {_dataset: {type: "inc"},type: "button",class: options.hideSlider ? "bx-focusable" : "",tabindex: options.hideSlider ? 0 : -1}, "+")));if (self.$text = $text, self.$btnInc = $btnInc, self.$btnDec = $btnDec, self.onChange = onChange, self.onRangeInput = BxNumberStepper.onRangeInput.bind(self), self.onClick = BxNumberStepper.onClick.bind(self), self.onPointerUp = BxNumberStepper.onPointerUp.bind(self), self.onPointerDown = BxNumberStepper.onPointerDown.bind(self), self.controlMin = min, self.controlMax = max, self.isHolding = !1, self.options = options, self.uiMin = options.reverse ? -max : min, self.uiMax = options.reverse ? -min : max, self.steps = Math.max(options.steps || 1, 1), BxNumberStepper.setValue.call(self, value), options.disabled) return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), self.disabled = !0, self;if ($range = CE("input", {id: `bx_inp_setting_${key}`,type: "range",min: self.uiMin,max: self.uiMax,value: options.reverse ? -value : value,step: self.steps,tabindex: 0}), self.$range = $range, options.hideSlider && $range.classList.add("bx-gone"), self.addEventListener("input", self.onRangeInput), self.appendChild($range), options.ticks || options.exactTicks) {let markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId });if ($range.setAttribute("list", markersId), options.exactTicks) {let start = Math.max(Math.floor(min / options.exactTicks), 1) * options.exactTicks;if (start === min) start += options.exactTicks;for (let i = start;i < max; i += options.exactTicks)$markers.appendChild(CE("option", {value: options.reverse ? -i : i}));} else for (let i = self.uiMin + options.ticks;i < self.uiMax; i += options.ticks)$markers.appendChild(CE("option", { value: i }));self.appendChild($markers);}return BxNumberStepper.updateButtonsVisibility.call(self), self.addEventListener("click", self.onClick), self.addEventListener("pointerdown", self.onPointerDown), self.addEventListener("contextmenu", BxNumberStepper.onContextMenu), setNearby(self, {focus: options.hideSlider ? $btnInc : $range}), Object.defineProperty(self, "value", {get() {return self.controlValue;},set(value2) {BxNumberStepper.setValue.call(self, value2);}}), Object.defineProperty(self, "disabled", {get() {return $range.disabled;},set(value2) {$btnDec.disabled = value2, $btnInc.disabled = value2, $range.disabled = value2;}}), self;}static setValue(value) {if (this.controlValue = BxNumberStepper.normalizeValue.call(this, value), this.$text.textContent = BxNumberStepper.updateTextValue.call(this), this.$range) this.$range.value = (this.options.reverse ? -this.controlValue : this.controlValue).toString();BxNumberStepper.updateButtonsVisibility.call(this);}static normalizeValue(value) {return value = parseInt(value), value = Math.max(this.controlMin, value), value = Math.min(this.controlMax, value), value;}static onRangeInput(e) {let value = parseInt(e.target.value);if (this.options.reverse) value *= -1;if (BxNumberStepper.setValue.call(this, value), BxNumberStepper.updateButtonsVisibility.call(this), !e.ignoreOnChange && this.onChange) this.onChange(e, value);}static onClick(e) {if (e.preventDefault(), this.isHolding) return;let $btn = e.target.closest("button");$btn && BxNumberStepper.buttonPressed.call(this, e, $btn), BxNumberStepper.clearIntervalId.call(this), this.isHolding = !1;}static onPointerDown(e) {BxNumberStepper.clearIntervalId.call(this);let $btn = e.target.closest("button");if (!$btn) return;this.isHolding = !0, e.preventDefault(), this.intervalId = window.setInterval((e2) => {BxNumberStepper.buttonPressed.call(this, e2, $btn);}, 200), window.addEventListener("pointerup", this.onPointerUp, { once: !0 }), window.addEventListener("pointercancel", this.onPointerUp, { once: !0 });}static onPointerUp(e) {BxNumberStepper.clearIntervalId.call(this), this.isHolding = !1;}static onContextMenu(e) {e.preventDefault();}static updateTextValue() {let value = this.controlValue, textContent = null;if (this.options.customTextValue) textContent = this.options.customTextValue(value, this.controlMin, this.controlMax);if (textContent === null) textContent = value.toString() + this.options.suffix;return textContent;}static buttonPressed(e, $btn) {BxNumberStepper.change.call(this, $btn.dataset.type);}static change(direction) {let value = this.controlValue;if (value = this.options.reverse ? -value : value, direction === "dec") value = Math.max(this.uiMin, value - this.steps);else value = Math.min(this.uiMax, value + this.steps);value = this.options.reverse ? -value : value, BxNumberStepper.setValue.call(this, value), BxNumberStepper.updateButtonsVisibility.call(this), this.onChange && this.onChange(null, this.controlValue);}static clearIntervalId() {this.intervalId && clearInterval(this.intervalId), this.intervalId = null;}static updateButtonsVisibility() {if (this.$btnDec.classList.toggle("bx-inactive", this.controlValue === this.uiMin), this.$btnInc.classList.toggle("bx-inactive", this.controlValue === this.uiMax), this.controlValue === this.uiMin || this.controlValue === this.uiMax) BxNumberStepper.clearIntervalId.call(this);}} class SettingElement {static renderOptions(key, setting, currentValue, onChange) {let $control = CE("select", {tabindex: 0}), $parent;if (setting.optionsGroup) $parent = CE("optgroup", {label: setting.optionsGroup}), $control.appendChild($parent);else $parent = $control;for (let value in setting.options) {let label = setting.options[value], $option = CE("option", { value }, label);$parent.appendChild($option);}return $control.value = currentValue, onChange && $control.addEventListener("input", (e) => {let target = e.target, value = setting.type && setting.type === "number" ? parseInt(target.value) : target.value;!e.ignoreOnChange && onChange(e, value);}), $control.setValue = (value) => {$control.value = value;}, $control;}static renderMultipleOptions(key, setting, currentValue, onChange, params = {}) {let $control = CE("select", {multiple: !0,tabindex: 0}), totalOptions = Object.keys(setting.multipleOptions).length, size = params.size ? Math.min(params.size, totalOptions) : totalOptions;$control.setAttribute("size", size.toString());for (let value in setting.multipleOptions) {let label = setting.multipleOptions[value], $option = CE("option", { value }, label);$option.selected = currentValue.indexOf(value) > -1, $option.addEventListener("mousedown", function(e) {e.preventDefault();let target = e.target;target.selected = !target.selected;let $parent = target.parentElement;$parent.focus(), BxEvent.dispatch($parent, "input");}), $control.appendChild($option);}return $control.addEventListener("mousedown", function(e) {let self = this, orgScrollTop = self.scrollTop;window.setTimeout(() => self.scrollTop = orgScrollTop, 0);}), $control.addEventListener("mousemove", (e) => e.preventDefault()), onChange && $control.addEventListener("input", (e) => {let target = e.target, values = Array.from(target.selectedOptions).map((i) => i.value);!e.ignoreOnChange && onChange(e, values);}), Object.defineProperty($control, "value", {get() {return Array.from($control.options).filter((option) => option.selected).map((option) => option.value);},set(value) {let values = value.split(",");Array.from($control.options).forEach((option) => {option.selected = values.includes(option.value);});}}), $control;}static renderCheckbox(key, setting, currentValue, onChange) {let $control = CE("input", { type: "checkbox", tabindex: 0 });return $control.checked = currentValue, onChange && $control.addEventListener("input", (e) => {!e.ignoreOnChange && onChange(e, e.target.checked);}), $control.setValue = (value) => {$control.checked = !!value;}, $control;}static renderNumberStepper(key, setting, value, onChange, options = {}) {return BxNumberStepper.create(key, value, setting.min, setting.max, options, onChange);}static METHOD_MAP = {options: SettingElement.renderOptions,"multiple-options": SettingElement.renderMultipleOptions,"number-stepper": SettingElement.renderNumberStepper,checkbox: SettingElement.renderCheckbox};static render(type, key, setting, currentValue, onChange, options) {let method = SettingElement.METHOD_MAP[type], $control = method(...Array.from(arguments).slice(1));if (type !== "number-stepper") $control.id = `bx_setting_${escapeCssSelector(key)}`;if (type === "options" || type === "multiple-options") $control.name = $control.id;return $control;}static fromPref(key, onChange, overrideParams = {}) {let { definition, storage } = getPrefInfo(key);if (!definition) return null;let currentValue = storage.getSetting(key), type;if ("options" in definition) type = "options";else if ("multipleOptions" in definition) type = "multiple-options";else if (typeof definition.default === "number") type = "number-stepper";else type = "checkbox";let params = {};if ("params" in definition) params = Object.assign(overrideParams, definition.params || {});if (params.disabled) currentValue = definition.default;return SettingElement.render(type, key, definition, currentValue, (e, value) => {if (isGlobalPref(key)) setGlobalPref(key, value, "ui");else {let id = SettingsManager.getInstance().getTargetGameId();setGamePref(id, key, value, "ui");}onChange && onChange(e, value);}, params);}} class BxSelectElement extends HTMLSelectElement {isControllerFriendly;optionsList;indicatorsList;$indicators;visibleIndex;isMultiple;$select;$btnNext;$btnPrev;$label;$checkBox;static create($select, forceFriendly = !1) {let isControllerFriendly = forceFriendly || getGlobalPref("ui.controllerFriendly");if ($select.multiple && !isControllerFriendly) return $select.classList.add("bx-select"), $select;$select.removeAttribute("tabindex");let $wrapper = CE("div", {class: "bx-select",_dataset: {controllerFriendly: isControllerFriendly}});if ($select.classList.contains("bx-full-width")) $wrapper.classList.add("bx-full-width");let $content, self = $wrapper;self.isControllerFriendly = isControllerFriendly, self.isMultiple = $select.multiple, self.visibleIndex = $select.selectedIndex, self.$select = $select, self.optionsList = Array.from($select.querySelectorAll("option")), self.$indicators = CE("div", { class: "bx-select-indicators" }), self.indicatorsList = [];let $btnPrev, $btnNext;if (isControllerFriendly) {$btnPrev = createButton({label: "<",style: 64}), $btnNext = createButton({label: ">",style: 64}), setNearby($wrapper, {orientation: "horizontal",focus: $btnNext}), self.$btnNext = $btnNext, self.$btnPrev = $btnPrev;let boundOnPrevNext = BxSelectElement.onPrevNext.bind(self);$btnPrev.addEventListener("click", boundOnPrevNext), $btnNext.addEventListener("click", boundOnPrevNext);} else $select.addEventListener("change", (e) => {self.visibleIndex = $select.selectedIndex, BxSelectElement.resetIndicators.call(self), BxSelectElement.render.call(self);});if (self.isMultiple) $content = CE("button", {class: "bx-select-value bx-focusable",tabindex: 0}, CE("div", !1, self.$checkBox = CE("input", { type: "checkbox" }), self.$label = CE("span", !1, "")), self.$indicators), $content.addEventListener("click", (e) => {self.$checkBox.click();}), self.$checkBox.addEventListener("input", (e) => {let $option = BxSelectElement.getOptionAtIndex.call(self, self.visibleIndex);$option && ($option.selected = e.target.checked), BxEvent.dispatch($select, "input");});else $content = CE("div", !1, self.$label = CE("label", { for: $select.id + "_checkbox" }, ""), self.$indicators);return $select.addEventListener("input", BxSelectElement.render.bind(self)), new MutationObserver((mutationList, observer2) => {mutationList.forEach((mutation) => {if (mutation.type === "childList" || mutation.type === "attributes") self.visibleIndex = $select.selectedIndex, self.optionsList = Array.from($select.querySelectorAll("option")), BxSelectElement.resetIndicators.call(self), BxSelectElement.render.call(self);});}).observe($select, {subtree: !0,childList: !0,attributes: !0}), self.append($select, $btnPrev || "", $content, $btnNext || ""), BxSelectElement.resetIndicators.call(self), BxSelectElement.render.call(self), Object.defineProperty(self, "value", {get() {return $select.value;},set(value) {self.optionsList = Array.from($select.querySelectorAll("option")), $select.value = value, self.visibleIndex = $select.selectedIndex, BxSelectElement.resetIndicators.call(self), BxSelectElement.render.call(self);}}), Object.defineProperty(self, "disabled", {get() {return $select.disabled;},set(value) {$select.disabled = value;}}), self.addEventListener = function() {$select.addEventListener.apply($select, arguments);}, self.removeEventListener = function() {$select.removeEventListener.apply($select, arguments);}, self.dispatchEvent = function() {return $select.dispatchEvent.apply($select, arguments);}, self.appendChild = function(node) {return $select.appendChild(node), node;}, self;}static resetIndicators() {let {optionsList,indicatorsList,$indicators} = this, targetSize = optionsList.length;if (indicatorsList.length > targetSize) while (indicatorsList.length > targetSize)indicatorsList.pop()?.remove();else if (indicatorsList.length < targetSize) while (indicatorsList.length < targetSize) {let $indicator = CE("span", {});indicatorsList.push($indicator), $indicators.appendChild($indicator);}for (let $indicator of indicatorsList)clearDataSet($indicator);$indicators.classList.toggle("bx-invisible", targetSize <= 1);}static getOptionAtIndex(index) {return this.optionsList[index];}static render(e) {let {$label,$btnNext,$btnPrev,$checkBox,optionsList,indicatorsList} = this;if (e && e.manualTrigger) this.visibleIndex = this.$select.selectedIndex;this.visibleIndex = BxSelectElement.normalizeIndex.call(this, this.visibleIndex);let $option = BxSelectElement.getOptionAtIndex.call(this, this.visibleIndex), content = "";if ($option) {let $parent = $option.parentElement, hasLabel = $parent instanceof HTMLOptGroupElement || this.$select.querySelector("optgroup");if (content = $option.dataset.label || $option.textContent || "", content && hasLabel) {let groupLabel = $parent instanceof HTMLOptGroupElement ? $parent.label : " ";$label.innerHTML = "";let fragment = document.createDocumentFragment();fragment.appendChild(CE("span", !1, groupLabel)), fragment.appendChild(document.createTextNode(content)), $label.appendChild(fragment);} else $label.textContent = content;} else $label.textContent = content;if ($label.classList.toggle("bx-line-through", $option && $option.disabled), this.isMultiple) $checkBox.checked = $option?.selected || !1, $checkBox.classList.toggle("bx-gone", !content);let disableButtons = optionsList.length <= 1;$btnPrev?.classList.toggle("bx-gone", disableButtons), $btnNext?.classList.toggle("bx-gone", disableButtons);for (let i = 0;i < optionsList.length; i++) {let $option2 = optionsList[i], $indicator = indicatorsList[i];if (!$option2 || !$indicator) continue;if (clearDataSet($indicator), $option2.selected) $indicator.dataset.selected = "true";if ($option2.index === this.visibleIndex) $indicator.dataset.highlighted = "true";}}static normalizeIndex(index) {return Math.min(Math.max(index, 0), this.optionsList.length - 1);}static onPrevNext(e) {if (!e.target) return;let {$btnNext,$select,isMultiple,visibleIndex: currentIndex} = this, newIndex = e.target.closest("button") === $btnNext ? currentIndex + 1 : currentIndex - 1;if (newIndex > this.optionsList.length - 1) newIndex = 0;else if (newIndex < 0) newIndex = this.optionsList.length - 1;if (newIndex = BxSelectElement.normalizeIndex.call(this, newIndex), this.visibleIndex = newIndex, !isMultiple && newIndex !== currentIndex) $select.selectedIndex = newIndex;if (isMultiple) BxSelectElement.render.call(this);else BxEvent.dispatch($select, "input");}} diff --git a/src/utils/stream-settings.ts b/src/utils/stream-settings.ts index bc3deca..6b67b6f 100755 --- a/src/utils/stream-settings.ts +++ b/src/utils/stream-settings.ts @@ -13,7 +13,7 @@ import { ShortcutAction } from "@/enums/shortcut-actions"; import { KeyHelper } from "@/modules/mkb/key-helper"; import { BxEventBus } from "./bx-event-bus"; import { ControllerCustomizationsTable } from "./local-db/controller-customizations-table"; -import { getStreamPref, setStreamPref, STORAGE } from "@/utils/pref-utils"; +import { getStreamPref, STORAGE } from "@/utils/pref-utils"; export type StreamSettingsData = { @@ -191,7 +191,6 @@ export class StreamSettings { settings.mkbPreset = converted; - setStreamPref(StreamPref.MKB_P1_MAPPING_PRESET_ID, orgPreset.id, 'direct'); BxEventBus.Stream.emit('mkb.setting.updated', {}); } @@ -202,7 +201,6 @@ export class StreamSettings { if (presetId === KeyboardShortcutDefaultId.OFF) { settings.keyboardShortcuts = null; - setStreamPref(StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId, 'direct'); BxEventBus.Stream.emit('keyboardShortcuts.updated', {}); return; } @@ -222,7 +220,6 @@ export class StreamSettings { settings.keyboardShortcuts = converted; - setStreamPref(StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id, 'direct'); BxEventBus.Stream.emit('keyboardShortcuts.updated', {}); }