* Replace BxEvent.TITLE_INFO_READY with Event Bus

* Migrate more events

* Migrate stream events to event bus

* Migrate preset events

* Migrate more

* Fix dispatching "input" event twice in Number Stepper
This commit is contained in:
redphx 2024-12-08 17:55:44 +07:00 committed by GitHub
parent 160044c958
commit 79ebb1a817
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 282 additions and 191 deletions

View File

@ -141,7 +141,7 @@ function deepClone(obj) {
} }
var BxEvent; var BxEvent;
((BxEvent) => { ((BxEvent) => {
BxEvent.JUMP_BACK_IN_READY = "bx-jump-back-in-ready", BxEvent.POPSTATE = "bx-popstate", BxEvent.TITLE_INFO_READY = "bx-title-info-ready", BxEvent.SETTINGS_CHANGED = "bx-settings-changed", BxEvent.STREAM_LOADING = "bx-stream-loading", BxEvent.STREAM_STARTING = "bx-stream-starting", BxEvent.STREAM_STARTED = "bx-stream-started", BxEvent.STREAM_PLAYING = "bx-stream-playing", BxEvent.STREAM_STOPPED = "bx-stream-stopped", BxEvent.STREAM_ERROR_PAGE = "bx-stream-error-page", BxEvent.STREAM_WEBRTC_CONNECTED = "bx-stream-webrtc-connected", BxEvent.STREAM_WEBRTC_DISCONNECTED = "bx-stream-webrtc-disconnected", BxEvent.MKB_UPDATED = "bx-mkb-updated", BxEvent.KEYBOARD_SHORTCUTS_UPDATED = "bx-keyboard-shortcuts-updated", BxEvent.STREAM_SESSION_READY = "bx-stream-session-ready", BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED = "bx-custom-touch-layouts-loaded", BxEvent.TOUCH_LAYOUT_MANAGER_READY = "bx-touch-layout-manager-ready", BxEvent.REMOTE_PLAY_READY = "bx-remote-play-ready", BxEvent.REMOTE_PLAY_FAILED = "bx-remote-play-failed", BxEvent.XCLOUD_SERVERS_READY = "bx-servers-ready", BxEvent.XCLOUD_SERVERS_UNAVAILABLE = "bx-servers-unavailable", BxEvent.DATA_CHANNEL_CREATED = "bx-data-channel-created", BxEvent.DEVICE_VIBRATION_CHANGED = "bx-device-vibration-changed", BxEvent.GAME_BAR_ACTION_ACTIVATED = "bx-game-bar-action-activated", BxEvent.MICROPHONE_STATE_CHANGED = "bx-microphone-state-changed", BxEvent.SPEAKER_STATE_CHANGED = "bx-speaker-state-changed", BxEvent.VIDEO_VISIBILITY_CHANGED = "bx-video-visibility-changed", BxEvent.CAPTURE_SCREENSHOT = "bx-capture-screenshot", BxEvent.POINTER_LOCK_REQUESTED = "bx-pointer-lock-requested", BxEvent.POINTER_LOCK_EXITED = "bx-pointer-lock-exited", BxEvent.NAVIGATION_FOCUS_CHANGED = "bx-nav-focus-changed", BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED = "bx-gh-pages-force-native-mkb-updated", BxEvent.XCLOUD_DIALOG_SHOWN = "bx-xcloud-dialog-shown", BxEvent.XCLOUD_DIALOG_DISMISSED = "bx-xcloud-dialog-dismissed", BxEvent.XCLOUD_GUIDE_MENU_SHOWN = "bx-xcloud-guide-menu-shown", BxEvent.XCLOUD_POLLING_MODE_CHANGED = "bx-xcloud-polling-mode-changed", BxEvent.XCLOUD_RENDERING_COMPONENT = "bx-xcloud-rendering-component", BxEvent.XCLOUD_ROUTER_HISTORY_READY = "bx-xcloud-router-history-ready"; BxEvent.JUMP_BACK_IN_READY = "bx-jump-back-in-ready", BxEvent.POPSTATE = "bx-popstate", BxEvent.STREAM_SESSION_READY = "bx-stream-session-ready", BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED = "bx-custom-touch-layouts-loaded", BxEvent.TOUCH_LAYOUT_MANAGER_READY = "bx-touch-layout-manager-ready", BxEvent.REMOTE_PLAY_READY = "bx-remote-play-ready", BxEvent.REMOTE_PLAY_FAILED = "bx-remote-play-failed", BxEvent.DATA_CHANNEL_CREATED = "bx-data-channel-created", BxEvent.GAME_BAR_ACTION_ACTIVATED = "bx-game-bar-action-activated", BxEvent.MICROPHONE_STATE_CHANGED = "bx-microphone-state-changed", BxEvent.SPEAKER_STATE_CHANGED = "bx-speaker-state-changed", BxEvent.VIDEO_VISIBILITY_CHANGED = "bx-video-visibility-changed", BxEvent.CAPTURE_SCREENSHOT = "bx-capture-screenshot", BxEvent.POINTER_LOCK_REQUESTED = "bx-pointer-lock-requested", BxEvent.POINTER_LOCK_EXITED = "bx-pointer-lock-exited", BxEvent.NAVIGATION_FOCUS_CHANGED = "bx-nav-focus-changed", BxEvent.XCLOUD_DIALOG_SHOWN = "bx-xcloud-dialog-shown", BxEvent.XCLOUD_DIALOG_DISMISSED = "bx-xcloud-dialog-dismissed", BxEvent.XCLOUD_GUIDE_MENU_SHOWN = "bx-xcloud-guide-menu-shown", BxEvent.XCLOUD_POLLING_MODE_CHANGED = "bx-xcloud-polling-mode-changed", BxEvent.XCLOUD_RENDERING_COMPONENT = "bx-xcloud-rendering-component", BxEvent.XCLOUD_ROUTER_HISTORY_READY = "bx-xcloud-router-history-ready";
function dispatch(target, eventName, data) { function dispatch(target, eventName, data) {
if (!target) return; if (!target) return;
if (!eventName) { if (!eventName) {
@ -156,6 +156,33 @@ var BxEvent;
BxEvent.dispatch = dispatch; BxEvent.dispatch = dispatch;
})(BxEvent ||= {}); })(BxEvent ||= {});
window.BxEvent = BxEvent; window.BxEvent = BxEvent;
class EventBus {
listeners = new Map;
static Script = new EventBus;
static Stream = new EventBus;
on(event, callback) {
if (!this.listeners.has(event)) this.listeners.set(event, new Set);
this.listeners.get(event).add(callback), BX_FLAGS.Debug && BxLogger.warning("EventBus", "on", event, callback);
}
off(event, callback) {
if (BX_FLAGS.Debug && BxLogger.warning("EventBus", "off", event, callback), !callback) {
this.listeners.delete(event);
return;
}
let callbacks = this.listeners.get(event);
if (!callbacks) return;
if (callbacks.delete(callback), callbacks.size === 0) this.listeners.delete(event);
}
offAll() {
this.listeners.clear();
}
emit(event, payload) {
BX_FLAGS.Debug && BxLogger.warning("EventBus", "emit", event, payload);
let callbacks = this.listeners.get(event) || [];
for (let callback of callbacks)
callback(payload);
}
}
class GhPagesUtils { class GhPagesUtils {
static fetchLatestCommit() { static fetchLatestCommit() {
NATIVE_FETCH("https://api.github.com/repos/redphx/better-xcloud/branches/gh-pages", { NATIVE_FETCH("https://api.github.com/repos/redphx/better-xcloud/branches/gh-pages", {
@ -179,7 +206,7 @@ class GhPagesUtils {
static getNativeMkbCustomList(update = !1) { static getNativeMkbCustomList(update = !1) {
let key = "BetterXcloud.GhPages.ForceNativeMkb"; let key = "BetterXcloud.GhPages.ForceNativeMkb";
update && NATIVE_FETCH(GhPagesUtils.getUrl("native-mkb/ids.json")).then((response) => response.json()).then((json) => { update && NATIVE_FETCH(GhPagesUtils.getUrl("native-mkb/ids.json")).then((response) => response.json()).then((json) => {
if (json.$schemaVersion === 1) window.localStorage.setItem(key, JSON.stringify(json)), BxEvent.dispatch(window, BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED); if (json.$schemaVersion === 1) window.localStorage.setItem(key, JSON.stringify(json)), EventBus.Script.emit("listForcedNativeMkbUpdated", {});
}); });
let info = JSON.parse(window.localStorage.getItem(key) || "{}"); let info = JSON.parse(window.localStorage.getItem(key) || "{}");
if (info.$schemaVersion !== 1) return window.localStorage.removeItem(key), {}; if (info.$schemaVersion !== 1) return window.localStorage.removeItem(key), {};
@ -878,7 +905,7 @@ class BaseSettingsStore {
return this.settings[key]; return this.settings[key];
} }
setSetting(key, value, emitEvent = !1) { setSetting(key, value, emitEvent = !1) {
return value = this.validateValue("set", key, value), this.settings[key] = this.validateValue("get", key, value), this.saveSettings(), emitEvent && BxEvent.dispatch(window, BxEvent.SETTINGS_CHANGED, { return value = this.validateValue("set", key, value), this.settings[key] = this.validateValue("get", key, value), this.saveSettings(), emitEvent && EventBus.Script.emit("settingChanged", {
storageKey: this.storageKey, storageKey: this.storageKey,
settingKey: key, settingKey: key,
settingValue: value settingValue: value
@ -1512,7 +1539,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: [], default: [],
unsupported: !AppInterface && UserAgent.isMobile(), unsupported: !AppInterface && UserAgent.isMobile(),
ready: (setting) => { ready: (setting) => {
if (!setting.unsupported) setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(!0), window.addEventListener(BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED, (e) => { if (!setting.unsupported) setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(!0), EventBus.Script.on("listForcedNativeMkbUpdated", () => {
setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(); setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList();
}); });
}, },
@ -2141,7 +2168,7 @@ class StreamStatsCollector {
} catch (e) {} } catch (e) {}
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", () => {
StreamStatsCollector.getInstance().reset(); StreamStatsCollector.getInstance().reset();
}); });
} }
@ -2279,7 +2306,7 @@ class StreamStats {
this.refreshStyles(), document.documentElement.appendChild(this.$container); this.refreshStyles(), document.documentElement.appendChild(this.$container);
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", () => {
let PREF_STATS_QUICK_GLANCE = getPref("stats.quickGlance.enabled"), PREF_STATS_SHOW_WHEN_PLAYING = getPref("stats.showWhenPlaying"), streamStats = StreamStats.getInstance(); let PREF_STATS_QUICK_GLANCE = getPref("stats.quickGlance.enabled"), PREF_STATS_SHOW_WHEN_PLAYING = getPref("stats.showWhenPlaying"), streamStats = StreamStats.getInstance();
if (PREF_STATS_SHOW_WHEN_PLAYING) streamStats.start(); if (PREF_STATS_SHOW_WHEN_PLAYING) streamStats.start();
else if (PREF_STATS_QUICK_GLANCE) streamStats.quickGlanceSetup(), !PREF_STATS_SHOW_WHEN_PLAYING && streamStats.start(!0); else if (PREF_STATS_QUICK_GLANCE) streamStats.quickGlanceSetup(), !PREF_STATS_SHOW_WHEN_PLAYING && streamStats.start(!0);
@ -2573,7 +2600,7 @@ class StreamSettings {
if (!STATES.browser.capabilities.deviceVibration) return; if (!STATES.browser.capabilities.deviceVibration) return;
let mode = StreamSettings.getPref("deviceVibration.mode"), intensity = 0; let mode = StreamSettings.getPref("deviceVibration.mode"), intensity = 0;
if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = StreamSettings.getPref("deviceVibration.intensity") / 100; if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = StreamSettings.getPref("deviceVibration.intensity") / 100;
StreamSettings.settings.deviceVibrationIntensity = intensity, BxEvent.dispatch(window, BxEvent.DEVICE_VIBRATION_CHANGED); StreamSettings.settings.deviceVibrationIntensity = intensity, EventBus.Script.emit("deviceVibrationUpdated", {});
} }
static async refreshMkbSettings() { static async refreshMkbSettings() {
let settings = StreamSettings.settings, presetId = StreamSettings.getPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = { let settings = StreamSettings.settings, presetId = StreamSettings.getPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = {
@ -2587,12 +2614,12 @@ class StreamSettings {
if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex; if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex;
} }
let mouse = converted.mouse; let mouse = converted.mouse;
mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setPref("mkb.p1.preset.mappingId", orgPreset.id), BxEvent.dispatch(window, BxEvent.MKB_UPDATED); mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setPref("mkb.p1.preset.mappingId", orgPreset.id), EventBus.Script.emit("mkbSettingUpdated", {});
} }
static async refreshKeyboardShortcuts() { static async refreshKeyboardShortcuts() {
let settings = StreamSettings.settings, presetId = StreamSettings.getPref("keyboardShortcuts.preset.inGameId"); let settings = StreamSettings.settings, presetId = StreamSettings.getPref("keyboardShortcuts.preset.inGameId");
if (presetId === 0) { if (presetId === 0) {
settings.keyboardShortcuts = null, setPref("keyboardShortcuts.preset.inGameId", presetId), BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); settings.keyboardShortcuts = null, setPref("keyboardShortcuts.preset.inGameId", presetId), EventBus.Script.emit("keyboardShortcutsUpdated", {});
return; return;
} }
let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action; let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action;
@ -2600,7 +2627,7 @@ class StreamSettings {
let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`; let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`;
converted[key] = action; converted[key] = action;
} }
settings.keyboardShortcuts = converted, setPref("keyboardShortcuts.preset.inGameId", orgPreset.id), BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); settings.keyboardShortcuts = converted, setPref("keyboardShortcuts.preset.inGameId", orgPreset.id), EventBus.Script.emit("keyboardShortcutsUpdated", {});
} }
static async refreshAllSettings() { static async refreshAllSettings() {
window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts(); window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts();
@ -2627,7 +2654,7 @@ class MkbPopup {
$btnActivate; $btnActivate;
mkbHandler; mkbHandler;
constructor() { constructor() {
this.render(), window.addEventListener(BxEvent.KEYBOARD_SHORTCUTS_UPDATED, (e) => { this.render(), EventBus.Script.on("keyboardShortcutsUpdated", () => {
let $newButton = this.createActivateButton(); let $newButton = this.createActivateButton();
this.$btnActivate.replaceWith($newButton), this.$btnActivate = $newButton; this.$btnActivate.replaceWith($newButton), this.$btnActivate = $newButton;
}); });
@ -3616,7 +3643,6 @@ class BxNumberStepper extends HTMLInputElement {
$btnInc; $btnInc;
$btnDec; $btnDec;
$range; $range;
onInput;
onRangeInput; onRangeInput;
onClick; onClick;
onPointerUp; onPointerUp;
@ -3643,7 +3669,7 @@ class BxNumberStepper extends HTMLInputElement {
class: options.hideSlider ? "bx-focusable" : "", class: options.hideSlider ? "bx-focusable" : "",
tabindex: options.hideSlider ? 0 : -1 tabindex: options.hideSlider ? 0 : -1
}, "+"))); }, "+")));
if (self.$text = $text, self.$btnInc = $btnInc, self.$btnDec = $btnDec, self.onChange = onChange, self.onInput = BxNumberStepper.onInput.bind(self), self.onRangeInput = BxNumberStepper.onRangeInput.bind(self), self.onClick = BxNumberStepper.onClick.bind(self), self.onPointerUp = BxNumberStepper.onPointerUp.bind(self), self.onPointerDown = BxNumberStepper.onPointerDown.bind(self), self.controlMin = min, self.controlMax = max, self.isHolding = !1, self.options = options, self.uiMin = options.reverse ? -max : min, self.uiMax = options.reverse ? -min : max, self.steps = Math.max(options.steps || 1, 1), BxNumberStepper.setValue.call(self, value), options.disabled) return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), self.disabled = !0, self; if (self.$text = $text, self.$btnInc = $btnInc, self.$btnDec = $btnDec, self.onChange = onChange, self.onRangeInput = BxNumberStepper.onRangeInput.bind(self), self.onClick = BxNumberStepper.onClick.bind(self), self.onPointerUp = BxNumberStepper.onPointerUp.bind(self), self.onPointerDown = BxNumberStepper.onPointerDown.bind(self), self.controlMin = min, self.controlMax = max, self.isHolding = !1, self.options = options, self.uiMin = options.reverse ? -max : min, self.uiMax = options.reverse ? -min : max, self.steps = Math.max(options.steps || 1, 1), BxNumberStepper.setValue.call(self, value), options.disabled) return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), self.disabled = !0, self;
if ($range = CE("input", { if ($range = CE("input", {
id: `bx_inp_setting_${key}`, id: `bx_inp_setting_${key}`,
type: "range", type: "range",
@ -3652,7 +3678,7 @@ class BxNumberStepper extends HTMLInputElement {
value: options.reverse ? -value : value, value: options.reverse ? -value : value,
step: self.steps, step: self.steps,
tabindex: 0 tabindex: 0
}), self.$range = $range, options.hideSlider && $range.classList.add("bx-gone"), $range.addEventListener("input", self.onRangeInput), self.addEventListener("input", self.onInput), self.appendChild($range), options.ticks || options.exactTicks) { }), self.$range = $range, options.hideSlider && $range.classList.add("bx-gone"), self.addEventListener("input", self.onRangeInput), self.appendChild($range), options.ticks || options.exactTicks) {
let markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId }); let markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId });
if ($range.setAttribute("list", markersId), options.exactTicks) { if ($range.setAttribute("list", markersId), options.exactTicks) {
let start = Math.max(Math.floor(min / options.exactTicks), 1) * options.exactTicks; let start = Math.max(Math.floor(min / options.exactTicks), 1) * options.exactTicks;
@ -3683,9 +3709,6 @@ class BxNumberStepper extends HTMLInputElement {
static normalizeValue(value) { static normalizeValue(value) {
return value = parseInt(value), value = Math.max(this.controlMin, value), value = Math.min(this.controlMax, value), value; return value = parseInt(value), value = Math.max(this.controlMin, value), value = Math.min(this.controlMax, value), value;
} }
static onInput(e) {
BxEvent.dispatch(this.$range, "input");
}
static onRangeInput(e) { static onRangeInput(e) {
let value = parseInt(e.target.value); let value = parseInt(e.target.value);
if (this.options.reverse) value *= -1; if (this.options.reverse) value *= -1;
@ -4255,12 +4278,9 @@ class SettingsDialog extends NavigationDialog {
}, },
onCreated: (setting, $elm) => { onCreated: (setting, $elm) => {
let $range = $elm.querySelector("input[type=range"); let $range = $elm.querySelector("input[type=range");
window.addEventListener(BxEvent.SETTINGS_CHANGED, (e) => { EventBus.Script.on("settingChanged", (payload) => {
let { storageKey, settingKey, settingValue } = e; let { storageKey, settingKey, settingValue } = payload;
if (storageKey !== "BetterXcloud" || settingKey !== "audio.volume") return; if (storageKey === "BetterXcloud" && settingKey === "audio.volume") $range.value = settingValue, BxEvent.dispatch($range, "input", { ignoreOnChange: !0 });
$range.value = settingValue, BxEvent.dispatch($range, "input", {
ignoreOnChange: !0
});
}); });
} }
}] }]
@ -5435,7 +5455,7 @@ class XcloudInterceptor {
ip && request.headers.set("X-Forwarded-For", ip); ip && request.headers.set("X-Forwarded-For", ip);
} }
let response = await NATIVE_FETCH(request, init); let response = await NATIVE_FETCH(request, init);
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response; if (response.status !== 200) return EventBus.Script.emit("xcloudServerUnavailable", {}), response;
let obj = await response.clone().json(); let obj = await response.clone().json();
RemotePlayManager.getInstance()?.setXcloudToken(obj.gsToken); RemotePlayManager.getInstance()?.setXcloudToken(obj.gsToken);
let serverRegex = /\/\/(\w+)\./, serverExtra = XcloudInterceptor.SERVER_EXTRA_INFO, region; let serverRegex = /\/\/(\w+)\./, serverExtra = XcloudInterceptor.SERVER_EXTRA_INFO, region;
@ -5447,7 +5467,7 @@ class XcloudInterceptor {
else region.contintent = "other"; else region.contintent = "other";
region.shortName = shortName.toUpperCase(), STATES.serverRegions[region.name] = Object.assign({}, region); region.shortName = shortName.toUpperCase(), STATES.serverRegions[region.name] = Object.assign({}, region);
} }
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_READY); EventBus.Script.emit("xcloudServerReady", {});
let preferredRegion = getPreferredServerRegion(); let preferredRegion = getPreferredServerRegion();
if (preferredRegion && preferredRegion in STATES.serverRegions) { if (preferredRegion && preferredRegion in STATES.serverRegions) {
let tmp = Object.assign({}, STATES.serverRegions[preferredRegion]); let tmp = Object.assign({}, STATES.serverRegions[preferredRegion]);
@ -5456,7 +5476,7 @@ class XcloudInterceptor {
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response; return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
} }
static async handlePlay(request, init) { static async handlePlay(request, init) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING); EventBus.Stream.emit("stateLoading", {});
let PREF_STREAM_TARGET_RESOLUTION = getPref("stream.video.resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream.locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url), badgeRegion = parsedUrl.host.split(".", 1)[0]; let PREF_STREAM_TARGET_RESOLUTION = getPref("stream.video.resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream.locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url), badgeRegion = parsedUrl.host.split(".", 1)[0];
for (let regionName in STATES.serverRegions) { for (let regionName in STATES.serverRegions) {
let region = STATES.serverRegions[regionName]; let region = STATES.serverRegions[regionName];
@ -5492,7 +5512,7 @@ class XcloudInterceptor {
if (request.method !== "GET") return NATIVE_FETCH(request, init); if (request.method !== "GET") return NATIVE_FETCH(request, init);
let response = await NATIVE_FETCH(request, init), text = await response.clone().text(); let response = await NATIVE_FETCH(request, init), text = await response.clone().text();
if (!text.length) return response; if (!text.length) return response;
BxEvent.dispatch(window, BxEvent.STREAM_STARTING); EventBus.Stream.emit("stateStarting", {});
let obj = JSON.parse(text), overrides = JSON.parse(obj.clientStreamingConfigOverrides || "{}") || {}; let obj = JSON.parse(text), overrides = JSON.parse(obj.clientStreamingConfigOverrides || "{}") || {};
overrides.inputConfiguration = overrides.inputConfiguration || {}, overrides.inputConfiguration.enableVibration = !0; overrides.inputConfiguration = overrides.inputConfiguration || {}, overrides.inputConfiguration.enableVibration = !0;
let overrideMkb = null; let overrideMkb = null;
@ -5725,7 +5745,7 @@ function onHistoryChanged(e) {
window.setTimeout(RemotePlayManager.detect, 10); window.setTimeout(RemotePlayManager.detect, 10);
let $settings = document.querySelector(".bx-settings-container"); let $settings = document.querySelector(".bx-settings-container");
if ($settings) $settings.classList.add("bx-gone"); if ($settings) $settings.classList.add("bx-gone");
NavigationDialogManager.getInstance().hide(), LoadingScreen.reset(), window.setTimeout(HeaderSection.watchHeader, 2000), BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); NavigationDialogManager.getInstance().hide(), LoadingScreen.reset(), window.setTimeout(HeaderSection.watchHeader, 2000), EventBus.Stream.emit("stateStopped", {});
} }
function setCodecPreferences(sdp, preferredCodec) { function setCodecPreferences(sdp, preferredCodec) {
let h264Pattern = /a=fmtp:(\d+).*profile-level-id=([0-9a-f]{6})/g, profilePrefix = preferredCodec === "high" ? "4d" : preferredCodec === "low" ? "420" : "42e", preferredCodecIds = [], matches = sdp.matchAll(h264Pattern) || []; let h264Pattern = /a=fmtp:(\d+).*profile-level-id=([0-9a-f]{6})/g, profilePrefix = preferredCodec === "high" ? "4d" : preferredCodec === "low" ? "420" : "42e", preferredCodecIds = [], matches = sdp.matchAll(h264Pattern) || [];
@ -6081,7 +6101,7 @@ function patchVideoApi() {
contrast: getPref("video.contrast"), contrast: getPref("video.contrast"),
brightness: getPref("video.brightness") brightness: getPref("video.brightness")
}; };
STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref("video.player.type"), playerOptions), BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, { STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref("video.player.type"), playerOptions), EventBus.Stream.emit("statePlaying", {
$video: this $video: this
}); });
}, nativePlay = HTMLMediaElement.prototype.play; }, nativePlay = HTMLMediaElement.prototype.play;
@ -6280,7 +6300,7 @@ class StreamUiHandler {
if (!($elm instanceof HTMLElement)) return; if (!($elm instanceof HTMLElement)) return;
let className = $elm.className || ""; let className = $elm.className || "";
if (className.includes("PureErrorPage")) { if (className.includes("PureErrorPage")) {
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE); EventBus.Stream.emit("stateError", {});
return; return;
} }
if (className.startsWith("StreamMenu-module__container")) { if (className.startsWith("StreamMenu-module__container")) {
@ -6401,25 +6421,25 @@ window.addEventListener(BxEvent.POPSTATE, onHistoryChanged);
window.addEventListener("popstate", onHistoryChanged); window.addEventListener("popstate", onHistoryChanged);
window.history.pushState = patchHistoryMethod("pushState"); window.history.pushState = patchHistoryMethod("pushState");
window.history.replaceState = patchHistoryMethod("replaceState"); window.history.replaceState = patchHistoryMethod("replaceState");
window.addEventListener(BxEvent.XCLOUD_SERVERS_UNAVAILABLE, (e) => { EventBus.Script.on("xcloudServerUnavailable", () => {
if (STATES.supportedRegion = !1, window.setTimeout(HeaderSection.watchHeader, 2000), document.querySelector("div[class^=UnsupportedMarketPage-module__container]")) SettingsDialog.getInstance().show(); if (EventBus.Script.off("xcloudServerUnavailable", null), STATES.supportedRegion = !1, window.setTimeout(HeaderSection.watchHeader, 2000), document.querySelector("div[class^=UnsupportedMarketPage-module__container]")) SettingsDialog.getInstance().show();
}, { once: !0 }); });
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, (e) => { EventBus.Script.on("xcloudServerReady", () => {
STATES.isSignedIn = !0, window.setTimeout(HeaderSection.watchHeader, 2000); STATES.isSignedIn = !0, window.setTimeout(HeaderSection.watchHeader, 2000);
}); });
window.addEventListener(BxEvent.STREAM_LOADING, (e) => { EventBus.Stream.on("stateLoading", () => {
if (window.location.pathname.includes("/launch/") && STATES.currentStream.titleInfo) STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title); if (window.location.pathname.includes("/launch/") && STATES.currentStream.titleInfo) STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title);
else STATES.currentStream.titleSlug = "remote-play"; else STATES.currentStream.titleSlug = "remote-play";
}); });
getPref("loadingScreen.gameArt.show") && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup); getPref("loadingScreen.gameArt.show") && EventBus.Script.on("titleInfoReady", LoadingScreen.setup);
window.addEventListener(BxEvent.STREAM_STARTING, (e) => { EventBus.Stream.on("stateStarting", () => {
LoadingScreen.hide(); LoadingScreen.hide();
}); });
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", (payload) => {
window.BX_STREAM_SETTINGS = StreamSettings.settings, StreamSettings.refreshAllSettings(), STATES.isPlaying = !0, StreamUiHandler.observe(), updateVideoPlayer(); window.BX_STREAM_SETTINGS = StreamSettings.settings, StreamSettings.refreshAllSettings(), STATES.isPlaying = !0, StreamUiHandler.observe(), updateVideoPlayer();
}); });
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, (e) => { EventBus.Stream.on("stateError", () => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit("stateStopped", {});
}); });
window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, (e) => { window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, (e) => {
let dataChannel = e.dataChannel; let dataChannel = e.dataChannel;
@ -6441,9 +6461,9 @@ function unload() {
if (!STATES.isPlaying) return; if (!STATES.isPlaying) return;
STATES.currentStream.streamPlayer?.destroy(), STATES.isPlaying = !1, STATES.currentStream = {}, window.BX_EXPOSED.shouldShowSensorControls = !1, window.BX_EXPOSED.stopTakRendering = !1, NavigationDialogManager.getInstance().hide(), StreamStats.getInstance().destroy(), StreamBadges.getInstance().destroy(); STATES.currentStream.streamPlayer?.destroy(), STATES.isPlaying = !1, STATES.currentStream = {}, window.BX_EXPOSED.shouldShowSensorControls = !1, window.BX_EXPOSED.stopTakRendering = !1, NavigationDialogManager.getInstance().hide(), StreamStats.getInstance().destroy(), StreamBadges.getInstance().destroy();
} }
window.addEventListener(BxEvent.STREAM_STOPPED, unload); EventBus.Stream.on("stateStopped", unload);
window.addEventListener("pagehide", (e) => { window.addEventListener("pagehide", (e) => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit("stateStopped", {});
}); });
function main() { function main() {
if (GhPagesUtils.fetchLatestCommit(), getPref("nativeMkb.mode") !== "off") { if (GhPagesUtils.fetchLatestCommit(), getPref("nativeMkb.mode") !== "off") {

View File

@ -143,7 +143,7 @@ function deepClone(obj) {
} }
var BxEvent; var BxEvent;
((BxEvent) => { ((BxEvent) => {
BxEvent.JUMP_BACK_IN_READY = "bx-jump-back-in-ready", BxEvent.POPSTATE = "bx-popstate", BxEvent.TITLE_INFO_READY = "bx-title-info-ready", BxEvent.SETTINGS_CHANGED = "bx-settings-changed", BxEvent.STREAM_LOADING = "bx-stream-loading", BxEvent.STREAM_STARTING = "bx-stream-starting", BxEvent.STREAM_STARTED = "bx-stream-started", BxEvent.STREAM_PLAYING = "bx-stream-playing", BxEvent.STREAM_STOPPED = "bx-stream-stopped", BxEvent.STREAM_ERROR_PAGE = "bx-stream-error-page", BxEvent.STREAM_WEBRTC_CONNECTED = "bx-stream-webrtc-connected", BxEvent.STREAM_WEBRTC_DISCONNECTED = "bx-stream-webrtc-disconnected", BxEvent.MKB_UPDATED = "bx-mkb-updated", BxEvent.KEYBOARD_SHORTCUTS_UPDATED = "bx-keyboard-shortcuts-updated", BxEvent.STREAM_SESSION_READY = "bx-stream-session-ready", BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED = "bx-custom-touch-layouts-loaded", BxEvent.TOUCH_LAYOUT_MANAGER_READY = "bx-touch-layout-manager-ready", BxEvent.REMOTE_PLAY_READY = "bx-remote-play-ready", BxEvent.REMOTE_PLAY_FAILED = "bx-remote-play-failed", BxEvent.XCLOUD_SERVERS_READY = "bx-servers-ready", BxEvent.XCLOUD_SERVERS_UNAVAILABLE = "bx-servers-unavailable", BxEvent.DATA_CHANNEL_CREATED = "bx-data-channel-created", BxEvent.DEVICE_VIBRATION_CHANGED = "bx-device-vibration-changed", BxEvent.GAME_BAR_ACTION_ACTIVATED = "bx-game-bar-action-activated", BxEvent.MICROPHONE_STATE_CHANGED = "bx-microphone-state-changed", BxEvent.SPEAKER_STATE_CHANGED = "bx-speaker-state-changed", BxEvent.VIDEO_VISIBILITY_CHANGED = "bx-video-visibility-changed", BxEvent.CAPTURE_SCREENSHOT = "bx-capture-screenshot", BxEvent.POINTER_LOCK_REQUESTED = "bx-pointer-lock-requested", BxEvent.POINTER_LOCK_EXITED = "bx-pointer-lock-exited", BxEvent.NAVIGATION_FOCUS_CHANGED = "bx-nav-focus-changed", BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED = "bx-gh-pages-force-native-mkb-updated", BxEvent.XCLOUD_DIALOG_SHOWN = "bx-xcloud-dialog-shown", BxEvent.XCLOUD_DIALOG_DISMISSED = "bx-xcloud-dialog-dismissed", BxEvent.XCLOUD_GUIDE_MENU_SHOWN = "bx-xcloud-guide-menu-shown", BxEvent.XCLOUD_POLLING_MODE_CHANGED = "bx-xcloud-polling-mode-changed", BxEvent.XCLOUD_RENDERING_COMPONENT = "bx-xcloud-rendering-component", BxEvent.XCLOUD_ROUTER_HISTORY_READY = "bx-xcloud-router-history-ready"; BxEvent.JUMP_BACK_IN_READY = "bx-jump-back-in-ready", BxEvent.POPSTATE = "bx-popstate", BxEvent.STREAM_SESSION_READY = "bx-stream-session-ready", BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED = "bx-custom-touch-layouts-loaded", BxEvent.TOUCH_LAYOUT_MANAGER_READY = "bx-touch-layout-manager-ready", BxEvent.REMOTE_PLAY_READY = "bx-remote-play-ready", BxEvent.REMOTE_PLAY_FAILED = "bx-remote-play-failed", BxEvent.DATA_CHANNEL_CREATED = "bx-data-channel-created", BxEvent.GAME_BAR_ACTION_ACTIVATED = "bx-game-bar-action-activated", BxEvent.MICROPHONE_STATE_CHANGED = "bx-microphone-state-changed", BxEvent.SPEAKER_STATE_CHANGED = "bx-speaker-state-changed", BxEvent.VIDEO_VISIBILITY_CHANGED = "bx-video-visibility-changed", BxEvent.CAPTURE_SCREENSHOT = "bx-capture-screenshot", BxEvent.POINTER_LOCK_REQUESTED = "bx-pointer-lock-requested", BxEvent.POINTER_LOCK_EXITED = "bx-pointer-lock-exited", BxEvent.NAVIGATION_FOCUS_CHANGED = "bx-nav-focus-changed", BxEvent.XCLOUD_DIALOG_SHOWN = "bx-xcloud-dialog-shown", BxEvent.XCLOUD_DIALOG_DISMISSED = "bx-xcloud-dialog-dismissed", BxEvent.XCLOUD_GUIDE_MENU_SHOWN = "bx-xcloud-guide-menu-shown", BxEvent.XCLOUD_POLLING_MODE_CHANGED = "bx-xcloud-polling-mode-changed", BxEvent.XCLOUD_RENDERING_COMPONENT = "bx-xcloud-rendering-component", BxEvent.XCLOUD_ROUTER_HISTORY_READY = "bx-xcloud-router-history-ready";
function dispatch(target, eventName, data) { function dispatch(target, eventName, data) {
if (!target) return; if (!target) return;
if (!eventName) { if (!eventName) {
@ -185,6 +185,33 @@ var GamepadKeyName = {
202: ["Right Stick Left", "↽"], 202: ["Right Stick Left", "↽"],
203: ["Right Stick Right", "⇁"] 203: ["Right Stick Right", "⇁"]
}; };
class EventBus {
listeners = new Map;
static Script = new EventBus;
static Stream = new EventBus;
on(event, callback) {
if (!this.listeners.has(event)) this.listeners.set(event, new Set);
this.listeners.get(event).add(callback), BX_FLAGS.Debug && BxLogger.warning("EventBus", "on", event, callback);
}
off(event, callback) {
if (BX_FLAGS.Debug && BxLogger.warning("EventBus", "off", event, callback), !callback) {
this.listeners.delete(event);
return;
}
let callbacks = this.listeners.get(event);
if (!callbacks) return;
if (callbacks.delete(callback), callbacks.size === 0) this.listeners.delete(event);
}
offAll() {
this.listeners.clear();
}
emit(event, payload) {
BX_FLAGS.Debug && BxLogger.warning("EventBus", "emit", event, payload);
let callbacks = this.listeners.get(event) || [];
for (let callback of callbacks)
callback(payload);
}
}
class GhPagesUtils { class GhPagesUtils {
static fetchLatestCommit() { static fetchLatestCommit() {
NATIVE_FETCH("https://api.github.com/repos/redphx/better-xcloud/branches/gh-pages", { NATIVE_FETCH("https://api.github.com/repos/redphx/better-xcloud/branches/gh-pages", {
@ -208,7 +235,7 @@ class GhPagesUtils {
static getNativeMkbCustomList(update = !1) { static getNativeMkbCustomList(update = !1) {
let key = "BetterXcloud.GhPages.ForceNativeMkb"; let key = "BetterXcloud.GhPages.ForceNativeMkb";
update && NATIVE_FETCH(GhPagesUtils.getUrl("native-mkb/ids.json")).then((response) => response.json()).then((json) => { update && NATIVE_FETCH(GhPagesUtils.getUrl("native-mkb/ids.json")).then((response) => response.json()).then((json) => {
if (json.$schemaVersion === 1) window.localStorage.setItem(key, JSON.stringify(json)), BxEvent.dispatch(window, BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED); if (json.$schemaVersion === 1) window.localStorage.setItem(key, JSON.stringify(json)), EventBus.Script.emit("listForcedNativeMkbUpdated", {});
}); });
let info = JSON.parse(window.localStorage.getItem(key) || "{}"); let info = JSON.parse(window.localStorage.getItem(key) || "{}");
if (info.$schemaVersion !== 1) return window.localStorage.removeItem(key), {}; if (info.$schemaVersion !== 1) return window.localStorage.removeItem(key), {};
@ -953,7 +980,7 @@ class BaseSettingsStore {
return this.settings[key]; return this.settings[key];
} }
setSetting(key, value, emitEvent = !1) { setSetting(key, value, emitEvent = !1) {
return value = this.validateValue("set", key, value), this.settings[key] = this.validateValue("get", key, value), this.saveSettings(), emitEvent && BxEvent.dispatch(window, BxEvent.SETTINGS_CHANGED, { return value = this.validateValue("set", key, value), this.settings[key] = this.validateValue("get", key, value), this.saveSettings(), emitEvent && EventBus.Script.emit("settingChanged", {
storageKey: this.storageKey, storageKey: this.storageKey,
settingKey: key, settingKey: key,
settingValue: value settingValue: value
@ -1587,7 +1614,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: [], default: [],
unsupported: !AppInterface && UserAgent.isMobile(), unsupported: !AppInterface && UserAgent.isMobile(),
ready: (setting) => { ready: (setting) => {
if (!setting.unsupported) setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(!0), window.addEventListener(BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED, (e) => { if (!setting.unsupported) setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(!0), EventBus.Script.on("listForcedNativeMkbUpdated", () => {
setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList(); setting.multipleOptions = GhPagesUtils.getNativeMkbCustomList();
}); });
}, },
@ -2250,7 +2277,7 @@ class StreamStatsCollector {
} catch (e) {} } catch (e) {}
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", () => {
StreamStatsCollector.getInstance().reset(); StreamStatsCollector.getInstance().reset();
}); });
} }
@ -2388,7 +2415,7 @@ class StreamStats {
this.refreshStyles(), document.documentElement.appendChild(this.$container); this.refreshStyles(), document.documentElement.appendChild(this.$container);
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", () => {
let PREF_STATS_QUICK_GLANCE = getPref("stats.quickGlance.enabled"), PREF_STATS_SHOW_WHEN_PLAYING = getPref("stats.showWhenPlaying"), streamStats = StreamStats.getInstance(); let PREF_STATS_QUICK_GLANCE = getPref("stats.quickGlance.enabled"), PREF_STATS_SHOW_WHEN_PLAYING = getPref("stats.showWhenPlaying"), streamStats = StreamStats.getInstance();
if (PREF_STATS_SHOW_WHEN_PLAYING) streamStats.start(); if (PREF_STATS_SHOW_WHEN_PLAYING) streamStats.start();
else if (PREF_STATS_QUICK_GLANCE) streamStats.quickGlanceSetup(), !PREF_STATS_SHOW_WHEN_PLAYING && streamStats.start(!0); else if (PREF_STATS_QUICK_GLANCE) streamStats.quickGlanceSetup(), !PREF_STATS_SHOW_WHEN_PLAYING && streamStats.start(!0);
@ -2688,7 +2715,7 @@ class StreamSettings {
if (!STATES.browser.capabilities.deviceVibration) return; if (!STATES.browser.capabilities.deviceVibration) return;
let mode = StreamSettings.getPref("deviceVibration.mode"), intensity = 0; let mode = StreamSettings.getPref("deviceVibration.mode"), intensity = 0;
if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = StreamSettings.getPref("deviceVibration.intensity") / 100; if (mode === "on" || mode === "auto" && !hasGamepad()) intensity = StreamSettings.getPref("deviceVibration.intensity") / 100;
StreamSettings.settings.deviceVibrationIntensity = intensity, BxEvent.dispatch(window, BxEvent.DEVICE_VIBRATION_CHANGED); StreamSettings.settings.deviceVibrationIntensity = intensity, EventBus.Script.emit("deviceVibrationUpdated", {});
} }
static async refreshMkbSettings() { static async refreshMkbSettings() {
let settings = StreamSettings.settings, presetId = StreamSettings.getPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = { let settings = StreamSettings.settings, presetId = StreamSettings.getPref("mkb.p1.preset.mappingId"), orgPreset = await MkbMappingPresetsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data, converted = {
@ -2702,12 +2729,12 @@ class StreamSettings {
if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex; if (typeof keyName === "string") converted.mapping[keyName] = buttonIndex;
} }
let mouse = converted.mouse; let mouse = converted.mouse;
mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setPref("mkb.p1.preset.mappingId", orgPreset.id), BxEvent.dispatch(window, BxEvent.MKB_UPDATED); mouse["sensitivityX"] *= 0.001, mouse["sensitivityY"] *= 0.001, mouse["deadzoneCounterweight"] *= 0.01, settings.mkbPreset = converted, setPref("mkb.p1.preset.mappingId", orgPreset.id), EventBus.Script.emit("mkbSettingUpdated", {});
} }
static async refreshKeyboardShortcuts() { static async refreshKeyboardShortcuts() {
let settings = StreamSettings.settings, presetId = StreamSettings.getPref("keyboardShortcuts.preset.inGameId"); let settings = StreamSettings.settings, presetId = StreamSettings.getPref("keyboardShortcuts.preset.inGameId");
if (presetId === 0) { if (presetId === 0) {
settings.keyboardShortcuts = null, setPref("keyboardShortcuts.preset.inGameId", presetId), BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); settings.keyboardShortcuts = null, setPref("keyboardShortcuts.preset.inGameId", presetId), EventBus.Script.emit("keyboardShortcutsUpdated", {});
return; return;
} }
let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action; let orgPreset = await KeyboardShortcutsTable.getInstance().getPreset(presetId), orgPresetData = orgPreset.data.mapping, converted = {}, action;
@ -2715,7 +2742,7 @@ class StreamSettings {
let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`; let info = orgPresetData[action], key = `${info.code}:${info.modifiers || 0}`;
converted[key] = action; converted[key] = action;
} }
settings.keyboardShortcuts = converted, setPref("keyboardShortcuts.preset.inGameId", orgPreset.id), BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); settings.keyboardShortcuts = converted, setPref("keyboardShortcuts.preset.inGameId", orgPreset.id), EventBus.Script.emit("keyboardShortcutsUpdated", {});
} }
static async refreshAllSettings() { static async refreshAllSettings() {
window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts(); window.BX_STREAM_SETTINGS = StreamSettings.settings, await StreamSettings.refreshControllerSettings(), await StreamSettings.refreshMkbSettings(), await StreamSettings.refreshKeyboardShortcuts();
@ -2742,7 +2769,7 @@ class MkbPopup {
$btnActivate; $btnActivate;
mkbHandler; mkbHandler;
constructor() { constructor() {
this.render(), window.addEventListener(BxEvent.KEYBOARD_SHORTCUTS_UPDATED, (e) => { this.render(), EventBus.Script.on("keyboardShortcutsUpdated", () => {
let $newButton = this.createActivateButton(); let $newButton = this.createActivateButton();
this.$btnActivate.replaceWith($newButton), this.$btnActivate = $newButton; this.$btnActivate.replaceWith($newButton), this.$btnActivate = $newButton;
}); });
@ -3237,11 +3264,11 @@ class EmulatedMkbHandler extends MkbHandler {
this.waitForMouseData(!0), this.mouseDataProvider?.stop(); this.waitForMouseData(!0), this.mouseDataProvider?.stop();
} }
static setupEvents() { static setupEvents() {
if (window.addEventListener(BxEvent.STREAM_PLAYING, () => { if (EventBus.Stream.on("statePlaying", () => {
if (STATES.currentStream.titleInfo?.details.hasMkbSupport) NativeMkbHandler.getInstance()?.init(); if (STATES.currentStream.titleInfo?.details.hasMkbSupport) NativeMkbHandler.getInstance()?.init();
else EmulatedMkbHandler.getInstance()?.init(); else EmulatedMkbHandler.getInstance()?.init();
}), EmulatedMkbHandler.isAllowed()) }), EmulatedMkbHandler.isAllowed())
window.addEventListener(BxEvent.MKB_UPDATED, () => { EventBus.Script.on("mkbSettingUpdated", () => {
EmulatedMkbHandler.getInstance()?.refreshPresetData(); EmulatedMkbHandler.getInstance()?.refreshPresetData();
}); });
} }
@ -4756,7 +4783,6 @@ class BxNumberStepper extends HTMLInputElement {
$btnInc; $btnInc;
$btnDec; $btnDec;
$range; $range;
onInput;
onRangeInput; onRangeInput;
onClick; onClick;
onPointerUp; onPointerUp;
@ -4783,7 +4809,7 @@ class BxNumberStepper extends HTMLInputElement {
class: options.hideSlider ? "bx-focusable" : "", class: options.hideSlider ? "bx-focusable" : "",
tabindex: options.hideSlider ? 0 : -1 tabindex: options.hideSlider ? 0 : -1
}, "+"))); }, "+")));
if (self.$text = $text, self.$btnInc = $btnInc, self.$btnDec = $btnDec, self.onChange = onChange, self.onInput = BxNumberStepper.onInput.bind(self), self.onRangeInput = BxNumberStepper.onRangeInput.bind(self), self.onClick = BxNumberStepper.onClick.bind(self), self.onPointerUp = BxNumberStepper.onPointerUp.bind(self), self.onPointerDown = BxNumberStepper.onPointerDown.bind(self), self.controlMin = min, self.controlMax = max, self.isHolding = !1, self.options = options, self.uiMin = options.reverse ? -max : min, self.uiMax = options.reverse ? -min : max, self.steps = Math.max(options.steps || 1, 1), BxNumberStepper.setValue.call(self, value), options.disabled) return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), self.disabled = !0, self; if (self.$text = $text, self.$btnInc = $btnInc, self.$btnDec = $btnDec, self.onChange = onChange, self.onRangeInput = BxNumberStepper.onRangeInput.bind(self), self.onClick = BxNumberStepper.onClick.bind(self), self.onPointerUp = BxNumberStepper.onPointerUp.bind(self), self.onPointerDown = BxNumberStepper.onPointerDown.bind(self), self.controlMin = min, self.controlMax = max, self.isHolding = !1, self.options = options, self.uiMin = options.reverse ? -max : min, self.uiMax = options.reverse ? -min : max, self.steps = Math.max(options.steps || 1, 1), BxNumberStepper.setValue.call(self, value), options.disabled) return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), self.disabled = !0, self;
if ($range = CE("input", { if ($range = CE("input", {
id: `bx_inp_setting_${key}`, id: `bx_inp_setting_${key}`,
type: "range", type: "range",
@ -4792,7 +4818,7 @@ class BxNumberStepper extends HTMLInputElement {
value: options.reverse ? -value : value, value: options.reverse ? -value : value,
step: self.steps, step: self.steps,
tabindex: 0 tabindex: 0
}), self.$range = $range, options.hideSlider && $range.classList.add("bx-gone"), $range.addEventListener("input", self.onRangeInput), self.addEventListener("input", self.onInput), self.appendChild($range), options.ticks || options.exactTicks) { }), self.$range = $range, options.hideSlider && $range.classList.add("bx-gone"), self.addEventListener("input", self.onRangeInput), self.appendChild($range), options.ticks || options.exactTicks) {
let markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId }); let markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId });
if ($range.setAttribute("list", markersId), options.exactTicks) { if ($range.setAttribute("list", markersId), options.exactTicks) {
let start = Math.max(Math.floor(min / options.exactTicks), 1) * options.exactTicks; let start = Math.max(Math.floor(min / options.exactTicks), 1) * options.exactTicks;
@ -4823,9 +4849,6 @@ class BxNumberStepper extends HTMLInputElement {
static normalizeValue(value) { static normalizeValue(value) {
return value = parseInt(value), value = Math.max(this.controlMin, value), value = Math.min(this.controlMax, value), value; return value = parseInt(value), value = Math.max(this.controlMin, value), value = Math.min(this.controlMax, value), value;
} }
static onInput(e) {
BxEvent.dispatch(this.$range, "input");
}
static onRangeInput(e) { static onRangeInput(e) {
let value = parseInt(e.target.value); let value = parseInt(e.target.value);
if (this.options.reverse) value *= -1; if (this.options.reverse) value *= -1;
@ -6116,12 +6139,9 @@ class SettingsDialog extends NavigationDialog {
}, },
onCreated: (setting, $elm) => { onCreated: (setting, $elm) => {
let $range = $elm.querySelector("input[type=range"); let $range = $elm.querySelector("input[type=range");
window.addEventListener(BxEvent.SETTINGS_CHANGED, (e) => { EventBus.Script.on("settingChanged", (payload) => {
let { storageKey, settingKey, settingValue } = e; let { storageKey, settingKey, settingValue } = payload;
if (storageKey !== "BetterXcloud" || settingKey !== "audio.volume") return; if (storageKey === "BetterXcloud" && settingKey === "audio.volume") $range.value = settingValue, BxEvent.dispatch($range, "input", { ignoreOnChange: !0 });
$range.value = settingValue, BxEvent.dispatch($range, "input", {
ignoreOnChange: !0
});
}); });
} }
}] }]
@ -6968,7 +6988,7 @@ var BxExposed = {
if (touchControllerAvailability === "off") supportedInputTypes = supportedInputTypes.filter((i) => i !== "CustomTouchOverlay" && i !== "GenericTouch"), titleInfo.details.supportedTabs = []; if (touchControllerAvailability === "off") supportedInputTypes = supportedInputTypes.filter((i) => i !== "CustomTouchOverlay" && i !== "GenericTouch"), titleInfo.details.supportedTabs = [];
if (titleInfo.details.hasNativeTouchSupport = supportedInputTypes.includes("NativeTouch"), titleInfo.details.hasTouchSupport = titleInfo.details.hasNativeTouchSupport || supportedInputTypes.includes("CustomTouchOverlay") || supportedInputTypes.includes("GenericTouch"), !titleInfo.details.hasTouchSupport && touchControllerAvailability === "all") titleInfo.details.hasFakeTouchSupport = !0, supportedInputTypes.push("GenericTouch"); if (titleInfo.details.hasNativeTouchSupport = supportedInputTypes.includes("NativeTouch"), titleInfo.details.hasTouchSupport = titleInfo.details.hasNativeTouchSupport || supportedInputTypes.includes("CustomTouchOverlay") || supportedInputTypes.includes("GenericTouch"), !titleInfo.details.hasTouchSupport && touchControllerAvailability === "all") titleInfo.details.hasFakeTouchSupport = !0, supportedInputTypes.push("GenericTouch");
} }
return titleInfo.details.supportedInputTypes = supportedInputTypes, STATES.currentStream.titleInfo = titleInfo, BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY), titleInfo; return titleInfo.details.supportedInputTypes = supportedInputTypes, STATES.currentStream.titleInfo = titleInfo, EventBus.Script.emit("titleInfoReady", {}), titleInfo;
}, },
setupGainNode: ($media, audioStream) => { setupGainNode: ($media, audioStream) => {
if ($media instanceof HTMLAudioElement) $media.muted = !0, $media.addEventListener("playing", (e) => { if ($media instanceof HTMLAudioElement) $media.muted = !0, $media.addEventListener("playing", (e) => {
@ -7290,7 +7310,7 @@ class XhomeInterceptor {
return NATIVE_FETCH(request); return NATIVE_FETCH(request);
} }
static async handleConfiguration(request) { static async handleConfiguration(request) {
BxEvent.dispatch(window, BxEvent.STREAM_STARTING); EventBus.Stream.emit("stateStarting", {});
let response = await NATIVE_FETCH(request), obj = await response.clone().json(), serverDetails = obj.serverDetails, pairs = [ let response = await NATIVE_FETCH(request), obj = await response.clone().json(), serverDetails = obj.serverDetails, pairs = [
["ipAddress", "port"], ["ipAddress", "port"],
["ipV4Address", "ipV4Port"], ["ipV4Address", "ipV4Port"],
@ -7335,7 +7355,7 @@ class XhomeInterceptor {
}), NATIVE_FETCH(request); }), NATIVE_FETCH(request);
} }
static async handlePlay(request) { static async handlePlay(request) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING); EventBus.Stream.emit("stateLoading", {});
let body = await request.clone().json(), newRequest = new Request(request, { let body = await request.clone().json(), newRequest = new Request(request, {
body: JSON.stringify(body) body: JSON.stringify(body)
}); });
@ -7744,7 +7764,7 @@ class XcloudInterceptor {
ip && request.headers.set("X-Forwarded-For", ip); ip && request.headers.set("X-Forwarded-For", ip);
} }
let response = await NATIVE_FETCH(request, init); let response = await NATIVE_FETCH(request, init);
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response; if (response.status !== 200) return EventBus.Script.emit("xcloudServerUnavailable", {}), response;
let obj = await response.clone().json(); let obj = await response.clone().json();
RemotePlayManager.getInstance()?.setXcloudToken(obj.gsToken); RemotePlayManager.getInstance()?.setXcloudToken(obj.gsToken);
let serverRegex = /\/\/(\w+)\./, serverExtra = XcloudInterceptor.SERVER_EXTRA_INFO, region; let serverRegex = /\/\/(\w+)\./, serverExtra = XcloudInterceptor.SERVER_EXTRA_INFO, region;
@ -7756,7 +7776,7 @@ class XcloudInterceptor {
else region.contintent = "other"; else region.contintent = "other";
region.shortName = shortName.toUpperCase(), STATES.serverRegions[region.name] = Object.assign({}, region); region.shortName = shortName.toUpperCase(), STATES.serverRegions[region.name] = Object.assign({}, region);
} }
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_READY); EventBus.Script.emit("xcloudServerReady", {});
let preferredRegion = getPreferredServerRegion(); let preferredRegion = getPreferredServerRegion();
if (preferredRegion && preferredRegion in STATES.serverRegions) { if (preferredRegion && preferredRegion in STATES.serverRegions) {
let tmp = Object.assign({}, STATES.serverRegions[preferredRegion]); let tmp = Object.assign({}, STATES.serverRegions[preferredRegion]);
@ -7765,7 +7785,7 @@ class XcloudInterceptor {
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response; return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
} }
static async handlePlay(request, init) { static async handlePlay(request, init) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING); EventBus.Stream.emit("stateLoading", {});
let PREF_STREAM_TARGET_RESOLUTION = getPref("stream.video.resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream.locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url), badgeRegion = parsedUrl.host.split(".", 1)[0]; let PREF_STREAM_TARGET_RESOLUTION = getPref("stream.video.resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream.locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url), badgeRegion = parsedUrl.host.split(".", 1)[0];
for (let regionName in STATES.serverRegions) { for (let regionName in STATES.serverRegions) {
let region = STATES.serverRegions[regionName]; let region = STATES.serverRegions[regionName];
@ -7803,7 +7823,7 @@ class XcloudInterceptor {
else TouchController.enable(); else TouchController.enable();
let response = await NATIVE_FETCH(request, init), text = await response.clone().text(); let response = await NATIVE_FETCH(request, init), text = await response.clone().text();
if (!text.length) return response; if (!text.length) return response;
BxEvent.dispatch(window, BxEvent.STREAM_STARTING); EventBus.Stream.emit("stateStarting", {});
let obj = JSON.parse(text), overrides = JSON.parse(obj.clientStreamingConfigOverrides || "{}") || {}; let obj = JSON.parse(text), overrides = JSON.parse(obj.clientStreamingConfigOverrides || "{}") || {};
overrides.inputConfiguration = overrides.inputConfiguration || {}, overrides.inputConfiguration.enableVibration = !0; overrides.inputConfiguration = overrides.inputConfiguration || {}, overrides.inputConfiguration.enableVibration = !0;
let overrideMkb = null; let overrideMkb = null;
@ -8070,7 +8090,7 @@ function onHistoryChanged(e) {
window.setTimeout(RemotePlayManager.detect, 10); window.setTimeout(RemotePlayManager.detect, 10);
let $settings = document.querySelector(".bx-settings-container"); let $settings = document.querySelector(".bx-settings-container");
if ($settings) $settings.classList.add("bx-gone"); if ($settings) $settings.classList.add("bx-gone");
NavigationDialogManager.getInstance().hide(), LoadingScreen.reset(), window.setTimeout(HeaderSection.watchHeader, 2000), BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); NavigationDialogManager.getInstance().hide(), LoadingScreen.reset(), window.setTimeout(HeaderSection.watchHeader, 2000), EventBus.Stream.emit("stateStopped", {});
} }
function setCodecPreferences(sdp, preferredCodec) { function setCodecPreferences(sdp, preferredCodec) {
let h264Pattern = /a=fmtp:(\d+).*profile-level-id=([0-9a-f]{6})/g, profilePrefix = preferredCodec === "high" ? "4d" : preferredCodec === "low" ? "420" : "42e", preferredCodecIds = [], matches = sdp.matchAll(h264Pattern) || []; let h264Pattern = /a=fmtp:(\d+).*profile-level-id=([0-9a-f]{6})/g, profilePrefix = preferredCodec === "high" ? "4d" : preferredCodec === "low" ? "420" : "42e", preferredCodecIds = [], matches = sdp.matchAll(h264Pattern) || [];
@ -8427,7 +8447,7 @@ function patchVideoApi() {
contrast: getPref("video.contrast"), contrast: getPref("video.contrast"),
brightness: getPref("video.brightness") brightness: getPref("video.brightness")
}; };
STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref("video.player.type"), playerOptions), BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, { STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref("video.player.type"), playerOptions), EventBus.Stream.emit("statePlaying", {
$video: this $video: this
}); });
}, nativePlay = HTMLMediaElement.prototype.play; }, nativePlay = HTMLMediaElement.prototype.play;
@ -8983,7 +9003,7 @@ class StreamUiHandler {
if (!($elm instanceof HTMLElement)) return; if (!($elm instanceof HTMLElement)) return;
let className = $elm.className || ""; let className = $elm.className || "";
if (className.includes("PureErrorPage")) { if (className.includes("PureErrorPage")) {
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE); EventBus.Stream.emit("stateError", {});
return; return;
} }
if (className.startsWith("StreamMenu-module__container")) { if (className.startsWith("StreamMenu-module__container")) {
@ -9111,9 +9131,7 @@ class DeviceVibrationManager {
this.boundOnMessage = this.onMessage.bind(this), window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, (e) => { this.boundOnMessage = this.onMessage.bind(this), window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, (e) => {
let dataChannel = e.dataChannel; let dataChannel = e.dataChannel;
if (dataChannel?.label === "input") this.reset(), this.dataChannel = dataChannel, this.setupDataChannel(); if (dataChannel?.label === "input") this.reset(), this.dataChannel = dataChannel, this.setupDataChannel();
}), window.addEventListener(BxEvent.DEVICE_VIBRATION_CHANGED, (e) => { }), EventBus.Script.on("deviceVibrationUpdated", () => this.setupDataChannel());
this.setupDataChannel();
});
} }
setupDataChannel() { setupDataChannel() {
if (!this.dataChannel) return; if (!this.dataChannel) return;
@ -9203,37 +9221,37 @@ window.addEventListener(BxEvent.POPSTATE, onHistoryChanged);
window.addEventListener("popstate", onHistoryChanged); window.addEventListener("popstate", onHistoryChanged);
window.history.pushState = patchHistoryMethod("pushState"); window.history.pushState = patchHistoryMethod("pushState");
window.history.replaceState = patchHistoryMethod("replaceState"); window.history.replaceState = patchHistoryMethod("replaceState");
window.addEventListener(BxEvent.XCLOUD_SERVERS_UNAVAILABLE, (e) => { EventBus.Script.on("xcloudServerUnavailable", () => {
if (STATES.supportedRegion = !1, window.setTimeout(HeaderSection.watchHeader, 2000), document.querySelector("div[class^=UnsupportedMarketPage-module__container]")) SettingsDialog.getInstance().show(); if (EventBus.Script.off("xcloudServerUnavailable", null), STATES.supportedRegion = !1, window.setTimeout(HeaderSection.watchHeader, 2000), document.querySelector("div[class^=UnsupportedMarketPage-module__container]")) SettingsDialog.getInstance().show();
}, { once: !0 }); });
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, (e) => { EventBus.Script.on("xcloudServerReady", () => {
STATES.isSignedIn = !0, window.setTimeout(HeaderSection.watchHeader, 2000); STATES.isSignedIn = !0, window.setTimeout(HeaderSection.watchHeader, 2000);
}); });
window.addEventListener(BxEvent.STREAM_LOADING, (e) => { EventBus.Stream.on("stateLoading", () => {
if (window.location.pathname.includes("/launch/") && STATES.currentStream.titleInfo) STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title); if (window.location.pathname.includes("/launch/") && STATES.currentStream.titleInfo) STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title);
else STATES.currentStream.titleSlug = "remote-play"; else STATES.currentStream.titleSlug = "remote-play";
}); });
getPref("loadingScreen.gameArt.show") && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup); getPref("loadingScreen.gameArt.show") && EventBus.Script.on("titleInfoReady", LoadingScreen.setup);
window.addEventListener(BxEvent.STREAM_STARTING, (e) => { EventBus.Stream.on("stateStarting", () => {
LoadingScreen.hide(); LoadingScreen.hide();
{ {
let cursorHider = MouseCursorHider.getInstance(); let cursorHider = MouseCursorHider.getInstance();
if (cursorHider) cursorHider.start(), cursorHider.hide(); if (cursorHider) cursorHider.start(), cursorHider.hide();
} }
}); });
window.addEventListener(BxEvent.STREAM_PLAYING, (e) => { EventBus.Stream.on("statePlaying", (payload) => {
window.BX_STREAM_SETTINGS = StreamSettings.settings, StreamSettings.refreshAllSettings(), STATES.isPlaying = !0, StreamUiHandler.observe(); window.BX_STREAM_SETTINGS = StreamSettings.settings, StreamSettings.refreshAllSettings(), STATES.isPlaying = !0, StreamUiHandler.observe();
{ {
let gameBar = GameBar.getInstance(); let gameBar = GameBar.getInstance();
if (gameBar) gameBar.reset(), gameBar.enable(), gameBar.showBar(); if (gameBar) gameBar.reset(), gameBar.enable(), gameBar.showBar();
KeyboardShortcutHandler.getInstance().start(); KeyboardShortcutHandler.getInstance().start();
let $video = e.$video; let $video = payload.$video;
ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight), getPref("localCoOp.enabled") && BxExposed.toggleLocalCoOp(getPref("localCoOp.enabled")); ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight), getPref("localCoOp.enabled") && BxExposed.toggleLocalCoOp(getPref("localCoOp.enabled"));
} }
updateVideoPlayer(); updateVideoPlayer();
}); });
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, (e) => { EventBus.Stream.on("stateError", () => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit("stateStopped", {});
}); });
window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, (e) => { window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, (e) => {
if (e.component === "product-detail") ProductDetailsPage.injectButtons(); if (e.component === "product-detail") ProductDetailsPage.injectButtons();
@ -9258,9 +9276,9 @@ function unload() {
if (!STATES.isPlaying) return; if (!STATES.isPlaying) return;
KeyboardShortcutHandler.getInstance().stop(), EmulatedMkbHandler.getInstance()?.destroy(), NativeMkbHandler.getInstance()?.destroy(), DeviceVibrationManager.getInstance()?.reset(), STATES.currentStream.streamPlayer?.destroy(), STATES.isPlaying = !1, STATES.currentStream = {}, window.BX_EXPOSED.shouldShowSensorControls = !1, window.BX_EXPOSED.stopTakRendering = !1, NavigationDialogManager.getInstance().hide(), StreamStats.getInstance().destroy(), StreamBadges.getInstance().destroy(), MouseCursorHider.getInstance()?.stop(), TouchController.reset(), GameBar.getInstance()?.disable(); KeyboardShortcutHandler.getInstance().stop(), EmulatedMkbHandler.getInstance()?.destroy(), NativeMkbHandler.getInstance()?.destroy(), DeviceVibrationManager.getInstance()?.reset(), STATES.currentStream.streamPlayer?.destroy(), STATES.isPlaying = !1, STATES.currentStream = {}, window.BX_EXPOSED.shouldShowSensorControls = !1, window.BX_EXPOSED.stopTakRendering = !1, NavigationDialogManager.getInstance().hide(), StreamStats.getInstance().destroy(), StreamBadges.getInstance().destroy(), MouseCursorHider.getInstance()?.stop(), TouchController.reset(), GameBar.getInstance()?.disable();
} }
window.addEventListener(BxEvent.STREAM_STOPPED, unload); EventBus.Stream.on("stateStopped", unload);
window.addEventListener("pagehide", (e) => { window.addEventListener("pagehide", (e) => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit("stateStopped", {});
}); });
window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, (e) => { window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, (e) => {
ScreenshotManager.getInstance().takeScreenshot(); ScreenshotManager.getInstance().takeScreenshot();

View File

@ -44,6 +44,7 @@ import { StreamSettings } from "./utils/stream-settings";
import { KeyboardShortcutHandler } from "./modules/mkb/keyboard-shortcut-handler"; import { KeyboardShortcutHandler } from "./modules/mkb/keyboard-shortcut-handler";
import { GhPagesUtils } from "./utils/gh-pages"; import { GhPagesUtils } from "./utils/gh-pages";
import { DeviceVibrationManager } from "./modules/device-vibration-manager"; import { DeviceVibrationManager } from "./modules/device-vibration-manager";
import { EventBus } from "./utils/event-bus";
// Handle login page // Handle login page
if (window.location.pathname.includes('/auth/msa')) { if (window.location.pathname.includes('/auth/msa')) {
@ -190,7 +191,9 @@ window.addEventListener('popstate', onHistoryChanged);
window.history.pushState = patchHistoryMethod('pushState'); window.history.pushState = patchHistoryMethod('pushState');
window.history.replaceState = patchHistoryMethod('replaceState'); window.history.replaceState = patchHistoryMethod('replaceState');
window.addEventListener(BxEvent.XCLOUD_SERVERS_UNAVAILABLE, e => { EventBus.Script.on('xcloudServerUnavailable', () => {
EventBus.Script.off('xcloudServerUnavailable', null);
STATES.supportedRegion = false; STATES.supportedRegion = false;
window.setTimeout(HeaderSection.watchHeader, 2000); window.setTimeout(HeaderSection.watchHeader, 2000);
@ -199,14 +202,14 @@ window.addEventListener(BxEvent.XCLOUD_SERVERS_UNAVAILABLE, e => {
if ($unsupportedPage) { if ($unsupportedPage) {
SettingsDialog.getInstance().show(); SettingsDialog.getInstance().show();
} }
}, { once: true }); });
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, e => { EventBus.Script.on('xcloudServerReady', () => {
STATES.isSignedIn = true; STATES.isSignedIn = true;
window.setTimeout(HeaderSection.watchHeader, 2000); window.setTimeout(HeaderSection.watchHeader, 2000);
}); });
window.addEventListener(BxEvent.STREAM_LOADING, e => { EventBus.Stream.on('stateLoading', () => {
// Get title ID for screenshot's name // Get title ID for screenshot's name
if (window.location.pathname.includes('/launch/') && STATES.currentStream.titleInfo) { if (window.location.pathname.includes('/launch/') && STATES.currentStream.titleInfo) {
STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title); STATES.currentStream.titleSlug = productTitleToSlug(STATES.currentStream.titleInfo.product.title);
@ -216,9 +219,9 @@ window.addEventListener(BxEvent.STREAM_LOADING, e => {
}); });
// Setup loading screen // Setup loading screen
getPref(PrefKey.LOADING_SCREEN_GAME_ART) && window.addEventListener(BxEvent.TITLE_INFO_READY, LoadingScreen.setup); getPref(PrefKey.LOADING_SCREEN_GAME_ART) && EventBus.Script.on('titleInfoReady', LoadingScreen.setup);
window.addEventListener(BxEvent.STREAM_STARTING, e => { EventBus.Stream.on('stateStarting', () => {
// Hide loading screen // Hide loading screen
LoadingScreen.hide(); LoadingScreen.hide();
@ -232,7 +235,7 @@ window.addEventListener(BxEvent.STREAM_STARTING, e => {
} }
}); });
window.addEventListener(BxEvent.STREAM_PLAYING, e => { EventBus.Stream.on('statePlaying', payload => {
window.BX_STREAM_SETTINGS = StreamSettings.settings; window.BX_STREAM_SETTINGS = StreamSettings.settings;
StreamSettings.refreshAllSettings(); StreamSettings.refreshAllSettings();
@ -251,7 +254,7 @@ window.addEventListener(BxEvent.STREAM_PLAYING, e => {
KeyboardShortcutHandler.getInstance().start(); KeyboardShortcutHandler.getInstance().start();
// Setup screenshot // Setup screenshot
const $video = (e as any).$video as HTMLVideoElement; const $video = payload.$video as HTMLVideoElement;
ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight); ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight);
// Setup local co-op // Setup local co-op
@ -262,8 +265,8 @@ window.addEventListener(BxEvent.STREAM_PLAYING, e => {
updateVideoPlayer(); updateVideoPlayer();
}); });
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, e => { EventBus.Stream.on('stateError', () => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit('stateStopped', {});
}); });
isFullVersion() && window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, e => { isFullVersion() && window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, e => {
@ -340,9 +343,9 @@ function unload() {
} }
} }
window.addEventListener(BxEvent.STREAM_STOPPED, unload); EventBus.Stream.on('stateStopped', unload);
window.addEventListener('pagehide', e => { window.addEventListener('pagehide', e => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit('stateStopped', {});
}); });
isFullVersion() && window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, e => { isFullVersion() && window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, e => {

View File

@ -1,6 +1,7 @@
import { AppInterface, STATES } from "@utils/global"; import { AppInterface, STATES } from "@utils/global";
import { BxEvent } from "@utils/bx-event"; import { BxEvent } from "@utils/bx-event";
import { StreamSettings } from "@/utils/stream-settings"; import { StreamSettings } from "@/utils/stream-settings";
import { EventBus } from "@/utils/event-bus";
const VIBRATION_DATA_MAP = { const VIBRATION_DATA_MAP = {
gamepadIndex: 8, gamepadIndex: 8,
@ -47,9 +48,7 @@ export class DeviceVibrationManager {
} }
}); });
window.addEventListener(BxEvent.DEVICE_VIBRATION_CHANGED, e => { EventBus.Script.on('deviceVibrationUpdated', () => this.setupDataChannel());
this.setupDataChannel();
});
} }
private setupDataChannel() { private setupDataChannel() {

View File

@ -18,6 +18,7 @@ import { MkbPopup } from "./mkb-popup";
import type { MkbConvertedPresetData } from "@/types/presets"; import type { MkbConvertedPresetData } from "@/types/presets";
import { StreamSettings } from "@/utils/stream-settings"; import { StreamSettings } from "@/utils/stream-settings";
import { ShortcutAction } from "@/enums/shortcut-actions"; import { ShortcutAction } from "@/enums/shortcut-actions";
import { EventBus } from "@/utils/event-bus";
const PointerToMouseButton = { const PointerToMouseButton = {
1: 0, 1: 0,
@ -639,7 +640,7 @@ export class EmulatedMkbHandler extends MkbHandler {
static setupEvents() { static setupEvents() {
if (isFullVersion()) { if (isFullVersion()) {
window.addEventListener(BxEvent.STREAM_PLAYING, () => { EventBus.Stream.on('statePlaying', () => {
if (STATES.currentStream.titleInfo?.details.hasMkbSupport) { if (STATES.currentStream.titleInfo?.details.hasMkbSupport) {
// Enable native MKB in Android app // Enable native MKB in Android app
NativeMkbHandler.getInstance()?.init(); NativeMkbHandler.getInstance()?.init();
@ -649,7 +650,7 @@ export class EmulatedMkbHandler extends MkbHandler {
}); });
if (EmulatedMkbHandler.isAllowed()) { if (EmulatedMkbHandler.isAllowed()) {
window.addEventListener(BxEvent.MKB_UPDATED, () => { EventBus.Script.on('mkbSettingUpdated', () => {
EmulatedMkbHandler.getInstance()?.refreshPresetData(); EmulatedMkbHandler.getInstance()?.refreshPresetData();
}); });
} }

View File

@ -1,12 +1,12 @@
import { CE, createButton, ButtonStyle, type BxButtonOptions } from "@/utils/html"; import { CE, createButton, ButtonStyle, type BxButtonOptions } from "@/utils/html";
import { t } from "@/utils/translation"; import { t } from "@/utils/translation";
import { BxEvent } from "@/utils/bx-event";
import { ShortcutAction } from "@/enums/shortcut-actions"; import { ShortcutAction } from "@/enums/shortcut-actions";
import { SettingsDialog } from "../ui/dialog/settings-dialog"; import { SettingsDialog } from "../ui/dialog/settings-dialog";
import type { MkbHandler } from "./base-mkb-handler"; import type { MkbHandler } from "./base-mkb-handler";
import { NativeMkbHandler } from "./native-mkb-handler"; import { NativeMkbHandler } from "./native-mkb-handler";
import { StreamSettings } from "@/utils/stream-settings"; import { StreamSettings } from "@/utils/stream-settings";
import { KeyHelper } from "./key-helper"; import { KeyHelper } from "./key-helper";
import { EventBus } from "@/utils/event-bus";
type MkbPopupType = 'virtual' | 'native'; type MkbPopupType = 'virtual' | 'native';
@ -24,7 +24,7 @@ export class MkbPopup {
constructor() { constructor() {
this.render(); this.render();
window.addEventListener(BxEvent.KEYBOARD_SHORTCUTS_UPDATED, e => { EventBus.Script.on('keyboardShortcutsUpdated', () => {
const $newButton = this.createActivateButton(); const $newButton = this.createActivateButton();
this.$btnActivate.replaceWith($newButton); this.$btnActivate.replaceWith($newButton);
this.$btnActivate = $newButton; this.$btnActivate = $newButton;

View File

@ -1,4 +1,3 @@
import { BxEvent } from "@utils/bx-event"
import { CE } from "@utils/html" import { CE } from "@utils/html"
import { t } from "@utils/translation" import { t } from "@utils/translation"
import { STATES } from "@utils/global" import { STATES } from "@utils/global"
@ -7,6 +6,7 @@ import { getPref } from "@/utils/settings-storages/global-settings-storage"
import { StreamStatsCollector, type StreamStatGrade } from "@/utils/stream-stats-collector" import { StreamStatsCollector, type StreamStatGrade } from "@/utils/stream-stats-collector"
import { BxLogger } from "@/utils/bx-logger" import { BxLogger } from "@/utils/bx-logger"
import { StreamStat } from "@/enums/pref-values" import { StreamStat } from "@/enums/pref-values"
import { EventBus } from "@/utils/event-bus"
export class StreamStats { export class StreamStats {
@ -230,7 +230,7 @@ export class StreamStats {
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, e => { EventBus.Stream.on('statePlaying', () => {
const PREF_STATS_QUICK_GLANCE = getPref(PrefKey.STATS_QUICK_GLANCE_ENABLED); const PREF_STATS_QUICK_GLANCE = getPref(PrefKey.STATS_QUICK_GLANCE_ENABLED);
const PREF_STATS_SHOW_WHEN_PLAYING = getPref(PrefKey.STATS_SHOW_WHEN_PLAYING); const PREF_STATS_SHOW_WHEN_PLAYING = getPref(PrefKey.STATS_SHOW_WHEN_PLAYING);

View File

@ -1,11 +1,11 @@
import { STATES } from "@utils/global.ts"; import { STATES } from "@utils/global.ts";
import { createSvgIcon } from "@utils/html.ts"; import { createSvgIcon } from "@utils/html.ts";
import { BxIcon } from "@utils/bx-icon"; import { BxIcon } from "@utils/bx-icon";
import { BxEvent } from "@utils/bx-event.ts";
import { t } from "@utils/translation.ts"; import { t } from "@utils/translation.ts";
import { StreamBadges } from "./stream-badges.ts"; import { StreamBadges } from "./stream-badges.ts";
import { StreamStats } from "./stream-stats.ts"; import { StreamStats } from "./stream-stats.ts";
import { SettingsDialog } from "../ui/dialog/settings-dialog.ts"; import { SettingsDialog } from "../ui/dialog/settings-dialog.ts";
import { EventBus } from "@/utils/event-bus.ts";
export class StreamUiHandler { export class StreamUiHandler {
@ -243,7 +243,7 @@ export class StreamUiHandler {
// Error Page: .PureErrorPage.ErrorScreen // Error Page: .PureErrorPage.ErrorScreen
if (className.includes('PureErrorPage')) { if (className.includes('PureErrorPage')) {
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE); EventBus.Stream.emit('stateError', {});
return; return;
} }

View File

@ -30,6 +30,7 @@ import { SuggestionsSetting } from "./settings/suggestions";
import { StreamSettings } from "@/utils/stream-settings"; import { StreamSettings } from "@/utils/stream-settings";
import { MkbExtraSettings } from "./settings/mkb-extra"; import { MkbExtraSettings } from "./settings/mkb-extra";
import { BxExposed } from "@/utils/bx-exposed"; import { BxExposed } from "@/utils/bx-exposed";
import { EventBus } from "@/utils/event-bus";
type SettingTabSectionItem = Partial<{ type SettingTabSectionItem = Partial<{
@ -434,16 +435,13 @@ export class SettingsDialog extends NavigationDialog {
}, },
onCreated: (setting: SettingTabSectionItem, $elm: HTMLElement) => { onCreated: (setting: SettingTabSectionItem, $elm: HTMLElement) => {
const $range = $elm.querySelector<HTMLInputElement>('input[type=range')!; const $range = $elm.querySelector<HTMLInputElement>('input[type=range')!;
window.addEventListener(BxEvent.SETTINGS_CHANGED, e => {
const { storageKey, settingKey, settingValue } = e as any;
if (storageKey !== StorageKey.GLOBAL || settingKey !== PrefKey.AUDIO_VOLUME) {
return;
}
$range.value = settingValue; EventBus.Script.on('settingChanged', payload => {
BxEvent.dispatch($range, 'input', { const { storageKey, settingKey, settingValue } = payload;
ignoreOnChange: true, if (storageKey === StorageKey.GLOBAL && settingKey === PrefKey.AUDIO_VOLUME) {
}); $range.value = settingValue;
BxEvent.dispatch($range, 'input', { ignoreOnChange: true });
}
}); });
}, },
}], }],

View File

@ -7,23 +7,6 @@ export namespace BxEvent {
export const JUMP_BACK_IN_READY = 'bx-jump-back-in-ready'; export const JUMP_BACK_IN_READY = 'bx-jump-back-in-ready';
export const POPSTATE = 'bx-popstate'; export const POPSTATE = 'bx-popstate';
export const TITLE_INFO_READY = 'bx-title-info-ready';
export const SETTINGS_CHANGED = 'bx-settings-changed';
export const STREAM_LOADING = 'bx-stream-loading';
export const STREAM_STARTING = 'bx-stream-starting';
export const STREAM_STARTED = 'bx-stream-started';
export const STREAM_PLAYING = 'bx-stream-playing';
export const STREAM_STOPPED = 'bx-stream-stopped';
export const STREAM_ERROR_PAGE = 'bx-stream-error-page';
export const STREAM_WEBRTC_CONNECTED = 'bx-stream-webrtc-connected';
export const STREAM_WEBRTC_DISCONNECTED = 'bx-stream-webrtc-disconnected';
export const MKB_UPDATED = 'bx-mkb-updated';
export const KEYBOARD_SHORTCUTS_UPDATED = 'bx-keyboard-shortcuts-updated';
// export const STREAM_EVENT_TARGET_READY = 'bx-stream-event-target-ready'; // export const STREAM_EVENT_TARGET_READY = 'bx-stream-event-target-ready';
export const STREAM_SESSION_READY = 'bx-stream-session-ready'; export const STREAM_SESSION_READY = 'bx-stream-session-ready';
@ -33,11 +16,7 @@ export namespace BxEvent {
export const REMOTE_PLAY_READY = 'bx-remote-play-ready'; export const REMOTE_PLAY_READY = 'bx-remote-play-ready';
export const REMOTE_PLAY_FAILED = 'bx-remote-play-failed'; export const REMOTE_PLAY_FAILED = 'bx-remote-play-failed';
export const XCLOUD_SERVERS_READY = 'bx-servers-ready';
export const XCLOUD_SERVERS_UNAVAILABLE = 'bx-servers-unavailable';
export const DATA_CHANNEL_CREATED = 'bx-data-channel-created'; export const DATA_CHANNEL_CREATED = 'bx-data-channel-created';
export const DEVICE_VIBRATION_CHANGED = 'bx-device-vibration-changed';
export const GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated'; export const GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated';
export const MICROPHONE_STATE_CHANGED = 'bx-microphone-state-changed'; export const MICROPHONE_STATE_CHANGED = 'bx-microphone-state-changed';
@ -51,8 +30,6 @@ export namespace BxEvent {
export const NAVIGATION_FOCUS_CHANGED = 'bx-nav-focus-changed'; export const NAVIGATION_FOCUS_CHANGED = 'bx-nav-focus-changed';
export const GH_PAGES_FORCE_NATIVE_MKB_UPDATED = 'bx-gh-pages-force-native-mkb-updated';
// xCloud Dialog events // xCloud Dialog events
export const XCLOUD_DIALOG_SHOWN = 'bx-xcloud-dialog-shown'; export const XCLOUD_DIALOG_SHOWN = 'bx-xcloud-dialog-shown';
export const XCLOUD_DIALOG_DISMISSED = 'bx-xcloud-dialog-dismissed'; export const XCLOUD_DIALOG_DISMISSED = 'bx-xcloud-dialog-dismissed';
@ -86,7 +63,7 @@ export namespace BxEvent {
target.dispatchEvent(event); target.dispatchEvent(event);
AppInterface && AppInterface.onEvent(eventName); AppInterface && AppInterface.onEvent(eventName);
BX_FLAGS.Debug && BxLogger.warning('BxEvent', 'dispatch', eventName, data) BX_FLAGS.Debug && BxLogger.warning('BxEvent', 'dispatch', eventName, data);
} }
} }

View File

@ -1,7 +1,6 @@
import { isFullVersion } from "@macros/build" with { type: "macro" }; import { isFullVersion } from "@macros/build" with { type: "macro" };
import { ControllerShortcut } from "@/modules/controller-shortcut"; import { ControllerShortcut } from "@/modules/controller-shortcut";
import { BxEvent } from "@utils/bx-event";
import { deepClone, STATES } from "@utils/global"; import { deepClone, STATES } from "@utils/global";
import { BxLogger } from "./bx-logger"; import { BxLogger } from "./bx-logger";
import { BX_FLAGS } from "./bx-flags"; import { BX_FLAGS } from "./bx-flags";
@ -12,6 +11,7 @@ import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
import { TouchController } from "@/modules/touch-controller"; import { TouchController } from "@/modules/touch-controller";
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values"; import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
import { Patcher, type PatchPage } from "@/modules/patcher/patcher"; import { Patcher, type PatchPage } from "@/modules/patcher/patcher";
import { EventBus } from "./event-bus";
export enum SupportedInputType { export enum SupportedInputType {
CONTROLLER = 'Controller', CONTROLLER = 'Controller',
@ -139,7 +139,7 @@ export const BxExposed = {
// Save this info in STATES // Save this info in STATES
STATES.currentStream.titleInfo = titleInfo; STATES.currentStream.titleInfo = titleInfo;
BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY); EventBus.Script.emit('titleInfoReady', {});
return titleInfo; return titleInfo;
}, },

81
src/utils/event-bus.ts Normal file
View File

@ -0,0 +1,81 @@
import type { PrefKey, StorageKey } from "@/enums/pref-keys";
import { BX_FLAGS } from "./bx-flags";
import { BxLogger } from "./bx-logger";
type EventCallback<T = any> = (payload: T) => void;
type ScriptEvents = {
xcloudServerReady: {};
xcloudServerUnavailable: {};
titleInfoReady: {};
settingChanged: {
storageKey: StorageKey;
settingKey: PrefKey;
settingValue: any;
};
mkbSettingUpdated: {};
keyboardShortcutsUpdated: {};
deviceVibrationUpdated: {};
// GH pages
listForcedNativeMkbUpdated: {};
};
type StreamEvents = {
stateLoading: {};
stateStarting: {};
statePlaying: { $video?: HTMLVideoElement };
stateStopped: {};
stateError: {};
};
export class EventBus<TEvents extends Record<string, any>> {
private listeners: Map<keyof TEvents, Set<EventCallback<any>>> = new Map();
static readonly Script = new EventBus<ScriptEvents>();
static readonly Stream = new EventBus<StreamEvents>();
on<K extends keyof TEvents>(event: K, callback: EventCallback<TEvents[K]>): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(callback);
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'on', event, callback);
}
off<K extends keyof TEvents>(event: K, callback: EventCallback<TEvents[K]> | null): void {
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'off', event, callback);
if (!callback) {
// Remove all listener callbacks
this.listeners.delete(event);
return;
}
const callbacks = this.listeners.get(event);
if (!callbacks) {
return;
}
callbacks.delete(callback);
if (callbacks.size === 0) {
this.listeners.delete(event);
}
}
offAll(): void {
this.listeners.clear();
}
emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void {
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'emit', event, payload);
const callbacks = this.listeners.get(event) || [];
for (const callback of callbacks) {
callback(payload);
}
}
}

View File

@ -1,7 +1,7 @@
import { StorageKey } from "@/enums/pref-keys"; import { StorageKey } from "@/enums/pref-keys";
import { NATIVE_FETCH } from "./bx-flags"; import { NATIVE_FETCH } from "./bx-flags";
import { BxLogger } from "./bx-logger"; import { BxLogger } from "./bx-logger";
import { BxEvent } from "./bx-event"; import { EventBus } from "./event-bus";
export type ForceNativeMkbResponse = { export type ForceNativeMkbResponse = {
@ -53,7 +53,7 @@ export class GhPagesUtils {
if (json.$schemaVersion === supportedSchema) { if (json.$schemaVersion === supportedSchema) {
// Save to storage // Save to storage
window.localStorage.setItem(key, JSON.stringify(json)); window.localStorage.setItem(key, JSON.stringify(json));
BxEvent.dispatch(window, BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED); EventBus.Script.emit('listForcedNativeMkbUpdated', {});
} }
}); });

View File

@ -3,14 +3,15 @@ import { LoadingScreen } from "@modules/loading-screen";
import { RemotePlayManager } from "@/modules/remote-play-manager"; import { RemotePlayManager } from "@/modules/remote-play-manager";
import { HeaderSection } from "@/modules/ui/header"; import { HeaderSection } from "@/modules/ui/header";
import { NavigationDialogManager } from "@/modules/ui/dialog/navigation-dialog"; import { NavigationDialogManager } from "@/modules/ui/dialog/navigation-dialog";
import { EventBus } from "./event-bus";
export function patchHistoryMethod(type: 'pushState' | 'replaceState') { export function patchHistoryMethod(type: 'pushState' | 'replaceState') {
const orig = window.history[type]; const orig = window.history[type];
return function(...args: any[]) { return function(...args: any[]) {
BxEvent.dispatch(window, BxEvent.POPSTATE, { BxEvent.dispatch(window, BxEvent.POPSTATE, {
arguments: args, arguments: args,
}); });
// @ts-ignore // @ts-ignore
return orig.apply(this, arguments); return orig.apply(this, arguments);
@ -38,5 +39,5 @@ export function onHistoryChanged(e: PopStateEvent) {
LoadingScreen.reset(); LoadingScreen.reset();
window.setTimeout(HeaderSection.watchHeader, 2000); window.setTimeout(HeaderSection.watchHeader, 2000);
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED); EventBus.Stream.emit('stateStopped', {});
} }

View File

@ -7,6 +7,7 @@ import { PrefKey } from "@/enums/pref-keys";
import { getPref, getPrefDefinition } from "./settings-storages/global-settings-storage"; import { getPref, getPrefDefinition } from "./settings-storages/global-settings-storage";
import { CodecProfile } from "@/enums/pref-values"; import { CodecProfile } from "@/enums/pref-values";
import type { SettingDefinition } from "@/types/setting-definition"; import type { SettingDefinition } from "@/types/setting-definition";
import { EventBus } from "./event-bus";
export function patchVideoApi() { export function patchVideoApi() {
const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.UI_SKIP_SPLASH_VIDEO); const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.UI_SKIP_SPLASH_VIDEO);
@ -27,9 +28,9 @@ export function patchVideoApi() {
} satisfies StreamPlayerOptions; } satisfies StreamPlayerOptions;
STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref(PrefKey.VIDEO_PLAYER_TYPE), playerOptions); STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref(PrefKey.VIDEO_PLAYER_TYPE), playerOptions);
BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, { EventBus.Stream.emit('statePlaying', {
$video: this, $video: this,
}); })
} }
const nativePlay = HTMLMediaElement.prototype.play; const nativePlay = HTMLMediaElement.prototype.play;

View File

@ -1,16 +1,16 @@
import type { PrefKey } from "@/enums/pref-keys"; import type { PrefKey, StorageKey } from "@/enums/pref-keys";
import type { NumberStepperParams, SettingAction, SettingDefinitions } from "@/types/setting-definition"; import type { NumberStepperParams, SettingAction, SettingDefinitions } from "@/types/setting-definition";
import { BxEvent } from "../bx-event";
import { t } from "../translation"; import { t } from "../translation";
import { SCRIPT_VARIANT } from "../global"; import { SCRIPT_VARIANT } from "../global";
import { EventBus } from "../event-bus";
export class BaseSettingsStore { export class BaseSettingsStore {
private storage: Storage; private storage: Storage;
private storageKey: string; private storageKey: StorageKey;
private _settings: object | null; private _settings: object | null;
private definitions: SettingDefinitions; private definitions: SettingDefinitions;
constructor(storageKey: string, definitions: SettingDefinitions) { constructor(storageKey: StorageKey, definitions: SettingDefinitions) {
this.storage = window.localStorage; this.storage = window.localStorage;
this.storageKey = storageKey; this.storageKey = storageKey;
@ -93,7 +93,7 @@ export class BaseSettingsStore {
this.settings[key] = this.validateValue('get', key, value); this.settings[key] = this.validateValue('get', key, value);
this.saveSettings(); this.saveSettings();
emitEvent && BxEvent.dispatch(window, BxEvent.SETTINGS_CHANGED, { emitEvent && EventBus.Script.emit('settingChanged', {
storageKey: this.storageKey, storageKey: this.storageKey,
settingKey: key, settingKey: key,
settingValue: value, settingValue: value,

View File

@ -12,7 +12,7 @@ import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerSty
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table"; import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table"; import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
import { GhPagesUtils } from "../gh-pages"; import { GhPagesUtils } from "../gh-pages";
import { BxEvent } from "../bx-event"; import { EventBus } from "../event-bus";
function getSupportedCodecProfiles() { function getSupportedCodecProfiles() {
@ -432,7 +432,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
if (!setting.unsupported) { if (!setting.unsupported) {
(setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList(true); (setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList(true);
window.addEventListener(BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED, e => { EventBus.Script.on('listForcedNativeMkbUpdated', () => {
(setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList(); (setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList();
}); });
} }

View File

@ -10,10 +10,10 @@ import { hasGamepad } from "./gamepad";
import { MkbMappingPresetsTable } from "./local-db/mkb-mapping-presets-table"; import { MkbMappingPresetsTable } from "./local-db/mkb-mapping-presets-table";
import type { GamepadKey } from "@/enums/gamepad"; import type { GamepadKey } from "@/enums/gamepad";
import { MkbPresetKey, MouseConstant } from "@/enums/mkb"; import { MkbPresetKey, MouseConstant } from "@/enums/mkb";
import { BxEvent } from "./bx-event";
import { KeyboardShortcutDefaultId, KeyboardShortcutsTable } from "./local-db/keyboard-shortcuts-table"; import { KeyboardShortcutDefaultId, KeyboardShortcutsTable } from "./local-db/keyboard-shortcuts-table";
import { ShortcutAction } from "@/enums/shortcut-actions"; import { ShortcutAction } from "@/enums/shortcut-actions";
import { KeyHelper } from "@/modules/mkb/key-helper"; import { KeyHelper } from "@/modules/mkb/key-helper";
import { EventBus } from "./event-bus";
export type StreamSettingsData = { export type StreamSettingsData = {
@ -110,7 +110,7 @@ export class StreamSettings {
} }
StreamSettings.settings.deviceVibrationIntensity = intensity; StreamSettings.settings.deviceVibrationIntensity = intensity;
BxEvent.dispatch(window, BxEvent.DEVICE_VIBRATION_CHANGED); EventBus.Script.emit('deviceVibrationUpdated', {});
} }
static async refreshMkbSettings() { static async refreshMkbSettings() {
@ -148,7 +148,7 @@ export class StreamSettings {
settings.mkbPreset = converted; settings.mkbPreset = converted;
setPref(PrefKey.MKB_P1_MAPPING_PRESET_ID, orgPreset.id); setPref(PrefKey.MKB_P1_MAPPING_PRESET_ID, orgPreset.id);
BxEvent.dispatch(window, BxEvent.MKB_UPDATED); EventBus.Script.emit('mkbSettingUpdated', {});
} }
static async refreshKeyboardShortcuts() { static async refreshKeyboardShortcuts() {
@ -159,7 +159,7 @@ export class StreamSettings {
settings.keyboardShortcuts = null; settings.keyboardShortcuts = null;
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId); setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId);
BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); EventBus.Script.emit('keyboardShortcutsUpdated', {});
return; return;
} }
@ -179,7 +179,7 @@ export class StreamSettings {
settings.keyboardShortcuts = converted; settings.keyboardShortcuts = converted;
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id); setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id);
BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED); EventBus.Script.emit('keyboardShortcutsUpdated', {});
} }
static async refreshAllSettings() { static async refreshAllSettings() {

View File

@ -1,10 +1,10 @@
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { BxEvent } from "./bx-event";
import { STATES } from "./global"; import { STATES } from "./global";
import { humanFileSize, secondsToHm } from "./html"; import { humanFileSize, secondsToHm } from "./html";
import { getPref } from "./settings-storages/global-settings-storage"; import { getPref } from "./settings-storages/global-settings-storage";
import { BxLogger } from "./bx-logger"; import { BxLogger } from "./bx-logger";
import { StreamStat } from "@/enums/pref-values"; import { StreamStat } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export type StreamStatGrade = '' | 'bad' | 'ok' | 'good'; export type StreamStatGrade = '' | 'bad' | 'ok' | 'good';
@ -310,9 +310,8 @@ export class StreamStatsCollector {
} }
static setupEvents() { static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, e => { EventBus.Stream.on('statePlaying', () => {
const statsCollector = StreamStatsCollector.getInstance(); StreamStatsCollector.getInstance().reset();
statsCollector.reset();
}); });
} }
} }

View File

@ -4,7 +4,6 @@ import { LoadingScreen } from "@modules/loading-screen";
import { RemotePlayManager } from "@/modules/remote-play-manager"; import { RemotePlayManager } from "@/modules/remote-play-manager";
import { StreamBadges } from "@modules/stream/stream-badges"; import { StreamBadges } from "@modules/stream/stream-badges";
import { TouchController } from "@modules/touch-controller"; import { TouchController } from "@modules/touch-controller";
import { BxEvent } from "./bx-event";
import { NATIVE_FETCH, BX_FLAGS } from "./bx-flags"; import { NATIVE_FETCH, BX_FLAGS } from "./bx-flags";
import { STATES } from "./global"; import { STATES } from "./global";
import { generateMsDeviceInfo, getOsNameFromResolution, patchIceCandidates } from "./network"; import { generateMsDeviceInfo, getOsNameFromResolution, patchIceCandidates } from "./network";
@ -13,6 +12,7 @@ import { BypassServerIps } from "@/enums/bypass-servers";
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "./settings-storages/global-settings-storage"; import { getPref } from "./settings-storages/global-settings-storage";
import { NativeMkbMode, StreamResolution, TouchControllerMode } from "@/enums/pref-values"; import { NativeMkbMode, StreamResolution, TouchControllerMode } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export class XcloudInterceptor { export class XcloudInterceptor {
private static readonly SERVER_EXTRA_INFO: Record<string, [string, ServerContinent]> = { private static readonly SERVER_EXTRA_INFO: Record<string, [string, ServerContinent]> = {
@ -52,7 +52,7 @@ export class XcloudInterceptor {
const response = await NATIVE_FETCH(request, init); const response = await NATIVE_FETCH(request, init);
if (response.status !== 200) { if (response.status !== 200) {
// Unsupported region // Unsupported region
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE); EventBus.Script.emit('xcloudServerUnavailable', {});
return response; return response;
} }
@ -89,7 +89,7 @@ export class XcloudInterceptor {
STATES.serverRegions[region.name] = Object.assign({}, region); STATES.serverRegions[region.name] = Object.assign({}, region);
} }
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_READY); EventBus.Script.emit('xcloudServerReady', {});
const preferredRegion = getPreferredServerRegion(); const preferredRegion = getPreferredServerRegion();
if (preferredRegion && preferredRegion in STATES.serverRegions) { if (preferredRegion && preferredRegion in STATES.serverRegions) {
@ -107,7 +107,7 @@ export class XcloudInterceptor {
} }
private static async handlePlay(request: RequestInfo | URL, init?: RequestInit) { private static async handlePlay(request: RequestInfo | URL, init?: RequestInit) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING); EventBus.Stream.emit('stateLoading', {});
const PREF_STREAM_TARGET_RESOLUTION = getPref<StreamResolution>(PrefKey.STREAM_RESOLUTION); const PREF_STREAM_TARGET_RESOLUTION = getPref<StreamResolution>(PrefKey.STREAM_RESOLUTION);
const PREF_STREAM_PREFERRED_LOCALE = getPref<StreamPreferredLocale>(PrefKey.STREAM_PREFERRED_LOCALE); const PREF_STREAM_PREFERRED_LOCALE = getPref<StreamPreferredLocale>(PrefKey.STREAM_PREFERRED_LOCALE);
@ -189,7 +189,7 @@ export class XcloudInterceptor {
return response; return response;
} }
BxEvent.dispatch(window, BxEvent.STREAM_STARTING); EventBus.Stream.emit('stateStarting', {});
const obj = JSON.parse(text); const obj = JSON.parse(text);
let overrides = JSON.parse(obj.clientStreamingConfigOverrides || '{}') || {}; let overrides = JSON.parse(obj.clientStreamingConfigOverrides || '{}') || {};

View File

@ -9,6 +9,7 @@ import { getPref } from "./settings-storages/global-settings-storage";
import type { RemotePlayConsoleAddresses } from "@/types/network"; import type { RemotePlayConsoleAddresses } from "@/types/network";
import { RemotePlayManager } from "@/modules/remote-play-manager"; import { RemotePlayManager } from "@/modules/remote-play-manager";
import { StreamResolution, TouchControllerMode } from "@/enums/pref-values"; import { StreamResolution, TouchControllerMode } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export class XhomeInterceptor { export class XhomeInterceptor {
private static consoleAddrs: RemotePlayConsoleAddresses = {}; private static consoleAddrs: RemotePlayConsoleAddresses = {};
@ -35,7 +36,7 @@ export class XhomeInterceptor {
} }
private static async handleConfiguration(request: Request | URL) { private static async handleConfiguration(request: Request | URL) {
BxEvent.dispatch(window, BxEvent.STREAM_STARTING); EventBus.Stream.emit('stateStarting', {});
const response = await NATIVE_FETCH(request); const response = await NATIVE_FETCH(request);
const obj = await response.clone().json(); const obj = await response.clone().json();
@ -124,7 +125,7 @@ export class XhomeInterceptor {
} }
private static async handlePlay(request: RequestInfo | URL) { private static async handlePlay(request: RequestInfo | URL) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING); EventBus.Stream.emit('stateLoading', {});
const clone = (request as Request).clone(); const clone = (request as Request).clone();
const body = await clone.json(); const body = await clone.json();

View File

@ -1,5 +1,4 @@
import type { NumberStepperParams } from "@/types/setting-definition"; import type { NumberStepperParams } from "@/types/setting-definition";
import { BxEvent } from "@/utils/bx-event";
import { CE, escapeCssSelector } from "@/utils/html"; import { CE, escapeCssSelector } from "@/utils/html";
import { setNearby } from "@/utils/navigation-utils"; import { setNearby } from "@/utils/navigation-utils";
import type { BxHtmlSettingElement } from "@/utils/setting-element"; import type { BxHtmlSettingElement } from "@/utils/setting-element";
@ -26,7 +25,6 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
private $btnDec!: HTMLButtonElement; private $btnDec!: HTMLButtonElement;
private $range!: HTMLInputElement | null; private $range!: HTMLInputElement | null;
onInput!: typeof BxNumberStepper['onInput'];
onRangeInput!: typeof BxNumberStepper['onRangeInput']; onRangeInput!: typeof BxNumberStepper['onRangeInput'];
onClick!: typeof BxNumberStepper['onClick']; onClick!: typeof BxNumberStepper['onClick'];
onPointerUp!: typeof BxNumberStepper['onPointerUp']; onPointerUp!: typeof BxNumberStepper['onPointerUp'];
@ -77,7 +75,6 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
self.$btnDec = $btnDec; self.$btnDec = $btnDec;
self.onChange = onChange; self.onChange = onChange;
self.onInput = BxNumberStepper.onInput.bind(self);
self.onRangeInput = BxNumberStepper.onRangeInput.bind(self); self.onRangeInput = BxNumberStepper.onRangeInput.bind(self);
self.onClick = BxNumberStepper.onClick.bind(self); self.onClick = BxNumberStepper.onClick.bind(self);
self.onPointerUp = BxNumberStepper.onPointerUp.bind(self); self.onPointerUp = BxNumberStepper.onPointerUp.bind(self);
@ -117,8 +114,7 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
self.$range = $range; self.$range = $range;
options.hideSlider && $range.classList.add('bx-gone'); options.hideSlider && $range.classList.add('bx-gone');
$range.addEventListener('input', self.onRangeInput); self.addEventListener('input', self.onRangeInput);
self.addEventListener('input', self.onInput);
self.appendChild($range); self.appendChild($range);
if (options.ticks || options.exactTicks) { if (options.ticks || options.exactTicks) {
@ -183,10 +179,6 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
return value; return value;
} }
private static onInput(this: BxNumberStepper, e: Event) {
BxEvent.dispatch(this.$range, 'input');
}
private static onRangeInput(this: BxNumberStepper, e: Event) { private static onRangeInput(this: BxNumberStepper, e: Event) {
let value = parseInt((e.target as HTMLInputElement).value); let value = parseInt((e.target as HTMLInputElement).value);
if (this.options.reverse) { if (this.options.reverse) {