Refactor Remote Play feature

This commit is contained in:
redphx
2025-04-20 20:28:48 +07:00
parent 4a65221ad0
commit da6ab51ba0
19 changed files with 136 additions and 195 deletions

View File

@@ -36,19 +36,25 @@ export class PatcherUtils {
return txt.substring(0, index) + toString + txt.substring(index + fromString.length);
}
static replaceAfterIndex(txt: string, search: string, replaceWith: string, index: number) {
const before = txt.slice(0, index);
const after = txt.slice(index).replace(search, replaceWith);
return before + after;
}
static filterPatches(patches: Array<PatchName | false>): PatchArray {
return patches.filter((item): item is PatchName => !!item);
}
static patchBeforePageLoad(str: string, page: PatchPage): string | false {
let text = `chunkName:()=>"${page}-page",`;
if (!str.includes(text)) {
const index = str.indexOf(`chunkName:()=>"${page}-page",`);
if (index < 0) {
return false;
}
str = str.replace('requireAsync(e){', `requireAsync(e){window.BX_EXPOSED.beforePageLoad("${page}");`);
str = str.replace('requireSync(e){', `requireSync(e){window.BX_EXPOSED.beforePageLoad("${page}");`);
str = PatcherUtils.replaceAfterIndex(str, 'requireAsync(e){', `requireAsync(e){window.BX_EXPOSED.beforePageLoad("${page}");`, index);
str = PatcherUtils.replaceAfterIndex(str, 'requireSync(e){', `requireSync(e){window.BX_EXPOSED.beforePageLoad("${page}");`, index);
console.log(str);
return str;
}

View File

@@ -21,7 +21,7 @@ import { PatcherUtils } from "./patcher-utils.js";
export type PatchName = keyof typeof PATCHES;
export type PatchArray = PatchName[];
export type PatchPage = 'home' | 'stream' | 'product-detail';
export type PatchPage = 'home' | 'stream' | 'remote-play-stream' | 'product-detail';
type PatchFunction = (str: string) => string | false;
const LOG_TAG = 'Patcher';
@@ -114,18 +114,6 @@ const PATCHES = {
return str;
},
// Enable Remote Play feature
remotePlayConnectMode(str: string) {
let text = 'connectMode:"cloud-connect",';
if (!str.includes(text)) {
return false;
}
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 str.replace(text, newCode);
},
// Remote Play: Disable achievement toast
remotePlayDisableAchievementToast(str: string) {
let text = '.AchievementUnlock:{';
@@ -133,7 +121,7 @@ remotePlayServerId: (window.BX_REMOTE_PLAY_CONFIG && window.BX_REMOTE_PLAY_CONFI
return false;
}
const newCode = `if (window.location.pathname.includes('/consoles/launch/')) return;`;
const newCode = `if (window.location.pathname.includes('/play/consoles/launch/')) return;`;
return str.replace(text, text + newCode);
},
@@ -936,6 +924,10 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
return PatcherUtils.patchBeforePageLoad(str, 'stream');
},
remotePlayStreamPageBeforeLoad(str: string) {
return PatcherUtils.patchBeforePageLoad(str, 'remote-play-stream');
},
disableAbsoluteMouse(str: string) {
let text = 'sendAbsoluteMouseCapableMessage(e){';
if (!str.includes(text)) {
@@ -1260,6 +1252,7 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([
'injectErrorPageUseEffect',
'streamPageBeforeLoad',
'remotePlayStreamPageBeforeLoad',
'injectGuideHomeUseEffect',
'injectAchievementsProgressUseEffect',
@@ -1298,7 +1291,7 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([
'disableTelemetryProvider',
] : []) as PatchArray,
...(getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED) ? [
...(!getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.REMOTE_PLAY) ? [
'remotePlayKeepAlive',
'remotePlayDisableAchievementToast',
STATES.userAgent.capabilities.touch && 'patchUpdateInputConfigurationAsync',
@@ -1365,10 +1358,9 @@ let STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([
getGlobalPref(GlobalPref.STREAM_COMBINE_SOURCES) && 'streamCombineSources',
...(getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED) ? [
...(!getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.REMOTE_PLAY) ? [
'remotePlayPostStreamRedirectUrl',
'patchRemotePlayMkb',
'remotePlayConnectMode',
] : []) as PatchArray,
// Native MKB
@@ -1388,6 +1380,7 @@ export class Patcher {
private static remainingPatches: { [key in PatchPage]: PatchArray } = {
home: HOME_PAGE_PATCH_ORDERS,
stream: STREAM_PAGE_PATCH_ORDERS,
'remote-play-stream': STREAM_PAGE_PATCH_ORDERS,
'product-detail': PRODUCT_DETAIL_PAGE_PATCH_ORDERS,
};
@@ -1551,7 +1544,9 @@ export class PatcherCache {
BxLogger.info(LOG_TAG, 'Cache', this.CACHE);
const pathName = window.location.pathname;
if (pathName.includes('/play/launch/')) {
if (pathName.includes('/play/consoles/launch/')) {
Patcher.patchPage('remote-play-stream');
} else if (pathName.includes('/play/launch/')) {
Patcher.patchPage('stream');
} else if (pathName.includes('/play/games/')) {
Patcher.patchPage('product-detail');

View File

@@ -3,7 +3,7 @@ declare const e: string;
try {
const msg = JSON.parse(e);
if (msg.reason === 'WarningForBeingIdle' && window.location.pathname.includes('/consoles/launch/')) {
if (msg.reason === 'WarningForBeingIdle' && window.location.pathname.includes('/play/consoles/launch/')) {
$this$.sendKeepAlive();
// @ts-ignore
return;

View File

@@ -8,6 +8,7 @@ import { HeaderSection } from "./ui/header";
import { GlobalPref } from "@/enums/pref-keys";
import { getGlobalPref, setGlobalPref } from "@/utils/pref-utils";
import { RemotePlayDialog } from "./ui/dialog/remote-play-dialog";
import { BlockFeature } from "@/enums/pref-values";
export const enum RemotePlayConsoleState {
ON = 'On',
@@ -37,7 +38,7 @@ export class RemotePlayManager {
private static instance: RemotePlayManager | null | undefined;
public static getInstance(): typeof RemotePlayManager['instance'] {
if (typeof RemotePlayManager.instance === 'undefined') {
if (getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) {
if (!getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.REMOTE_PLAY)) {
RemotePlayManager.instance = new RemotePlayManager();
} else {
RemotePlayManager.instance = null;
@@ -194,13 +195,7 @@ export class RemotePlayManager {
setGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, resolution, 'ui');
}
STATES.remotePlay.config = {
serverId: serverId,
};
window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config;
localRedirect('/launch/fortnite/BT5P2X999VH2#remote-play');
setTimeout(() => localRedirect('/consoles/launch/' + serverId), 100);
localRedirect('/consoles/launch/' + serverId);
}
togglePopup(force = null) {
@@ -226,21 +221,6 @@ export class RemotePlayManager {
RemotePlayDialog.getInstance().show();
}
static detect() {
if (!getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) {
return;
}
STATES.remotePlay.isPlaying = window.location.pathname.includes('/launch/') && window.location.hash.startsWith('#remote-play');
if (STATES.remotePlay?.isPlaying) {
window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config;
// Remove /launch/... from URL
window.history.replaceState({origin: 'better-xcloud'}, '', 'https://www.xbox.com/' + location.pathname.substring(1, 6) + '/play');
} else {
window.BX_REMOTE_PLAY_CONFIG = null;
}
}
isReady() {
return this.consoles !== null;
}

View File

@@ -51,7 +51,7 @@ export class RemotePlayDialog extends NavigationDialog {
$resolutions.addEventListener('input', (e: Event) => {
const value = (e.target as HTMLSelectElement).value;
$settingNote.textContent = value === StreamResolution.DIM_1080P ? '' + t('can-stream-xbox-360-games') : '❌ ' + t('cant-stream-xbox-360-games');
$settingNote.textContent = `${t('xbox-360-games')} ${value === StreamResolution.DIM_1080P_HQ ? '' : '✅'} ${t('xbox-apps')}`;
setGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, value, 'ui');
});

View File

@@ -191,7 +191,6 @@ export class SettingsDialog extends NavigationDialog {
},
GlobalPref.SERVER_BYPASS_RESTRICTION,
GlobalPref.UI_CONTROLLER_FRIENDLY,
GlobalPref.REMOTE_PLAY_ENABLED,
],
}, {
group: 'server',

View File

@@ -11,6 +11,7 @@ import { GlobalPref } from "@/enums/pref-keys";
import { getGlobalPref } from "@/utils/pref-utils";
import { BxLogger } from "@/utils/bx-logger";
import { BxEventBus } from "@/utils/bx-event-bus";
import { BlockFeature } from "@/enums/pref-values";
export class HeaderSection {
private static instance: HeaderSection;
@@ -44,7 +45,7 @@ export class HeaderSection {
});
this.$buttonsWrapper = CE('div', false,
getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED) ? this.$btnRemotePlay : null,
!getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.REMOTE_PLAY) ? this.$btnRemotePlay : null,
this.$btnSettings,
);