Compare commits

...

24 Commits

Author SHA1 Message Date
77c4e3981f Bump version to 3.5.2 2024-04-13 05:53:11 +07:00
1cb4038abf Fix typo when calling Response.clone() (#361) 2024-04-13 05:31:18 +07:00
ec6c3fc8a3 Minor fix in showGamepadToast() 2024-04-12 20:37:45 +07:00
b27f84b980 Fix region selection box not selecting correct value 2024-04-12 20:35:00 +07:00
37d0a0c6e2 Update translations 2024-04-12 18:38:05 +07:00
5d272425db Don't show Toast for virtual controller 2024-04-12 18:36:06 +07:00
6b5cdca51a Change stick decay's default value to 100 2024-04-12 18:31:21 +07:00
90a5613959 Change style of the MKB Settings button 2024-04-12 18:27:42 +07:00
1bdfa6c23f Change the toggling MKB key from F9 to F8 2024-04-12 18:06:35 +07:00
47fbbdcb59 Remote Play popup: use radio inputs & add Help button 2024-04-12 17:24:54 +07:00
69a8db092e Add "disableTelemetryProvider" patch 2024-04-12 15:44:09 +07:00
928a1484d7 Fix getPreferredServerRegion() when shortName is empty 2024-04-12 05:56:36 +07:00
65ca3dab0e Only remove the "Cloud Gaming" text when the screen is too small 2024-04-12 05:48:51 +07:00
35a783c53e Bump version to 3.5.1 2024-04-11 17:42:03 +07:00
afe3809061 Upper case server's short name in dropdown box 2024-04-11 17:31:44 +07:00
5eeb15f201 Prevent Remote Play from breaking in the future 2024-04-11 17:23:21 +07:00
71b4109385 Remove the "Cloud Gaming" text in header 2024-04-11 17:12:29 +07:00
8286429cc3 Add "patchStreamHud" patch 2024-04-11 17:12:15 +07:00
ae24005f08 Fix exception in fetch() 2024-04-11 17:11:59 +07:00
2626408cbe Fix the Guide/Nexus button not working in Remote Play 2024-04-11 15:09:59 +07:00
804f751646 Fix Remote Play 2024-04-11 12:43:46 +07:00
13a20f30e5 Fix Remote Play stopped working 2024-04-11 07:30:16 +07:00
46265f2ccd Show server's short name in header 2024-04-10 20:57:40 +07:00
93d77c3783 Fix not updating SCRIPT_VERSION 2024-04-08 17:50:47 +07:00
2 changed files with 234 additions and 87 deletions

View File

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

View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Better xCloud // @name Better xCloud
// @namespace https://github.com/redphx // @namespace https://github.com/redphx
// @version 3.5.0 // @version 3.5.2
// @description Improve Xbox Cloud Gaming (xCloud) experience // @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx // @author redphx
// @license MIT // @license MIT
@ -16,7 +16,7 @@
/* ADDITIONAL CODE */ /* ADDITIONAL CODE */
const SCRIPT_VERSION = '3.4.0'; const SCRIPT_VERSION = '3.5.2';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud'; const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
// Setup flags // Setup flags
@ -491,7 +491,7 @@ const Translations = {
"左下", "左下",
"좌측 하단", "좌측 하단",
"Lewy dolny róg", "Lewy dolny róg",
"Inferior Esquerdo", "Inferior esquerdo",
"Левый нижний угол", "Левый нижний угол",
"Sol alt", "Sol alt",
"Внизу ліворуч", "Внизу ліворуч",
@ -508,7 +508,7 @@ const Translations = {
"右下", "右下",
"우측 하단", "우측 하단",
"Prawy dolny róg", "Prawy dolny róg",
"Inferior-direito", "Inferior direito",
"Правый нижний угол", "Правый нижний угол",
"Sağ alt", "Sağ alt",
"Внизу праворуч", "Внизу праворуч",
@ -669,7 +669,7 @@ const Translations = {
"关闭", "关闭",
], ],
"combine-audio-video-streams": [ "combine-audio-video-streams": [
, "Audio- und Video-Streams kombinieren",
"Gabung audio & video stream", "Gabung audio & video stream",
"Combine audio & video streams", "Combine audio & video streams",
, ,
@ -686,7 +686,7 @@ const Translations = {
"合并视频音频流", "合并视频音频流",
], ],
"combine-audio-video-streams-summary": [ "combine-audio-video-streams-summary": [
, "Könnte das Problem mit verzögertem Ton beheben",
"Mungkin memperbaiki masalah lag pada audio", "Mungkin memperbaiki masalah lag pada audio",
"May fix the laggy audio problem", "May fix the laggy audio problem",
, ,
@ -695,7 +695,7 @@ const Translations = {
"音声の遅延を改善できる可能性があります", "音声の遅延を改善できる可能性があります",
, ,
"Może rozwiązać problem z zacinającym dźwiękiem", "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", "Sesteki gecikme sorununa çözüm olabilir",
"Може виправити проблему із затримкою звуку", "Може виправити проблему із затримкою звуку",
@ -882,7 +882,7 @@ const Translations = {
"カスタム", "カスタム",
"사용자 지정", "사용자 지정",
"Niestandardowe", "Niestandardowe",
"Customizado", "Personalizado",
"Вручную", "Вручную",
"Özel", "Özel",
"Користувацькі", "Користувацькі",
@ -899,7 +899,7 @@ const Translations = {
"デッドゾーンのカウンターウエイト", "デッドゾーンのカウンターウエイト",
, ,
"Przeciwwaga martwej strefy", "Przeciwwaga martwej strefy",
"Contador da Zona Morta", "Contrapeso de zona morta",
"Противодействие мертвой зоне игры", "Противодействие мертвой зоне игры",
"Ölü alan denge ağırlığı", "Ölü alan denge ağırlığı",
"Противага Deadzone", "Противага Deadzone",
@ -1264,7 +1264,7 @@ const Translations = {
"启用", "启用",
], ],
"experimental": [ "experimental": [
, "Experimentell",
"Eksperimental", "Eksperimental",
"Experimental", "Experimental",
, ,
@ -1409,7 +1409,7 @@ const Translations = {
"Webページのスクロールバーを隠す", "Webページのスクロールバーを隠す",
, ,
"Ukryj pasek przewijania strony", "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", "Yandaki kaydırma çubuğunu gizle",
"Приховати смугу прокрутки вебсторінок", "Приховати смугу прокрутки вебсторінок",
@ -1579,7 +1579,7 @@ const Translations = {
"ロード画面", "ロード画面",
"로딩 화면", "로딩 화면",
"Ekran wczytywania", "Ekran wczytywania",
"Tela de Carregamento", "Tela de carregamento",
"Экран загрузки", "Экран загрузки",
"Yükleme ekranı", "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", "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", "Bu seçenek etkinken bile oyun içi seçeneklerden hassasiyet ve ölü bölge ayarlarını düzeltmeniz gerekebilir",
"Можливо, вам також доведеться регулювати чутливість і deadzone у параметрах гри", "Можливо, вам також доведеться регулювати чутливість і deadzone у параметрах гри",
@ -2004,7 +2004,7 @@ const Translations = {
"IPv6 サーバーを優先", "IPv6 サーバーを優先",
"IPv6 서버 우선", "IPv6 서버 우선",
"Preferuj serwer IPv6", "Preferuj serwer IPv6",
"Preferir servidor IPV6", "Preferir servidor IPv6",
"Предпочитать IPv6 сервер", "Предпочитать IPv6 сервер",
"IPv6 sunucusunu tercih et", "IPv6 sunucusunu tercih et",
"Віддавати перевагу IPv6", "Віддавати перевагу IPv6",
@ -2174,7 +2174,7 @@ const Translations = {
"リモートプレイ", "リモートプレイ",
"리모트 플레이", "리모트 플레이",
"Gra zdalna", "Gra zdalna",
"Jogo Remoto", "Reprodução remota",
"Удаленная игра", "Удаленная игра",
"Uzaktan Bağlanma", "Uzaktan Bağlanma",
"Віддалена гра", "Віддалена гра",
@ -2361,7 +2361,7 @@ const Translations = {
"スクリーンショットにビデオフィルターを適用", "スクリーンショットにビデオフィルターを適用",
, ,
"Stosuje filtry wideo do zrzutów ekranu", "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", "Görsel filtreleri ekran görüntülerine de uygular",
"Застосовує відеофільтри до знімків екрана", "Застосовує відеофільтри до знімків екрана",
@ -2786,7 +2786,7 @@ const Translations = {
"スティックの減衰の最小値", "スティックの減衰の最小値",
, ,
"Minimalne opóźnienie drążka", "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", "Çubuğun ortalanma süresi minimumu",
"Мінімальне згасання стіка", "Мінімальне згасання стіка",
@ -2803,7 +2803,7 @@ const Translations = {
"スティックの減衰の強さ", "スティックの減衰の強さ",
, ,
"Siła opóźnienia drążka", "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ü", "Çubuğun ortalanma gücü",
"Сила згасання стіка", "Сила згасання стіка",
@ -2854,7 +2854,7 @@ const Translations = {
"Better xCloudをサポート", "Better xCloudをサポート",
, ,
"Wesprzyj Better xCloud", "Wesprzyj Better xCloud",
"Suporte ao Better xCloud", "Apoie o Better xCloud",
"Поддержать Better xCloud", "Поддержать Better xCloud",
"Better xCloud'a destek ver", "Better xCloud'a destek ver",
"Підтримати Better xCloud", "Підтримати Better xCloud",
@ -2990,7 +2990,7 @@ const Translations = {
"ミュートカラー", "ミュートカラー",
"저채도 색상", "저채도 색상",
"Stonowane kolory", "Stonowane kolory",
"Cores silenciadas", "Cores opacas",
"Приглушенные цвета", "Приглушенные цвета",
"Yumuşak renkler", "Yumuşak renkler",
"Приглушені кольори", "Приглушені кольори",
@ -3041,7 +3041,7 @@ const Translations = {
"上", "上",
"중앙 상단", "중앙 상단",
"Wyśrodkowany na górze", "Wyśrodkowany na górze",
"Superior-centralizado", "Superior centralizado",
"Сверху", "Сверху",
"Orta üst", "Orta üst",
"Зверху по центру", "Зверху по центру",
@ -3058,7 +3058,7 @@ const Translations = {
"左上", "左上",
"좌측 상단", "좌측 상단",
"Lewy górny róg", "Lewy górny róg",
"Superior-esquerdo", "Superior esquerdo",
"Левый верхний угол", "Левый верхний угол",
"Sol üst", "Sol üst",
"Зверху ліворуч", "Зверху ліворуч",
@ -3075,7 +3075,7 @@ const Translations = {
"右上", "右上",
"우측 상단", "우측 상단",
"Prawy górny róg", "Prawy górny róg",
"Superior-direito", "Superior direito",
"Справа", "Справа",
"Sağ üst", "Sağ üst",
"Зверху праворуч", "Зверху праворуч",
@ -3601,6 +3601,7 @@ class RemotePlay {
static XCLOUD_TOKEN; static XCLOUD_TOKEN;
static XHOME_TOKEN; static XHOME_TOKEN;
static #CONSOLES = null; static #CONSOLES = null;
static #REGIONS;
static #STATE_LABELS = { static #STATE_LABELS = {
'On': t('powered-on'), 'On': t('powered-on'),
@ -3672,40 +3673,56 @@ class RemotePlay {
if (!RemotePlay.#CONSOLES || RemotePlay.#CONSOLES.length === 0) { if (!RemotePlay.#CONSOLES || RemotePlay.#CONSOLES.length === 0) {
$fragment.appendChild(CE('span', {}, t('no-consoles-found'))); $fragment.appendChild(CE('span', {}, t('no-consoles-found')));
} else { RemotePlay.#$content = CE('div', {}, $fragment);
const $settingNote = CE('p', {}); return;
}
const resolutions = [1080, 720]; const $settingNote = CE('p', {});
const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION);
const $resolutionSelect = CE('select', {});
for (const resolution of resolutions) {
const value = `${resolution}p`;
const $option = CE('option', {'value': value}, value); const resolutions = [1080, 720];
if (currentResolution === value) { const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION);
$option.selected = true; const $resolutionGroup = CE('div', {});
} for (const resolution of resolutions) {
const value = `${resolution}p`;
const id = `bx_radio_xhome_resolution_${resolution}`;
$resolutionSelect.appendChild($option); const $radio = CE('input', {
} 'type': 'radio',
$resolutionSelect.addEventListener('change', e => { 'value': value,
const value = $resolutionSelect.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'); $settingNote.textContent = value === '1080p' ? '✅ ' + t('can-stream-xbox-360-games') : '❌ ' + t('cant-stream-xbox-360-games');
setPref(Preferences.REMOTE_PLAY_RESOLUTION, value); setPref(Preferences.REMOTE_PLAY_RESOLUTION, value);
}); });
$resolutionSelect.dispatchEvent(new Event('change'));
const $qualitySettings = CE('div', {'class': 'bx-remote-play-settings'}, const $label = CE('label', {
CE('div', {}, 'for': id,
CE('label', {}, t('target-resolution'), $settingNote), 'class': 'bx-remote-play-resolution',
$resolutionSelect, }, $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) { for (let con of RemotePlay.#CONSOLES) {
let $connectButton; let $connectButton;
const $child = CE('div', {'class': 'bx-remote-play-device-wrapper'}, const $child = CE('div', {'class': 'bx-remote-play-device-wrapper'},
@ -3731,6 +3748,14 @@ class RemotePlay {
$fragment.appendChild($child); $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); RemotePlay.#$content = CE('div', {}, $fragment);
} }
@ -3775,6 +3800,7 @@ class RemotePlay {
}, },
}).then(resp => resp.json()) }).then(resp => resp.json())
.then(json => { .then(json => {
RemotePlay.#REGIONS = json.offeringSettings.regions;
RemotePlay.XHOME_TOKEN = json.gsToken; RemotePlay.XHOME_TOKEN = json.gsToken;
callback(); callback();
}); });
@ -3786,13 +3812,6 @@ class RemotePlay {
return; return;
} }
let servers;
if (!REMOTE_PLAY_SERVER) {
servers = ['wus2', 'eus', 'uks']; // Possible values: wus2 (WestUS2), eus (EastUS), uks (UkSouth)
} else {
servers = REMOTE_PLAY_SERVER;
}
const options = { const options = {
method: 'GET', method: 'GET',
headers: { headers: {
@ -3801,16 +3820,16 @@ class RemotePlay {
}; };
// Test servers one by one // Test servers one by one
for (const server of servers) { for (const region of RemotePlay.#REGIONS) {
try { try {
const url = `https://${server}.gssv-play-prodxhome.xboxlive.com/v6/servers/home?mr=50`; const url = `${region.baseUri}/v6/servers/home?mr=50`;
const resp = await fetch(url, options); const resp = await fetch(url, options);
const json = await resp.json(); const json = await resp.json();
RemotePlay.#CONSOLES = json.results; RemotePlay.#CONSOLES = json.results;
// Store working server // Store working server
REMOTE_PLAY_SERVER = server; REMOTE_PLAY_SERVER = region.baseUri;
callback(); callback();
} catch (e) {} } catch (e) {}
@ -4991,7 +5010,7 @@ class MkbPreset {
[MkbPreset.KEY_MOUSE_STICK_DECAY_STRENGTH]: { [MkbPreset.KEY_MOUSE_STICK_DECAY_STRENGTH]: {
label: t('stick-decay-strength'), label: t('stick-decay-strength'),
type: SettingElement.TYPE_NUMBER_STEPPER, type: SettingElement.TYPE_NUMBER_STEPPER,
default: 18, default: 100,
min: 10, min: 10,
max: 100, max: 100,
@ -5004,7 +5023,7 @@ class MkbPreset {
[MkbPreset.KEY_MOUSE_STICK_DECAY_MIN]: { [MkbPreset.KEY_MOUSE_STICK_DECAY_MIN]: {
label: t('stick-decay-minimum'), label: t('stick-decay-minimum'),
type: SettingElement.TYPE_NUMBER_STEPPER, type: SettingElement.TYPE_NUMBER_STEPPER,
default: 6, default: 10,
min: 1, min: 1,
max: 10, max: 10,
@ -5271,8 +5290,10 @@ class MkbHandler {
static get DEFAULT_DEADZONE_COUNTERWEIGHT() { return 0.01; } static get DEFAULT_DEADZONE_COUNTERWEIGHT() { return 0.01; }
static get MAXIMUM_STICK_RANGE() { return 1.1; } static get MAXIMUM_STICK_RANGE() { return 1.1; }
VIRTUAL_GAMEPAD_ID = 'Xbox 360 Controller';
#VIRTUAL_GAMEPAD = { #VIRTUAL_GAMEPAD = {
id: 'Xbox 360 Controller', id: MkbHandler.VIRTUAL_GAMEPAD_ID,
index: 3, index: 3,
connected: false, connected: false,
hapticActuators: null, hapticActuators: null,
@ -5394,7 +5415,7 @@ class MkbHandler {
const isKeyDown = e.type === 'keydown'; const isKeyDown = e.type === 'keydown';
// Toggle MKB feature // Toggle MKB feature
if (isKeyDown && e.code === 'F9') { if (isKeyDown && e.code === 'F8') {
e.preventDefault(); e.preventDefault();
this.toggle(); this.toggle();
return; return;
@ -5534,7 +5555,7 @@ class MkbHandler {
this.#enabled = !this.#enabled; this.#enabled = !this.#enabled;
this.#enabled ? document.pointerLockElement && this.start() : this.stop(); 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) { if (this.#enabled) {
!document.pointerLockElement && this.#waitForPointerLock(true); !document.pointerLockElement && this.#waitForPointerLock(true);
@ -5605,6 +5626,7 @@ class MkbHandler {
this.#$message = CE('div', {'class': 'bx-mkb-pointer-lock-msg bx-gone'}, this.#$message = CE('div', {'class': 'bx-mkb-pointer-lock-msg bx-gone'},
createButton({ createButton({
icon: Icon.MOUSE_SETTINGS, icon: Icon.MOUSE_SETTINGS,
style: ButtonStyle.PRIMARY,
onClick: e => { onClick: e => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -5614,7 +5636,7 @@ class MkbHandler {
}), }),
CE('div', {}, CE('div', {},
CE('p', {}, t('mkb-click-to-activate')), 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'})),
), ),
); );
@ -7909,6 +7931,26 @@ class Patcher {
return funcStr.replace(text, '.disableTelemetry=function(){return!0}'); 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 // Disable IndexDB logging
disableIndexDbLogging(funcStr) { disableIndexDbLogging(funcStr) {
const text = 'async addLog(e,t=1e4){'; const text = 'async addLog(e,t=1e4){';
@ -7967,6 +8009,16 @@ class Patcher {
return funcStr.replace(text, `connectMode:window.BX_REMOTE_PLAY_CONFIG?"xhome-connect":"cloud-connect",remotePlayServerId:(window.BX_REMOTE_PLAY_CONFIG&&window.BX_REMOTE_PLAY_CONFIG.serverId)||''`); return funcStr.replace(text, `connectMode:window.BX_REMOTE_PLAY_CONFIG?"xhome-connect":"cloud-connect",remotePlayServerId:(window.BX_REMOTE_PLAY_CONFIG&&window.BX_REMOTE_PLAY_CONFIG.serverId)||''`);
}, },
// Fix the Guide/Nexus button not working in Remote Play
remotePlayGuideWorkaround: function(funcStr) {
const text = 'nexusButtonHandler:this.featureGates.EnableClientGuideInStream';
if (!funcStr.includes(text)) {
return false;
}
return funcStr.replace(text, `nexusButtonHandler: !window.BX_REMOTE_PLAY_CONFIG && this.featureGates.EnableClientGuideInStream`);
},
// Disable trackEvent() function // Disable trackEvent() function
disableTrackEvent: function(funcStr) { disableTrackEvent: function(funcStr) {
const text = 'this.trackEvent='; const text = 'this.trackEvent=';
@ -7999,7 +8051,7 @@ class Patcher {
}, },
enableXcloudLogger: function(funcStr) { 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)) { if (!funcStr.includes(text)) {
return false; return false;
} }
@ -8239,6 +8291,22 @@ if (gamepadFound) {
funcStr = funcStr.replace(text, 'this.useCombinedAudioVideoStream=true'); funcStr = funcStr.replace(text, 'this.useCombinedAudioVideoStream=true');
return funcStr; return funcStr;
}, },
patchStreamHud: function(funcStr) {
const text = 'let{onCollapse';
if (!funcStr.includes(text)) {
return false;
}
// Restore the "..." button
funcStr = funcStr.replace(text, 'e.guideUI = null;' + text);
// Remove the TAK Edit button when the touch controller is disabled
if (getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'off') {
funcStr = funcStr.replace(text, 'e.canShowTakHUD = false;' + text);
}
return funcStr;
},
}; };
static #PATCH_ORDERS = [ static #PATCH_ORDERS = [
@ -8247,6 +8315,8 @@ if (gamepadFound) {
'disableTelemetry', 'disableTelemetry',
], ],
getPref(Preferences.BLOCK_TRACKING) && ['disableTelemetryProvider'],
['disableStreamGate'], ['disableStreamGate'],
getPref(Preferences.UI_LAYOUT) === 'tv' && ['tvLayout'], getPref(Preferences.UI_LAYOUT) === 'tv' && ['tvLayout'],
@ -8279,6 +8349,9 @@ if (gamepadFound) {
// Only when playing // Only when playing
static #PLAYING_PATCH_ORDERS = [ static #PLAYING_PATCH_ORDERS = [
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'], getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayGuideWorkaround'],
['patchStreamHud'],
['playVibration'], ['playVibration'],
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'], HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
@ -8558,6 +8631,13 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
left: -9999px; left: -9999px;
} }
/* 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 { a.bx-button {
display: inline-block; display: inline-block;
} }
@ -8648,31 +8728,27 @@ a.bx-button.bx-full-width {
text-align: center; text-align: center;
} }
.bx-remote-play-button { .bx-header-remote-play-button {
height: auto; height: auto;
margin-right: 8px !important; margin-right: 8px !important;
} }
.bx-remote-play-button svg { .bx-header-remote-play-button svg {
width: 28px; width: 24px;
height: 46px; height: 46px;
} }
.bx-remote-play-button[disabled] { .bx-header-settings-button {
opacity: 0.5;
}
.bx-settings-button {
line-height: 30px; line-height: 30px;
font-size: 14px; font-size: 14px;
text-transform: none; text-transform: uppercase;
} }
.bx-settings-button[data-update-available]::after { .bx-header-settings-button[data-update-available]::after {
content: ' 🌟'; content: ' 🌟';
} }
.bx-button.bx-focusable, .bx-settings-button { .bx-button.bx-focusable, .bx-header-settings-button {
position: relative; position: relative;
} }
@ -9681,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 { .bx-remote-play-settings {
margin-bottom: 12px; margin-bottom: 12px;
padding-bottom: 12px; padding-bottom: 12px;
@ -9710,6 +9791,19 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
text-align: center; 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 { .bx-remote-play-device-wrapper {
display: flex; display: flex;
margin-bottom: 12px; margin-bottom: 12px;
@ -9903,15 +9997,25 @@ body::-webkit-scrollbar {
} }
function getPreferredServerRegion() { function getPreferredServerRegion(shortName = false) {
let preferredRegion = getPref(Preferences.SERVER_REGION); let preferredRegion = getPref(Preferences.SERVER_REGION);
if (preferredRegion in SERVER_REGIONS) { if (preferredRegion in SERVER_REGIONS) {
return preferredRegion; if (shortName && SERVER_REGIONS[preferredRegion].shortName) {
return SERVER_REGIONS[preferredRegion].shortName;
} else {
return preferredRegion;
}
} }
for (let regionName in SERVER_REGIONS) { for (let regionName in SERVER_REGIONS) {
const region = SERVER_REGIONS[regionName]; const region = SERVER_REGIONS[regionName];
if (region.isDefault) { if (!region.isDefault) {
continue;
}
if (shortName && region.shortName) {
return region.shortName;
} else {
return regionName; return regionName;
} }
} }
@ -10141,7 +10245,7 @@ function interceptHttpRequests() {
} }
const index = request.url.indexOf('.xboxlive.com'); const index = request.url.indexOf('.xboxlive.com');
let newUrl = `https://${REMOTE_PLAY_SERVER}.gssv-play-prodxhome` + request.url.substring(index); let newUrl = REMOTE_PLAY_SERVER + request.url.substring(index + 13);
request = new Request(newUrl, opts); request = new Request(newUrl, opts);
@ -10266,7 +10370,38 @@ function interceptHttpRequests() {
// Get server list // Get server list
if (!Object.keys(SERVER_REGIONS).length) { if (!Object.keys(SERVER_REGIONS).length) {
const serverEmojis = {
AustraliaEast: '🇦🇺',
AustraliaSouthEast: '🇦🇺',
BrazilSouth: '🇧🇷',
EastUS: '🇺🇸',
EastUS2: '🇺🇸',
JapanEast: '🇯🇵',
KoreaCentral: '🇰🇷',
MexicoCentral: '🇲🇽',
NorthCentralUs: '🇺🇸',
SouthCentralUS: '🇺🇸',
UKSouth: '🇬🇧',
WestEurope: '🇪🇺',
WestUS: '🇺🇸',
WestUS2: '🇺🇸',
};
const regex = /\/\/(\w+)\./;
for (let region of obj.offeringSettings.regions) { for (let region of obj.offeringSettings.regions) {
let shortName = region.name;
let match = regex.exec(region.baseUri);
if (match) {
shortName = match[1];
if (serverEmojis[region.name]) {
shortName = serverEmojis[region.name] + ' ' + shortName;
}
}
region.shortName = shortName.toUpperCase();
SERVER_REGIONS[region.name] = Object.assign({}, region); SERVER_REGIONS[region.name] = Object.assign({}, region);
} }
@ -10334,7 +10469,7 @@ function interceptHttpRequests() {
if (PREF_UI_LOADING_SCREEN_WAIT_TIME && url.includes('xboxlive.com') && url.includes('/waittime/')) { if (PREF_UI_LOADING_SCREEN_WAIT_TIME && url.includes('xboxlive.com') && url.includes('/waittime/')) {
const response = await NATIVE_FETCH(...arg); const response = await NATIVE_FETCH(...arg);
const json = await response.clone.json(); const json = await response.clone().json();
if (json.estimatedAllocationTimeInSeconds > 0) { if (json.estimatedAllocationTimeInSeconds > 0) {
// Setup wait time overlay // Setup wait time overlay
LoadingScreen.setupWaitTime(json.estimatedTotalWaitTimeInSeconds); LoadingScreen.setupWaitTime(json.estimatedTotalWaitTimeInSeconds);
@ -10405,7 +10540,7 @@ function interceptHttpRequests() {
return response; return response;
} }
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.includes('/titles') || url.includes('/mru'))) { if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.includes('/v2/titles') || url.includes('/mru'))) {
const response = await NATIVE_FETCH(...arg); const response = await NATIVE_FETCH(...arg);
const json = await response.clone().json() const json = await response.clone().json()
for (let game of json.results) { for (let game of json.results) {
@ -10645,15 +10780,20 @@ function setupSettingsUi() {
}); });
selectedValue = PREF_PREFERRED_REGION; selectedValue = PREF_PREFERRED_REGION;
setting.options = {}; setting.options = {};
for (let regionName in SERVER_REGIONS) { for (let regionName in SERVER_REGIONS) {
const region = SERVER_REGIONS[regionName]; const region = SERVER_REGIONS[regionName];
let value = regionName; let value = regionName;
let label = regionName; let label = `${region.shortName} - ${regionName}`;
if (region.isDefault) { if (region.isDefault) {
label += ` (${t('default')})`; label += ` (${t('default')})`;
value = 'default'; value = 'default';
if (selectedValue === regionName) {
selectedValue = 'default';
}
} }
setting.options[value] = label; setting.options[value] = label;
@ -10663,9 +10803,11 @@ function setupSettingsUi() {
const label = setting.options[value]; const label = setting.options[value];
const $option = CE('option', {value: value}, label); const $option = CE('option', {value: value}, label);
$option.selected = value === selectedValue || label.includes(selectedValue);
$control.appendChild($option); $control.appendChild($option);
} }
// Select preferred region
$control.value = selectedValue;
} else { } else {
if (settingId === Preferences.BETTER_XCLOUD_LOCALE) { if (settingId === Preferences.BETTER_XCLOUD_LOCALE) {
$control = PREFS.toElement(settingId, e => { $control = PREFS.toElement(settingId, e => {
@ -10742,7 +10884,7 @@ function injectSettingsButton($parent) {
return; return;
} }
const PREF_PREFERRED_REGION = getPreferredServerRegion(); const PREF_PREFERRED_REGION = getPreferredServerRegion(true);
const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION); const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION);
const $headerFragment = document.createDocumentFragment(); const $headerFragment = document.createDocumentFragment();
@ -10750,7 +10892,7 @@ function injectSettingsButton($parent) {
// Remote Play button // Remote Play button
if (getPref(Preferences.REMOTE_PLAY_ENABLED)) { if (getPref(Preferences.REMOTE_PLAY_ENABLED)) {
const $remotePlayBtn = createButton({ const $remotePlayBtn = createButton({
classes: ['bx-remote-play-button'], classes: ['bx-header-remote-play-button'],
icon: Icon.REMOTE_PLAY, icon: Icon.REMOTE_PLAY,
title: t('remote-play'), title: t('remote-play'),
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE, style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
@ -10764,7 +10906,7 @@ function injectSettingsButton($parent) {
// Setup Settings button // Setup Settings button
const $settingsBtn = createButton({ const $settingsBtn = createButton({
classes: ['bx-settings-button'], classes: ['bx-header-settings-button'],
label: PREF_PREFERRED_REGION, label: PREF_PREFERRED_REGION,
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT, style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
onClick: e => { onClick: e => {
@ -10889,7 +11031,7 @@ div[data-testid="media-container"] {
function checkHeader() { function checkHeader() {
const $button = document.querySelector('.bx-settings-button'); const $button = document.querySelector('.bx-header-settings-button');
if (!$button) { if (!$button) {
const $rightHeader = document.querySelector('#PageContent div[class*=EdgewaterHeader-module__rightSectionSpacing]'); const $rightHeader = document.querySelector('#PageContent div[class*=EdgewaterHeader-module__rightSectionSpacing]');
@ -11957,6 +12099,11 @@ MkbHandler.setupEvents();
// Show a toast when connecting/disconecting controller // Show a toast when connecting/disconecting controller
function showGamepadToast(gamepad) { function showGamepadToast(gamepad) {
// Don't show Toast for virtual controller
if (gamepad.id === MkbHandler.VIRTUAL_GAMEPAD_ID) {
return;
}
console.log(gamepad); console.log(gamepad);
let text = '🎮'; let text = '🎮';