Compare commits

..

1 Commits

Author SHA1 Message Date
1d018cc0a3 Test native MKB feature with Remote Play 2024-01-21 18:05:19 +07:00
3 changed files with 113 additions and 327 deletions

View File

@ -16,8 +16,7 @@ If you like this project please give it a 🌟. Thank you 🙏.
[![Total stars](https://img.shields.io/github/stars/redphx/better-xcloud?color=%23cca400)](https://github.com/redphx/better-xcloud/stargazers)
## Full documentations
- For the full details please visit: https://better-xcloud.github.io
- [Demo video](https://youtu.be/hyp69Jrb2sQ)
For the full details please visit: https://better-xcloud.github.io
⚠️ Please DO NOT report **Better xCloud**'s bugs on [/r/xcloud subreddit](https://reddit.com/r/xcloud/). Report bugs in [Issues](https://github.com/redphx/better-xcloud/issues) or [Telegram channel](https://t.me/betterxcloud) instead.

View File

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

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.1.1
// @version 3.0.5
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -13,15 +13,14 @@
// ==/UserScript==
'use strict';
const SCRIPT_VERSION = '3.1.1';
const SCRIPT_VERSION = '3.0.5';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
const ENABLE_XCLOUD_LOGGER = false;
const ENABLE_PRELOAD_BX_UI = false;
const USE_DEV_TOUCH_LAYOUT = false;
const ENABLE_NATIVE_MKB_BETA = false;
window.NATIVE_MKB_TITLES = [
'BT5P2X999VH2',
// Not working anymore
// '9PMQDM08SNK9', // MS Flight Simulator
// '9NP1P1WFS0LB', // Halo Infinite
@ -38,6 +37,16 @@ window.NATIVE_MKB_TITLES = [
// '9P731Z4BBCT3', // Atomic Heart
];
window.REMOTE_PLAY_NATIVE_MKB_TITLES = [
// DOOM 64
'DOOM64',
'BT5P2X999VH2',
// Cyperpunk 2077
'222473492',
'BX3M8L83BBRW',
];
console.log(`[Better xCloud] readyState: ${document.readyState}`);
const BxEvent = {
@ -47,8 +56,6 @@ const BxEvent = {
STREAM_STARTING: 'bx-stream-starting',
STREAM_STARTED: 'bx-stream-started',
STREAM_STOPPED: 'bx-stream-stopped',
CUSTOM_TOUCH_LAYOUTS_LOADED: 'bx-custom-touch-layouts-loaded',
};
// Quickly create a tree of elements without having to use innerHTML
@ -709,7 +716,6 @@ const Translations = {
"en-US": "Deadzone counterweight",
"es-ES": "Contrapeso de la zona muerta",
"ja-JP": "デッドゾーンのカウンターウエイト",
"pl-PL": "Przeciwwaga martwej strefy",
"pt-BR": "Contador da Zona Morta",
"ru-RU": "Противодействие мертвой зоне игры",
"tr-TR": "Ölü alan denge ağırlığı",
@ -1284,17 +1290,6 @@ const Translations = {
"vi-VN": "Nhấn vào để kích hoạt",
"zh-CN": "单击以启用",
},
"mkb-disclaimer": {
"de-DE": "Das Nutzen dieser Funktion beim Online-Spielen könnte als Betrug angesehen werden",
"en-US": "Using this feature when playing online could be viewed as cheating",
"es-ES": "Usar esta función al jugar en línea podría ser visto como trampas",
"ja-JP": "オンラインプレイでこの機能を使用すると不正行為と判定される可能性があります",
"pl-PL": "Używanie tej funkcji podczas grania online może być postrzegane jako oszukiwanie",
"pt-BR": "Usar esta função em jogos online pode ser considerado como uma forma de trapaça",
"ru-RU": "Использование этой функции при игре онлайн может рассматриваться как читерство",
"uk-UA": "Використання цієї функції під час гри онлайн може розглядатися як шахрайство",
"vi-VN": "Sử dụng chức năng này khi chơi trực tuyến có thể bị xem là gian lận",
},
"mouse-and-keyboard": {
"de-DE": "Maus & Tastatur",
"en-US": "Mouse & Keyboard",
@ -2176,7 +2171,6 @@ const Translations = {
"en-US": "Stick decay minimum",
"es-ES": "Disminuir mínimamente el analógico",
"ja-JP": "スティックの減衰の最小値",
"pl-PL": "Minimalne opóźnienie drążka",
"pt-BR": "Mínimo decaimento do analógico",
"ru-RU": "Минимальная перезарядка стика",
"tr-TR": "Çubuğun ortalanma süresi minimumu",
@ -2189,7 +2183,6 @@ const Translations = {
"en-US": "Stick decay strength",
"es-ES": "Intensidad de decaimiento del analógico",
"ja-JP": "スティックの減衰の強さ",
"pl-PL": "Siła opóźnienia drążka",
"pt-BR": "Força de decaimento do analógico",
"ru-RU": "Скорость перезарядки стика",
"tr-TR": "Çubuğun ortalanma gücü",
@ -2234,7 +2227,6 @@ const Translations = {
"en-US": "Support Better xCloud",
"es-ES": "Apoyar a Better xCloud",
"ja-JP": "Better xCloudをサポート",
"pl-PL": "Wesprzyj Better xCloud",
"pt-BR": "Suporte ao Melhor xCloud",
"ru-RU": "Поддержать Better xCloud",
"tr-TR": "Better xCloud'a destek ver",
@ -2762,7 +2754,6 @@ window.addEventListener('load', e => {
});
const NATIVE_FETCH = window.fetch;
const SERVER_REGIONS = {};
var IS_PLAYING = false;
var STREAM_WEBRTC;
@ -2771,12 +2762,9 @@ var STREAM_AUDIO_GAIN_NODE;
var $STREAM_VIDEO;
var $SCREENSHOT_CANVAS;
var GAME_TITLE_ID;
var GAME_XBOX_TITLE_ID;
var GAME_PRODUCT_ID;
var APP_CONTEXT;
window.BX_EXPOSED = {};
let IS_REMOTE_PLAYING;
let REMOTE_PLAY_CONFIG;
@ -2798,8 +2786,6 @@ const Icon = {
REMOTE_PLAY: '<g transform="matrix(.492308 0 0 .581818 -14.7692 -11.6364)"><clipPath id="A"><path d="M30 20h65v55H30z"/></clipPath><g clip-path="url(#A)"><g transform="matrix(.395211 0 0 .334409 11.913 7.01124)"><g transform="matrix(.555556 0 0 .555556 57.8889 -20.2417)" fill="none" stroke="#fff" stroke-width="13.88"><path d="M200 140.564c-42.045-33.285-101.955-33.285-144 0M168 165c-23.783-17.3-56.217-17.3-80 0"/></g><g transform="matrix(-.555556 0 0 -.555556 200.111 262.393)"><g transform="matrix(1 0 0 1 0 11.5642)"><path d="M200 129c-17.342-13.728-37.723-21.795-58.636-24.198C111.574 101.378 80.703 109.444 56 129" fill="none" stroke="#fff" stroke-width="13.88"/></g><path d="M168 165c-23.783-17.3-56.217-17.3-80 0" fill="none" stroke="#fff" stroke-width="13.88"/></g><g transform="matrix(.75 0 0 .75 32 32)"><path d="M24 72h208v93.881H24z" fill="none" stroke="#fff" stroke-linejoin="miter" stroke-width="9.485"/><circle cx="188" cy="128" r="12" stroke-width="10" transform="matrix(.708333 0 0 .708333 71.8333 12.8333)"/><path d="M24.358 103.5h110" fill="none" stroke="#fff" stroke-linecap="butt" stroke-width="10.282"/></g></g></g></g>',
HAND_TAP: '<path d="M6.537 8.906c0-4.216 3.469-7.685 7.685-7.685s7.685 3.469 7.685 7.685M7.719 30.778l-4.333-7.389C3.133 22.944 3 22.44 3 21.928a2.97 2.97 0 0 1 2.956-2.956 2.96 2.96 0 0 1 2.55 1.461l2.761 4.433V8.906a2.97 2.97 0 0 1 2.956-2.956 2.97 2.97 0 0 1 2.956 2.956v8.276a2.97 2.97 0 0 1 2.956-2.956 2.97 2.97 0 0 1 2.956 2.956v2.365a2.97 2.97 0 0 1 2.956-2.956A2.97 2.97 0 0 1 29 19.547v5.32c0 3.547-1.182 5.911-1.182 5.911"/>',
SCREENSHOT_B64: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDMyIDMyIiBmaWxsPSIjZmZmIj48cGF0aCBkPSJNMjguMzA4IDUuMDM4aC00LjI2NWwtMi4wOTctMy4xNDVhMS4yMyAxLjIzIDAgMCAwLTEuMDIzLS41NDhoLTkuODQ2YTEuMjMgMS4yMyAwIDAgMC0xLjAyMy41NDhMNy45NTYgNS4wMzhIMy42OTJBMy43MSAzLjcxIDAgMCAwIDAgOC43MzF2MTcuMjMxYTMuNzEgMy43MSAwIDAgMCAzLjY5MiAzLjY5MmgyNC42MTVBMy43MSAzLjcxIDAgMCAwIDMyIDI1Ljk2MlY4LjczMWEzLjcxIDMuNzEgMCAwIDAtMy42OTItMy42OTJ6bS02Ljc2OSAxMS42OTJjMCAzLjAzOS0yLjUgNS41MzgtNS41MzggNS41MzhzLTUuNTM4LTIuNS01LjUzOC01LjUzOCAyLjUtNS41MzggNS41MzgtNS41MzggNS41MzggMi41IDUuNTM4IDUuNTM4eiIvPjwvc3ZnPgo=',
};
@ -3127,7 +3113,6 @@ class TitlesInfo {
const details = titleInfo.details;
TitlesInfo.update(details.productId, {
titleId: titleInfo.titleId,
xboxTitleId: details.xboxTitleId,
// Has more than one input type -> must have touch support
hasTouchSupport: (details.supportedInputTypes.length > 1),
});
@ -3344,7 +3329,7 @@ class LoadingScreen {
class TouchController {
static get #EVENT_SHOW_DEFAULT_CONTROLLER() {
static get #EVENT_SHOW_CONTROLLER() {
return new MessageEvent('message', {
data: '{"content":"{\\"layoutId\\":\\"\\"}","target":"/streaming/touchcontrols/showlayoutv2","type":"Message"}',
origin: 'better-xcloud',
@ -3365,9 +3350,6 @@ class TouchController {
static #showing = false;
static #dataChannel;
static #customLayouts = {};
static #currentLayoutId;
static enable() {
TouchController.#enable = true;
}
@ -3380,13 +3362,8 @@ class TouchController {
return TouchController.#enable;
}
static #showDefault() {
TouchController.#dispatchMessage(TouchController.#EVENT_SHOW_DEFAULT_CONTROLLER);
TouchController.#showing = true;
}
static #show() {
TouchController.loadCustomLayout(GAME_XBOX_TITLE_ID, TouchController.#currentLayoutId, 0);
TouchController.#dispatchMessage(TouchController.#EVENT_SHOW_CONTROLLER);
TouchController.#showing = true;
}
@ -3422,86 +3399,6 @@ class TouchController {
}, 10);
}
static getCustomLayouts(xboxTitleId) {
const dispatchLayouts = data => {
const event = new Event(BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED);
event.data = data;
window.dispatchEvent(event);
};
xboxTitleId = '' + xboxTitleId;
if (xboxTitleId in TouchController.#customLayouts) {
dispatchLayouts(TouchController.#customLayouts[xboxTitleId]);
return;
}
let url = 'https://raw.githubusercontent.com/redphx/better-xcloud/gh-pages/touch-layouts/';
if (USE_DEV_TOUCH_LAYOUT) {
url += `dev/${xboxTitleId}.json`;
} else {
url += `${xboxTitleId}.json`;
}
NATIVE_FETCH(url)
.then(resp => resp.json(), () => {
TouchController.#customLayouts[xboxTitleId] = null;
// Wait for BX_EXPOSED.touch_layout_manager
setTimeout(() => dispatchLayouts(null), 1000);
})
.then(json => {
// Normalize data
const schema_version = json.schema_version || 1;
let layout;
try {
if (schema_version === 1) {
json.layouts = {
default: {
name: 'Default',
content: json.layout,
},
};
json.default_layout = 'default';
delete json.layout;
}
} catch (e) {}
TouchController.#customLayouts[xboxTitleId] = json;
// Wait for BX_EXPOSED.touch_layout_manager
setTimeout(() => dispatchLayouts(json), 1000);
});
}
static loadCustomLayout(xboxTitleId, layoutId, delay) {
if (!window.BX_EXPOSED.touch_layout_manager) {
return;
}
TouchController.#currentLayoutId = layoutId;
xboxTitleId = '' + xboxTitleId;
const layoutData = TouchController.#customLayouts[xboxTitleId];
if (!xboxTitleId || !layoutId || !layoutData) {
TouchController.#enable && TouchController.#showDefault();
return;
}
const layout = (layoutData.layouts[layoutId] || layoutData.layouts[layoutData.default_layout]);
layout && setTimeout(() => {
window.BX_EXPOSED.touch_layout_manager.changeLayoutForScope({
type: 'showLayout',
scope: xboxTitleId,
subscope: 'base',
layout: {
id: 'System.Standard',
displayName: 'System',
layoutFile: {
content: layout.content,
},
}
});
}, delay);
}
static setup() {
const $style = document.createElement('style');
document.documentElement.appendChild($style);
@ -3534,7 +3431,7 @@ class TouchController {
const nativeCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
RTCPeerConnection.prototype.createDataChannel = function() {
const dataChannel = nativeCreateDataChannel.apply(this, arguments);
if (dataChannel.label !== 'message') {
if (!TouchController.#enable || dataChannel.label !== 'message') {
return dataChannel;
}
@ -3568,18 +3465,8 @@ class TouchController {
// Dispatch a message to display generic touch controller
if (msg.data.includes('touchcontrols/showtitledefault')) {
TouchController.#enable && TouchController.getCustomLayouts(GAME_XBOX_TITLE_ID);
return;
TouchController.#show();
}
// Load custom touch layout
try {
if (msg.data.includes('/titleinfo')) {
const json = JSON.parse(JSON.parse(msg.data).content);
const xboxTitleId = parseInt(json.titleid, 16);
GAME_XBOX_TITLE_ID = xboxTitleId;
}
} catch (e) { console.log(e) }
});
return dataChannel;
@ -6332,8 +6219,7 @@ class Preferences {
'ready': () => {
const options = Preferences.SETTINGS[Preferences.STREAM_CODEC_PROFILE].options;
if (Object.keys(options).length <= 1) {
Preferences.SETTINGS[Preferences.STREAM_CODEC_PROFILE].unsupported = true;
Preferences.SETTINGS[Preferences.STREAM_CODEC_PROFILE].note = '⚠️ ' + __('browser-unsupported-feature');
Preferences.SETTINGS[Preferences.STREAM_CODEC_PROFILE].unsupported = __('browser-unsupported-feature');
}
},
},
@ -6361,7 +6247,7 @@ class Preferences {
'all': __('tc-all-games'),
'off': __('off'),
},
'unsupported': !HAS_TOUCH_SUPPORT,
'unsupported': !HAS_TOUCH_SUPPORT ? __('device-unsupported-touch') : false,
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
'default': 'default',
@ -6370,7 +6256,7 @@ class Preferences {
'white': __('tc-all-white'),
'muted': __('tc-muted-colors'),
},
'unsupported': !HAS_TOUCH_SUPPORT,
'unsupported': !HAS_TOUCH_SUPPORT ? __('device-unsupported-touch') : false,
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: {
'default': 'default',
@ -6378,7 +6264,7 @@ class Preferences {
'default': __('default'),
'muted': __('tc-muted-colors'),
},
'unsupported': !HAS_TOUCH_SUPPORT,
'unsupported': !HAS_TOUCH_SUPPORT ? __('device-unsupported-touch') : false,
},
[Preferences.STREAM_SIMPLIFY_MENU]: {
'default': false,
@ -6425,11 +6311,6 @@ class Preferences {
const userAgent = (window.navigator.orgUserAgent || window.navigator.userAgent || '').toLowerCase();
return userAgent.match(/(android|iphone|ipad)/) ? __('browser-unsupported-feature') : false;
})(),
'ready': () => {
const pref = Preferences.SETTINGS[Preferences.MKB_ENABLED];
const note = __(pref.unsupported ? 'browser-unsupported-feature' : 'mkb-disclaimer');
Preferences.SETTINGS[Preferences.MKB_ENABLED].note = '⚠️ ' + note;
},
},
[Preferences.MKB_DEFAULT_PRESET_ID]: {
@ -6836,7 +6717,7 @@ class Patcher {
funcStr = funcStr.replace('onServerDisconnectMessage(e){', `onServerDisconnectMessage(e) {
const msg = JSON.parse(e);
if (msg.reason === 'WarningForBeingIdle' && !window.location.pathname.includes('/launch/')) {
if (msg.reason === 'WarningForBeingIdle') {
try {
this.sendKeepAlive();
return;
@ -6849,12 +6730,32 @@ class Patcher {
// Enable Remote Play feature
remotePlayConnectMode: function(funcStr) {
const text = 'connectMode:"cloud-connect"';
const text = 'connectMode:"cloud-connect",';
if (!funcStr.includes(text)) {
return false;
}
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)||''`);
const newCode = `
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, newCode);
},
// Remote Play MKB support
remotePlayMkb: function(funcStr) {
const text = 'handleRemotePlayTitleInputConfig(e){';
if (!funcStr.includes(text)) {
return false;
}
const newCode = `
const supportMkb = window.REMOTE_PLAY_NATIVE_MKB_TITLES.includes(e.titleId);
this.gameStream.session.updateInputConfigurationAsync({ enableMouseAndKeyboard: supportMkb });
`;
return funcStr.replace(text, text + newCode);
},
// Disable trackEvent() function
@ -6937,13 +6838,11 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
];
// Enable native Mouse and Keyboard support
if (ENABLE_NATIVE_MKB_BETA) {
newSettings.push('EnableMouseAndKeyboard: true');
newSettings.push('ShowMouseKeyboardSetting: true');
newSettings.push('EnableMouseAndKeyboard: true');
newSettings.push('ShowMouseKeyboardSetting: true');
if (getPref(Preferences.MKB_ABSOLUTE_MOUSE)) {
newSettings.push('EnableAbsoluteMouse: true');
}
if (getPref(Preferences.MKB_ABSOLUTE_MOUSE)) {
newSettings.push('EnableAbsoluteMouse: true');
}
const newCode = newSettings.join(',');
@ -6953,12 +6852,12 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
},
mkbIsMouseAndKeyboardTitle: function(funcStr) {
const text = 'isMouseAndKeyboardTitle:()=>yn';
const text = 'isMouseAndKeyboardTitle:()=>';
if (!funcStr.includes(text)) {
return false;
}
return funcStr.replace(text, `isMouseAndKeyboardTitle:()=>(function(e) { return e && e.details ? window.NATIVE_MKB_TITLES.includes(e.details.productId) : true; })`);
return funcStr.replace(text, text + `(function(e) { return e && e.details ? (window.BX_REMOTE_PLAY_CONFIG ? window.REMOTE_PLAY_NATIVE_MKB_TITLES : window.NATIVE_MKB_TITLES).includes(e.details.productId) : true; }),uwuwu:()=>`);
},
mkbMouseAndKeyboardEnabled: function(funcStr) {
@ -7017,16 +6916,6 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
funcStr = funcStr.substring(0, bracketIndex) + 'return 0;' + funcStr.substring(bracketIndex);
return funcStr;
},
exposeTouchLayoutManager: function(funcStr) {
const text = 'this._perScopeLayoutsStream=new';
if (!funcStr.includes(text)) {
return false;
}
funcStr = funcStr.replace(text, 'window.BX_EXPOSED["touch_layout_manager"] = this,' + text);
return funcStr;
},
};
static #PATCH_ORDERS = [
@ -7056,7 +6945,7 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
[
'overrideSettings',
ENABLE_NATIVE_MKB_BETA && 'mkbIsMouseAndKeyboardTitle',
'mkbIsMouseAndKeyboardTitle',
HAS_TOUCH_SUPPORT && 'patchUpdateInputConfigurationAsync',
],
];
@ -7066,13 +6955,14 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
['playVibration'],
getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
ENABLE_XCLOUD_LOGGER && ['enableConsoleLogging'],
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayMkb'],
[
'disableGamepadDisconnectedScreen',
ENABLE_NATIVE_MKB_BETA && 'mkbMouseAndKeyboardEnabled',
'mkbMouseAndKeyboardEnabled',
],
];
@ -8743,6 +8633,8 @@ function interceptHttpRequests() {
const PREF_STREAM_TOUCH_CONTROLLER = getPref(Preferences.STREAM_TOUCH_CONTROLLER);
const PREF_AUDIO_MIC_ON_PLAYING = getPref(Preferences.AUDIO_MIC_ON_PLAYING);
const orgFetch = window.fetch;
const consoleAddrs = {};
const patchIceCandidates = function(...arg) {
@ -8751,7 +8643,7 @@ function interceptHttpRequests() {
const url = (typeof request === 'string') ? request : request.url;
if (url && url.endsWith('/ice') && url.includes('/sessions/') && request.method === 'GET') {
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().text().then(text => {
@ -8790,7 +8682,7 @@ function interceptHttpRequests() {
}
if (IS_REMOTE_PLAYING && (url.includes('/sessions/home') || url.includes('inputconfigs'))) {
TouchController.disable();
TouchController.enable();
const clone = request.clone();
@ -8826,7 +8718,7 @@ function interceptHttpRequests() {
// Get console IP
if (url.includes('/configuration')) {
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().json().then(obj => {
@ -8844,37 +8736,12 @@ function interceptHttpRequests() {
response.json = () => Promise.resolve(obj);
response.text = () => Promise.resolve(JSON.stringify(obj));
return response;
});
});
} else if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && url.includes('inputconfigs')) {
const promise = NATIVE_FETCH(...arg);
return promise.then(response => {
return response.clone().json().then(obj => {
if (obj[0].supportedTabs.length > 0) {
TouchController.disable();
const event = new Event(BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED);
event.data = null;
window.dispatchEvent(event);
} else {
TouchController.enable();
const xboxTitleId = JSON.parse(opts.body).titleIds[0];
GAME_XBOX_TITLE_ID = xboxTitleId;
TouchController.getCustomLayouts(xboxTitleId);
}
response.json = () => Promise.resolve(obj);
response.text = () => Promise.resolve(JSON.stringify(obj));
return response;
});
});
}
return patchIceCandidates(...arg) || NATIVE_FETCH(...arg);
return patchIceCandidates(...arg) || orgFetch(...arg);
}
if (IS_REMOTE_PLAYING && url.includes('/login/user')) {
@ -8898,7 +8765,7 @@ function interceptHttpRequests() {
console.log(e);
}
return NATIVE_FETCH(...arg);
return orgFetch(...arg);
}
if (IS_REMOTE_PLAYING && url.includes('/titles')) {
@ -8918,7 +8785,7 @@ function interceptHttpRequests() {
});
arg[0] = request;
return NATIVE_FETCH(...arg);
return orgFetch(...arg);
}
// ICE server candidates
@ -8929,7 +8796,7 @@ function interceptHttpRequests() {
// Server list
if (!url.includes('xhome.') && url.endsWith('/v2/login/user')) {
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().json().then(obj => {
@ -9004,12 +8871,12 @@ function interceptHttpRequests() {
});
arg[0] = newRequest;
return NATIVE_FETCH(...arg);
return orgFetch(...arg);
}
// Get wait time
if (PREF_UI_LOADING_SCREEN_WAIT_TIME && url.includes('xboxlive.com') && url.includes('/waittime/')) {
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().json().then(json => {
if (json.estimatedAllocationTimeInSeconds > 0) {
@ -9025,7 +8892,7 @@ function interceptHttpRequests() {
if (url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') {
PREF_UI_LOADING_SCREEN_GAME_ART && LoadingScreen.hide();
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
// Touch controller for all games
if (PREF_STREAM_TOUCH_CONTROLLER === 'all') {
@ -9052,9 +8919,7 @@ function interceptHttpRequests() {
overrides.inputConfiguration = overrides.inputConfiguration || {};
overrides.inputConfiguration.enableVibration = true;
if (ENABLE_NATIVE_MKB_BETA) {
overrides.inputConfiguration.enableMouseAndKeyboard = true;
}
overrides.inputConfiguration.enableMouseAndKeyboard = true;
// Enable touch controller
if (TouchController.isEnabled()) {
@ -9080,7 +8945,7 @@ function interceptHttpRequests() {
// catalog.gamepass
if (url.startsWith('https://catalog.gamepass.com') && url.includes('/products')) {
const promise = NATIVE_FETCH(...arg);
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().json().then(json => {
for (let productId in json.Products) {
@ -9092,8 +8957,8 @@ function interceptHttpRequests() {
});
}
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.includes('/titles') || url.includes('/mru'))) {
const promise = NATIVE_FETCH(...arg);
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.endsWith('/titles') || url.endsWith('/mru'))) {
const promise = orgFetch(...arg);
return promise.then(response => {
return response.clone().json().then(json => {
for (let game of json.results) {
@ -9116,7 +8981,7 @@ function interceptHttpRequests() {
});
}
return NATIVE_FETCH(...arg);
return orgFetch(...arg);
}
}
@ -9232,7 +9097,6 @@ function injectSettingsButton($parent) {
},
*/
[__('touch-controller')]: {
_note: !HAS_TOUCH_SUPPORT ? '⚠️ ' + __('device-unsupported-touch') : null,
[Preferences.STREAM_TOUCH_CONTROLLER]: __('tc-availability'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: __('tc-standard-layout-style'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: __('tc-custom-layout-style'),
@ -9278,8 +9142,14 @@ function injectSettingsButton($parent) {
const setting = Preferences.SETTINGS[settingId];
const settingLabel = SETTINGS_UI[groupLabel][settingId];
const settingNote = setting.note;
let settingLabel;
let settingNote;
if (Array.isArray(SETTINGS_UI[groupLabel][settingId])) {
[settingLabel, settingNote] = SETTINGS_UI[groupLabel][settingId];
} else {
settingLabel = SETTINGS_UI[groupLabel][settingId];
}
let $control, $inpCustomUserAgent;
let labelAttrs = {};
@ -9350,8 +9220,11 @@ function injectSettingsButton($parent) {
// Disable unsupported settings
if (setting.unsupported) {
$control.disabled = true;
$control.title = setting.unsupported;
}
$control.disabled && ($control.style.cursor = 'help');
const $label = CE('label', labelAttrs, settingLabel);
if (settingNote) {
$label.appendChild(CE('b', {}, settingNote));
@ -9888,9 +9761,8 @@ function setupQuickSettingsBar() {
group: 'audio',
label: __('audio'),
help_url: 'https://better-xcloud.github.io/ingame-features/#audio',
items: [
{
pref: Preferences.AUDIO_VOLUME,
items: {
[Preferences.AUDIO_VOLUME]: {
label: __('volume'),
onChange: (e, value) => {
STREAM_AUDIO_GAIN_NODE && (STREAM_AUDIO_GAIN_NODE.gain.value = (value / 100).toFixed(2));
@ -9899,7 +9771,7 @@ function setupQuickSettingsBar() {
disabled: !getPref(Preferences.AUDIO_ENABLE_VOLUME_CONTROL),
},
},
],
},
},
{
@ -9907,38 +9779,33 @@ function setupQuickSettingsBar() {
label: __('video'),
help_url: 'https://better-xcloud.github.io/ingame-features/#video',
note: CE('div', {'class': 'bx-quick-settings-bar-note bx-clarity-boost-warning'}, `⚠️ ${__('clarity-boost-warning')}`),
items: [
{
pref: Preferences.VIDEO_RATIO,
items: {
[Preferences.VIDEO_RATIO]: {
label: __('ratio'),
onChange: updateVideoPlayerCss,
},
{
pref: Preferences.VIDEO_CLARITY,
[Preferences.VIDEO_CLARITY]: {
label: __('clarity'),
onChange: updateVideoPlayerCss,
unsupported: isSafari,
},
{
pref: Preferences.VIDEO_SATURATION,
[Preferences.VIDEO_SATURATION]: {
label: __('saturation'),
onChange: updateVideoPlayerCss,
},
{
pref: Preferences.VIDEO_CONTRAST,
[Preferences.VIDEO_CONTRAST]: {
label: __('contrast'),
onChange: updateVideoPlayerCss,
},
{
pref: Preferences.VIDEO_BRIGHTNESS,
[Preferences.VIDEO_BRIGHTNESS]: {
label: __('brightness'),
onChange: updateVideoPlayerCss,
},
],
},
},
],
},
@ -9951,91 +9818,29 @@ function setupQuickSettingsBar() {
group: 'controller',
label: __('controller'),
help_url: 'https://better-xcloud.github.io/ingame-features/#controller',
items: [
{
pref: Preferences.CONTROLLER_ENABLE_VIBRATION,
items: {
[Preferences.CONTROLLER_ENABLE_VIBRATION]: {
label: __('controller-vibration'),
unsupported: !VibrationManager.supportControllerVibration(),
onChange: VibrationManager.updateGlobalVars,
},
{
pref: Preferences.CONTROLLER_DEVICE_VIBRATION,
[Preferences.CONTROLLER_DEVICE_VIBRATION]: {
label: __('device-vibration'),
unsupported: !VibrationManager.supportDeviceVibration(),
onChange: VibrationManager.updateGlobalVars,
},
(VibrationManager.supportControllerVibration() || VibrationManager.supportDeviceVibration()) && {
pref: Preferences.CONTROLLER_VIBRATION_INTENSITY,
[Preferences.CONTROLLER_VIBRATION_INTENSITY]: (VibrationManager.supportControllerVibration() || VibrationManager.supportDeviceVibration()) && {
label: __('vibration-intensity'),
unsupported: !VibrationManager.supportDeviceVibration(),
onChange: VibrationManager.updateGlobalVars,
},
],
},
},
],
},
HAS_TOUCH_SUPPORT && {
icon: Icon.HAND_TAP,
group: 'touch-controller',
items: [
{
group: 'touch-controller',
label: __('touch-controller'),
items: [
{
label: __('layout'),
content: CE('select', {disabled: true}, CE('option', {}, __('default'))),
onMounted: $elm => {
$elm.addEventListener('change', e => {
TouchController.loadCustomLayout(GAME_XBOX_TITLE_ID, $elm.value, 1000);
});
window.addEventListener(BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, e => {
const data = e.data;
if (GAME_XBOX_TITLE_ID && $elm.xboxTitleId === GAME_XBOX_TITLE_ID) {
$elm.dispatchEvent(new Event('change'));
return;
}
$elm.xboxTitleId = GAME_XBOX_TITLE_ID;
// Clear options
while ($elm.firstChild) {
$elm.removeChild($elm.firstChild);
}
$elm.disabled = !data;
if (!data) {
$elm.appendChild(CE('option', {value: ''}, __('default')));
$elm.value = '';
$elm.dispatchEvent(new Event('change'));
return;
}
// Add options
const $fragment = document.createDocumentFragment();
for (const key in data.layouts) {
const layout = data.layouts[key];
const $option = CE('option', {value: key}, layout.name);
$fragment.appendChild($option);
}
$elm.appendChild($fragment);
$elm.value = data.default_layout;
$elm.dispatchEvent(new Event('change'));
});
},
},
],
}
],
},
{
icon: Icon.STREAM_STATS,
group: 'stats',
@ -10044,49 +9849,41 @@ function setupQuickSettingsBar() {
group: 'stats',
label: __('menu-stream-stats'),
help_url: 'https://better-xcloud.github.io/stream-stats/',
items: [
{
pref: Preferences.STATS_SHOW_WHEN_PLAYING,
items: {
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
label: __('show-stats-on-startup'),
},
{
pref: Preferences.STATS_QUICK_GLANCE,
[Preferences.STATS_QUICK_GLANCE]: {
label: __('enable-quick-glance-mode'),
onChange: e => {
e.target.checked ? StreamStats.quickGlanceSetup() : StreamStats.quickGlanceStop();
},
},
{
pref: Preferences.STATS_ITEMS,
[Preferences.STATS_ITEMS]: {
label: __('stats'),
onChange: StreamStats.refreshStyles,
},
{
pref: Preferences.STATS_POSITION,
[Preferences.STATS_POSITION]: {
label: __('position'),
onChange: StreamStats.refreshStyles,
},
{
pref: Preferences.STATS_TEXT_SIZE,
[Preferences.STATS_TEXT_SIZE]: {
label: __('text-size'),
onChange: StreamStats.refreshStyles,
},
{
pref: Preferences.STATS_OPACITY,
[Preferences.STATS_OPACITY]: {
label: __('opacity'),
onChange: StreamStats.refreshStyles,
},
{
pref: Preferences.STATS_TRANSPARENT,
[Preferences.STATS_TRANSPARENT]: {
label: __('transparent-background'),
onChange: StreamStats.refreshStyles,
},
{
pref: Preferences.STATS_CONDITIONAL_FORMATTING,
[Preferences.STATS_CONDITIONAL_FORMATTING]: {
label: __('conditional-formatting'),
onChange: StreamStats.refreshStyles,
},
],
},
},
],
},
@ -10161,21 +9958,14 @@ function setupQuickSettingsBar() {
continue;
}
if (!settingGroup.items) {
settingGroup.items = [];
}
for (const setting of settingGroup.items) {
for (const pref in settingGroup.items) {
const setting = settingGroup.items[pref];
if (!setting) {
continue;
}
const pref = setting.pref;
let $control;
if (setting.content) {
$control = setting.content;
} else if (!setting.unsupported) {
if (!setting.unsupported) {
$control = PREFS.toElement(pref, setting.onChange, setting.params);
}
@ -10188,8 +9978,6 @@ function setupQuickSettingsBar() {
);
$group.appendChild($content);
setting.onMounted && setting.onMounted($control);
}
}
@ -10342,11 +10130,10 @@ function onStreamStarted($video) {
GAME_PRODUCT_ID = matches.groups.product_id;
} else {
GAME_TITLE_ID = 'remote-play';
GAME_PRODUCT_ID = null;
}
// Enable MKB
if (getPref(Preferences.MKB_ENABLED) && (!ENABLE_NATIVE_MKB_BETA || !window.NATIVE_MKB_TITLES.includes(GAME_PRODUCT_ID))) {
if (getPref(Preferences.MKB_ENABLED) && (!window.NATIVE_MKB_TITLES.includes(GAME_PRODUCT_ID))) {
console.log('Emulate MKB');
MkbHandler.INSTANCE.init();
}