Migrate PatcherCache to singleton class

This commit is contained in:
redphx 2024-11-01 07:22:21 +07:00
parent ec3daa09fd
commit b090d325ae
3 changed files with 68 additions and 62 deletions

View File

@ -4489,7 +4489,7 @@ class Patcher {
if (arguments[1] === 0 || typeof arguments[1] === "function") valid = !0; if (arguments[1] === 0 || typeof arguments[1] === "function") valid = !0;
} }
if (!valid) return nativeBind.apply(this, arguments); if (!valid) return nativeBind.apply(this, arguments);
if (PatcherCache.init(), typeof arguments[1] === "function") BxLogger.info(LOG_TAG2, "Restored Function.prototype.bind()"), Function.prototype.bind = nativeBind; if (PatcherCache.getInstance().init(), typeof arguments[1] === "function") BxLogger.info(LOG_TAG2, "Restored Function.prototype.bind()"), Function.prototype.bind = nativeBind;
let orgFunc = this, newFunc = (a, item2) => { let orgFunc = this, newFunc = (a, item2) => {
Patcher.patch(item2), orgFunc(a, item2); Patcher.patch(item2), orgFunc(a, item2);
}; };
@ -4497,10 +4497,10 @@ class Patcher {
}; };
} }
static patch(item) { static patch(item) {
let patchesToCheck, appliedPatches, patchesMap = {}; let patchesToCheck, appliedPatches, patchesMap = {}, patcherCache = PatcherCache.getInstance();
for (let id in item[1]) { for (let id in item[1]) {
appliedPatches = []; appliedPatches = [];
let cachedPatches = PatcherCache.getPatches(id); let cachedPatches = patcherCache.getPatches(id);
if (cachedPatches) patchesToCheck = cachedPatches.slice(0), patchesToCheck.push(...PATCH_ORDERS); if (cachedPatches) patchesToCheck = cachedPatches.slice(0), patchesToCheck.push(...PATCH_ORDERS);
else patchesToCheck = PATCH_ORDERS.slice(0); else patchesToCheck = PATCH_ORDERS.slice(0);
if (!patchesToCheck.length) continue; if (!patchesToCheck.length) continue;
@ -4520,18 +4520,20 @@ class Patcher {
} }
if (appliedPatches.length) patchesMap[id] = appliedPatches; if (appliedPatches.length) patchesMap[id] = appliedPatches;
} }
if (Object.keys(patchesMap).length) PatcherCache.saveToCache(patchesMap); if (Object.keys(patchesMap).length) patcherCache.saveToCache(patchesMap);
} }
static init() { static init() {
Patcher.#patchFunctionBind(); Patcher.#patchFunctionBind();
} }
} }
class PatcherCache { class PatcherCache {
static KEY_CACHE = "better_xcloud_patches_cache"; static instance;
static KEY_SIGNATURE = "better_xcloud_patches_cache_signature"; static getInstance = () => PatcherCache.instance ?? (PatcherCache.instance = new PatcherCache);
static CACHE; KEY_CACHE = "better_xcloud_patches_cache";
static isInitialized = !1; KEY_SIGNATURE = "better_xcloud_patches_cache_signature";
static getSignature() { CACHE;
isInitialized = !1;
getSignature() {
let scriptVersion = SCRIPT_VERSION, patches = JSON.stringify(ALL_PATCHES), webVersion = "", $link = document.querySelector('link[data-chunk="client"][href*="/client."]'); let scriptVersion = SCRIPT_VERSION, patches = JSON.stringify(ALL_PATCHES), webVersion = "", $link = document.querySelector('link[data-chunk="client"][href*="/client."]');
if ($link) { if ($link) {
let match = /\/client\.([^\.]+)\.js/.exec($link.href); let match = /\/client\.([^\.]+)\.js/.exec($link.href);
@ -4539,38 +4541,38 @@ class PatcherCache {
} else webVersion = document.querySelector("meta[name=gamepass-app-version]")?.content ?? ""; } else webVersion = document.querySelector("meta[name=gamepass-app-version]")?.content ?? "";
return hashCode(scriptVersion + webVersion + patches); return hashCode(scriptVersion + webVersion + patches);
} }
static clear() { clear() {
window.localStorage.removeItem(PatcherCache.KEY_CACHE), PatcherCache.CACHE = {}; window.localStorage.removeItem(this.KEY_CACHE), this.CACHE = {};
} }
static checkSignature() { checkSignature() {
let storedSig = window.localStorage.getItem(PatcherCache.KEY_SIGNATURE) || 0, currentSig = PatcherCache.getSignature(); let storedSig = window.localStorage.getItem(this.KEY_SIGNATURE) || 0, currentSig = this.getSignature();
if (currentSig !== parseInt(storedSig)) BxLogger.warning(LOG_TAG2, "Signature changed"), window.localStorage.setItem(PatcherCache.KEY_SIGNATURE, currentSig.toString()), PatcherCache.clear(); if (currentSig !== parseInt(storedSig)) BxLogger.warning(LOG_TAG2, "Signature changed"), window.localStorage.setItem(this.KEY_SIGNATURE, currentSig.toString()), this.clear();
else BxLogger.info(LOG_TAG2, "Signature unchanged"); else BxLogger.info(LOG_TAG2, "Signature unchanged");
} }
static cleanupPatches(patches) { cleanupPatches(patches) {
return patches.filter((item2) => { return patches.filter((item2) => {
for (let id2 in PatcherCache.CACHE) for (let id2 in this.CACHE)
if (PatcherCache.CACHE[id2].includes(item2)) return !1; if (this.CACHE[id2].includes(item2)) return !1;
return !0; return !0;
}); });
} }
static getPatches(id2) { getPatches(id2) {
return PatcherCache.CACHE[id2]; return this.CACHE[id2];
} }
static saveToCache(subCache) { saveToCache(subCache) {
for (let id2 in subCache) { for (let id2 in subCache) {
let patchNames = subCache[id2], data = PatcherCache.CACHE[id2]; let patchNames = subCache[id2], data = this.CACHE[id2];
if (!data) PatcherCache.CACHE[id2] = patchNames; if (!data) this.CACHE[id2] = patchNames;
else for (let patchName of patchNames) else for (let patchName of patchNames)
if (!data.includes(patchName)) data.push(patchName); if (!data.includes(patchName)) data.push(patchName);
} }
window.localStorage.setItem(PatcherCache.KEY_CACHE, JSON.stringify(PatcherCache.CACHE)); window.localStorage.setItem(this.KEY_CACHE, JSON.stringify(this.CACHE));
} }
static init() { init() {
if (PatcherCache.isInitialized) return; if (this.isInitialized) return;
if (PatcherCache.isInitialized = !0, PatcherCache.checkSignature(), PatcherCache.CACHE = JSON.parse(window.localStorage.getItem(PatcherCache.KEY_CACHE) || "{}"), BxLogger.info(LOG_TAG2, PatcherCache.CACHE), window.location.pathname.includes("/play/")) PATCH_ORDERS.push(...PLAYING_PATCH_ORDERS); if (this.isInitialized = !0, this.checkSignature(), this.CACHE = JSON.parse(window.localStorage.getItem(this.KEY_CACHE) || "{}"), BxLogger.info(LOG_TAG2, this.CACHE), window.location.pathname.includes("/play/")) PATCH_ORDERS.push(...PLAYING_PATCH_ORDERS);
else PATCH_ORDERS.push(ENDING_CHUNKS_PATCH_NAME); else PATCH_ORDERS.push(ENDING_CHUNKS_PATCH_NAME);
PATCH_ORDERS = PatcherCache.cleanupPatches(PATCH_ORDERS), PLAYING_PATCH_ORDERS = PatcherCache.cleanupPatches(PLAYING_PATCH_ORDERS), BxLogger.info(LOG_TAG2, PATCH_ORDERS.slice(0)), BxLogger.info(LOG_TAG2, PLAYING_PATCH_ORDERS.slice(0)); PATCH_ORDERS = this.cleanupPatches(PATCH_ORDERS), PLAYING_PATCH_ORDERS = this.cleanupPatches(PLAYING_PATCH_ORDERS), BxLogger.info(LOG_TAG2, PATCH_ORDERS.slice(0)), BxLogger.info(LOG_TAG2, PLAYING_PATCH_ORDERS.slice(0));
} }
} }
class FullscreenText { class FullscreenText {
@ -5254,7 +5256,7 @@ class SettingsNavigationDialog extends NavigationDialog {
return $svg.dataset.group = settingTab.group, $svg.tabIndex = 0, settingTab.lazyContent && ($svg.dataset.lazy = settingTab.lazyContent.toString()), $svg.addEventListener("click", this.onTabClicked.bind(this)), $svg; return $svg.dataset.group = settingTab.group, $svg.tabIndex = 0, settingTab.lazyContent && ($svg.dataset.lazy = settingTab.lazyContent.toString()), $svg.addEventListener("click", this.onTabClicked.bind(this)), $svg;
} }
onGlobalSettingChanged(e) { onGlobalSettingChanged(e) {
PatcherCache.clear(), this.$btnReload.classList.add("bx-danger"), this.$noteGlobalReload.classList.add("bx-gone"), this.$btnGlobalReload.classList.remove("bx-gone"), this.$btnGlobalReload.classList.add("bx-danger"); PatcherCache.getInstance().clear(), this.$btnReload.classList.add("bx-danger"), this.$noteGlobalReload.classList.add("bx-gone"), this.$btnGlobalReload.classList.remove("bx-gone"), this.$btnGlobalReload.classList.add("bx-danger");
} }
renderServerSetting(setting) { renderServerSetting(setting) {
let selectedValue = getPref("server_region"), continents = { let selectedValue = getPref("server_region"), continents = {

View File

@ -1116,7 +1116,7 @@ export class Patcher {
return nativeBind.apply(this, arguments); return nativeBind.apply(this, arguments);
} }
PatcherCache.init(); PatcherCache.getInstance().init();
if (typeof arguments[1] === 'function') { if (typeof arguments[1] === 'function') {
BxLogger.info(LOG_TAG, 'Restored Function.prototype.bind()'); BxLogger.info(LOG_TAG, 'Restored Function.prototype.bind()');
@ -1141,11 +1141,12 @@ export class Patcher {
let appliedPatches: PatchArray; let appliedPatches: PatchArray;
const patchesMap: Record<string, PatchArray> = {}; const patchesMap: Record<string, PatchArray> = {};
const patcherCache = PatcherCache.getInstance();
for (let id in item[1]) { for (let id in item[1]) {
appliedPatches = []; appliedPatches = [];
const cachedPatches = PatcherCache.getPatches(id); const cachedPatches = patcherCache.getPatches(id);
if (cachedPatches) { if (cachedPatches) {
patchesToCheck = cachedPatches.slice(0); patchesToCheck = cachedPatches.slice(0);
patchesToCheck.push(...PATCH_ORDERS); patchesToCheck.push(...PATCH_ORDERS);
@ -1212,7 +1213,7 @@ export class Patcher {
} }
if (Object.keys(patchesMap).length) { if (Object.keys(patchesMap).length) {
PatcherCache.saveToCache(patchesMap); patcherCache.saveToCache(patchesMap);
} }
} }
@ -1222,17 +1223,20 @@ export class Patcher {
} }
export class PatcherCache { export class PatcherCache {
private static readonly KEY_CACHE = 'better_xcloud_patches_cache'; private static instance: PatcherCache;
private static readonly KEY_SIGNATURE = 'better_xcloud_patches_cache_signature'; public static getInstance = () => PatcherCache.instance ?? (PatcherCache.instance = new PatcherCache());
private static CACHE: any; private readonly KEY_CACHE = 'better_xcloud_patches_cache';
private readonly KEY_SIGNATURE = 'better_xcloud_patches_cache_signature';
private static isInitialized = false; private CACHE: any;
private isInitialized = false;
/** /**
* Get patch's signature * Get patch's signature
*/ */
private static getSignature(): number { private getSignature(): number {
const scriptVersion = SCRIPT_VERSION; const scriptVersion = SCRIPT_VERSION;
const patches = JSON.stringify(ALL_PATCHES); const patches = JSON.stringify(ALL_PATCHES);
@ -1253,31 +1257,31 @@ export class PatcherCache {
return sig; return sig;
} }
static clear() { clear() {
// Clear cache // Clear cache
window.localStorage.removeItem(PatcherCache.KEY_CACHE); window.localStorage.removeItem(this.KEY_CACHE);
PatcherCache.CACHE = {}; this.CACHE = {};
} }
static checkSignature() { private checkSignature() {
const storedSig = window.localStorage.getItem(PatcherCache.KEY_SIGNATURE) || 0; const storedSig = window.localStorage.getItem(this.KEY_SIGNATURE) || 0;
const currentSig = PatcherCache.getSignature(); const currentSig = this.getSignature();
if (currentSig !== parseInt(storedSig as string)) { if (currentSig !== parseInt(storedSig as string)) {
// Save new signature // Save new signature
BxLogger.warning(LOG_TAG, 'Signature changed'); BxLogger.warning(LOG_TAG, 'Signature changed');
window.localStorage.setItem(PatcherCache.KEY_SIGNATURE, currentSig.toString()); window.localStorage.setItem(this.KEY_SIGNATURE, currentSig.toString());
PatcherCache.clear(); this.clear();
} else { } else {
BxLogger.info(LOG_TAG, 'Signature unchanged'); BxLogger.info(LOG_TAG, 'Signature unchanged');
} }
} }
private static cleanupPatches(patches: PatchArray): PatchArray { private cleanupPatches(patches: PatchArray): PatchArray {
return patches.filter(item => { return patches.filter(item => {
for (const id in PatcherCache.CACHE) { for (const id in this.CACHE) {
const cached = PatcherCache.CACHE[id]; const cached = this.CACHE[id];
if (cached.includes(item)) { if (cached.includes(item)) {
return false; return false;
@ -1288,17 +1292,17 @@ export class PatcherCache {
}); });
} }
static getPatches(id: string): PatchArray { getPatches(id: string): PatchArray {
return PatcherCache.CACHE[id]; return this.CACHE[id];
} }
static saveToCache(subCache: Record<string, PatchArray>) { saveToCache(subCache: Record<string, PatchArray>) {
for (const id in subCache) { for (const id in subCache) {
const patchNames = subCache[id]; const patchNames = subCache[id];
let data = PatcherCache.CACHE[id]; let data = this.CACHE[id];
if (!data) { if (!data) {
PatcherCache.CACHE[id] = patchNames; this.CACHE[id] = patchNames;
} else { } else {
for (const patchName of patchNames) { for (const patchName of patchNames) {
if (!data.includes(patchName)) { if (!data.includes(patchName)) {
@ -1309,20 +1313,20 @@ export class PatcherCache {
} }
// Save to storage // Save to storage
window.localStorage.setItem(PatcherCache.KEY_CACHE, JSON.stringify(PatcherCache.CACHE)); window.localStorage.setItem(this.KEY_CACHE, JSON.stringify(this.CACHE));
} }
static init() { init() {
if (PatcherCache.isInitialized) { if (this.isInitialized) {
return; return;
} }
PatcherCache.isInitialized = true; this.isInitialized = true;
PatcherCache.checkSignature(); this.checkSignature();
// Read cache from storage // Read cache from storage
PatcherCache.CACHE = JSON.parse(window.localStorage.getItem(PatcherCache.KEY_CACHE) || '{}'); this.CACHE = JSON.parse(window.localStorage.getItem(this.KEY_CACHE) || '{}');
BxLogger.info(LOG_TAG, PatcherCache.CACHE); BxLogger.info(LOG_TAG, this.CACHE);
if (window.location.pathname.includes('/play/')) { if (window.location.pathname.includes('/play/')) {
PATCH_ORDERS.push(...PLAYING_PATCH_ORDERS); PATCH_ORDERS.push(...PLAYING_PATCH_ORDERS);
@ -1331,8 +1335,8 @@ export class PatcherCache {
} }
// Remove cached patches from PATCH_ORDERS & PLAYING_PATCH_ORDERS // Remove cached patches from PATCH_ORDERS & PLAYING_PATCH_ORDERS
PATCH_ORDERS = PatcherCache.cleanupPatches(PATCH_ORDERS); PATCH_ORDERS = this.cleanupPatches(PATCH_ORDERS);
PLAYING_PATCH_ORDERS = PatcherCache.cleanupPatches(PLAYING_PATCH_ORDERS); PLAYING_PATCH_ORDERS = this.cleanupPatches(PLAYING_PATCH_ORDERS);
BxLogger.info(LOG_TAG, PATCH_ORDERS.slice(0)); BxLogger.info(LOG_TAG, PATCH_ORDERS.slice(0));
BxLogger.info(LOG_TAG, PLAYING_PATCH_ORDERS.slice(0)); BxLogger.info(LOG_TAG, PLAYING_PATCH_ORDERS.slice(0));

View File

@ -1034,7 +1034,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
private onGlobalSettingChanged(e: Event) { private onGlobalSettingChanged(e: Event) {
// Clear PatcherCache; // Clear PatcherCache;
isFullVersion() && PatcherCache.clear(); isFullVersion() && PatcherCache.getInstance().clear();
this.$btnReload.classList.add('bx-danger'); this.$btnReload.classList.add('bx-danger');