mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-10-14 14:49:31 +02:00
Reduce Virtual Controller's input latency
This commit is contained in:
151
dist/better-xcloud.user.js
vendored
151
dist/better-xcloud.user.js
vendored
@@ -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
|
||||
});
|
||||
|
Reference in New Issue
Block a user