Use hash of client.js file for calculating patch's signature

This commit is contained in:
redphx 2024-11-01 07:16:11 +07:00
parent b2a2e4d27e
commit ec3daa09fd
2 changed files with 58 additions and 43 deletions

View File

@ -4527,46 +4527,50 @@ class Patcher {
} }
} }
class PatcherCache { class PatcherCache {
static #KEY_CACHE = "better_xcloud_patches_cache"; static KEY_CACHE = "better_xcloud_patches_cache";
static #KEY_SIGNATURE = "better_xcloud_patches_cache_signature"; static KEY_SIGNATURE = "better_xcloud_patches_cache_signature";
static #CACHE; static CACHE;
static #isInitialized = !1; static isInitialized = !1;
static #getSignature() { static getSignature() {
let scriptVersion = SCRIPT_VERSION, webVersion = document.querySelector("meta[name=gamepass-app-version]")?.content, patches = JSON.stringify(ALL_PATCHES); let scriptVersion = SCRIPT_VERSION, patches = JSON.stringify(ALL_PATCHES), webVersion = "", $link = document.querySelector('link[data-chunk="client"][href*="/client."]');
if ($link) {
let match = /\/client\.([^\.]+)\.js/.exec($link.href);
match && (webVersion = match[1]);
} else webVersion = document.querySelector("meta[name=gamepass-app-version]")?.content ?? "";
return hashCode(scriptVersion + webVersion + patches); return hashCode(scriptVersion + webVersion + patches);
} }
static clear() { static clear() {
window.localStorage.removeItem(PatcherCache.#KEY_CACHE), PatcherCache.#CACHE = {}; window.localStorage.removeItem(PatcherCache.KEY_CACHE), PatcherCache.CACHE = {};
} }
static checkSignature() { static checkSignature() {
let storedSig = window.localStorage.getItem(PatcherCache.#KEY_SIGNATURE) || 0, currentSig = PatcherCache.#getSignature(); let storedSig = window.localStorage.getItem(PatcherCache.KEY_SIGNATURE) || 0, currentSig = PatcherCache.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(PatcherCache.KEY_SIGNATURE, currentSig.toString()), PatcherCache.clear();
else BxLogger.info(LOG_TAG2, "Signature unchanged"); else BxLogger.info(LOG_TAG2, "Signature unchanged");
} }
static #cleanupPatches(patches) { static cleanupPatches(patches) {
return patches.filter((item2) => { return patches.filter((item2) => {
for (let id2 in PatcherCache.#CACHE) for (let id2 in PatcherCache.CACHE)
if (PatcherCache.#CACHE[id2].includes(item2)) return !1; if (PatcherCache.CACHE[id2].includes(item2)) return !1;
return !0; return !0;
}); });
} }
static getPatches(id2) { static getPatches(id2) {
return PatcherCache.#CACHE[id2]; return PatcherCache.CACHE[id2];
} }
static saveToCache(subCache) { static 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 = PatcherCache.CACHE[id2];
if (!data) PatcherCache.#CACHE[id2] = patchNames; if (!data) PatcherCache.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(PatcherCache.KEY_CACHE, JSON.stringify(PatcherCache.CACHE));
} }
static init() { static init() {
if (PatcherCache.#isInitialized) return; if (PatcherCache.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 (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);
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 = 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));
} }
} }
class FullscreenText { class FullscreenText {

View File

@ -1222,21 +1222,32 @@ export class Patcher {
} }
export class PatcherCache { export class PatcherCache {
static #KEY_CACHE = 'better_xcloud_patches_cache'; private static readonly KEY_CACHE = 'better_xcloud_patches_cache';
static #KEY_SIGNATURE = 'better_xcloud_patches_cache_signature'; private static readonly KEY_SIGNATURE = 'better_xcloud_patches_cache_signature';
static #CACHE: any; private static CACHE: any;
static #isInitialized = false; private static isInitialized = false;
/** /**
* Get patch's signature * Get patch's signature
*/ */
static #getSignature(): number { private static getSignature(): number {
const scriptVersion = SCRIPT_VERSION; const scriptVersion = SCRIPT_VERSION;
const webVersion = (document.querySelector<HTMLMetaElement>('meta[name=gamepass-app-version]'))?.content;
const patches = JSON.stringify(ALL_PATCHES); const patches = JSON.stringify(ALL_PATCHES);
// Get client.js's hash
let webVersion = '';
const $link = document.querySelector<HTMLLinkElement>('link[data-chunk="client"][href*="/client."]');
if ($link) {
const match = /\/client\.([^\.]+)\.js/.exec($link.href);
match && (webVersion = match[1]);
} else {
// Get version from <meta>
// Sometimes this value is missing
webVersion = (document.querySelector<HTMLMetaElement>('meta[name=gamepass-app-version]'))?.content ?? '';
}
// Calculate signature // Calculate signature
const sig = hashCode(scriptVersion + webVersion + patches) const sig = hashCode(scriptVersion + webVersion + patches)
return sig; return sig;
@ -1244,18 +1255,18 @@ export class PatcherCache {
static clear() { static clear() {
// Clear cache // Clear cache
window.localStorage.removeItem(PatcherCache.#KEY_CACHE); window.localStorage.removeItem(PatcherCache.KEY_CACHE);
PatcherCache.#CACHE = {}; PatcherCache.CACHE = {};
} }
static checkSignature() { static checkSignature() {
const storedSig = window.localStorage.getItem(PatcherCache.#KEY_SIGNATURE) || 0; const storedSig = window.localStorage.getItem(PatcherCache.KEY_SIGNATURE) || 0;
const currentSig = PatcherCache.#getSignature(); const currentSig = PatcherCache.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(PatcherCache.KEY_SIGNATURE, currentSig.toString());
PatcherCache.clear(); PatcherCache.clear();
} else { } else {
@ -1263,10 +1274,10 @@ export class PatcherCache {
} }
} }
static #cleanupPatches(patches: PatchArray): PatchArray { private static cleanupPatches(patches: PatchArray): PatchArray {
return patches.filter(item => { return patches.filter(item => {
for (const id in PatcherCache.#CACHE) { for (const id in PatcherCache.CACHE) {
const cached = PatcherCache.#CACHE[id]; const cached = PatcherCache.CACHE[id];
if (cached.includes(item)) { if (cached.includes(item)) {
return false; return false;
@ -1278,16 +1289,16 @@ export class PatcherCache {
} }
static getPatches(id: string): PatchArray { static getPatches(id: string): PatchArray {
return PatcherCache.#CACHE[id]; return PatcherCache.CACHE[id];
} }
static saveToCache(subCache: Record<string, PatchArray>) { static 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 = PatcherCache.CACHE[id];
if (!data) { if (!data) {
PatcherCache.#CACHE[id] = patchNames; PatcherCache.CACHE[id] = patchNames;
} else { } else {
for (const patchName of patchNames) { for (const patchName of patchNames) {
if (!data.includes(patchName)) { if (!data.includes(patchName)) {
@ -1298,20 +1309,20 @@ export class PatcherCache {
} }
// Save to storage // Save to storage
window.localStorage.setItem(PatcherCache.#KEY_CACHE, JSON.stringify(PatcherCache.#CACHE)); window.localStorage.setItem(PatcherCache.KEY_CACHE, JSON.stringify(PatcherCache.CACHE));
} }
static init() { static init() {
if (PatcherCache.#isInitialized) { if (PatcherCache.isInitialized) {
return; return;
} }
PatcherCache.#isInitialized = true; PatcherCache.isInitialized = true;
PatcherCache.checkSignature(); PatcherCache.checkSignature();
// Read cache from storage // Read cache from storage
PatcherCache.#CACHE = JSON.parse(window.localStorage.getItem(PatcherCache.#KEY_CACHE) || '{}'); PatcherCache.CACHE = JSON.parse(window.localStorage.getItem(PatcherCache.KEY_CACHE) || '{}');
BxLogger.info(LOG_TAG, PatcherCache.#CACHE); BxLogger.info(LOG_TAG, PatcherCache.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);
@ -1320,8 +1331,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 = PatcherCache.cleanupPatches(PATCH_ORDERS);
PLAYING_PATCH_ORDERS = PatcherCache.#cleanupPatches(PLAYING_PATCH_ORDERS); PLAYING_PATCH_ORDERS = PatcherCache.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));