Compare commits

..

34 Commits
v3.5.1 ... main

Author SHA1 Message Date
f883d590f1 Bump version to 4.0.0 2024-04-28 18:16:18 +07:00
a25a733ef7 Fix Kiwi 124 2024-04-20 07:18:52 +07:00
97f2ae0df9 Bump version to 3.5.3 2024-04-17 17:05:38 +07:00
c38ccaf050 Update translations 2024-04-17 16:37:16 +07:00
a137c5eb57 Remove Clarity Boost restriction 2024-04-17 16:35:07 +07:00
7bd071d527 Fix crashing on Chromium 124 2024-04-17 15:45:45 +07:00
6f9ae9a555 Move DEFAULT_FLAGS below ADDITIONAL CODE section 2024-04-17 08:07:01 +07:00
2008806507 Fix the star icon in Settings button 2024-04-16 17:09:12 +07:00
c758827232 Move handleDeepLink() to the app 2024-04-15 18:05:29 +07:00
9322480059 Minor fixes 2024-04-15 08:06:14 +07:00
992605690f Minor fixes 2024-04-15 07:56:42 +07:00
08f2db0846 Remove hash in handleDeepLink() 2024-04-15 07:53:01 +07:00
645155bee0 Add handleDeepLink() 2024-04-15 07:45:43 +07:00
e6dad23547 Update 02-feature-request.yml 2024-04-13 18:01:44 +07:00
04a26680d5 Update 01-bug-report.yml 2024-04-13 18:01:29 +07:00
8ff50ecab8 Delete .github/ISSUE_TEMPLATE/feature_request.md 2024-04-13 17:42:37 +07:00
e20f661c36 Add Feature Request form 2024-04-13 17:42:23 +07:00
f958244e43 Delete .github/ISSUE_TEMPLATE/bug_report.md 2024-04-13 17:34:35 +07:00
ec76731e37 Add Bug Report form 2024-04-13 17:33:51 +07:00
aedc617298 Fix screenshot button blocking Guide UI 2024-04-13 16:41:08 +07:00
d451cfeac9 Add "SafariWorkaround" flag 2024-04-13 10:33:07 +07:00
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
6 changed files with 433 additions and 206 deletions

View File

@ -0,0 +1,83 @@
name: Bug Report 🐞
description: File a bug report
title: "[Bug] "
labels:
- bug
body:
- type: markdown
attributes:
value: |
Please fill out the following information to help us resolve the issue.
> [!warning]
> Only use English. Any other languages will be deleted.
- type: dropdown
id: device_type
attributes:
label: Device
description: "Which device are you using?"
options:
- Phone/Tablet
- Laptop
- Desktop
- TV
- Other
multiple: false
validations:
required: true
- type: dropdown
id: os
attributes:
label: "Operating System"
description: "Which operating system is it running?"
options:
- Windows
- macOS
- Linux
- Android
- iOS/iPadOS
- Other
multiple: false
validations:
required: true
- type: dropdown
id: browser
attributes:
label: "Browser"
description: "Which browser are you using?"
options:
- Chrome/Edge/Chromium
- Kiwi Browser
- Safari
- Other
multiple: false
validations:
required: true
- type: input
id: browser_version
attributes:
label: "Browser Version"
description: "What is the version of the browser?"
placeholder: "e.g., 122.0"
validations:
required: true
- type: input
id: extension_version
attributes:
label: "Better xCloud Version"
description: "What is the version of Better xCloud?"
placeholder: "e.g., 3.5.0"
validations:
required: true
- type: textarea
id: repro
attributes:
label: "Reproduction Steps"
description: |
How did you trigger this bug? Please provide screenshot/video if possible.
placeholder: |
Example:
1. Open game X
2. Click on Y
3. Error
validations:
required: true

View File

