Compare commits

..

13 Commits

2 changed files with 153 additions and 62 deletions

View File

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

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.5.1
// @version 3.5.2
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -16,7 +16,7 @@
/* ADDITIONAL CODE */
const SCRIPT_VERSION = '3.5.1';
const SCRIPT_VERSION = '3.5.2';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
// Setup flags
@ -491,7 +491,7 @@ const Translations = {
"左下",
"좌측 하단",
"Lewy dolny róg",
"Inferior Esquerdo",
"Inferior esquerdo",
"Левый нижний угол",
"Sol alt",
"Внизу ліворуч",
@ -508,7 +508,7 @@ const Translations = {
"右下",
"우측 하단",
"Prawy dolny róg",
"Inferior-direito",
"Inferior direito",
"Правый нижний угол",
"Sağ alt",
"Внизу праворуч",
@ -669,7 +669,7 @@ const Translations = {
"关闭",
],
"combine-audio-video-streams": [
,
"Audio- und Video-Streams kombinieren",
"Gabung audio & video stream",
"Combine audio & video streams",
,
@ -686,7 +686,7 @@ const Translations = {
"合并视频音频流",
],
"combine-audio-video-streams-summary": [
,
"Könnte das Problem mit verzögertem Ton beheben",
"Mungkin memperbaiki masalah lag pada audio",
"May fix the laggy audio problem",
,
@ -695,7 +695,7 @@ const Translations = {
"音声の遅延を改善できる可能性があります",
,
"Może rozwiązać problem z zacinającym dźwiękiem",
"Pode corrigir o problema de áudio Laggy",
"Pode corrigir o problema de áudio atrasado",
"Может исправить проблему подвисания звука",
"Sesteki gecikme sorununa çözüm olabilir",
"Може виправити проблему із затримкою звуку",
@ -882,7 +882,7 @@ const Translations = {
"カスタム",
"사용자 지정",
"Niestandardowe",
"Customizado",
"Personalizado",
"Вручную",
"Özel",
"Користувацькі",
@ -899,7 +899,7 @@ const Translations = {
"デッドゾーンのカウンターウエイト",
,
"Przeciwwaga martwej strefy",
"Contador da Zona Morta",
"Contrapeso de zona morta",
"Противодействие мертвой зоне игры",
"Ölü alan denge ağırlığı",
"Противага Deadzone",
@ -1264,7 +1264,7 @@ const Translations = {
"启用",
],
"experimental": [
,
"Experimentell",
"Eksperimental",
"Experimental",
,
@ -1409,7 +1409,7 @@ const Translations = {
"Webページのスクロールバーを隠す",
,
"Ukryj pasek przewijania strony",
"Oculta a barra de rolagem da página",
"Ocultar a barra de rolagem da página",
"Скрыть полосу прокрутки страницы",
"Yandaki kaydırma çubuğunu gizle",
"Приховати смугу прокрутки вебсторінок",
@ -1579,7 +1579,7 @@ const Translations = {
"ロード画面",
"로딩 화면",
"Ekran wczytywania",
"Tela de Carregamento",
"Tela de carregamento",
"Экран загрузки",
"Yükleme ekranı",
"Екран завантаження",
@ -1698,7 +1698,7 @@ const Translations = {
"ゲーム内の設定で感度とデッドゾーンの調整が必要な場合があります",
,
"Może być również konieczne dostosowanie czułości w grze i ustawienia 'martwej strefy' urządzenia",
"Você também pode precisar ajustar as configurações de sensibilidade e zona morta no jogo",
"Você talvez também precise ajustar as configurações de sensibilidade e zona morta no jogo",
"Также может потребоваться изменить настройки чувствительности и мертвой зоны в игре",
"Bu seçenek etkinken bile oyun içi seçeneklerden hassasiyet ve ölü bölge ayarlarını düzeltmeniz gerekebilir",
"Можливо, вам також доведеться регулювати чутливість і deadzone у параметрах гри",
@ -2004,7 +2004,7 @@ const Translations = {
"IPv6 サーバーを優先",
"IPv6 서버 우선",
"Preferuj serwer IPv6",
"Preferir servidor IPV6",
"Preferir servidor IPv6",
"Предпочитать IPv6 сервер",
"IPv6 sunucusunu tercih et",
"Віддавати перевагу IPv6",
@ -2174,7 +2174,7 @@ const Translations = {
"リモートプレイ",
"리모트 플레이",
"Gra zdalna",
"Jogo Remoto",
"Reprodução remota",
"Удаленная игра",
"Uzaktan Bağlanma",
"Віддалена гра",
@ -2361,7 +2361,7 @@ const Translations = {
"スクリーンショットにビデオフィルターを適用",
,
"Stosuje filtry wideo do zrzutów ekranu",
"Aplicar filtros às capturas de tela",
"Aplicar filtros de vídeo às capturas de tela",
"Применяет фильтры видео к скриншотам",
"Görsel filtreleri ekran görüntülerine de uygular",
"Застосовує відеофільтри до знімків екрана",
@ -2786,7 +2786,7 @@ const Translations = {
"スティックの減衰の最小値",
,
"Minimalne opóźnienie drążka",
"Mínimo decaimento do analógico",
"Tempo mínimo de redefinição do analógico",
"Минимальная перезарядка стика",
"Çubuğun ortalanma süresi minimumu",
"Мінімальне згасання стіка",
@ -2803,7 +2803,7 @@ const Translations = {
"スティックの減衰の強さ",
,
"Siła opóźnienia drążka",
"Força de decaimento do analógico",
"Velocidade de redefinição do analógico",
"Скорость перезарядки стика",
"Çubuğun ortalanma gücü",
"Сила згасання стіка",
@ -2854,7 +2854,7 @@ const Translations = {
"Better xCloudをサポート",
,
"Wesprzyj Better xCloud",
"Suporte ao Better xCloud",
"Apoie o Better xCloud",
"Поддержать Better xCloud",
"Better xCloud'a destek ver",
"Підтримати Better xCloud",
@ -2990,7 +2990,7 @@ const Translations = {
"ミュートカラー",
"저채도 색상",
"Stonowane kolory",
"Cores silenciadas",
"Cores opacas",
"Приглушенные цвета",
"Yumuşak renkler",
"Приглушені кольори",
@ -3041,7 +3041,7 @@ const Translations = {
"上",
"중앙 상단",
"Wyśrodkowany na górze",
"Superior-centralizado",
"Superior centralizado",
"Сверху",
"Orta üst",
"Зверху по центру",
@ -3058,7 +3058,7 @@ const Translations = {
"左上",
"좌측 상단",
"Lewy górny róg",
"Superior-esquerdo",
"Superior esquerdo",
"Левый верхний угол",
"Sol üst",
"Зверху ліворуч",
@ -3075,7 +3075,7 @@ const Translations = {
"右上",
"우측 상단",
"Prawy górny róg",
"Superior-direito",
"Superior direito",
"Справа",
"Sağ üst",
"Зверху праворуч",
@ -3673,40 +3673,56 @@ class RemotePlay {
if (!RemotePlay.#CONSOLES || RemotePlay.#CONSOLES.length === 0) {
$fragment.appendChild(CE('span', {}, t('no-consoles-found')));
} else {
const $settingNote = CE('p', {});
RemotePlay.#$content = CE('div', {}, $fragment);
return;
}
const resolutions = [1080, 720];
const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION);
const $resolutionSelect = CE('select', {});
for (const resolution of resolutions) {
const value = `${resolution}p`;
const $settingNote = CE('p', {});
const $option = CE('option', {'value': value}, value);
if (currentResolution === value) {
$option.selected = true;
}
const resolutions = [1080, 720];
const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION);
const $resolutionGroup = CE('div', {});
for (const resolution of resolutions) {
const value = `${resolution}p`;
const id = `bx_radio_xhome_resolution_${resolution}`;
$resolutionSelect.appendChild($option);
}
$resolutionSelect.addEventListener('change', e => {
const value = $resolutionSelect.value;
const $radio = CE('input', {
'type': 'radio',
'value': value,
'id': id,
'name': 'bx_radio_xhome_resolution',
}, value);
$radio.addEventListener('change', e => {
const value = e.target.value;
$settingNote.textContent = value === '1080p' ? '✅ ' + t('can-stream-xbox-360-games') : '❌ ' + t('cant-stream-xbox-360-games');
setPref(Preferences.REMOTE_PLAY_RESOLUTION, value);
});
$resolutionSelect.dispatchEvent(new Event('change'));
const $qualitySettings = CE('div', {'class': 'bx-remote-play-settings'},
CE('div', {},
CE('label', {}, t('target-resolution'), $settingNote),
$resolutionSelect,
)
);
const $label = CE('label', {
'for': id,
'class': 'bx-remote-play-resolution',
}, $radio, `${resolution}p`);
$fragment.appendChild($qualitySettings);
$resolutionGroup.appendChild($label);
if (currentResolution === value) {
$radio.checked = true;
$radio.dispatchEvent(new Event('change'));
}
}
const $qualitySettings = CE('div', {'class': 'bx-remote-play-settings'},
CE('div', {},
CE('label', {}, t('target-resolution'), $settingNote),
$resolutionGroup,
)
);
$fragment.appendChild($qualitySettings);
// Render concoles list
for (let con of RemotePlay.#CONSOLES) {
let $connectButton;
const $child = CE('div', {'class': 'bx-remote-play-device-wrapper'},
@ -3732,6 +3748,14 @@ class RemotePlay {
$fragment.appendChild($child);
}
// Add Help button
$fragment.appendChild(createButton({
icon: Icon.QUESTION,
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
url: 'https://better-xcloud.github.io/remote-play',
label: t('help'),
}));
RemotePlay.#$content = CE('div', {}, $fragment);
}
@ -4986,7 +5010,7 @@ class MkbPreset {
[MkbPreset.KEY_MOUSE_STICK_DECAY_STRENGTH]: {
label: t('stick-decay-strength'),
type: SettingElement.TYPE_NUMBER_STEPPER,
default: 18,
default: 100,
min: 10,
max: 100,
@ -4999,7 +5023,7 @@ class MkbPreset {
[MkbPreset.KEY_MOUSE_STICK_DECAY_MIN]: {
label: t('stick-decay-minimum'),
type: SettingElement.TYPE_NUMBER_STEPPER,
default: 6,
default: 10,
min: 1,
max: 10,
@ -5266,8 +5290,10 @@ class MkbHandler {
static get DEFAULT_DEADZONE_COUNTERWEIGHT() { return 0.01; }
static get MAXIMUM_STICK_RANGE() { return 1.1; }
VIRTUAL_GAMEPAD_ID = 'Xbox 360 Controller';
#VIRTUAL_GAMEPAD = {
id: 'Xbox 360 Controller',
id: MkbHandler.VIRTUAL_GAMEPAD_ID,
index: 3,
connected: false,
hapticActuators: null,
@ -5389,7 +5415,7 @@ class MkbHandler {
const isKeyDown = e.type === 'keydown';
// Toggle MKB feature
if (isKeyDown && e.code === 'F9') {
if (isKeyDown && e.code === 'F8') {
e.preventDefault();
this.toggle();
return;
@ -5529,7 +5555,7 @@ class MkbHandler {
this.#enabled = !this.#enabled;
this.#enabled ? document.pointerLockElement && this.start() : this.stop();
Toast.show(t('mouse-and-keyboard'), t(this.#enabled ? 'enabled' : 'disabled'));
Toast.show(t('mouse-and-keyboard'), t(this.#enabled ? 'enabled' : 'disabled'), {instant: true});
if (this.#enabled) {
!document.pointerLockElement && this.#waitForPointerLock(true);
@ -5600,6 +5626,7 @@ class MkbHandler {
this.#$message = CE('div', {'class': 'bx-mkb-pointer-lock-msg bx-gone'},
createButton({
icon: Icon.MOUSE_SETTINGS,
style: ButtonStyle.PRIMARY,
onClick: e => {
e.preventDefault();
e.stopPropagation();
@ -5609,7 +5636,7 @@ class MkbHandler {
}),
CE('div', {},
CE('p', {}, t('mkb-click-to-activate')),
CE('p', {}, t('press-key-to-toggle-mkb')({key: 'F9'})),
CE('p', {}, t('press-key-to-toggle-mkb')({key: 'F8'})),
),
);
@ -7904,6 +7931,26 @@ class Patcher {
return funcStr.replace(text, '.disableTelemetry=function(){return!0}');
},
disableTelemetryProvider: function(funcStr) {
const text = 'this.enableLightweightTelemetry=!';
if (!funcStr.includes(text)) {
return false;
}
const newCode = [
'this.trackEvent',
'this.trackPageView',
'this.trackHttpCompleted',
'this.trackHttpFailed',
'this.trackError',
'this.trackErrorLike',
'this.onTrackEvent',
'()=>{}',
].join('=');
return funcStr.replace(text, newCode + ';' + text);
},
// Disable IndexDB logging
disableIndexDbLogging(funcStr) {
const text = 'async addLog(e,t=1e4){';
@ -8004,7 +8051,7 @@ class Patcher {
},
enableXcloudLogger: function(funcStr) {
const text = 'this.telemetryProvider=e}log(e,t,r){';
const text = 'this.telemetryProvider=e}log(e,t,i){';
if (!funcStr.includes(text)) {
return false;
}
@ -8268,6 +8315,8 @@ if (gamepadFound) {
'disableTelemetry',
],
getPref(Preferences.BLOCK_TRACKING) && ['disableTelemetryProvider'],
['disableStreamGate'],
getPref(Preferences.UI_LAYOUT) === 'tv' && ['tvLayout'],
@ -8582,9 +8631,11 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
left: -9999px;
}
/* Remove the "Cloud Gaming" text in header */
header a[href="/play"] {
display: none;
/* Remove the "Cloud Gaming" text in header when the screen is too small */
@media screen and (max-width: 600px) {
header a[href="/play"] {
display: none;
}
}
a.bx-button {
@ -9706,6 +9757,11 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
}
}
.bx-remote-play-container > .bx-button {
display: table;
margin: 0 0 0 auto;
}
.bx-remote-play-settings {
margin-bottom: 12px;
padding-bottom: 12px;
@ -9735,6 +9791,19 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
text-align: center;
}
.bx-remote-play-resolution {
display: block;
}
.bx-remote-play-resolution input[type="radio"] {
accent-color: var(--bx-primary-button-color);
margin-right: 6px;
}
.bx-remote-play-resolution input[type="radio"]:focus {
accent-color: var(--bx-primary-button-hover-color);
}
.bx-remote-play-device-wrapper {
display: flex;
margin-bottom: 12px;
@ -9931,13 +10000,23 @@ body::-webkit-scrollbar {
function getPreferredServerRegion(shortName = false) {
let preferredRegion = getPref(Preferences.SERVER_REGION);
if (preferredRegion in SERVER_REGIONS) {
return shortName ? SERVER_REGIONS[preferredRegion].shortName : preferredRegion;
if (shortName && SERVER_REGIONS[preferredRegion].shortName) {
return SERVER_REGIONS[preferredRegion].shortName;
} else {
return preferredRegion;
}
}
for (let regionName in SERVER_REGIONS) {
const region = SERVER_REGIONS[regionName];
if (region.isDefault) {
return shortName ? region.shortName : regionName;
if (!region.isDefault) {
continue;
}
if (shortName && region.shortName) {
return region.shortName;
} else {
return regionName;
}
}
@ -10390,7 +10469,7 @@ function interceptHttpRequests() {
if (PREF_UI_LOADING_SCREEN_WAIT_TIME && url.includes('xboxlive.com') && url.includes('/waittime/')) {
const response = await NATIVE_FETCH(...arg);
const json = await response.clone.json();
const json = await response.clone().json();
if (json.estimatedAllocationTimeInSeconds > 0) {
// Setup wait time overlay
LoadingScreen.setupWaitTime(json.estimatedTotalWaitTimeInSeconds);
@ -10701,6 +10780,7 @@ function setupSettingsUi() {
});
selectedValue = PREF_PREFERRED_REGION;
setting.options = {};
for (let regionName in SERVER_REGIONS) {
const region = SERVER_REGIONS[regionName];
@ -10710,6 +10790,10 @@ function setupSettingsUi() {
if (region.isDefault) {
label += ` (${t('default')})`;
value = 'default';
if (selectedValue === regionName) {
selectedValue = 'default';
}
}
setting.options[value] = label;
@ -10719,9 +10803,11 @@ function setupSettingsUi() {
const label = setting.options[value];
const $option = CE('option', {value: value}, label);
$option.selected = value === selectedValue || label.includes(selectedValue);
$control.appendChild($option);
}
// Select preferred region
$control.value = selectedValue;
} else {
if (settingId === Preferences.BETTER_XCLOUD_LOCALE) {
$control = PREFS.toElement(settingId, e => {
@ -12013,6 +12099,11 @@ MkbHandler.setupEvents();
// Show a toast when connecting/disconecting controller
function showGamepadToast(gamepad) {
// Don't show Toast for virtual controller
if (gamepad.id === MkbHandler.VIRTUAL_GAMEPAD_ID) {
return;
}
console.log(gamepad);
let text = '🎮';