Compare commits

...

10 Commits

2 changed files with 155 additions and 49 deletions

View File

@ -1,5 +1,5 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.2.1
// @version 3.2.2
// ==/UserScript==

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.2.1
// @version 3.2.2
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -14,7 +14,7 @@
// ==/UserScript==
'use strict';
const SCRIPT_VERSION = '3.2.1';
const SCRIPT_VERSION = '3.2.2';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
const ENABLE_XCLOUD_LOGGER = false;
@ -996,7 +996,7 @@ const Translations = {
"启用手柄快捷方式",
],
"enable-local-co-op-support": [
"Lokale Coop-Unterstützung aktivieren",
"Lokale Koop-Unterstützung aktivieren",
"Enable local co-op support",
"Habilitar soporte co-op local",
,
@ -1009,7 +1009,7 @@ const Translations = {
"Yerel çok oyuncu desteğini aktive et",
"Увімкнути локальну co-op підтримку",
"Kích hoạt tính năng chơi chung cục bộ",
,
"启用本地多人联机",
],
"enable-local-co-op-support-note": [
"Funktioniert nur, wenn das Spiel kein anderes Profil benötigt",
@ -1025,7 +1025,7 @@ const Translations = {
"Bu seçenek ancak oyun ayrı profillere giriş yapılmasını istemiyorsa etki eder",
"Працює, лише якщо для гри не потрібен інший профіль",
"Chỉ hoạt động nếu game không yêu cầu thêm tài khoản khác",
,
"仅在当前游戏不要求切换账户时才能使用",
],
"enable-mic-on-startup": [
"Mikrofon bei Spielstart aktivieren",
@ -1169,7 +1169,7 @@ const Translations = {
"Mobil cihazda Fortnite: Dünyayı Kurtar modunu etkinleştir",
"Дозволити відтворення режиму STW на мобільному пристрої",
"Cho phép chơi chế độ STW trên điện thoại",
,
"允许游玩Save the World模式",
],
"fortnite-force-console-version": [
"Fortnite: Erzwinge Konsolenversion",
@ -1185,7 +1185,7 @@ const Translations = {
"Fortnite'ın konsol sürümünü aç",
"Fortnite: примусова консольна версія",
"Fortnite: bắt buộc phiên bản console",
,
"Fortnite: 强制使用主机版客户端",
],
"getting-consoles-list": [
"Rufe Liste der Konsolen ab...",
@ -1380,20 +1380,20 @@ const Translations = {
"载入画面",
],
"local-co-op": [
,
"Lokales Koop",
"Local co-op",
"Co-op local",
,
,
"ローカルマルチプレイ",
,
,
"Lokalna kooperacja",
"Co-op local",
,
,
"Локальная кооперативная игра",
"Yerel çoklu oyunculu",
"Локальний co-op",
"Chơi chung cục bộ",
,
"本地多人联机",
],
"map-mouse-to": [
"Maus binden an",
@ -2123,13 +2123,13 @@ const Translations = {
,
"タッチコントローラーとコントローラー#1を分ける",
,
,
"Oddziel Kontroler dotykowy i Kontroler #1",
"Separar o Controle por Toque e o Controle #1",
"Раздельный сенсорный контроллер и контроллер #1",
,
"Dokunmatik kumandayı ve birincil kumandayı ayrı tut",
"Окремо Сенсорний контролер та Контролер #1",
"Tách biệt Bộ điều khiển cảm ứng và Tay cầm #1",
,
"虚拟摇杆和手柄分别控制不同角色",
],
"separate-touch-controller-note": [
"Touch-Controller ist Spieler 1, Controller #1 ist Spieler 2",
@ -2139,13 +2139,13 @@ const Translations = {
,
"タッチコントローラーがプレイヤー1、コントローラー#1がプレイヤー2に割り当てられます",
,
,
"Kontroler dotykowy to Gracz 1, Kontroler #1 to Gracz 2",
"O Controle por Toque é o Jogador 1, o Controle #1 é o Jogador 2",
"Сенсорный контроллер — игрок 1, контроллер #1 — игрок 2",
,
"Dokunmaktik kumanda birinci oyuncu, birincil kumanda ikinci oyuncu",
"Сенсорний контролер це Гравець 1, Контролер #1 це Гравець 2",
"Bộ điều khiển cảm ứng là Người chơi 1, Tay cầm #1 là Người chơi 2",
,
"虚拟摇杆为玩家1手柄#1为玩家2",
],
"server": [
"Server",
@ -2627,6 +2627,22 @@ const Translations = {
"Trắng hoàn toàn",
"白色",
],
"tc-auto-off": [
"Aus, wenn Controller gefunden",
"Off when controller found",
"Desactivar cuando se encuentra el controlador",
,
,
"コントローラー接続時に無効化",
,
"Wyłącz, gdy kontroler zostanie znaleziony",
,
,
"Başka bir kumanda bağlandığında kapat",
"Вимкнено, коли контролер знайдено",
"Tắt khi sử dụng tay cầm",
"手柄连接时隐藏虚拟摇杆",
],
"tc-availability": [
"Verfügbarkeit",
"Availability",
@ -3061,7 +3077,7 @@ const Translations = {
],
}
const LOCALE = Translations.getLocale();
let LOCALE = Translations.getLocale();
const t = Translations.get;
@ -6712,6 +6728,7 @@ class Preferences {
static get STREAM_SIMPLIFY_MENU() { return 'stream_simplify_menu'; }
static get STREAM_TOUCH_CONTROLLER() { return 'stream_touch_controller'; }
static get STREAM_TOUCH_CONTROLLER_AUTO_OFF() { return 'stream_touch_controller_auto_off'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_STANDARD() { return 'stream_touch_controller_style_standard'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM() { return 'stream_touch_controller_style_custom'; }
@ -6929,21 +6946,25 @@ class Preferences {
[Preferences.HIDE_DOTS_ICON]: {
'default': false,
},
[Preferences.STREAM_TOUCH_CONTROLLER]: {
'default': 'all',
'options': {
'default': t('default'),
'all': t('tc-all-games'),
'off': t('off'),
},
'unsupported': !HAS_TOUCH_SUPPORT,
'ready': () => {
const setting = Preferences.SETTINGS[Preferences.STREAM_TOUCH_CONTROLLER];
if (setting.unsupported) {
setting.default = 'off';
setting.default = 'default';
}
},
},
[Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: {
'default': false,
'unsupported': !HAS_TOUCH_SUPPORT,
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
'default': 'default',
'options': {
@ -6961,6 +6982,7 @@ class Preferences {
},
'unsupported': !HAS_TOUCH_SUPPORT,
},
[Preferences.STREAM_SIMPLIFY_MENU]: {
'default': false,
},
@ -7482,12 +7504,23 @@ class Patcher {
// Block WebRTC stats collector
blockWebRtcStatsCollector: function(funcStr) {
const text = 'this.intervalMs=0,';
const text = 'this.shouldCollectStats=!0';
if (!funcStr.includes(text)) {
return false;
}
return funcStr.replace(text, 'false,' + text);
return funcStr.replace(text, 'this.shouldCollectStats=!1');
},
blockGamepadStatsCollector: function(funcStr) {
const text = 'this.inputPollingIntervalStats.addValue';
if (!funcStr.includes(text)) {
return false;
}
funcStr = funcStr.replace('this.inputPollingIntervalStats.addValue', '');
funcStr = funcStr.replace('this.inputPollingDurationStats.addValue', '');
return funcStr;
},
enableXcloudLogger: function(funcStr) {
@ -7690,6 +7723,32 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
funcStr = funcStr.replace(text, text + newCode);
return funcStr;
},
disableTakRenderer: function(funcStr) {
const text = 'const{TakRenderer:';
if (!funcStr.includes(text)) {
return false;
}
const newCode = `
const gamepads = window.navigator.getGamepads();
let gamepadFound = false;
for (let gamepad of gamepads) {
if (gamepad && gamepad.connected) {
gamepadFound = true;
break;
}
}
if (gamepadFound) {
return;
}
`;
funcStr = funcStr.replace(text, newCode + text);
return funcStr;
},
};
static #PATCH_ORDERS = [
@ -7733,10 +7792,13 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
['playVibration'],
getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF) && ['disableTakRenderer'],
ENABLE_XCLOUD_LOGGER && ['enableConsoleLogging'],
getPref(Preferences.BLOCK_TRACKING) && ['blockGamepadStatsCollector'],
[
'disableGamepadDisconnectedScreen',
ENABLE_NATIVE_MKB_BETA && 'mkbMouseAndKeyboardEnabled',
@ -7979,6 +8041,7 @@ function addCss() {
--bx-danger-button-disabled-color: #a26c6c;
--bx-toast-z-index: 9999;
--bx-reload-button-z-index: 9200;
--bx-dialog-z-index: 9101;
--bx-dialog-overlay-z-index: 9100;
--bx-stats-bar-z-index: 9001;
@ -8128,6 +8191,22 @@ a.bx-button {
bottom: 0;
}
.bx-settings-reload-button-wrapper {
z-index: var(--bx-reload-button-z-index);
position: fixed;
bottom: 0;
left: 0;
right: 0;
text-align: center;
background: #000000cf;
padding: 10px;
}
.bx-settings-reload-button-wrapper button {
max-width: 450px;
margin: 0 !important;
}
.better_xcloud_settings {
background-color: #151515;
user-select: none;
@ -8271,6 +8350,15 @@ a.bx-button {
.bx-settings-row input {
align-self: center;
accent-color: var(--bx-primary-button-color);
}
.bx-settings-row select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
color: #fff;
}
.bx-settings-wrapper .bx-button.bx-primary {
@ -8687,6 +8775,10 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
margin-bottom: 0 !important;
}
.bx-quick-settings-row input {
accent-color: var(--bx-primary-button-color);
}
.bx-quick-settings-tab-contents h2 {
margin-bottom: 8px;
display: flex;
@ -8818,13 +8910,18 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
}
.bx-mkb-settings select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
text-align: right;
border: none;
color: #fff;
}
.bx-quick-settings-row select:disabled {
text-align: right;
-webkit-appearance: none;
background: transparent;
text-align-last: right;
}
.bx-mkb-pointer-lock-msg {
@ -9058,11 +9155,6 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
font-size: 12px;
}
.bx-remote-play-settings input {
display: block;
margin: 0 auto;
}
.bx-remote-play-settings span {
font-weight: bold;
font-size: 18px;
@ -9181,15 +9273,6 @@ div[class*=StreamHUD-module__buttonsContainer] {
`;
}
// Hide touch controller
if (getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'off') {
css += `
#MultiTouchSurface, #BabylonCanvasContainer-main {
display: none !important;
}
`;
}
// Simplify Stream's menu
css += `
div[class*=StreamMenu-module__menu] {
@ -9806,6 +9889,7 @@ function injectSettingsButton($parent) {
const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION);
const $headerFragment = document.createDocumentFragment();
let $reloadBtnWrapper;
// Remote Play button
if (getPref(Preferences.REMOTE_PLAY_ENABLED)) {
@ -9919,6 +10003,7 @@ function injectSettingsButton($parent) {
[t('touch-controller')]: {
_note: !HAS_TOUCH_SUPPORT ? '⚠️ ' + t('device-unsupported-touch') : null,
[Preferences.STREAM_TOUCH_CONTROLLER]: t('tc-availability'),
[Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: t('tc-auto-off'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: t('tc-standard-layout-style'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: t('tc-custom-layout-style'),
},
@ -9956,6 +10041,23 @@ function injectSettingsButton($parent) {
$wrapper.appendChild($group);
let onChange = e => {
if (!$reloadBtnWrapper) {
return;
}
$reloadBtnWrapper.classList.remove('bx-gone');
if (e.target.id === 'bx_setting_' + Preferences.BETTER_XCLOUD_LOCALE) {
// Update locale
LOCALE = Translations.getLocale();
const $btn = $reloadBtnWrapper.firstElementChild;
$btn.textContent = t('settings-reloading');
$btn.click();
}
};
for (let settingId in SETTINGS_UI[groupLabel]) {
if (settingId.startsWith('_')) {
continue;
@ -9978,6 +10080,7 @@ function injectSettingsButton($parent) {
});
$inpCustomUserAgent.addEventListener('change', e => {
setPref(Preferences.USER_AGENT_CUSTOM, e.target.value.trim());
onChange(e);
});
$control = PREFS.toElement(Preferences.USER_AGENT_PROFILE, e => {
@ -9988,6 +10091,8 @@ function injectSettingsButton($parent) {
$inpCustomUserAgent.value = userAgent;
$inpCustomUserAgent.readOnly = !isCustom;
$inpCustomUserAgent.disabled = !isCustom;
onChange(e);
});
} else if (settingId === Preferences.SERVER_REGION) {
let selectedValue;
@ -9995,6 +10100,7 @@ function injectSettingsButton($parent) {
$control = CE('select', {id: `bx_setting_${settingId}`});
$control.addEventListener('change', e => {
setPref(settingId, e.target.value);
onChange(e);
});
selectedValue = PREF_PREFERRED_REGION;
@ -10020,15 +10126,14 @@ function injectSettingsButton($parent) {
$control.appendChild($option);
}
} else {
let onChange = null;
if (settingId === Preferences.BETTER_XCLOUD_LOCALE) {
onChange = e => {
$control = PREFS.toElement(settingId, e => {
localStorage.setItem('better_xcloud_locale', e.target.value);
window.location.reload();
}
onChange(e);
});
} else {
$control = PREFS.toElement(settingId, onChange);
}
$control = PREFS.toElement(settingId, onChange);
labelAttrs = {'for': $control.id, 'tabindex': 0};
}
@ -10059,9 +10164,8 @@ function injectSettingsButton($parent) {
// Setup Reload button
const $reloadBtn = createButton({
classes: ['bx-settings-reload-button'],
label: t('settings-reload'),
style: ButtonStyle.PRIMARY | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
style: ButtonStyle.DANGER | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
onClick: e => {
window.location.reload();
$reloadBtn.disabled = true;
@ -10069,7 +10173,9 @@ function injectSettingsButton($parent) {
},
});
$reloadBtn.setAttribute('tabindex', 0);
$wrapper.appendChild($reloadBtn);
$reloadBtnWrapper = CE('div', {'class': 'bx-settings-reload-button-wrapper bx-gone'}, $reloadBtn);
$wrapper.appendChild($reloadBtnWrapper);
// Donation link
const $donationLink = CE('a', {'class': 'bx-donation-link', href: 'https://ko-fi.com/redphx', target: '_blank'}, `❤️ ${t('support-better-xcloud')}`);