@ -0,0 +1,61 @@
name: Feature Request 🚀
description: Request a new feature, improvement, or general suggestion
title: "[Feature] "
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
Please fill out the following information to help us understand your request.
> [!warning]
> Only use English. Any other languages will be deleted.
- type: dropdown
id: device_type
attributes:
label: Device
description: "Which device are you using?"
options:
- Phone/Tablet
- Laptop
- Desktop
- TV
- Other
multiple: false
validations:
required: true
- type: dropdown
id: os
attributes:
label: "Operating System"
description: "Which operating system is it running?"
options:
- Windows
- macOS
- Linux
- Android
- iOS/iPadOS
- Other
multiple: false
validations:
required: true
- type: dropdown
id: browser
attributes:
label: "Browser"
description: "Which browser are you using?"
options:
- Chrome/Edge/Chromium
- Kiwi Browser
- Safari
- Other
multiple: false
validations:
required: true
- type: textarea
id: suggestion
attributes:
label: "Suggestion"
description: "What do you want to suggest?"
validations:
required: true

View File

@ -1,23 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug] ..."
labels: bug
assignees: ''
---
<!-- ⚠️ Only use English. Any other languages will be deleted. -->
**Platform**
- Device: Phone, Laptop, Desktop, TV...
- OS: Windows, Android, iOS...
- Browser: Chrome, Safari, Kiwi...
- Browser Version:
- Better xCloud Version:
**Describe the bug**
...
**Screenshots/Videos**
If applicable, add screenshots/videos to help explain your problem.

View File

