Git游戏存档隔离脚本
// ==UserScript==
// @name Git游戏存档隔离脚本
// @version 0.1.3
// @description 隔离gityx不同游戏的存储内容,防止存储冲突造成坏档、丢档
// @author DreamNya
// @match *://*.github.io/*
// @match *://*.g8hh.com.cn/*
// @match *://*.g8hh.com/*
// @icon https://gityx.com/static/web/images/github.png
// @grant none
// @run-at document-start
// @license MIT
// @namespace https://scriptcat.org/zh-CN/users/55086
// ==/UserScript==
const prefix = getPrefix();
const prototype = Reflect.ownKeys(Storage.prototype);
const deepProp = ["getItem", "setItem", "removeItem"];
const deepProxy = {};
// 根据网址中游戏名计算前缀
function getPrefix() {
if (location.hostname.includes("github.io") || location.hostname.includes("yx.g8hh.com")) {
return location.pathname.split("/").filter((i) => i)[0] + "_";
}
if (location.hostname.includes("g8hh.com")) {
return location.hostname.split(".")[0] + "_";
}
return "";
}
deepProp.forEach((key) => {
deepProxy[key] = new Proxy(localStorage[key], {
// 拦截方法调用
apply: (target, thisArgument, args) => {
args[0] = prefix + args[0];
return Reflect.apply(target, thisArgument, args);
},
});
});
window.realLocalStorage = window.localStorage;
window.hookLocalStorage = new Proxy(window.localStorage, {
// 拦截不用方法而是直接访问的傻鸟
get: (target, property, receiver) => {
// 原型链上方法不劫持
if (!prototype.includes(property)) {
property = prefix + property;
}
// 特殊处理
if (property == "length") {
return Object.keys(window.hookLocalStorage).length;
}
if (property == "key") {
return (index) => Object.keys(window.hookLocalStorage)[index];
}
if (property == "clear") {
return () => Object.keys(window.hookLocalStorage).forEach((k) => window.hookLocalStorage.removeItem(k));
}
const ref = deepProp.includes(property)
? Reflect.get(deepProxy, property, receiver)
: Reflect.get(target, property, receiver);
// 方法拦截需要绑定this,否则抛出非法调用错误
return ref?.bind ? ref.bind(window.realLocalStorage) : ref;
},
set: (target, property, newValue, receiver) => {
if (!prototype.includes(property)) {
property = prefix + property;
}
return Reflect.set(target, property, newValue, receiver);
},
// 拦截遍历方法
ownKeys: (target) => {
const replacement = new RegExp("^" + prefix);
return Reflect.ownKeys(target)
.filter((key) => key.startsWith(prefix))
.map((key) => key.replace(replacement, ""));
},
// Object.keys() 通过ownKeys拦截 无法返回不存在的属性 需要指定属性描述符
getOwnPropertyDescriptor: (target, property) => {
return (
Reflect.getOwnPropertyDescriptor(target, property) ?? {
enumerable: true,
configurable: true,
writable: true,
}
);
},
has: (target, property) => {
if (!prototype.includes(property)) {
property = prefix + property;
}
return Reflect.has(target, property);
},
});
// Proxy 做不到的事来了
Object.defineProperty(window, "localStorage", {
get: () => window.hookLocalStorage,
});
console.log("%c Git游戏存档隔离脚本 加载成功\nprefix: " + prefix, "color:blue");