mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-07-02 04:11:44 +02:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
c207025df9 | |||
83b35dfc61 | |||
e6ec664087 | |||
06790c8098 | |||
7310a009df | |||
5392414abd | |||
d78e55586e | |||
77abc44f3d | |||
788ac3d527 | |||
47ef5a9cd2 |
@ -1,5 +1,5 @@
|
||||
// ==UserScript==
|
||||
// @name Better xCloud
|
||||
// @namespace https://github.com/redphx
|
||||
// @version 3.2.1
|
||||
// @version 3.2.2
|
||||
// ==/UserScript==
|
||||
|
@ -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')}`);
|
||||
|
Reference in New Issue
Block a user