@ -1,18 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature] ..."
labels: enhancement
assignees: ''
---
<!-- ⚠️ Only use English. Any other languages will be deleted. -->
**I'm using:**
- Device:
- OS:
- Browser:
**I want to suggest this feature:**
...

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.1 // @version 4.0.0
// ==/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.1 // @version 3.5.3
// @description Improve Xbox Cloud Gaming (xCloud) experience // @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx // @author redphx
// @license MIT // @license MIT
@ -14,17 +14,62 @@
// ==/UserScript== // ==/UserScript==
'use strict'; 'use strict';
/* ADDITIONAL CODE */ const SCRIPT_VERSION = '3.5.3';
const SCRIPT_VERSION = '3.5.1';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud'; const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
const AppInterface = window.AppInterface;
const BxEvent = {
JUMP_BACK_IN_READY: 'bx-jump-back-in-ready',
POPSTATE: 'bx-popstate',
STREAM_LOADING: 'bx-stream-loading',
STREAM_STARTING: 'bx-stream-starting',
STREAM_STARTED: 'bx-stream-started',
STREAM_PLAYING: 'bx-stream-playing',
STREAM_STOPPED: 'bx-stream-stopped',
STREAM_ERROR_PAGE: 'bx-stream-error-page',
STREAM_MENU_SHOWN: 'bx-stream-menu-shown',
STREAM_MENU_HIDDEN: 'bx-stream-menu-hidden',
STREAM_WEBRTC_CONNECTED: 'bx-stream-webrtc-connected',
STREAM_WEBRTC_DISCONNECTED: 'bx-stream-webrtc-disconnected',
CUSTOM_TOUCH_LAYOUTS_LOADED: 'bx-custom-touch-layouts-loaded',
REMOTE_PLAY_READY: 'bx-remote-play-ready',
REMOTE_PLAY_FAILED: 'bx-remote-play-failed',
DATA_CHANNEL_CREATED: 'bx-data-channel-created',
dispatch: (target, eventName, data) => {
if (!eventName) {
alert('BxEvent.dispatch(): eventName is null');
return;
}
const event = new Event(eventName);
if (data) {
for (const key in data) {
event[key] = data[key];
}
}
AppInterface && AppInterface.onEvent(eventName);
target.dispatchEvent(event);
},
};
/* ADDITIONAL CODE */
// Setup flags // Setup flags
const DEFAULT_FLAGS = { const DEFAULT_FLAGS = {
CheckForUpdate: true, CheckForUpdate: true,
PreloadRemotePlay: true, PreloadRemotePlay: true,
PreloadUi: false, PreloadUi: false,
EnableXcloudLogging: false, EnableXcloudLogging: false,
SafariWorkaround: true,
UseDevTouchLayout: false, UseDevTouchLayout: false,
} }
@ -32,8 +77,6 @@ const DEFAULT_FLAGS = {
const BX_FLAGS = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {}); const BX_FLAGS = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {});
delete window.BX_FLAGS; delete window.BX_FLAGS;
const AppInterface = window.AppInterface;
let REMOTE_PLAY_SERVER; let REMOTE_PLAY_SERVER;
const ENABLE_NATIVE_MKB_BETA = false; const ENABLE_NATIVE_MKB_BETA = false;
@ -67,45 +110,6 @@ if (window.location.pathname.includes('/auth/msa')) {
console.log(`[Better xCloud] readyState: ${document.readyState}`); console.log(`[Better xCloud] readyState: ${document.readyState}`);
const BxEvent = {
JUMP_BACK_IN_READY: 'bx-jump-back-in-ready',
POPSTATE: 'bx-popstate',
STREAM_LOADING: 'bx-stream-loading',
STREAM_STARTING: 'bx-stream-starting',
STREAM_STARTED: 'bx-stream-started',
STREAM_PLAYING: 'bx-stream-playing',
STREAM_STOPPED: 'bx-stream-stopped',
STREAM_ERROR_PAGE: 'bx-stream-error-page',
STREAM_MENU_SHOWN: 'bx-stream-menu-shown',
STREAM_MENU_HIDDEN: 'bx-stream-menu-hidden',
STREAM_WEBRTC_CONNECTED: 'bx-stream-webrtc-connected',
STREAM_WEBRTC_DISCONNECTED: 'bx-stream-webrtc-disconnected',
CUSTOM_TOUCH_LAYOUTS_LOADED: 'bx-custom-touch-layouts-loaded',
DATA_CHANNEL_CREATED: 'bx-data-channel-created',
dispatch: (target, eventName, data) => {
if (!eventName) {
alert('BxEvent.dispatch(): eventName is null');
return;
}
const event = new Event(eventName);
if (data) {
for (const key in data) {
event[key] = data[key];
}
}
AppInterface && AppInterface.onEvent(eventName);
target.dispatchEvent(event);
},
};
// Quickly create a tree of elements without having to use innerHTML // Quickly create a tree of elements without having to use innerHTML
function createElement(elmName, props = {}) { function createElement(elmName, props = {}) {
@ -491,7 +495,7 @@ const Translations = {
"左下", "左下",
"좌측 하단", "좌측 하단",
"Lewy dolny róg", "Lewy dolny róg",
"Inferior Esquerdo", "Inferior esquerdo",
"Левый нижний угол", "Левый нижний угол",
"Sol alt", "Sol alt",
"Внизу ліворуч", "Внизу ліворуч",
@ -508,7 +512,7 @@ const Translations = {
"右下", "右下",
"우측 하단", "우측 하단",
"Prawy dolny róg", "Prawy dolny róg",
"Inferior-direito", "Inferior direito",
"Правый нижний угол", "Правый нижний угол",
"Sağ alt", "Sağ alt",
"Внизу праворуч", "Внизу праворуч",
@ -669,10 +673,10 @@ 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",
, "Combinar flujos de audio y vídeo",
, ,
, ,
"音声を映像ストリーミングと統合", "音声を映像ストリーミングと統合",
@ -686,16 +690,16 @@ 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",
, "Puede arreglar el problema de audio con retraso",
, ,
, ,
"音声の遅延を改善できる可能性があります", "音声の遅延を改善できる可能性があります",
, ,
"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 +886,7 @@ const Translations = {
"カスタム", "カスタム",
"사용자 지정", "사용자 지정",
"Niestandardowe", "Niestandardowe",
"Customizado", "Personalizado",
"Вручную", "Вручную",
"Özel", "Özel",
"Користувацькі", "Користувацькі",
@ -899,7 +903,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,10 +1268,10 @@ const Translations = {
"启用", "启用",
], ],
"experimental": [ "experimental": [
, "Experimentell",
"Eksperimental", "Eksperimental",
"Experimental", "Experimental",
, "Experimental",
, ,
, ,
"実験的機能", "実験的機能",
@ -1403,13 +1407,13 @@ const Translations = {
"Scrollbalken der Webseite ausblenden", "Scrollbalken der Webseite ausblenden",
"Sembunyikan bilah gulir halaman", "Sembunyikan bilah gulir halaman",
"Hide web page's scrollbar", "Hide web page's scrollbar",
, "Oculta la barra de desplazamiento de la página",
, ,
, ,
"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",
"Приховати смугу прокрутки вебсторінок", "Приховати смугу прокрутки вебсторінок",
@ -1471,7 +1475,7 @@ const Translations = {
"\"Better xCloud\" App für Android installieren", "\"Better xCloud\" App für Android installieren",
"Pasang aplikasi Better xCloud untuk Android", "Pasang aplikasi Better xCloud untuk Android",
"Install Better xCloud app for Android", "Install Better xCloud app for Android",
, "Instale la aplicación Better xCloud para Android",
, ,
, ,
"Android用のBetter xCloudをインストール", "Android用のBetter xCloudをインストール",
@ -1579,7 +1583,7 @@ const Translations = {
"ロード画面", "ロード画面",
"로딩 화면", "로딩 화면",
"Ekran wczytywania", "Ekran wczytywania",
"Tela de Carregamento", "Tela de carregamento",
"Экран загрузки", "Экран загрузки",
"Yükleme ekranı", "Yükleme ekranı",
"Екран завантаження", "Екран завантаження",
@ -1698,7 +1702,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 +2008,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 +2178,7 @@ const Translations = {
"リモートプレイ", "リモートプレイ",
"리모트 플레이", "리모트 플레이",
"Gra zdalna", "Gra zdalna",
"Jogo Remoto", "Reprodução remota",
"Удаленная игра", "Удаленная игра",
"Uzaktan Bağlanma", "Uzaktan Bağlanma",
"Віддалена гра", "Віддалена гра",
@ -2355,13 +2359,13 @@ const Translations = {
"Videofilter auf Screenshots anwenden", "Videofilter auf Screenshots anwenden",
"Terapkan filter video pada screenshot", "Terapkan filter video pada screenshot",
"Applies video filters to screenshots", "Applies video filters to screenshots",
, "Aplica filtros de vídeo a las capturas de pantalla",
, ,
, ,
"スクリーンショットにビデオフィルターを適用", "スクリーンショットにビデオフィルターを適用",
, ,
"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 +2790,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 +2807,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 +2858,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 +2994,7 @@ const Translations = {
"ミュートカラー", "ミュートカラー",
"저채도 색상", "저채도 색상",
"Stonowane kolory", "Stonowane kolory",
"Cores silenciadas", "Cores opacas",
"Приглушенные цвета", "Приглушенные цвета",
"Yumuşak renkler", "Yumuşak renkler",
"Приглушені кольори", "Приглушені кольори",
@ -3041,7 +3045,7 @@ const Translations = {
"上", "上",
"중앙 상단", "중앙 상단",
"Wyśrodkowany na górze", "Wyśrodkowany na górze",
"Superior-centralizado", "Superior centralizado",
"Сверху", "Сверху",
"Orta üst", "Orta üst",
"Зверху по центру", "Зверху по центру",
@ -3058,7 +3062,7 @@ const Translations = {
"左上", "左上",
"좌측 상단", "좌측 상단",
"Lewy górny róg", "Lewy górny róg",
"Superior-esquerdo", "Superior esquerdo",
"Левый верхний угол", "Левый верхний угол",
"Sol üst", "Sol üst",
"Зверху ліворуч", "Зверху ліворуч",
@ -3075,7 +3079,7 @@ const Translations = {
"右上", "右上",
"우측 상단", "우측 상단",
"Prawy górny róg", "Prawy górny róg",
"Superior-direito", "Superior direito",
"Справа", "Справа",
"Sağ üst", "Sağ üst",
"Зверху праворуч", "Зверху праворуч",
@ -3273,7 +3277,7 @@ const Translations = {
"Vibration", "Vibration",
"Getaran", "Getaran",
"Vibration", "Vibration",
, "Vibración",
, ,
, ,
"振動", "振動",
@ -3428,8 +3432,7 @@ let LOCALE = Translations.getLocale();
const t = Translations.get; const t = Translations.get;
const ENABLE_SAFARI_WORKAROUND = true; if (BX_FLAGS.SafariWorkaround && document.readyState !== 'loading') {
if (ENABLE_SAFARI_WORKAROUND && document.readyState !== 'loading') {
// Stop loading // Stop loading
window.stop(); window.stop();
@ -3487,7 +3490,48 @@ var GAME_XBOX_TITLE_ID;
var GAME_PRODUCT_ID; var GAME_PRODUCT_ID;
var APP_CONTEXT; var APP_CONTEXT;
window.BX_EXPOSED = {}; window.BX_EXPOSED = {
onPollingModeChanged: mode => {
if (!IS_PLAYING) {
return false;
}
const $screenshotBtn = document.querySelector('.bx-screenshot-button');
const $touchControllerBar = document.getElementById('bx-touch-controller-bar');
if (mode !== 'None') {
// Hide screenshot button
$screenshotBtn && $screenshotBtn.classList.add('bx-gone');
// Hide touch controller bar
$touchControllerBar && $touchControllerBar.classList.add('bx-gone');
} else {
// Show screenshot button
$screenshotBtn && $screenshotBtn.classList.remove('bx-gone');
// Show touch controller bar
$touchControllerBar && $touchControllerBar.classList.remove('bx-gone');
}
},
};
function localRedirect(path) {
const url = window.location.href.substring(0, 31) + path;
const $pageContent = document.getElementById('PageContent');
const $anchor = CE('a', { href: url, class: 'bx-hidden bx-offscreen' }, '');
$anchor.addEventListener('click', e => {
setTimeout(() => {
$pageContent.removeChild($anchor);
}, 1000);
});
$pageContent.appendChild($anchor);
$anchor.click();
}
let IS_REMOTE_PLAYING; let IS_REMOTE_PLAYING;
let REMOTE_PLAY_CONFIG; let REMOTE_PLAY_CONFIG;
@ -3664,6 +3708,7 @@ class RemotePlay {
RemotePlay.#getConsolesList(() => { RemotePlay.#getConsolesList(() => {
console.log(RemotePlay.#CONSOLES); console.log(RemotePlay.#CONSOLES);
RemotePlay.#renderConsoles(); RemotePlay.#renderConsoles();
BxEvent.dispatch(window, BxEvent.REMOTE_PLAY_READY);
}); });
}); });
} }
@ -3673,40 +3718,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);
return;
}
const $settingNote = CE('p', {}); const $settingNote = CE('p', {});
const resolutions = [1080, 720]; const resolutions = [1080, 720];
const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION); const currentResolution = getPref(Preferences.REMOTE_PLAY_RESOLUTION);
const $resolutionSelect = CE('select', {}); const $resolutionGroup = CE('div', {});
for (const resolution of resolutions) { for (const resolution of resolutions) {
const value = `${resolution}p`; const value = `${resolution}p`;
const id = `bx_radio_xhome_resolution_${resolution}`;
const $option = CE('option', {'value': value}, value); const $radio = CE('input', {
if (currentResolution === value) { 'type': 'radio',
$option.selected = true; 'value': value,
} 'id': id,
'name': 'bx_radio_xhome_resolution',
}, value);
$resolutionSelect.appendChild($option); $radio.addEventListener('change', e => {
} const value = e.target.value;
$resolutionSelect.addEventListener('change', e => {
const value = $resolutionSelect.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 $label = CE('label', {
'for': id,
'class': 'bx-remote-play-resolution',
}, $radio, `${resolution}p`);
$resolutionGroup.appendChild($label);
if (currentResolution === value) {
$radio.checked = true;
$radio.dispatchEvent(new Event('change'));
}
}
const $qualitySettings = CE('div', {'class': 'bx-remote-play-settings'}, const $qualitySettings = CE('div', {'class': 'bx-remote-play-settings'},
CE('div', {}, CE('div', {},
CE('label', {}, t('target-resolution'), $settingNote), CE('label', {}, t('target-resolution'), $settingNote),
$resolutionSelect, $resolutionGroup,
) )
); );
$fragment.appendChild($qualitySettings); $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'},
@ -3732,6 +3793,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);
} }
@ -3831,19 +3900,7 @@ class RemotePlay {
}; };
window.BX_REMOTE_PLAY_CONFIG = REMOTE_PLAY_CONFIG; window.BX_REMOTE_PLAY_CONFIG = REMOTE_PLAY_CONFIG;
const url = window.location.href.substring(0, 31) + '/launch/fortnite/BT5P2X999VH2#remote-play'; localRedirect('/launch/fortnite/BT5P2X999VH2#remote-play');
const $pageContent = document.getElementById('PageContent');
const $anchor = CE('a', { href: url, class: 'bx-hidden bx-offscreen' }, '');
$anchor.addEventListener('click', e => {
setTimeout(() => {
$pageContent.removeChild($anchor);
}, 1000);
});
$pageContent.appendChild($anchor);
$anchor.click();
RemotePlay.detachPopup(); RemotePlay.detachPopup();
} }
@ -4986,7 +5043,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,
@ -4999,7 +5056,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,
@ -5266,8 +5323,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,
@ -5389,7 +5448,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;
@ -5529,7 +5588,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);
@ -5600,6 +5659,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();
@ -5609,7 +5669,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'})),
), ),
); );
@ -7083,15 +7143,9 @@ class UserAgent {
const profile = getPref(Preferences.USER_AGENT_PROFILE); const profile = getPref(Preferences.USER_AGENT_PROFILE);
if (profile === UserAgent.PROFILE_DEFAULT) { if (profile === UserAgent.PROFILE_DEFAULT) {
// Fix Kiwi 124
if (window.navigator.userAgent.includes('Chrome/124.0.0.0')) {
newUserAgent = window.navigator.userAgent.replace('Chrome/124.0.0.0', 'Chrome/122.0.0.0')
} else {
return; return;
} }
}
if (!newUserAgent) { if (!newUserAgent) {
newUserAgent = UserAgent.get(profile); newUserAgent = UserAgent.get(profile);
} }
@ -7904,6 +7958,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){';
@ -8004,7 +8078,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;
} }
@ -8260,6 +8334,19 @@ if (gamepadFound) {
} }
return funcStr; return funcStr;
}, },
broadcastPollingMode: function(funcStr) {
const text = '.setPollingMode=e=>{';
if (!funcStr.includes(text)) {
return false;
}
const newCode = `
window.BX_EXPOSED.onPollingModeChanged && window.BX_EXPOSED.onPollingModeChanged(e);
`;
funcStr = funcStr.replace(text, text + newCode);
return funcStr;
},
}; };
static #PATCH_ORDERS = [ static #PATCH_ORDERS = [
@ -8270,6 +8357,8 @@ if (gamepadFound) {
['disableStreamGate'], ['disableStreamGate'],
['broadcastPollingMode'],
getPref(Preferences.UI_LAYOUT) === 'tv' && ['tvLayout'], getPref(Preferences.UI_LAYOUT) === 'tv' && ['tvLayout'],
BX_FLAGS.EnableXcloudLogging && [ BX_FLAGS.EnableXcloudLogging && [
@ -8282,6 +8371,10 @@ if (gamepadFound) {
getPref(Preferences.BLOCK_TRACKING) && [ getPref(Preferences.BLOCK_TRACKING) && [
'blockWebRtcStatsCollector', 'blockWebRtcStatsCollector',
'disableIndexDbLogging', 'disableIndexDbLogging',
],
getPref(Preferences.BLOCK_TRACKING) && [
'disableTelemetryProvider',
'disableTrackEvent', 'disableTrackEvent',
], ],
@ -8291,9 +8384,10 @@ if (gamepadFound) {
[ [
'overrideSettings', 'overrideSettings',
ENABLE_NATIVE_MKB_BETA && 'mkbIsMouseAndKeyboardTitle', ENABLE_NATIVE_MKB_BETA && 'mkbIsMouseAndKeyboardTitle',
HAS_TOUCH_SUPPORT && 'patchUpdateInputConfigurationAsync',
], ],
getPref(Preferences.REMOTE_PLAY_ENABLED) && HAS_TOUCH_SUPPORT && ['patchUpdateInputConfigurationAsync'],
getPref(Preferences.GAME_FORTNITE_FORCE_CONSOLE) && ['forceFortniteConsole'], getPref(Preferences.GAME_FORTNITE_FORCE_CONSOLE) && ['forceFortniteConsole'],
]; ];
@ -8582,9 +8676,11 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
left: -9999px; left: -9999px;
} }
/* Remove the "Cloud Gaming" text in header */ /* Remove the "Cloud Gaming" text in header when the screen is too small */
header a[href="/play"] { @media screen and (max-width: 600px) {
header a[href="/play"] {
display: none; display: none;
}
} }
a.bx-button { a.bx-button {
@ -8693,8 +8789,11 @@ a.bx-button.bx-full-width {
text-transform: uppercase; text-transform: uppercase;
} }
.bx-header-settings-button[data-update-available]::after { .bx-header-settings-button[data-update-available]::before {
content: ' 🌟'; content: '🌟' !important;
line-height: var(--bx-button-height);
display: inline-block;
margin-left: 4px;
} }
.bx-button.bx-focusable, .bx-header-settings-button { .bx-button.bx-focusable, .bx-header-settings-button {
@ -9269,24 +9368,6 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
overflow: hidden; overflow: hidden;
} }
.bx-quick-settings-bar:not([data-clarity-boost="true"]) .bx-clarity-boost-warning {
display: none;
}
.bx-quick-settings-bar[data-clarity-boost="true"] .bx-clarity-boost-warning {
display: block;
margin: 0px 8px;
padding: 12px;
font-size: 16px;
font-weight: normal;
background: #282828;
border-radius: 4px;
}
.bx-quick-settings-bar[data-clarity-boost="true"] div[data-type="video"] {
display: none;
}
.bx-quick-settings-tab-contents *:focus { .bx-quick-settings-tab-contents *:focus {
outline: none !important; outline: none !important;
} }
@ -9621,7 +9702,7 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
} }
#bx-touch-controller-bar[data-showing=true] { #bx-touch-controller-bar[data-showing=true] {
display: block !important; display: block;
} }
.bx-wait-time-box { .bx-wait-time-box {
@ -9706,6 +9787,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;
@ -9735,6 +9821,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;
@ -9931,13 +10030,23 @@ body::-webkit-scrollbar {
function getPreferredServerRegion(shortName = false) { 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 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) { for (let regionName in SERVER_REGIONS) {
const region = SERVER_REGIONS[regionName]; const region = SERVER_REGIONS[regionName];
if (region.isDefault) { if (!region.isDefault) {
return shortName ? region.shortName : regionName; continue;
}
if (shortName && region.shortName) {
return region.shortName;
} else {
return regionName;
} }
} }
@ -10252,7 +10361,7 @@ function interceptHttpRequests() {
return NATIVE_FETCH(...arg); return NATIVE_FETCH(...arg);
} }
if (IS_REMOTE_PLAYING && url.includes('/titles')) { if (IS_REMOTE_PLAYING && url.endsWith('/titles')) {
const clone = request.clone(); const clone = request.clone();
const headers = {}; const headers = {};
@ -10390,7 +10499,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);
@ -10701,6 +10810,7 @@ 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];
@ -10710,6 +10820,10 @@ function setupSettingsUi() {
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;
@ -10719,9 +10833,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 => {
@ -11160,9 +11276,6 @@ function injectStreamMenuButtons() {
hideGripHandle(); hideGripHandle();
e.preventDefault(); e.preventDefault();
const msVideoProcessing = $STREAM_VIDEO.msVideoProcessing;
$quickBar.setAttribute('data-clarity-boost', (msVideoProcessing && msVideoProcessing !== 'default'));
// Show Quick settings bar // Show Quick settings bar
$quickBar.classList.remove('bx-gone'); $quickBar.classList.remove('bx-gone');
@ -11312,11 +11425,18 @@ function patchRtcCodecs() {
try { try {
nativeSetCodecPreferences.apply(this, [newCodecs]); nativeSetCodecPreferences.apply(this, [newCodecs]);
} catch (e) { } catch (e) {
// Didn't work -> use default codecs try {
// Fix Kiwi 124
console.log(e); console.log(e);
newCodec.push(...RTCRtpSender.getCapabilities('video').codecs);
nativeSetCodecPreferences.apply(this, [newCodecs]);
} catch (x) {
// Didn't work -> use default codecs
console.log(x);
nativeSetCodecPreferences.apply(this, [codecs]); nativeSetCodecPreferences.apply(this, [codecs]);
} }
} }
}
} }
@ -11363,7 +11483,6 @@ function setupQuickSettingsBar() {
group: 'video', group: 'video',
label: t('video'), label: t('video'),
help_url: 'https://better-xcloud.github.io/ingame-features/#video', help_url: 'https://better-xcloud.github.io/ingame-features/#video',
note: CE('div', {'class': 'bx-quick-settings-bar-note bx-clarity-boost-warning'}, `⚠️ ${t('clarity-boost-warning')}`),
items: [ items: [
{ {
pref: Preferences.VIDEO_RATIO, pref: Preferences.VIDEO_RATIO,
@ -12013,6 +12132,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 = '🎮';